Skip to content

Commit 0411b29

Browse files
committed
[clang][frontend] Add support for attribute plugins for statement attributes
We already have support for declaration attributes; this is just a matter of extending the plugin infrastructure to cover one more case.
1 parent 2804775 commit 0411b29

File tree

5 files changed

+108
-12
lines changed

5 files changed

+108
-12
lines changed

clang/docs/ClangPlugins.rst

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,6 @@ The members of ``ParsedAttrInfo`` that a plugin attribute must define are:
9292
attribute, each of which consists of an attribute syntax and how the
9393
attribute name is spelled for that syntax. If the syntax allows a scope then
9494
the spelling must be "scope::attr" if a scope is present or "::attr" if not.
95-
* ``handleDeclAttribute``, which is the function that applies the attribute to
96-
a declaration. It is responsible for checking that the attribute's arguments
97-
are valid, and typically applies the attribute by adding an ``Attr`` to the
98-
``Decl``. It returns either ``AttributeApplied``, to indicate that the
99-
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.
10095

10196
The members of ``ParsedAttrInfo`` that may need to be defined, depending on the
10297
attribute, are:
@@ -105,6 +100,18 @@ attribute, are:
105100
arguments to the attribute.
106101
* ``diagAppertainsToDecl``, which checks if the attribute has been used on the
107102
right kind of declaration and issues a diagnostic if not.
103+
* ``handleDeclAttribute``, which is the function that applies the attribute to
104+
a declaration. It is responsible for checking that the attribute's arguments
105+
are valid, and typically applies the attribute by adding an ``Attr`` to the
106+
``Decl``. It returns either ``AttributeApplied``, to indicate that the
107+
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.
108+
* ``diagAppertainsToStmt``, which checks if the attribute has been used on the
109+
right kind of statement and issues a diagnostic if not.
110+
* ``handleStmtAttribute``, which is the function that applies the attribute to
111+
a statement. It is responsible for checking that the attribute's arguments
112+
are valid, and typically applies the attribute by adding an ``Attr`` to the
113+
``Stmt``. It returns either ``AttributeApplied``, to indicate that the
114+
attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't.
108115
* ``diagLangOpts``, which checks if the attribute is permitted for the current
109116
language mode and issues a diagnostic if not.
110117
* ``existsInTarget``, which checks if the attribute is permitted for the given

clang/examples/Attribute/Attribute.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,55 @@ struct ExampleAttrInfo : public ParsedAttrInfo {
9494
}
9595
return AttributeApplied;
9696
}
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_str)
103+
<< Attr << Attr.isRegularKeywordAttribute() << "for loop statements";
104+
return false;
105+
}
106+
return true;
107+
}
108+
109+
AttrHandling handleStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &Attr,
110+
class Attr *&Result) const override {
111+
// We make some rules here:
112+
// 1. Only accept at most 3 arguments here.
113+
// 2. The first argument must be a string literal if it exists.
114+
if (Attr.getNumArgs() > 3) {
115+
unsigned ID = S.getDiagnostics().getCustomDiagID(
116+
DiagnosticsEngine::Error,
117+
"'example' attribute only accepts at most three arguments");
118+
S.Diag(Attr.getLoc(), ID);
119+
return AttributeNotApplied;
120+
}
121+
// If there are arguments, the first argument should be a string literal.
122+
if (Attr.getNumArgs() > 0) {
123+
auto *Arg0 = Attr.getArgAsExpr(0);
124+
StringLiteral *Literal =
125+
dyn_cast<StringLiteral>(Arg0->IgnoreParenCasts());
126+
if (!Literal) {
127+
unsigned ID = S.getDiagnostics().getCustomDiagID(
128+
DiagnosticsEngine::Error, "first argument to the 'example' "
129+
"attribute must be a string literal");
130+
S.Diag(Attr.getLoc(), ID);
131+
return AttributeNotApplied;
132+
}
133+
SmallVector<Expr *, 16> ArgsBuf;
134+
for (unsigned i = 0; i < Attr.getNumArgs(); i++) {
135+
ArgsBuf.push_back(Attr.getArgAsExpr(i));
136+
}
137+
Result = AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(),
138+
ArgsBuf.size(), Attr.getRange());
139+
} else {
140+
// Attach an annotate attribute to the Decl.
141+
Result = AnnotateAttr::Create(S.Context, "example", nullptr, 0,
142+
Attr.getRange());
143+
}
144+
return AttributeApplied;
145+
}
97146
};
98147

