aaron.ballman created this revision.
aaron.ballman added reviewers: rsmith, hans.
aaron.ballman added a subscriber: cfe-commits.

PR24559 brings up a long-standing issue in Clang where attribute order with 
differing syntax impacts whether a parse will succeed or fail with poor 
diagnostics. For instance:

struct __attribute__((lockable)) __declspec(dllexport) S { }; // ok
struct __declspec(dllexport) __attribute__((lockable)) T { }; // error about 
declaring anonymous structs and a warning about declarations not declaring 
anything

When you consider attributes as extraneous semantic information to attach to 
entities, ordering of the attributes really should not matter between the 
various attribute syntaxes we support -- they all serve the same purpose.

This patch begins to address the issue by providing a MaybeParseAttributes() 
function accepting a mask of what attribute syntaxes are allowed and parses the 
various syntaxes in a loop.

However, this patch is not complete, as the test cases for function attributes 
currently fail for the C++11-style attributes.

[[noreturn]] __attribute__((cdecl)) __declspec(dllexport) void f();

In this case, the [[noreturn]] is parsed as part of the top-level declaration, 
while the GNU and declspec attributes are parsed as part of the declaration 
specifier, and everything is accepted.

__attribute__((cdecl)) [[noreturn]] __declspec(dllexport) void f();

In this case, nothing is parsed as part of the top-level declaration, and 
everything is parsed as part of the declaration specifier. However, the C++ 
attribute is prohibited from appearing on a declaration specifier and so is 
diagnosed.

I think ParseDeclarationSpecifiers() needs to accept a 
ParsedAttributesWithRange object representing the top-level declaration 
attributes, and the parser needs to attach the C++ attributes to it up until we 
the first non-attribute token is found. But I'm not 100% certain that's the 
right approach.

Before taking the process further, I was wondering about two things:

1) Is the direction with MaybeParseAttributes() an acceptable approach?
2) If it is, is it reasonable to commit that, and work on the 
ParseDeclarationSpecifiers() portion in a follow-up commit?

Thanks!

~Aaron

http://reviews.llvm.org/D12375

Files:
  include/clang/Parse/Parser.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseExprCXX.cpp
  lib/Parse/Parser.cpp
  test/Parser/attr-order.cpp

Index: test/Parser/attr-order.cpp
===================================================================
--- test/Parser/attr-order.cpp
+++ test/Parser/attr-order.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -fms-extensions -std=c++14 -verify %s
+// expected-no-diagnostics
+
+struct [[]] __attribute__((lockable)) __declspec(dllexport) A {};
+struct [[]] __declspec(dllexport) __attribute__((lockable)) B {};
+struct __declspec(dllexport) [[]] __attribute__((lockable)) C {};
+struct __declspec(dllexport) __attribute__((lockable)) [[]] D {};
+struct __attribute__((lockable)) __declspec(dllexport) [[]] E {};
+struct __attribute__((lockable)) [[]] __declspec(dllexport) F {};
+
+[[]] __attribute__((cdecl)) __declspec(dllexport) void a();
+[[]] __declspec(dllexport) __attribute__((cdecl)) void b();
+__declspec(dllexport) [[]] __attribute__((cdecl)) void c();
+__declspec(dllexport) __attribute__((cdecl)) [[]] void d();
+__attribute__((cdecl)) __declspec(dllexport) [[]] void e();
+__attribute__((cdecl)) [[]] __declspec(dllexport) void f();
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -586,8 +586,7 @@
   }
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
-  MaybeParseMicrosoftAttributes(attrs);
+  MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, attrs);
 
   Result = ParseExternalDeclaration(attrs);
   return false;
@@ -1962,8 +1961,7 @@
   // FIXME: Support module import within __if_exists?
   while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
     ParsedAttributesWithRange attrs(AttrFactory);
-    MaybeParseCXX11Attributes(attrs);
-    MaybeParseMicrosoftAttributes(attrs);
+    MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, attrs);
     DeclGroupPtrTy Result = ParseExternalDeclaration(attrs);
     if (Result && !getCurScope()->getParent())
       Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -1090,14 +1090,10 @@
     SourceLocation RParenLoc = T.getCloseLocation();
     DeclEndLoc = RParenLoc;
 
-    // GNU-style attributes must be parsed before the mutable specifier to be
-    // compatible with GCC.
-    MaybeParseGNUAttributes(Attr, &DeclEndLoc);
+    // GNU-style and __declspec attributes must be parsed before the mutable
+    // specifier to be compatible with GCC/MSVC.
+    MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, Attr, &DeclEndLoc);
 
-    // MSVC-style attributes must be parsed before the mutable specifier to be
-    // compatible with MSVC.
-    MaybeParseMicrosoftDeclSpecs(Attr, &DeclEndLoc);
-
     // Parse 'mutable'[opt].
     SourceLocation MutableLoc;
     if (TryConsumeToken(tok::kw_mutable, MutableLoc))
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -212,8 +212,7 @@
   if (index == Ident.size()) {
     while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
-      MaybeParseMicrosoftAttributes(attrs);
+      MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, attrs);
       ParseExternalDeclaration(attrs);
     }
 
