xref: /llvm-project/clang/unittests/AST/AttrTest.cpp (revision 874217f99b99ab3c9026dc3b7bd84cd2beebde6e)
1 //===- unittests/AST/AttrTests.cpp --- Attribute tests --------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/AST/Attr.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Basic/AttrKinds.h"
13 #include "clang/Tooling/Tooling.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace clang;
18 
19 namespace {
20 
21 using clang::ast_matchers::constantExpr;
22 using clang::ast_matchers::equals;
23 using clang::ast_matchers::functionDecl;
24 using clang::ast_matchers::has;
25 using clang::ast_matchers::hasDescendant;
26 using clang::ast_matchers::hasName;
27 using clang::ast_matchers::integerLiteral;
28 using clang::ast_matchers::match;
29 using clang::ast_matchers::selectFirst;
30 using clang::ast_matchers::stringLiteral;
31 using clang::ast_matchers::varDecl;
32 using clang::tooling::buildASTFromCode;
33 using clang::tooling::buildASTFromCodeWithArgs;
34 
TEST(Attr,Doc)35 TEST(Attr, Doc) {
36   EXPECT_THAT(Attr::getDocumentation(attr::Used).str(),
37               testing::HasSubstr("The compiler must emit the definition even "
38                                  "if it appears to be unused"));
39 }
40 
getFunctionNode(ASTUnit * AST,const std::string & Name)41 const FunctionDecl *getFunctionNode(ASTUnit *AST, const std::string &Name) {
42   auto Result =
43       match(functionDecl(hasName(Name)).bind("fn"), AST->getASTContext());
44   EXPECT_EQ(Result.size(), 1u);
45   return Result[0].getNodeAs<FunctionDecl>("fn");
46 }
47 
getVariableNode(ASTUnit * AST,const std::string & Name)48 const VarDecl *getVariableNode(ASTUnit *AST, const std::string &Name) {
49   auto Result = match(varDecl(hasName(Name)).bind("var"), AST->getASTContext());
50   EXPECT_EQ(Result.size(), 1u);
51   return Result[0].getNodeAs<VarDecl>("var");
52 }
53 
54 template <class ModifiedTypeLoc>
AssertAnnotatedAs(TypeLoc TL,llvm::StringRef annotation,ModifiedTypeLoc & ModifiedTL,const AnnotateTypeAttr ** AnnotateOut=nullptr)55 void AssertAnnotatedAs(TypeLoc TL, llvm::StringRef annotation,
56                        ModifiedTypeLoc &ModifiedTL,
57                        const AnnotateTypeAttr **AnnotateOut = nullptr) {
58   const auto AttributedTL = TL.getAs<AttributedTypeLoc>();
59   ASSERT_FALSE(AttributedTL.isNull());
60   ModifiedTL = AttributedTL.getModifiedLoc().getAs<ModifiedTypeLoc>();
61   ASSERT_TRUE(ModifiedTL);
62 
63   ASSERT_NE(AttributedTL.getAttr(), nullptr);
64   const auto *Annotate = dyn_cast<AnnotateTypeAttr>(AttributedTL.getAttr());
65   ASSERT_NE(Annotate, nullptr);
66   EXPECT_EQ(Annotate->getAnnotation(), annotation);
67   if (AnnotateOut) {
68     *AnnotateOut = Annotate;
69   }
70 }
71 
TEST(Attr,AnnotateType)72 TEST(Attr, AnnotateType) {
73 
74   // Test that the AnnotateType attribute shows up in the right places and that
75   // it stores its arguments correctly.
76 
77   auto AST = buildASTFromCode(R"cpp(
78     void f(int* [[clang::annotate_type("foo", "arg1", 2)]] *,
79            int [[clang::annotate_type("bar")]]);
80 
81     int [[clang::annotate_type("int")]] * [[clang::annotate_type("ptr")]]
82       array[10] [[clang::annotate_type("arr")]];
83 
84     void (* [[clang::annotate_type("funcptr")]] fp)(void);
85 
86     struct S { int mem; };
87     int [[clang::annotate_type("int")]]
88     S::* [[clang::annotate_type("ptr_to_mem")]] ptr_to_member = &S::mem;
89   )cpp");
90 
91   {
92     const FunctionDecl *Func = getFunctionNode(AST.get(), "f");
93 
94     // First parameter.
95     const auto PointerTL = Func->getParamDecl(0)
96                                ->getTypeSourceInfo()
97                                ->getTypeLoc()
98                                .getAs<PointerTypeLoc>();
99     ASSERT_FALSE(PointerTL.isNull());
100     PointerTypeLoc PointerPointerTL;
101     const AnnotateTypeAttr *Annotate;
102     AssertAnnotatedAs(PointerTL.getPointeeLoc(), "foo", PointerPointerTL,
103                       &Annotate);
104 
105     EXPECT_EQ(Annotate->args_size(), 2u);
106     const auto *StringLit = selectFirst<StringLiteral>(
107         "str", match(constantExpr(hasDescendant(stringLiteral().bind("str"))),
108                      *Annotate->args_begin()[0], AST->getASTContext()));
109     ASSERT_NE(StringLit, nullptr);
110     EXPECT_EQ(StringLit->getString(), "arg1");
111     EXPECT_EQ(match(constantExpr(has(integerLiteral(equals(2u)).bind("int"))),
112                     *Annotate->args_begin()[1], AST->getASTContext())
113                   .size(),
114               1u);
115 
116     // Second parameter.
117     BuiltinTypeLoc IntTL;
118     AssertAnnotatedAs(Func->getParamDecl(1)->getTypeSourceInfo()->getTypeLoc(),
119                       "bar", IntTL);
120     EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
121   }
122 
123   {
124     const VarDecl *Var = getVariableNode(AST.get(), "array");
125 
126     ArrayTypeLoc ArrayTL;
127     AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "arr", ArrayTL);
128     PointerTypeLoc PointerTL;
129     AssertAnnotatedAs(ArrayTL.getElementLoc(), "ptr", PointerTL);
130     BuiltinTypeLoc IntTL;
131     AssertAnnotatedAs(PointerTL.getPointeeLoc(), "int", IntTL);
132     EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
133   }
134 
135   {
136     const VarDecl *Var = getVariableNode(AST.get(), "fp");
137 
138     PointerTypeLoc PointerTL;
139     AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "funcptr",
140                       PointerTL);
141     ASSERT_TRUE(
142         PointerTL.getPointeeLoc().IgnoreParens().getAs<FunctionTypeLoc>());
143   }
144 
145   {
146     const VarDecl *Var = getVariableNode(AST.get(), "ptr_to_member");
147 
148     MemberPointerTypeLoc MemberPointerTL;
149     AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "ptr_to_mem",
150                       MemberPointerTL);
151     BuiltinTypeLoc IntTL;
152     AssertAnnotatedAs(MemberPointerTL.getPointeeLoc(), "int", IntTL);
153     EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
154   }
155 
156   // Test type annotation on an `__auto_type` type in C mode.
157   AST = buildASTFromCodeWithArgs(R"c(
158     __auto_type [[clang::annotate_type("auto")]] auto_var = 1;
159   )c",
160                                  {},
161                                  "input.c");
162 
163   {
164     const VarDecl *Var = getVariableNode(AST.get(), "auto_var");
165 
166     AutoTypeLoc AutoTL;
167     AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "auto", AutoTL);
168   }
169 }
170 
TEST(Attr,RegularKeywordAttribute)171 TEST(Attr, RegularKeywordAttribute) {
172   auto AST = clang::tooling::buildASTFromCode("");
173   auto &Ctx = AST->getASTContext();
174   auto Funcref = clang::WebAssemblyFuncrefAttr::CreateImplicit(Ctx);
175   EXPECT_EQ(Funcref->getSyntax(), clang::AttributeCommonInfo::AS_Keyword);
176   ASSERT_FALSE(Funcref->isRegularKeywordAttribute());
177 
178   auto Streaming = clang::ArmStreamingAttr::CreateImplicit(Ctx);
179   EXPECT_EQ(Streaming->getSyntax(), clang::AttributeCommonInfo::AS_Keyword);
180   ASSERT_TRUE(Streaming->isRegularKeywordAttribute());
181 }
182 
183 } // namespace
184