egorzhdan created this revision.
egorzhdan added a reviewer: arphaman.
Herald added a subscriber: jdoerfert.
Herald added a project: All.
egorzhdan requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This adds support for multiple attributes in `#pragma clang attribute push`, 
for example:

  #pragma clang attribute push 
(__attribute__((disable_sanitizer_instrumentation)) 
__attribute__((section("S"))), apply_to=variable(is_global))

or

  #pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], 
apply_to = function)

Related attributes can now be applied with a single pragma, which makes it 
harder for developers to make an accidental error later when editing the code.

rdar://78269653


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D121283

Files:
  clang/include/clang/Basic/AttrSubjectMatchRules.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Parse/ParsePragma.cpp
  clang/test/AST/pragma-multiple-attributes-declspec.cpp
  clang/test/AST/pragma-multiple-attributes.cpp
  clang/test/FixIt/fixit-pragma-attribute.c
  clang/test/FixIt/fixit-pragma-attribute.cpp
  clang/test/Parser/pragma-attribute-declspec.cpp
  clang/test/Parser/pragma-attribute.cpp
  clang/test/Parser/pragma-multiple-attributes.cpp

Index: clang/test/Parser/pragma-multiple-attributes.cpp
===================================================================
--- /dev/null
+++ clang/test/Parser/pragma-multiple-attributes.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -Wno-pragma-clang-attribute -verify %s
+
+#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function)
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[clang::disable_tail_calls]] __attribute__((annotate("test"))), apply_to = function)
+#pragma clang attribute pop
+
+#pragma clang attribute push (__attribute__((disable_tail_calls)) __attribute__((annotate("test"))), apply_to = function)
+#pragma clang attribute pop
+
+#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{expected ','}}
+
+#pragma clang attribute push (test [[noreturn]]) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
+
+#pragma clang attribute push ([[noreturn]] test) // expected-error {{expected ',' or an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
Index: clang/test/Parser/pragma-attribute.cpp
===================================================================
--- clang/test/Parser/pragma-attribute.cpp
+++ clang/test/Parser/pragma-attribute.cpp
@@ -154,9 +154,6 @@
 #pragma clang attribute push ([[gnu::abi_tag]], apply_to=any(function))
 #pragma clang attribute pop
 
-#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
-#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
-
 #pragma clang attribute push ([[gnu::abi_tag]], apply_to=namespace)
 #pragma clang attribute pop
 
Index: clang/test/Parser/pragma-attribute-declspec.cpp
===================================================================
--- clang/test/Parser/pragma-attribute-declspec.cpp
+++ clang/test/Parser/pragma-attribute-declspec.cpp
@@ -6,7 +6,8 @@
 
 #pragma clang attribute pop
 
-#pragma clang attribute push(__declspec(dllexport, dllimport), apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
+#pragma clang attribute push(__declspec(dllexport, dllimport), apply_to = function)
+#pragma clang attribute pop
 
 #pragma clang attribute push(__declspec(align), apply_to = variable) // expected-error {{attribute 'align' is not supported by '#pragma clang attribute'}}
 
Index: clang/test/FixIt/fixit-pragma-attribute.cpp
===================================================================
--- clang/test/FixIt/fixit-pragma-attribute.cpp
+++ clang/test/FixIt/fixit-pragma-attribute.cpp
@@ -39,7 +39,7 @@
 #pragma clang attribute pop
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))) apply_to=function)
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", "
 #pragma clang attribute push (__attribute__((abi_tag("a"))) = function)
@@ -48,35 +48,33 @@
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = "
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))) 22)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:63}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
-#pragma clang attribute push (__attribute__((abi_tag("a"))) function)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:69}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:63}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))) (function))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:71}:", apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:71}:", apply_to = any(function, namespace, record(unless(is_union)), variable)"
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), )
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:62}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:62}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), = function)
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), any(function))
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:61}:"apply_to = "
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), 22)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:64}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:64}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), 1, 2)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:66}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:66}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), function)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:70}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:70}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), (function))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:72}:"apply_to = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:61-[[@LINE-1]]:72}:"apply_to = any(function, namespace, record(unless(is_union)), variable)"
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to)
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = any(function, namespace, record(unless(is_union)), variable)"
 #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to any(function))
 // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:70}:" = "
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to 41 (22))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:78}:" = any(record(unless(is_union)), variable, function, namespace)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:70-[[@LINE-1]]:78}:" = any(function, namespace, record(unless(is_union)), variable)"
 
 // Don't give fix-it to attributes without a strict subject set
 #pragma clang attribute push (__attribute__((annotate("a"))))
