Skip to content

Commit 81a9707

Browse files
[Attr] Apply GNU-style attributes to expression statements
Before this commit, expression statements could not be annotated with statement attributes. Whenever parser found attribute, it unconditionally assumed that it was followed by a declaration. This not only doesn't allow expression attributes to have attributes, but also produces spurious error diagnostics. In order to maintain all previously compiled code, we still assume that GNU attributes are followed by declarations unless ALL of those are statement attributes. And even in this case we are not forcing the parser to think that it should parse a statement, but rather let it proceed as if no attributes were found. Differential Revision: https://reviews.llvm.org/D93630
1 parent 7df4eaa commit 81a9707

File tree

5 files changed

+182
-1
lines changed

5 files changed

+182
-1
lines changed

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ EXTENSION(cxx_variable_templates, LangOpts.CPlusPlus)
253253
EXTENSION(overloadable_unmarked, true)
254254
EXTENSION(pragma_clang_attribute_namespaces, true)
255255
EXTENSION(pragma_clang_attribute_external_declaration, true)
256+
EXTENSION(statement_attributes_with_gnu_syntax, true)
256257
EXTENSION(gnu_asm, LangOpts.GNUAsm)
257258
EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm)
258259
EXTENSION(matrix_types, LangOpts.MatrixTypes)

clang/lib/Parse/ParseStmt.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "clang/Sema/DeclSpec.h"
2121
#include "clang/Sema/Scope.h"
2222
#include "clang/Sema/TypoCorrection.h"
23+
#include "llvm/ADT/STLExtras.h"
24+
2325
using namespace clang;
2426

2527
//===----------------------------------------------------------------------===//
@@ -215,7 +217,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
215217
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
216218
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
217219
ParsedStmtContext()) &&
218-
(GNUAttributeLoc.isValid() || isDeclarationStatement())) {
220+
((GNUAttributeLoc.isValid() &&
221+
!(!Attrs.empty() &&
222+
llvm::all_of(
223+
Attrs, [](ParsedAttr &Attr) { return Attr.isStmtAttr(); }))) ||
224+
isDeclarationStatement())) {
219225
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
220226
DeclGroupPtrTy Decl;
221227
if (GNUAttributeLoc.isValid()) {

clang/test/Parser/stmt-attributes.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
#if !__has_extension(statement_attributes_with_gnu_syntax)
4+
#error "We should have statement attributes with GNU syntax support"
5+
#endif
6+
7+
void foo(int i) {
8+
9+
__attribute__((unknown_attribute)); // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
10+
__attribute__(()) {}
11+
__attribute__(()) if (0) {}
12+
__attribute__(()) for (;;);
13+
__attribute__(()) do {
14+
__attribute__(()) continue;
15+
}
16+
while (0)
17+
;
18+
__attribute__(()) while (0);
19+
20+
__attribute__(()) switch (i) {
21+
__attribute__(()) case 0 :
22+
__attribute__(()) default :
23+
__attribute__(()) break;
24+
}
25+
26+
__attribute__(()) goto here;
27+
__attribute__(()) here :
28+
29+
__attribute__(()) return;
30+
31+
__attribute__((noreturn)) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}}
32+
__attribute__((noreturn)) if (0) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}}
33+
__attribute__((noreturn)) for (;;); // expected-error {{'noreturn' attribute cannot be applied to a statement}}
34+
__attribute__((noreturn)) do { // expected-error {{'noreturn' attribute cannot be applied to a statement}}
35+
__attribute__((unavailable)) continue; // expected-error {{'unavailable' attribute cannot be applied to a statement}}
36+
}
37+
while (0)
38+
;
39+
__attribute__((unknown_attribute)) while (0); // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
40+
41+
__attribute__((unused)) switch (i) { // expected-error {{'unused' attribute cannot be applied to a statement}}
42+
__attribute__((uuid)) case 0: // expected-warning {{unknown attribute 'uuid' ignored}}
43+
__attribute__((visibility)) default: // expected-error {{'visibility' attribute cannot be applied to a statement}}
44+
__attribute__((carries_dependency)) break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
45+
}
46+
47+
__attribute__((fastcall)) goto there; // expected-error {{'fastcall' attribute cannot be applied to a statement}}
48+
__attribute__((noinline)) there : // expected-warning {{'noinline' attribute only applies to functions}}
49+
50+
__attribute__((weakref)) return; // expected-error {{'weakref' attribute only applies to variables and functions}}
51+
52+
__attribute__((carries_dependency)); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
53+
__attribute__((carries_dependency)) {} // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
54+
__attribute__((carries_dependency)) if (0) {} // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
55+
__attribute__((carries_dependency)) for (;;); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
56+
__attribute__((carries_dependency)) do { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
57+
__attribute__((carries_dependency)) continue; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
58+
}
59+
while (0)
60+
;
61+
__attribute__((carries_dependency)) while (0); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
62+
63+
__attribute__((carries_dependency)) switch (i) { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
64+
__attribute__((carries_dependency)) case 0: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
65+
__attribute__((carries_dependency)) default: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
66+
__attribute__((carries_dependency)) break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
67+
}
68+
69+
__attribute__((carries_dependency)) goto here; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
70+
71+
__attribute__((carries_dependency)) return; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
72+
}
73+
74+
void bar();
75+
76+
void foobar() {
77+
__attribute__((nomerge)) bar();
78+
__attribute__(()) bar(); // expected-error {{expected identifier or '('}}
79+
__attribute__((unused, nomerge)) bar(); // expected-error {{expected identifier or '('}}
80+
__attribute__((nomerge, unused)) bar(); // expected-error {{expected identifier or '('}}
81+
__attribute__((nomerge(1, 2))) bar(); // expected-error {{'nomerge' attribute takes no arguments}}
82+
int x;
83+
__attribute__((nomerge)) x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}}
84+
85+
__attribute__((nomerge)) label : bar(); // expected-error {{'nomerge' attribute only applies to functions and statements}}
86+
}
87+
88+
int f();
89+
90+
__attribute__((nomerge)) static int i; // expected-error {{'nomerge' attribute only applies to functions and statements}}