99148
} // namespace

clang/include/clang/Basic/ParsedAttrInfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace clang {
2626

27+
class Attr;
2728
class Decl;
2829
class LangOptions;
2930
class ParsedAttr;
@@ -154,6 +155,15 @@ struct ParsedAttrInfo {
154155
const ParsedAttr &Attr) const {
155156
return NotHandled;
156157
}
158+
/// If this ParsedAttrInfo knows how to handle this ParsedAttr applied to this
159+
/// Stmt then do so (referencing the resulting Attr in Result) and return
160+
/// either AttributeApplied if it was applied or AttributeNotApplied if it
161+
/// wasn't. Otherwise return NotHandled.
162+
virtual AttrHandling handleStmtAttribute(Sema &S, Stmt *St,
163+
const ParsedAttr &Attr,
164+
class Attr *&Result) const {
165+
return NotHandled;
166+
}
157167

158168
static const ParsedAttrInfo &get(const AttributeCommonInfo &A);
159169
static ArrayRef<const ParsedAttrInfo *> getAllBuiltin();

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,10 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
680680
case ParsedAttr::AT_NoConvergent:
681681
return handleNoConvergentAttr(S, St, A, Range);
682682
default:
683+
if (Attr *AT = nullptr; A.getInfo().handleStmtAttribute(S, St, A, AT) !=
684+
ParsedAttrInfo::NotHandled) {
685+
return AT;
686+
}
683687
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
684688
// declaration attribute is not written on a statement, but this code is
685689
// needed for attributes in Attr.td that do not list any subjects.

clang/test/Frontend/plugin-attribute.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,33 @@
44
// REQUIRES: plugins, examples
55
//--- good_attr.cpp
66
// expected-no-diagnostics
7-
void fn1a() __attribute__((example)) {}
8-
[[example]] void fn1b() {}
9-
[[plugin::example]] void fn1c() {}
10-
void fn2() __attribute__((example("somestring", 1, 2.0))) {}
11-
// CHECK-COUNT-4: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example"
7+
void fn1a() __attribute__((example)) {
8+
__attribute__((example)) for (int i = 0; i < 10; ++i) {}
9+
}
10+
[[example]] void fn1b() {
11+
[[example]] for (int i = 0; i < 10; ++i) {}
12+
}
13+
[[plugin::example]] void fn1c() {
14+
[[plugin::example]] for (int i = 0; i < 10; ++i) {}
15+
}
16+
void fn2() __attribute__((example("somestring", 1, 2.0))) {
17+
__attribute__((example("abc", 3, 4.0))) for (int i = 0; i < 10; ++i) {}
18+
}
19+
// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}}
20+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example"
21+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example"
22+
// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}}
23+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example"
24+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example"
25+
// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}}
26+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example"
27+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example"
28+
// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}}
29+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example"
30+
// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "abc"
31+
// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 3
32+
// CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'double' 4.000000e+00
33+
// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example"
1234
// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "somestring"
1335
// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 1
1436
// CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'double' 2.000000e+00
@@ -18,5 +40,9 @@ int var1 __attribute__((example("otherstring"))) = 1; // expected-warning {{'exa
1840
class Example {
1941
void __attribute__((example)) fn3(); // expected-error {{'example' attribute only allowed at file scope}}
2042
};
21-
void fn4() __attribute__((example(123))) { } // expected-error {{first argument to the 'example' attribute must be a string literal}}
22-
void fn5() __attribute__((example("a","b", 3, 4.0))) { } // expected-error {{'example' attribute only accepts at most three arguments}}
43+
void fn4() __attribute__((example(123))) { // expected-error {{first argument to the 'example' attribute must be a string literal}}
44+
__attribute__((example("somestring"))) while (true); // expected-warning {{'example' attribute only applies to for loop statements}}
45+
}
46+
void fn5() __attribute__((example("a","b", 3, 4.0))) { // expected-error {{'example' attribute only accepts at most three arguments}}
47+
__attribute__((example("a","b", 3, 4.0))) for (int i = 0; i < 10; ++i) {} // expected-error {{'example' attribute only accepts at most three arguments}}
48+
}

0 commit comments

Comments
 (0)