@@ -303,8 +302,7 @@
                 Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation());
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
-  MaybeParseMicrosoftAttributes(attrs);
+  MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, attrs);
 
   if (Tok.isNot(tok::l_brace)) {
     // Reset the source range in DS, as the leading "extern"
@@ -354,8 +352,7 @@
       // Fall through.
     default:
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
-      MaybeParseMicrosoftAttributes(attrs);
+      MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, attrs);
       ParseExternalDeclaration(attrs);
       continue;
     }
@@ -554,8 +551,7 @@
   }
 
   ParsedAttributesWithRange Attrs(AttrFactory);
-  MaybeParseGNUAttributes(Attrs);
-  MaybeParseCXX11Attributes(Attrs);
+  MaybeParseAttributes(PAKM_CXX11 | PAKM_GNU, Attrs);
 
   // Maybe this is an alias-declaration.
   TypeResult TypeAlias;
@@ -1230,8 +1226,7 @@
 
   ParsedAttributesWithRange attrs(AttrFactory);
   // If attributes exist after tag, parse them.
-  MaybeParseGNUAttributes(attrs);
-  MaybeParseMicrosoftDeclSpecs(attrs);
+  MaybeParseAttributes(PAKM_Declspec | PAKM_GNU | PAKM_CXX11, attrs);
 
   // Parse inheritance specifiers.
   if (Tok.isOneOf(tok::kw___single_inheritance,
@@ -1239,11 +1234,6 @@
                   tok::kw___virtual_inheritance))
     ParseMicrosoftInheritanceClassAttributes(attrs);
 
-  // If C++0x attributes exist here, parse them.
-  // FIXME: Are we consistent with the ordering of parsing of different
-  // styles of attributes?
-  MaybeParseCXX11Attributes(attrs);
-
   // Source location used by FIXIT to insert misplaced
   // C++11 attributes
   SourceLocation AttrFixitLoc = Tok.getLocation();
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -3046,14 +3046,11 @@
       continue;
     }
 
-    // GNU attributes support.
+    // Attribute support.
     case tok::kw___attribute:
-      ParseGNUAttributes(DS.getAttributes(), nullptr, LateAttrs);
-      continue;
-
-    // Microsoft declspec support.
     case tok::kw___declspec:
-      ParseMicrosoftDeclSpecs(DS.getAttributes());
+      MaybeParseAttributes(PAKM_GNU | PAKM_Declspec, DS.getAttributes(),
+                           nullptr, LateAttrs);
       continue;
 
     // Microsoft single token adornments.
@@ -3731,9 +3728,7 @@
 
   // If attributes exist after tag, parse them.
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseGNUAttributes(attrs);
-  MaybeParseCXX11Attributes(attrs);
-  MaybeParseMicrosoftDeclSpecs(attrs);
+  MaybeParseAttributes(PAKM_GNU | PAKM_CXX11 | PAKM_Declspec, attrs);
 
   SourceLocation ScopedEnumKWLoc;
   bool IsScopedUsingClassTag = false;
@@ -3750,9 +3745,7 @@
     ProhibitAttributes(attrs);
 
     // They are allowed afterwards, though.
-    MaybeParseGNUAttributes(attrs);
-    MaybeParseCXX11Attributes(attrs);
-    MaybeParseMicrosoftDeclSpecs(attrs);
+    MaybeParseAttributes(PAKM_GNU | PAKM_CXX11 | PAKM_Declspec, attrs);
   }
 
   // C++11 [temp.explicit]p12:
@@ -5775,12 +5768,9 @@
     // Just use the ParsingDeclaration "scope" of the declarator.
     DeclSpec DS(AttrFactory);
 
-    // Parse any C++11 attributes.
-    MaybeParseCXX11Attributes(DS.getAttributes());
+    // Parse any C++11 and Microsoft attributes.
+    MaybeParseAttributes(PAKM_CXX11 | PAKM_Microsoft, DS.getAttributes());
 
-    // Skip any Microsoft attributes before a param.
-    MaybeParseMicrosoftAttributes(DS.getAttributes());
-
     SourceLocation DSStart = Tok.getLocation();
 
     // If the caller parsed attributes for the first argument, add them now.
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -2097,11 +2097,14 @@
       D.takeAttributes(attrs, endLoc);
     }
   }
-  void MaybeParseGNUAttributes(ParsedAttributes &attrs,
+  bool MaybeParseGNUAttributes(ParsedAttributes &attrs,
                                SourceLocation *endLoc = nullptr,
                                LateParsedAttrList *LateAttrs = nullptr) {
-    if (Tok.is(tok::kw___attribute))
+    if (Tok.is(tok::kw___attribute)) {
       ParseGNUAttributes(attrs, endLoc, LateAttrs);
+      return true;
+    }
+    return false;
   }
   void ParseGNUAttributes(ParsedAttributes &attrs,
                           SourceLocation *endLoc = nullptr,
@@ -2125,20 +2128,25 @@
       D.takeAttributes(attrs, endLoc);
     }
   }