clang/test/Parser/stmt-attributes.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fsyntax-only -verify %s
2+
3+
#if !__has_extension(statement_attributes_with_gnu_syntax)
4+
#error "We should have statement attributes with GNU syntax support"
5+
#endif
6+
7+
template <typename T = void>
8+
class __attribute__((nomerge)) A {
9+
// expected-error@-1 {{'nomerge' attribute only applies to functions and statements}}
10+
};
11+
12+
class B : public A<> {
13+
public:
14+
void bar();
15+
};
16+
17+
void bar();
18+
19+
void foo(A<> *obj) {
20+
__attribute__((nomerge)) static_cast<B *>(obj)->bar();
21+
__attribute__((nomerge))[obj]() { static_cast<B *>(obj)->bar(); }
22+
();
23+
__attribute__(()) try {
24+
bar();
25+
} catch (...) {
26+
}
27+
}

clang/test/Parser/stmt-attributes.m

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %clang_cc1 -verify %s \
2+
// RUN: -fblocks -fobjc-exceptions -fexceptions -fsyntax-only \
3+
// RUN: -Wno-unused-value -Wno-unused-getter-return-value
4+
5+
#if !__has_extension(statement_attributes_with_gnu_syntax)
6+
#error "We should have statement attributes with GNU syntax support"
7+
#endif
8+
9+
@interface Base
10+
@end
11+
12+
@interface Test : Base
13+
@property(getter=hasFoobar) int foobar;
14+
- (void)foo;
15+
- (void)bar;
16+
@end
17+
18+
Test *getTest();
19+
20+
@implementation Test
21+
- (void)foo __attribute__((nomerge)) {
22+
// expected-error@-1 {{'nomerge' attribute only applies to functions and statements}}
23+
}
24+
25+
- (void)bar {
26+
__attribute__(()) [self foo];
27+
// expected-error@-1 {{missing '[' at start of message send expression}}
28+
// expected-error@-2 {{expected ']'}}
29+
// expected-error@-3 {{expected identifier or '('}}
30+
// expected-note@-4 {{to match this '['}}
31+
__attribute__((nomerge)) [self foo];
32+
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
33+
__attribute__((nomerge)) [getTest() foo];
34+
35+
__attribute__(()) ^{};
36+
// expected-error@-1 {{expected identifier or '('}}
37+
__attribute__((nomerge)) ^{};
38+
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
39+
__attribute__((nomerge)) ^{ [self foo]; }();
40+
41+
__attribute__(()) @try {
42+
[self foo];
43+
} @finally {
44+
}
45+
46+
__attribute__((nomerge)) @try {
47+
[getTest() foo];
48+
} @finally {
49+
}
50+
51+
__attribute__((nomerge)) (__bridge void *)self;
52+
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
53+
54+
__attribute__((nomerge)) self.hasFoobar;
55+
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
56+
}
57+
@end

0 commit comments

Comments
 (0)