1 //===- Attribute.cpp ------------------------------------------------------===// 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 // Example clang plugin which adds an an annotation to file-scope declarations 10 // with the 'example' attribute. 11 // 12 // This plugin is used by clang/test/Frontend/plugin-attribute tests. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "clang/AST/ASTContext.h" 17 #include "clang/AST/Attr.h" 18 #include "clang/Sema/ParsedAttr.h" 19 #include "clang/Sema/Sema.h" 20 #include "clang/Sema/SemaDiagnostic.h" 21 #include "llvm/IR/Attributes.h" 22 using namespace clang; 23 24 namespace { 25 26 struct ExampleAttrInfo : public ParsedAttrInfo { 27 ExampleAttrInfo() { 28 // Can take up to 15 optional arguments, to emulate accepting a variadic 29 // number of arguments. This just illustrates how many arguments a 30 // `ParsedAttrInfo` can hold, we will not use that much in this example. 31 OptArgs = 15; 32 // GNU-style __attribute__(("example")) and C++/C23-style [[example]] and 33 // [[plugin::example]] supported. 34 static constexpr Spelling S[] = {{ParsedAttr::AS_GNU, "example"}, 35 {ParsedAttr::AS_C23, "example"}, 36 {ParsedAttr::AS_CXX11, "example"}, 37 {ParsedAttr::AS_CXX11, "plugin::example"}}; 38 Spellings = S; 39 } 40 41 bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, 42 const Decl *D) const override { 43 // This attribute appertains to functions only. 44 if (!isa<FunctionDecl>(D)) { 45 S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) 46 << Attr << Attr.isRegularKeywordAttribute() << ExpectedFunction; 47 return false; 48 } 49 return true; 50 } 51 52 AttrHandling handleDeclAttribute(Sema &S, Decl *D, 53 const ParsedAttr &Attr) const override { 54 // Check if the decl is at file scope. 55 if (!D->getDeclContext()->isFileContext()) { 56 unsigned ID = S.getDiagnostics().getCustomDiagID( 57 DiagnosticsEngine::Error, 58 "'example' attribute only allowed at file scope"); 59 S.Diag(Attr.getLoc(), ID); 60 return AttributeNotApplied; 61 } 62 // We make some rules here: 63 // 1. Only accept at most 3 arguments here. 64 // 2. The first argument must be a string literal if it exists. 65 if (Attr.getNumArgs() > 3) { 66 unsigned ID = S.getDiagnostics().getCustomDiagID( 67 DiagnosticsEngine::Error, 68 "'example' attribute only accepts at most three arguments"); 69 S.Diag(Attr.getLoc(), ID); 70 return AttributeNotApplied; 71 } 72 // If there are arguments, the first argument should be a string literal. 73 if (Attr.getNumArgs() > 0) { 74 auto *Arg0 = Attr.getArgAsExpr(0); 75 StringLiteral *Literal = 76 dyn_cast<StringLiteral>(Arg0->IgnoreParenCasts()); 77 if (!Literal) { 78 unsigned ID = S.getDiagnostics().getCustomDiagID( 79 DiagnosticsEngine::Error, "first argument to the 'example' " 80 "attribute must be a string literal"); 81 S.Diag(Attr.getLoc(), ID); 82 return AttributeNotApplied; 83 } 84 SmallVector<Expr *, 16> ArgsBuf; 85 for (unsigned i = 0; i < Attr.getNumArgs(); i++) { 86 ArgsBuf.push_back(Attr.getArgAsExpr(i)); 87 } 88 D->addAttr(AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(), 89 ArgsBuf.size(), Attr.getRange())); 90 } else { 91 // Attach an annotate attribute to the Decl. 92 D->addAttr(AnnotateAttr::Create(S.Context, "example", nullptr, 0, 93 Attr.getRange())); 94 } 95 return AttributeApplied; 96 } 97 98 bool diagAppertainsToStmt(Sema &S, const ParsedAttr &Attr, 99 const Stmt *St) const override { 100 // This attribute appertains to for loop statements only. 101 if (!isa<ForStmt>(St)) { 102 S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) 103 << Attr << Attr.isRegularKeywordAttribute() 104 << ExpectedForLoopStatement; 105 return false; 106 } 107 return true; 108 } 109 110 AttrHandling handleStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &Attr, 111 class Attr *&Result) const override { 112 // We make some rules here: 113 // 1. Only accept at most 3 arguments here. 114 // 2. The first argument must be a string literal if it exists. 115 if (Attr.getNumArgs() > 3) { 116 unsigned ID = S.getDiagnostics().getCustomDiagID( 117 DiagnosticsEngine::Error, 118 "'example' attribute only accepts at most three arguments"); 119 S.Diag(Attr.getLoc(), ID); 120 return AttributeNotApplied; 121 } 122 // If there are arguments, the first argument should be a string literal. 123 if (Attr.getNumArgs() > 0) { 124 auto *Arg0 = Attr.getArgAsExpr(0); 125 StringLiteral *Literal = 126 dyn_cast<StringLiteral>(Arg0->IgnoreParenCasts()); 127 if (!Literal) { 128 unsigned ID = S.getDiagnostics().getCustomDiagID( 129 DiagnosticsEngine::Error, "first argument to the 'example' " 130 "attribute must be a string literal"); 131 S.Diag(Attr.getLoc(), ID); 132 return AttributeNotApplied; 133 } 134 SmallVector<Expr *, 16> ArgsBuf; 135 for (unsigned i = 0; i < Attr.getNumArgs(); i++) { 136 ArgsBuf.push_back(Attr.getArgAsExpr(i)); 137 } 138 Result = AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(), 139 ArgsBuf.size(), Attr.getRange()); 140 } else { 141 Result = AnnotateAttr::Create(S.Context, "example", nullptr, 0, 142 Attr.getRange()); 143 } 144 return AttributeApplied; 145 } 146 }; 147 148 } // namespace 149 150 static ParsedAttrInfoRegistry::Add<ExampleAttrInfo> X("example", ""); 151