xref: /llvm-project/clang/examples/Attribute/Attribute.cpp (revision 41a94de75caacb979070ec7a010dfe3c4e9f116f)
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