Index: clang/test/FixIt/fixit-pragma-attribute.c
===================================================================
--- clang/test/FixIt/fixit-pragma-attribute.c
+++ clang/test/FixIt/fixit-pragma-attribute.c
@@ -3,4 +3,4 @@
 // rules that are not applicable in the current language mode.
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(record(unless(is_union)), variable, function)"
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to = any(function, record(unless(is_union)), variable)"
Index: clang/test/AST/pragma-multiple-attributes.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/pragma-multiple-attributes.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s
+
+#pragma clang attribute push (__attribute__((disable_sanitizer_instrumentation)) __attribute__((annotate("test1"))), apply_to=variable(is_global))
+int var1;
+#pragma clang attribute pop
+// CHECK: VarDecl {{.*}} var1
+// CHECK-NEXT: DisableSanitizerInstrumentationAttr {{.*}}
+// CHECK-NEXT: AnnotateAttr {{.*}} "test1"
+
+#pragma clang attribute push (__attribute__((disable_sanitizer_instrumentation)) [[clang::annotate("test2")]], apply_to=variable(is_global))
+int var2;
+#pragma clang attribute pop
+// CHECK: VarDecl {{.*}} var2
+// CHECK-NEXT: DisableSanitizerInstrumentationAttr {{.*}}
+// CHECK-NEXT: AnnotateAttr {{.*}} "test2"
+
+#pragma clang attribute push ([[clang::disable_sanitizer_instrumentation]] [[clang::annotate("test3")]], apply_to=variable(is_global))
+int var3;
+#pragma clang attribute pop
+// CHECK: VarDecl {{.*}} var3
+// CHECK-NEXT: DisableSanitizerInstrumentationAttr {{.*}}
+// CHECK-NEXT: AnnotateAttr {{.*}} "test3"
Index: clang/test/AST/pragma-multiple-attributes-declspec.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/pragma-multiple-attributes-declspec.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple i386-pc-win32 -fms-extensions -fms-compatibility -fsyntax-only -ast-dump %s | FileCheck %s
+
+#pragma clang attribute push (__declspec(dllexport) [[clang::annotate("test1")]], apply_to=variable(is_global))
+int var1;
+#pragma clang attribute pop
+// CHECK: VarDecl {{.*}} var1
+// CHECK-NEXT: DLLExportAttr {{.*}}
+// CHECK-NEXT: AnnotateAttr {{.*}} "test1"
+
+#pragma clang attribute push (__declspec(dllexport, dllimport) [[clang::annotate("test2")]], apply_to=function)
+void func2();
+#pragma clang attribute pop
+// CHECK: FunctionDecl {{.*}} func2
+// CHECK-NEXT: DLLExportAttr {{.*}}
+// CHECK-NEXT: AnnotateAttr {{.*}} "test2"
Index: clang/lib/Parse/ParsePragma.cpp
===================================================================
--- clang/lib/Parse/ParsePragma.cpp
+++ clang/lib/Parse/ParsePragma.cpp
@@ -21,6 +21,7 @@
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/Scope.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSwitch.h"
 using namespace clang;
 
@@ -1561,7 +1562,7 @@
 /// suggests the possible attribute subject rules in a fix-it together with
 /// any other missing tokens.
 DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
