| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "common/annotation_reader.h" |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/log/check.h" |
| #include "absl/strings/string_view.h" |
| #include "common/status_test_matchers.h" |
| #include "common/string_view_conversion.h" |
| #include "clang/Testing/TestAST.h" |
| |
| namespace crubit { |
| namespace { |
| |
| using testing::Eq; |
| using testing::HasSubstr; |
| using testing::Ne; |
| |
| template <class T> |
| T& LookupDecl(clang::ASTContext& context, absl::string_view name) { |
| clang::DeclContextLookupResult result = |
| context.getTranslationUnitDecl()->lookup( |
| &context.Idents.get(StringRefFromStringView(name))); |
| CHECK(result.isSingleResult()); |
| return *cast<T>(result.front()); |
| } |
| |
| TEST(GetAnnotateAttrArgsTest, Success) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo")]] int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| EXPECT_THAT(GetAnnotateAttrArgs(var, "foo"), IsOkAndHolds(Ne(std::nullopt))); |
| EXPECT_THAT(GetAnnotateAttrArgs(var, "bar"), IsOkAndHolds(Eq(std::nullopt))); |
| } |
| |
| TEST(GetAnnotateAttrArgsTest, FailureDoubleAnnotation) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo")]] [[clang::annotate("foo")]] int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| EXPECT_THAT( |
| GetAnnotateAttrArgs(var, "foo"), |
| StatusIs( |
| absl::StatusCode::kInvalidArgument, |
| HasSubstr( |
| "Only one `foo` annotation may be placed on a declaration."))); |
| } |
| |
| TEST(AnnotationReaderTest, GetAnnotateAttrFailureArgNotIntegralOrString) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo", 1.0)]] extern int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT(GetAnnotateAttrArgs(var, "foo"), |
| StatusIs(absl::StatusCode::kInvalidArgument, |
| HasSubstr("Arguments of `foo` annotation must be of " |
| "integral type or string literals"))); |
| } |
| |
| TEST(AnnotationReaderTest, GetAnnotateAttrSuccessConsistentAnnotations) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo", "arg1", 1)]] extern int i; |
| [[clang::annotate("foo", "arg1", 1)]] extern int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT(GetAnnotateAttrArgs(var, "foo"), IsOkAndHolds(Ne(std::nullopt))); |
| } |
| |
| TEST(AnnotationReaderTest, GetAnnotateAttrFailureConflictingIntArgs) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo", 1)]] extern int i; |
| [[clang::annotate("foo", 2)]] extern int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT( |
| GetAnnotateAttrArgs(var, "foo"), |
| StatusIs( |
| absl::StatusCode::kInvalidArgument, |
| HasSubstr( |
| "Different declarations have inconsistent `foo` annotations."))); |
| } |
| |
| TEST(AnnotationReaderTest, GetAnnotateAttrFailureConflictingStringArgs) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo", "1")]] extern int i; |
| [[clang::annotate("foo", "2")]] extern int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT( |
| GetAnnotateAttrArgs(var, "foo"), |
| StatusIs( |
| absl::StatusCode::kInvalidArgument, |
| HasSubstr( |
| "Different declarations have inconsistent `foo` annotations."))); |
| } |
| |
| TEST(AnnotationReaderTest, GetAnnotateAttrFailureConflictingArgCounts) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo")]] extern int i; |
| [[clang::annotate("foo", 1)]] extern int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT( |
| GetAnnotateAttrArgs(var, "foo"), |
| StatusIs( |
| absl::StatusCode::kInvalidArgument, |
| HasSubstr( |
| "Different declarations have inconsistent `foo` annotations."))); |
| } |
| |
| TEST(AnnotationReaderTest, |
| GetAnnotateAttrSuccessAnnotationMissingFromDefinition) { |
| clang::TestAST ast(R"cc( |
| [[clang::annotate("foo")]] extern int i; |
| int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT(GetAnnotateAttrArgs(var, "foo"), IsOkAndHolds(Ne(std::nullopt))); |
| } |
| |
| TEST(AnnotationReaderTest, |
| GetAnnotateAttrSuccessAnnotationMissingFromForwardDeclaration) { |
| clang::TestAST ast(R"cc( |
| extern int i; |
| [[clang::annotate("foo")]] int i; |
| )cc"); |
| |
| auto& var = LookupDecl<clang::VarDecl>(ast.context(), "i"); |
| |
| ASSERT_THAT(GetAnnotateAttrArgs(var, "foo"), IsOkAndHolds(Ne(std::nullopt))); |
| } |
| |
| } // namespace |
| } // namespace crubit |