//===- unittests/AST/AttrTests.cpp --- Attribute tests --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/AST/Attr.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/AttrKinds.h" #include "clang/Tooling/Tooling.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace clang; namespace { using clang::ast_matchers::constantExpr; using clang::ast_matchers::equals; using clang::ast_matchers::functionDecl; using clang::ast_matchers::has; using clang::ast_matchers::hasDescendant; using clang::ast_matchers::hasName; using clang::ast_matchers::integerLiteral; using clang::ast_matchers::match; using clang::ast_matchers::selectFirst; using clang::ast_matchers::stringLiteral; using clang::ast_matchers::varDecl; using clang::tooling::buildASTFromCode; using clang::tooling::buildASTFromCodeWithArgs; TEST(Attr, Doc) { EXPECT_THAT(Attr::getDocumentation(attr::Used).str(), testing::HasSubstr("The compiler must emit the definition even " "if it appears to be unused")); } const FunctionDecl *getFunctionNode(ASTUnit *AST, const std::string &Name) { auto Result = match(functionDecl(hasName(Name)).bind("fn"), AST->getASTContext()); EXPECT_EQ(Result.size(), 1u); return Result[0].getNodeAs("fn"); } const VarDecl *getVariableNode(ASTUnit *AST, const std::string &Name) { auto Result = match(varDecl(hasName(Name)).bind("var"), AST->getASTContext()); EXPECT_EQ(Result.size(), 1u); return Result[0].getNodeAs("var"); } template void AssertAnnotatedAs(TypeLoc TL, llvm::StringRef annotation, ModifiedTypeLoc &ModifiedTL, const AnnotateTypeAttr **AnnotateOut = nullptr) { const auto AttributedTL = TL.getAs(); ASSERT_FALSE(AttributedTL.isNull()); ModifiedTL = AttributedTL.getModifiedLoc().getAs(); ASSERT_TRUE(ModifiedTL); ASSERT_NE(AttributedTL.getAttr(), nullptr); const auto *Annotate = dyn_cast(AttributedTL.getAttr()); ASSERT_NE(Annotate, nullptr); EXPECT_EQ(Annotate->getAnnotation(), annotation); if (AnnotateOut) { *AnnotateOut = Annotate; } } TEST(Attr, AnnotateType) { // Test that the AnnotateType attribute shows up in the right places and that // it stores its arguments correctly. auto AST = buildASTFromCode(R"cpp( void f(int* [[clang::annotate_type("foo", "arg1", 2)]] *, int [[clang::annotate_type("bar")]]); int [[clang::annotate_type("int")]] * [[clang::annotate_type("ptr")]] array[10] [[clang::annotate_type("arr")]]; void (* [[clang::annotate_type("funcptr")]] fp)(void); struct S { int mem; }; int [[clang::annotate_type("int")]] S::* [[clang::annotate_type("ptr_to_mem")]] ptr_to_member = &S::mem; )cpp"); { const FunctionDecl *Func = getFunctionNode(AST.get(), "f"); // First parameter. const auto PointerTL = Func->getParamDecl(0) ->getTypeSourceInfo() ->getTypeLoc() .getAs(); ASSERT_FALSE(PointerTL.isNull()); PointerTypeLoc PointerPointerTL; const AnnotateTypeAttr *Annotate; AssertAnnotatedAs(PointerTL.getPointeeLoc(), "foo", PointerPointerTL, &Annotate); EXPECT_EQ(Annotate->args_size(), 2u); const auto *StringLit = selectFirst( "str", match(constantExpr(hasDescendant(stringLiteral().bind("str"))), *Annotate->args_begin()[0], AST->getASTContext())); ASSERT_NE(StringLit, nullptr); EXPECT_EQ(StringLit->getString(), "arg1"); EXPECT_EQ(match(constantExpr(has(integerLiteral(equals(2u)).bind("int"))), *Annotate->args_begin()[1], AST->getASTContext()) .size(), 1u); // Second parameter. BuiltinTypeLoc IntTL; AssertAnnotatedAs(Func->getParamDecl(1)->getTypeSourceInfo()->getTypeLoc(), "bar", IntTL); EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy); } { const VarDecl *Var = getVariableNode(AST.get(), "array"); ArrayTypeLoc ArrayTL; AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "arr", ArrayTL); PointerTypeLoc PointerTL; AssertAnnotatedAs(ArrayTL.getElementLoc(), "ptr", PointerTL); BuiltinTypeLoc IntTL; AssertAnnotatedAs(PointerTL.getPointeeLoc(), "int", IntTL); EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy); } { const VarDecl *Var = getVariableNode(AST.get(), "fp"); PointerTypeLoc PointerTL; AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "funcptr", PointerTL); ASSERT_TRUE( PointerTL.getPointeeLoc().IgnoreParens().getAs()); } { const VarDecl *Var = getVariableNode(AST.get(), "ptr_to_member"); MemberPointerTypeLoc MemberPointerTL; AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "ptr_to_mem", MemberPointerTL); BuiltinTypeLoc IntTL; AssertAnnotatedAs(MemberPointerTL.getPointeeLoc(), "int", IntTL); EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy); } // Test type annotation on an `__auto_type` type in C mode. AST = buildASTFromCodeWithArgs(R"c( __auto_type [[clang::annotate_type("auto")]] auto_var = 1; )c", {}, "input.c"); { const VarDecl *Var = getVariableNode(AST.get(), "auto_var"); AutoTypeLoc AutoTL; AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "auto", AutoTL); } } TEST(Attr, RegularKeywordAttribute) { auto AST = clang::tooling::buildASTFromCode(""); auto &Ctx = AST->getASTContext(); auto Funcref = clang::WebAssemblyFuncrefAttr::CreateImplicit(Ctx); EXPECT_EQ(Funcref->getSyntax(), clang::AttributeCommonInfo::AS_Keyword); ASSERT_FALSE(Funcref->isRegularKeywordAttribute()); auto Streaming = clang::ArmStreamingAttr::CreateImplicit(Ctx); EXPECT_EQ(Streaming->getSyntax(), clang::AttributeCommonInfo::AS_Keyword); ASSERT_TRUE(Streaming->isRegularKeywordAttribute()); } } // namespace