-    unsigned DiagID, ParsedAttr &Attribute,
+    unsigned DiagID, ParsedAttributes &Attrs,
     MissingAttributeSubjectRulesRecoveryPoint Point, Parser &PRef) {
   SourceLocation Loc = PRef.getEndOfPreviousToken();
   if (Loc.isInvalid())
@@ -1581,25 +1582,38 @@
   SourceRange FixItRange(Loc);
   if (EndPoint == MissingAttributeSubjectRulesRecoveryPoint::None) {
     // Gather the subject match rules that are supported by the attribute.
-    SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> SubjectMatchRuleSet;
-    Attribute.getMatchRules(PRef.getLangOpts(), SubjectMatchRuleSet);
-    if (SubjectMatchRuleSet.empty()) {
+    // Add all the possible rules initially.
+    llvm::BitVector IsMatchRuleAvailable(attr::SubjectMatchRule_Last, true);
+    // Remove the ones that are not supported by any of the attributes.
+    for (const ParsedAttr &Attribute : Attrs) {
+      SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> MatchRules;
+      Attribute.getMatchRules(PRef.getLangOpts(), MatchRules);
+      llvm::BitVector IsSupported(attr::SubjectMatchRule_Last);
+      for (const auto &Rule : MatchRules) {
+        // Ensure that the missing rule is reported in the fix-it only when it's
+        // supported in the current language mode.
+        if (!Rule.second)
+          continue;
+        IsSupported[Rule.first] = true;
+      }
+      IsMatchRuleAvailable &= IsSupported;
+    }
+    if (IsMatchRuleAvailable.count() == 0) {
       // FIXME: We can emit a "fix-it" with a subject list placeholder when
       // placeholders will be supported by the fix-its.
       return Diagnostic;
     }
     FixIt += "any(";
     bool NeedsComma = false;
-    for (const auto &I : SubjectMatchRuleSet) {
-      // Ensure that the missing rule is reported in the fix-it only when it's
-      // supported in the current language mode.
-      if (!I.second)
+    for (unsigned I = 0; I < attr::SubjectMatchRule_Last; I++) {
+      if (!IsMatchRuleAvailable[I])
         continue;
       if (NeedsComma)
         FixIt += ", ";
       else
         NeedsComma = true;
-      FixIt += attr::getSubjectMatchRuleSpelling(I.first);
+      FixIt += attr::getSubjectMatchRuleSpelling(
+          static_cast<attr::SubjectMatchRule>(I));
     }
     FixIt += ")";
     // Check if we need to remove the range
@@ -1649,99 +1663,104 @@
     ConsumeToken();
   };
 
-  if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
-    // Parse the CXX11 style attribute.
-    ParseCXX11AttributeSpecifier(Attrs);
-  } else if (Tok.is(tok::kw___attribute)) {
-    ConsumeToken();
-    if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
-                         "attribute"))
-      return SkipToEnd();
-    if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "("))
-      return SkipToEnd();
+  auto IsCXX11Attribute = [&] {
+    return Tok.is(tok::l_square) && NextToken().is(tok::l_square);
+  };
+  auto IsClangAttribute = [&] { return Tok.is(tok::kw___attribute); };
+  auto IsMicrosoftAttribute = [&] { return Tok.is(tok::kw___declspec); };
 
-    // FIXME: The practical usefulness of completion here is limited because
-    // we only get here if the line has balanced parens.
-    if (Tok.is(tok::code_completion)) {
-      cutOffParsing();
-      // FIXME: suppress completion of unsupported attributes?
-      Actions.CodeCompleteAttribute(AttributeCommonInfo::Syntax::AS_GNU);
-      return SkipToEnd();
-    }
+  do {
+    if (IsCXX11Attribute()) {
+      // Parse the CXX11 style attribute.
+      ParseCXX11AttributeSpecifier(Attrs);
+    } else if (IsClangAttribute()) {
+      ConsumeToken();
+      if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
+                           "attribute"))
+        return SkipToEnd();
+      if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "("))
+        return SkipToEnd();
+
+      // FIXME: The practical usefulness of completion here is limited because
+      // we only get here if the line has balanced parens.
+      if (Tok.is(tok::code_completion)) {
+        cutOffParsing();
+        // FIXME: suppress completion of unsupported attributes?
+        Actions.CodeCompleteAttribute(AttributeCommonInfo::Syntax::AS_GNU);
+        return SkipToEnd();
+      }
 
-    if (Tok.isNot(tok::identifier)) {
-      Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
-      SkipToEnd();
-      return;
-    }
-    IdentifierInfo *AttrName = Tok.getIdentifierInfo();
-    SourceLocation AttrNameLoc = ConsumeToken();
+      if (Tok.isNot(tok::identifier)) {
+        Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
+        SkipToEnd();
+        return;
+      }
+      IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+      SourceLocation AttrNameLoc = ConsumeToken();
 