-  void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
+  bool MaybeParseCXX11Attributes(ParsedAttributes &attrs,
                                  SourceLocation *endLoc = nullptr) {
     if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
       ParsedAttributesWithRange attrsWithRange(AttrFactory);
       ParseCXX11Attributes(attrsWithRange, endLoc);
       attrs.takeAllFrom(attrsWithRange);
+      return true;
     }
+    return false;
   }
-  void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
+  bool MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
                                  SourceLocation *endLoc = nullptr,
                                  bool OuterMightBeMessageSend = false) {
     if (getLangOpts().CPlusPlus11 &&
-        isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
+        isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) {
       ParseCXX11Attributes(attrs, endLoc);
+      return true;
+    }
+    return false;
   }
 
   void ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
@@ -2155,19 +2163,25 @@
 
   IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc);
 
-  void MaybeParseMicrosoftAttributes(ParsedAttributes &attrs,
+  bool MaybeParseMicrosoftAttributes(ParsedAttributes &attrs,
                                      SourceLocation *endLoc = nullptr) {
-    if (getLangOpts().MicrosoftExt && Tok.is(tok::l_square))
+    if (getLangOpts().MicrosoftExt && Tok.is(tok::l_square)) {
       ParseMicrosoftAttributes(attrs, endLoc);
+      return true;
+    }
+    return false;
   }
   void ParseMicrosoftAttributes(ParsedAttributes &attrs,
                                 SourceLocation *endLoc = nullptr);
-  void MaybeParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
+  bool MaybeParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
                                     SourceLocation *End = nullptr) {
     const auto &LO = getLangOpts();
     if ((LO.MicrosoftExt || LO.Borland || LO.CUDA) &&
-        Tok.is(tok::kw___declspec))
+        Tok.is(tok::kw___declspec)) {
       ParseMicrosoftDeclSpecs(Attrs, End);
+      return true;
+    }
+    return false;
   }
   void ParseMicrosoftDeclSpecs(ParsedAttributes &Attrs,
                                SourceLocation *End = nullptr);
@@ -2174,6 +2188,48 @@
   bool ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName,
                                   SourceLocation AttrNameLoc,
                                   ParsedAttributes &Attrs);
+
+  enum ParseAttrKindMask {
+    PAKM_GNU = 1 << 0,
+    PAKM_Declspec = 1 << 1,
+    PAKM_CXX11 = 1 << 2,
+    PAKM_Microsoft = 1 << 3
+  };
+  
+  /// \brief Possibly parse attributes based on what syntaxes are desired,
+  /// allowing for the order to vary. e.g. with PAKM_GNU | PAKM_Declspec:
+  /// __attribute__((...)) __declspec(...) __attribute__((...)))
+  void MaybeParseAttributes(unsigned WhichAttrKinds,
+                            ParsedAttributesWithRange &Attrs,
+                            SourceLocation *End = nullptr,
+                            LateParsedAttrList *LateAttrs = nullptr) {
+    bool MoreToParse;
+    do {
+      // Assume there's nothing left to parse, but if any attributes are in fact
+      // parsed, loop to ensure all attribute combinations are parsed.
+      MoreToParse = false;
+      if (WhichAttrKinds & PAKM_GNU)
+        MoreToParse |= MaybeParseGNUAttributes(Attrs, End, LateAttrs);
+      if (WhichAttrKinds & PAKM_Declspec)
+        MoreToParse |= MaybeParseMicrosoftDeclSpecs(Attrs, End);
+      if (WhichAttrKinds & PAKM_CXX11)
+        MoreToParse |= MaybeParseCXX11Attributes(Attrs, End);
+      if (WhichAttrKinds & PAKM_Microsoft)
+        MoreToParse |= MaybeParseMicrosoftAttributes(Attrs, End);
+    } while (MoreToParse);
+  }
+
+  /// \brief Possibly parse attributes based on what syntaxes are desired,
+  /// allowing for the order to vary. e.g. with PAKM_GNU | PAKM_Declspec:
+  /// __attribute__((...)) __declspec(...) __attribute__((...)))
+  void MaybeParseAttributes(unsigned WhichAttrKinds, ParsedAttributes &Attrs,
+                            SourceLocation *End = nullptr,
+                            LateParsedAttrList *LateAttrs = nullptr) {
+    ParsedAttributesWithRange AttrsWithRange(AttrFactory);
+    MaybeParseAttributes(WhichAttrKinds, AttrsWithRange, End, LateAttrs);
+    Attrs.takeAllFrom(AttrsWithRange);
+  }
+
   void ParseMicrosoftTypeAttributes(ParsedAttributes &attrs);
   void DiagnoseAndSkipExtendedMicrosoftTypeAttributes();
   SourceLocation SkipExtendedMicrosoftTypeAttributes();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to