blob: bc5f0c2c736e7bed51c3f58d20cdf78aedee7744 [file] [log] [blame] [edit]
// 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