-    if (Tok.isNot(tok::l_paren))
-      Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
-                   ParsedAttr::AS_GNU);
-    else
-      ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
-                            /*ScopeName=*/nullptr,
-                            /*ScopeLoc=*/SourceLocation(), ParsedAttr::AS_GNU,
-                            /*Declarator=*/nullptr);
-
-    if (ExpectAndConsume(tok::r_paren))
-      return SkipToEnd();
-    if (ExpectAndConsume(tok::r_paren))
-      return SkipToEnd();
-  } else if (Tok.is(tok::kw___declspec)) {
-    ParseMicrosoftDeclSpecs(Attrs);
-  } else {
-    Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax);
-    if (Tok.getIdentifierInfo()) {
-      // If we suspect that this is an attribute suggest the use of
-      // '__attribute__'.
-      if (ParsedAttr::getParsedKind(
-              Tok.getIdentifierInfo(), /*ScopeName=*/nullptr,
-              ParsedAttr::AS_GNU) != ParsedAttr::UnknownAttribute) {
-        SourceLocation InsertStartLoc = Tok.getLocation();
-        ConsumeToken();
-        if (Tok.is(tok::l_paren)) {
-          ConsumeAnyToken();
-          SkipUntil(tok::r_paren, StopBeforeMatch);
-          if (Tok.isNot(tok::r_paren))
-            return SkipToEnd();
+      if (Tok.isNot(tok::l_paren))
+        Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+                     ParsedAttr::AS_GNU);
+      else
+        ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
+                              /*ScopeName=*/nullptr,
+                              /*ScopeLoc=*/SourceLocation(), ParsedAttr::AS_GNU,
+                              /*Declarator=*/nullptr);
+
+      if (ExpectAndConsume(tok::r_paren))
+        return SkipToEnd();
+      if (ExpectAndConsume(tok::r_paren))
+        return SkipToEnd();
+    } else if (IsMicrosoftAttribute()) {
+      ParseMicrosoftDeclSpecs(Attrs);
+    } else {
+      Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax)
+          << !Attrs.empty();
+      if (Tok.getIdentifierInfo()) {
+        // If we suspect that this is an attribute suggest the use of
+        // '__attribute__'.
+        if (ParsedAttr::getParsedKind(
+                Tok.getIdentifierInfo(), /*ScopeName=*/nullptr,
+                ParsedAttr::AS_GNU) != ParsedAttr::UnknownAttribute) {
+          SourceLocation InsertStartLoc = Tok.getLocation();
+          ConsumeToken();
+          if (Tok.is(tok::l_paren)) {
+            ConsumeAnyToken();
+            SkipUntil(tok::r_paren, StopBeforeMatch);
+            if (Tok.isNot(tok::r_paren))
+              return SkipToEnd();
+          }
+          Diag(Tok, diag::note_pragma_attribute_use_attribute_kw)
+              << FixItHint::CreateInsertion(InsertStartLoc, "__attribute__((")
+              << FixItHint::CreateInsertion(Tok.getEndLoc(), "))");
         }
-        Diag(Tok, diag::note_pragma_attribute_use_attribute_kw)
-            << FixItHint::CreateInsertion(InsertStartLoc, "__attribute__((")
-            << FixItHint::CreateInsertion(Tok.getEndLoc(), "))");
       }
+      SkipToEnd();
+      return;
     }
-    SkipToEnd();
-    return;
-  }
-
-  if (Attrs.empty() || Attrs.begin()->isInvalid()) {
-    SkipToEnd();
-    return;
-  }
 
-  // Ensure that we don't have more than one attribute.
-  if (Attrs.size() > 1) {
-    SourceLocation Loc = Attrs[1].getLoc();
-    Diag(Loc, diag::err_pragma_attribute_multiple_attributes);
-    SkipToEnd();
-    return;
-  }
+    if (Attrs.empty() || Attrs.begin()->isInvalid()) {
+      SkipToEnd();
+      return;
+    }
+  } while (IsCXX11Attribute() || IsClangAttribute() || IsMicrosoftAttribute() ||
+           (Tok.getIdentifierInfo() &&
+            !Tok.getIdentifierInfo()->isStr("apply_to") &&
+            !Tok.getIdentifierInfo()->isStr("any")));
 
