[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
This commit is contained in:
Valeriy Savchenko 2020-12-21 12:05:14 +03:00
parent 7df4eaaa93
commit 81a9707723
5 changed files with 182 additions and 1 deletions

View file

@ -253,6 +253,7 @@ EXTENSION(cxx_variable_templates, LangOpts.CPlusPlus)
EXTENSION(overloadable_unmarked, true)
EXTENSION(pragma_clang_attribute_namespaces, true)
EXTENSION(pragma_clang_attribute_external_declaration, true)
EXTENSION(statement_attributes_with_gnu_syntax, true)
EXTENSION(gnu_asm, LangOpts.GNUAsm)
EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm)
EXTENSION(matrix_types, LangOpts.MatrixTypes)

View file

@ -20,6 +20,8 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/TypoCorrection.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
//===----------------------------------------------------------------------===//
@ -215,7 +217,11 @@ Retry:
if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt ||
(StmtCtx & ParsedStmtContext::AllowDeclarationsInC) !=
ParsedStmtContext()) &&
(GNUAttributeLoc.isValid() || isDeclarationStatement())) {
((GNUAttributeLoc.isValid() &&
!(!Attrs.empty() &&
llvm::all_of(
Attrs, [](ParsedAttr &Attr) { return Attr.isStmtAttr(); }))) ||
isDeclarationStatement())) {
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
DeclGroupPtrTy Decl;
if (GNUAttributeLoc.isValid()) {

View file

@ -0,0 +1,90 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
#if !__has_extension(statement_attributes_with_gnu_syntax)
#error "We should have statement attributes with GNU syntax support"
#endif
void foo(int i) {
__attribute__((unknown_attribute)); // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
__attribute__(()) {}
__attribute__(()) if (0) {}
__attribute__(()) for (;;);
__attribute__(()) do {
__attribute__(()) continue;
}
while (0)
;
__attribute__(()) while (0);
__attribute__(()) switch (i) {
__attribute__(()) case 0 :
__attribute__(()) default :
__attribute__(()) break;
}
__attribute__(()) goto here;
__attribute__(()) here :
__attribute__(()) return;
__attribute__((noreturn)) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}}
__attribute__((noreturn)) if (0) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}}
__attribute__((noreturn)) for (;;); // expected-error {{'noreturn' attribute cannot be applied to a statement}}
__attribute__((noreturn)) do { // expected-error {{'noreturn' attribute cannot be applied to a statement}}
__attribute__((unavailable)) continue; // expected-error {{'unavailable' attribute cannot be applied to a statement}}
}
while (0)
;
__attribute__((unknown_attribute)) while (0); // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
__attribute__((unused)) switch (i) { // expected-error {{'unused' attribute cannot be applied to a statement}}
__attribute__((uuid)) case 0: // expected-warning {{unknown attribute 'uuid' ignored}}
__attribute__((visibility)) default: // expected-error {{'visibility' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}
__attribute__((fastcall)) goto there; // expected-error {{'fastcall' attribute cannot be applied to a statement}}
__attribute__((noinline)) there : // expected-warning {{'noinline' attribute only applies to functions}}
__attribute__((weakref)) return; // expected-error {{'weakref' attribute only applies to variables and functions}}
__attribute__((carries_dependency)); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) {} // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) if (0) {} // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) for (;;); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) do { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) continue; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
}
while (0)
;
__attribute__((carries_dependency)) while (0); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) switch (i) { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
__attribute__((carries_dependency)) case 0: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) default: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}
__attribute__((carries_dependency)) goto here; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
__attribute__((carries_dependency)) return; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}
void bar();
void foobar() {
__attribute__((nomerge)) bar();
__attribute__(()) bar(); // expected-error {{expected identifier or '('}}
__attribute__((unused, nomerge)) bar(); // expected-error {{expected identifier or '('}}
__attribute__((nomerge, unused)) bar(); // expected-error {{expected identifier or '('}}
__attribute__((nomerge(1, 2))) bar(); // expected-error {{'nomerge' attribute takes no arguments}}
int x;
__attribute__((nomerge)) x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}}
__attribute__((nomerge)) label : bar(); // expected-error {{'nomerge' attribute only applies to functions and statements}}
}
int f();
__attribute__((nomerge)) static int i; // expected-error {{'nomerge' attribute only applies to functions and statements}}

View file

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fsyntax-only -verify %s
#if !__has_extension(statement_attributes_with_gnu_syntax)
#error "We should have statement attributes with GNU syntax support"
#endif
template <typename T = void>
class __attribute__((nomerge)) A {
// expected-error@-1 {{'nomerge' attribute only applies to functions and statements}}
};
class B : public A<> {
public:
void bar();
};
void bar();
void foo(A<> *obj) {
__attribute__((nomerge)) static_cast<B *>(obj)->bar();
__attribute__((nomerge))[obj]() { static_cast<B *>(obj)->bar(); }
();
__attribute__(()) try {
bar();
} catch (...) {
}
}

View file

@ -0,0 +1,57 @@
// RUN: %clang_cc1 -verify %s \
// RUN: -fblocks -fobjc-exceptions -fexceptions -fsyntax-only \
// RUN: -Wno-unused-value -Wno-unused-getter-return-value
#if !__has_extension(statement_attributes_with_gnu_syntax)
#error "We should have statement attributes with GNU syntax support"
#endif
@interface Base
@end
@interface Test : Base
@property(getter=hasFoobar) int foobar;
- (void)foo;
- (void)bar;
@end
Test *getTest();
@implementation Test
- (void)foo __attribute__((nomerge)) {
// expected-error@-1 {{'nomerge' attribute only applies to functions and statements}}
}
- (void)bar {
__attribute__(()) [self foo];
// expected-error@-1 {{missing '[' at start of message send expression}}
// expected-error@-2 {{expected ']'}}
// expected-error@-3 {{expected identifier or '('}}
// expected-note@-4 {{to match this '['}}
__attribute__((nomerge)) [self foo];
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
__attribute__((nomerge)) [getTest() foo];
__attribute__(()) ^{};
// expected-error@-1 {{expected identifier or '('}}
__attribute__((nomerge)) ^{};
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
__attribute__((nomerge)) ^{ [self foo]; }();
__attribute__(()) @try {
[self foo];
} @finally {
}
__attribute__((nomerge)) @try {
[getTest() foo];
} @finally {
}
__attribute__((nomerge)) (__bridge void *)self;
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
__attribute__((nomerge)) self.hasFoobar;
// expected-warning@-1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
}
@end