[clang] 81a9707 - [Attr] Apply GNU-style attributes to expression statements

Valeriy Savchenko via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 11 05:45:33 PST 2021


Author: Valeriy Savchenko
Date: 2021-02-11T16:44:41+03:00
New Revision: 81a9707723845a5b880245da24633c519493205c

URL: https://github.com/llvm/llvm-project/commit/81a9707723845a5b880245da24633c519493205c
DIFF: https://github.com/llvm/llvm-project/commit/81a9707723845a5b880245da24633c519493205c.diff

LOG: [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

Added: 
    clang/test/Parser/stmt-attributes.c
    clang/test/Parser/stmt-attributes.cpp
    clang/test/Parser/stmt-attributes.m

Modified: 
    clang/include/clang/Basic/Features.def
    clang/lib/Parse/ParseStmt.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 5424da67b62da..32830a3a532cc 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -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)

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 71344ff101552..f59271c458488 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -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 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
     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()) {

diff  --git a/clang/test/Parser/stmt-attributes.c b/clang/test/Parser/stmt-attributes.c
new file mode 100644
index 0000000000000..d142ce1b5b954
--- /dev/null
+++ b/clang/test/Parser/stmt-attributes.c
@@ -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}}

diff  --git a/clang/test/Parser/stmt-attributes.cpp b/clang/test/Parser/stmt-attributes.cpp
new file mode 100644
index 0000000000000..74192ea68141d
--- /dev/null
+++ b/clang/test/Parser/stmt-attributes.cpp
@@ -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 at -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 (...) {
+  }
+}

diff  --git a/clang/test/Parser/stmt-attributes.m b/clang/test/Parser/stmt-attributes.m
new file mode 100644
index 0000000000000..550781b5311a9
--- /dev/null
+++ b/clang/test/Parser/stmt-attributes.m
@@ -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
+
+ at interface Base
+ at end
+
+ at interface Test : Base
+ at property(getter=hasFoobar) int foobar;
+- (void)foo;
+- (void)bar;
+ at end
+
+Test *getTest();
+
+ at implementation Test
+- (void)foo __attribute__((nomerge)) {
+  // expected-error at -1 {{'nomerge' attribute only applies to functions and statements}}
+}
+
+- (void)bar {
+  __attribute__(()) [self foo];
+  // expected-error at -1 {{missing '[' at start of message send expression}}
+  // expected-error at -2 {{expected ']'}}
+  // expected-error at -3 {{expected identifier or '('}}
+  // expected-note at -4 {{to match this '['}}
+  __attribute__((nomerge)) [self foo];
+  // expected-warning at -1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
+  __attribute__((nomerge)) [getTest() foo];
+
+  __attribute__(()) ^{};
+  // expected-error at -1 {{expected identifier or '('}}
+  __attribute__((nomerge)) ^{};
+  // expected-warning at -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 at -1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
+
+  __attribute__((nomerge)) self.hasFoobar;
+  // expected-warning at -1 {{nomerge attribute is ignored because there exists no call expression inside the statement}}
+}
+ at end


        


More information about the cfe-commits mailing list