-  ParsedAttr &Attribute = *Attrs.begin();
-  if (!Attribute.isSupportedByPragmaAttribute()) {
-    Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
-        << Attribute;
-    SkipToEnd();
-    return;
+  for (const ParsedAttr &Attribute : Attrs) {
+    if (!Attribute.isSupportedByPragmaAttribute()) {
+      Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
+          << Attribute;
+      SkipToEnd();
+      return;
+    }
   }
 
   // Parse the subject-list.
   if (!TryConsumeToken(tok::comma)) {
     createExpectedAttributeSubjectRulesTokenDiagnostic(
-        diag::err_expected, Attribute,
+        diag::err_expected, Attrs,
         MissingAttributeSubjectRulesRecoveryPoint::Comma, *this)
         << tok::comma;
     SkipToEnd();
@@ -1750,7 +1769,7 @@
 
   if (Tok.isNot(tok::identifier)) {
     createExpectedAttributeSubjectRulesTokenDiagnostic(
-        diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
+        diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
         MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
     SkipToEnd();
     return;
@@ -1758,7 +1777,7 @@
   const IdentifierInfo *II = Tok.getIdentifierInfo();
   if (!II->isStr("apply_to")) {
     createExpectedAttributeSubjectRulesTokenDiagnostic(
-        diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
+        diag::err_pragma_attribute_invalid_subject_set_specifier, Attrs,
         MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
     SkipToEnd();
     return;
@@ -1767,7 +1786,7 @@
 
   if (!TryConsumeToken(tok::equal)) {
     createExpectedAttributeSubjectRulesTokenDiagnostic(
-        diag::err_expected, Attribute,
+        diag::err_expected, Attrs,
         MissingAttributeSubjectRulesRecoveryPoint::Equals, *this)
         << tok::equal;
     SkipToEnd();
@@ -1797,8 +1816,10 @@
   if (Info->Action == PragmaAttributeInfo::Push)
     Actions.ActOnPragmaAttributeEmptyPush(PragmaLoc, Info->Namespace);
 
-  Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
-                                        std::move(SubjectMatchRules));
+  for (ParsedAttr &Attribute : Attrs) {
+    Actions.ActOnPragmaAttributeAttribute(Attribute, PragmaLoc,
+                                          SubjectMatchRules);
+  }
 }
 
 // #pragma GCC visibility comes in two variants:
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -600,6 +600,8 @@
 bool Parser::ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName,
                                         SourceLocation AttrNameLoc,
                                         ParsedAttributes &Attrs) {
+  unsigned ExistingAttrs = Attrs.size();
+
   // If the attribute isn't known, we will not attempt to parse any
   // arguments.
   if (!hasAttribute(AttrSyntax::Declspec, nullptr, AttrName,
@@ -732,7 +734,7 @@
 
   // If this attribute's args were parsed, and it was expected to have
   // arguments but none were provided, emit a diagnostic.
-  if (!Attrs.empty() && Attrs.begin()->getMaxArgs() && !NumArgs) {
+  if (ExistingAttrs < Attrs.size() && Attrs.back().getMaxArgs() && !NumArgs) {
     Diag(OpenParenLoc, diag::err_attribute_requires_arguments) << AttrName;
     return false;
   }
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1237,10 +1237,8 @@
   "extra tokens after attribute in a '#pragma clang attribute push'">;
 def err_pragma_attribute_unsupported_attribute : Error<
   "attribute %0 is not supported by '#pragma clang attribute'">;
-def err_pragma_attribute_multiple_attributes : Error<
-  "more than one attribute specified in '#pragma clang attribute push'">;
 def err_pragma_attribute_expected_attribute_syntax : Error<
-  "expected an attribute that is specified using the GNU, C++11 or '__declspec'"
+  "expected %select{|',' or }0an attribute that is specified using the GNU, C++11 or '__declspec'"
   " syntax">;
 def note_pragma_attribute_use_attribute_kw : Note<"use the GNU '__attribute__' "
   "syntax">;
Index: clang/include/clang/Basic/AttrSubjectMatchRules.h
===================================================================
--- clang/include/clang/Basic/AttrSubjectMatchRules.h
+++ clang/include/clang/Basic/AttrSubjectMatchRules.h
@@ -18,6 +18,9 @@
 /// A list of all the recognized kinds of attributes.
 enum SubjectMatchRule {
 #define ATTR_MATCH_RULE(X, Spelling, IsAbstract) X,
+#include "clang/Basic/AttrSubMatchRulesList.inc"
+  SubjectMatchRule_Last = -1
+#define ATTR_MATCH_RULE(X, Spelling, IsAbstract) +1
 #include "clang/Basic/AttrSubMatchRulesList.inc"
 };
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to