Add #pragma clang attribute

This is a recommit of r300539 that was reverted in r300543 due to test failures.
The original commit message is displayed below:

The new '#pragma clang attribute' directive can be used to apply attributes to
multiple declarations. An attribute must satisfy the following conditions to
be supported by the pragma:
- It must have a subject list that's defined in the TableGen file.
- It must be documented.
- It must not be late parsed.
- It must have a GNU/C++11 spelling.

Differential Revision: https://reviews.llvm.org/D30009

llvm-svn: 300556
diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp
index 55e9601..724db45 100644
--- a/clang/lib/Sema/AttributeList.cpp
+++ b/clang/lib/Sema/AttributeList.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
+#include "clang/Basic/AttrSubjectMatchRules.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Sema/SemaInternal.h"
@@ -160,12 +161,16 @@
   unsigned IsType : 1;
   unsigned IsStmt : 1;
   unsigned IsKnownToGCC : 1;
+  unsigned IsSupportedByPragmaAttribute : 1;
 
   bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr,
                                const Decl *);
   bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr);
   bool (*ExistsInTarget)(const TargetInfo &Target);
   unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr);
+  void (*GetPragmaAttributeMatchRules)(
+      llvm::SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &Rules,
+      const LangOptions &LangOpts);
 };
 
 namespace {
@@ -192,6 +197,18 @@
   return getInfo(*this).DiagAppertainsToDecl(S, *this, D);
 }
 
+bool AttributeList::appliesToDecl(const Decl *D,
+                                  attr::SubjectMatchRule MatchRule) const {
+  return checkAttributeMatchRuleAppliesTo(D, MatchRule);
+}
+
+void AttributeList::getMatchRules(
+    const LangOptions &LangOpts,
+    SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &MatchRules)
+    const {
+  return getInfo(*this).GetPragmaAttributeMatchRules(MatchRules, LangOpts);
+}
+
 bool AttributeList::diagnoseLangOpts(Sema &S) const {
   return getInfo(*this).DiagLangOpts(S, *this);
 }
@@ -216,6 +233,10 @@
   return getInfo(*this).IsKnownToGCC;
 }
 
+bool AttributeList::isSupportedByPragmaAttribute() const {
+  return getInfo(*this).IsSupportedByPragmaAttribute;
+}
+
 unsigned AttributeList::getSemanticSpelling() const {
   return getInfo(*this).SpellingIndexToSemanticSpelling(*this);
 }
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 294b560..950f040 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -71,42 +71,35 @@
 }
 
 Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
-           TranslationUnitKind TUKind,
-           CodeCompleteConsumer *CodeCompleter)
-  : ExternalSource(nullptr),
-    isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()),
-    LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer),
-    Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
-    CollectStats(false), CodeCompleter(CodeCompleter),
-    CurContext(nullptr), OriginalLexicalContext(nullptr),
-    MSStructPragmaOn(false),
-    MSPointerToMemberRepresentationMethod(
-        LangOpts.getMSPointerToMemberRepresentationMethod()),
-    VtorDispStack(MSVtorDispAttr::Mode(LangOpts.VtorDispMode)),
-    PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr),
-    ConstSegStack(nullptr), CodeSegStack(nullptr), CurInitSeg(nullptr),
-    VisContext(nullptr),
-    IsBuildingRecoveryCallExpr(false),
-    Cleanup{}, LateTemplateParser(nullptr),
-    LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
-    StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
-    CXXTypeInfoDecl(nullptr), MSVCGuidDecl(nullptr),
-    NSNumberDecl(nullptr), NSValueDecl(nullptr),
-    NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr),
-    ValueWithBytesObjCTypeMethod(nullptr),
-    NSArrayDecl(nullptr), ArrayWithObjectsMethod(nullptr),
-    NSDictionaryDecl(nullptr), DictionaryWithObjectsMethod(nullptr),
-    GlobalNewDeleteDeclared(false),
-    TUKind(TUKind),
-    NumSFINAEErrors(0),
-    CachedFakeTopLevelModule(nullptr),
-    AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
-    NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
-    CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
-    TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
-    VarDataSharingAttributesStack(nullptr), CurScope(nullptr),
-    Ident_super(nullptr), Ident___float128(nullptr)
-{
+           TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter)
+    : ExternalSource(nullptr), isMultiplexExternalSource(false),
+      FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
+      Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
+      SourceMgr(PP.getSourceManager()), CollectStats(false),
+      CodeCompleter(CodeCompleter), CurContext(nullptr),
+      OriginalLexicalContext(nullptr), MSStructPragmaOn(false),
+      MSPointerToMemberRepresentationMethod(
+          LangOpts.getMSPointerToMemberRepresentationMethod()),
+      VtorDispStack(MSVtorDispAttr::Mode(LangOpts.VtorDispMode)), PackStack(0),
+      DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
+      CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr),
+      PragmaAttributeCurrentTargetDecl(nullptr),
+      IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr),
+      LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
+      StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
+      CXXTypeInfoDecl(nullptr), MSVCGuidDecl(nullptr), NSNumberDecl(nullptr),
+      NSValueDecl(nullptr), NSStringDecl(nullptr),
+      StringWithUTF8StringMethod(nullptr),
+      ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
+      ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
+      DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
+      TUKind(TUKind), NumSFINAEErrors(0), CachedFakeTopLevelModule(nullptr),
+      AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
+      NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
+      CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
+      TyposCorrected(0), AnalysisWarnings(*this),
+      ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
+      CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
   TUScope = nullptr;
 
   LoadedExternalKnownNamespaces = false;
@@ -731,6 +724,8 @@
     CheckDelayedMemberExceptionSpecs();
   }
 
+  DiagnoseUnterminatedPragmaAttribute();
+
   // All delayed member exception specs should be checked or we end up accepting
   // incompatible declarations.
   // FIXME: This is wrong for TUKind == TU_Prefix. In that case, we need to
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index c6e3cc8..dfc5d6c 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -368,6 +368,217 @@
   D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc));
 }
 
+namespace {
+
+Optional<attr::SubjectMatchRule>
+getParentAttrMatcherRule(attr::SubjectMatchRule Rule) {
+  using namespace attr;
+  switch (Rule) {
+  default:
+    return None;
+#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract)
+#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated)    \
+  case Value:                                                                  \
+    return Parent;
+#include "clang/Basic/AttrSubMatchRulesList.inc"
+  }
+}
+
+bool isNegatedAttrMatcherSubRule(attr::SubjectMatchRule Rule) {
+  using namespace attr;
+  switch (Rule) {
+  default:
+    return false;
+#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract)
+#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated)    \
+  case Value:                                                                  \
+    return IsNegated;
+#include "clang/Basic/AttrSubMatchRulesList.inc"
+  }
+}
+
+CharSourceRange replacementRangeForListElement(const Sema &S,
+                                               SourceRange Range) {
+  // Make sure that the ',' is removed as well.
+  SourceLocation AfterCommaLoc = Lexer::findLocationAfterToken(
+      Range.getEnd(), tok::comma, S.getSourceManager(), S.getLangOpts(),
+      /*SkipTrailingWhitespaceAndNewLine=*/false);
+  if (AfterCommaLoc.isValid())
+    return CharSourceRange::getCharRange(Range.getBegin(), AfterCommaLoc);
+  else
+    return CharSourceRange::getTokenRange(Range);
+}
+
+std::string
+attrMatcherRuleListToString(ArrayRef<attr::SubjectMatchRule> Rules) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  for (const auto &I : llvm::enumerate(Rules)) {
+    if (I.index())
+      OS << (I.index() == Rules.size() - 1 ? ", and " : ", ");
+    OS << "'" << attr::getSubjectMatchRuleSpelling(I.value()) << "'";
+  }
+  return OS.str();
+}
+
+} // end anonymous namespace
+
+void Sema::ActOnPragmaAttributePush(AttributeList &Attribute,
+                                    SourceLocation PragmaLoc,
+                                    attr::ParsedSubjectMatchRuleSet Rules) {
+  SmallVector<attr::SubjectMatchRule, 4> SubjectMatchRules;
+  // Gather the subject match rules that are supported by the attribute.
+  SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4>
+      StrictSubjectMatchRuleSet;
+  Attribute.getMatchRules(LangOpts, StrictSubjectMatchRuleSet);
+
+  // Figure out which subject matching rules are valid.
+  if (StrictSubjectMatchRuleSet.empty()) {
+    // Check for contradicting match rules. Contradicting match rules are
+    // either:
+    //  - a top-level rule and one of its sub-rules. E.g. variable and
+    //    variable(is_parameter).
+    //  - a sub-rule and a sibling that's negated. E.g.
+    //    variable(is_thread_local) and variable(unless(is_parameter))
+    llvm::SmallDenseMap<attr::SubjectMatchRule,
+                        std::pair<attr::SubjectMatchRule, SourceRange>, 2>
+        RulesToFirstSpecifiedNegatedSubRule;
+    for (const auto &Rule : Rules) {
+      Optional<attr::SubjectMatchRule> ParentRule =
+          getParentAttrMatcherRule(Rule.first);
+      if (!ParentRule)
+        continue;
+      auto It = Rules.find(*ParentRule);
+      if (It != Rules.end()) {
+        // A sub-rule contradicts a parent rule.
+        Diag(Rule.second.getBegin(),
+             diag::err_pragma_attribute_matcher_subrule_contradicts_rule)
+            << attr::getSubjectMatchRuleSpelling(Rule.first)
+            << attr::getSubjectMatchRuleSpelling(*ParentRule) << It->second
+            << FixItHint::CreateRemoval(
+                   replacementRangeForListElement(*this, Rule.second));
+        // Keep going without removing this rule as it won't change the set of
+        // declarations that receive the attribute.
+        continue;
+      }
+      if (isNegatedAttrMatcherSubRule(Rule.first))
+        RulesToFirstSpecifiedNegatedSubRule.insert(
+            std::make_pair(*ParentRule, Rule));
+    }
+    bool IgnoreNegatedSubRules = false;
+    for (const auto &Rule : Rules) {
+      Optional<attr::SubjectMatchRule> ParentRule =
+          getParentAttrMatcherRule(Rule.first);
+      if (!ParentRule)
+        continue;
+      auto It = RulesToFirstSpecifiedNegatedSubRule.find(*ParentRule);
+      if (It != RulesToFirstSpecifiedNegatedSubRule.end() &&
+          It->second != Rule) {
+        // Negated sub-rule contradicts another sub-rule.
+        Diag(
+            It->second.second.getBegin(),
+            diag::
+                err_pragma_attribute_matcher_negated_subrule_contradicts_subrule)
+            << attr::getSubjectMatchRuleSpelling(It->second.first)
+            << attr::getSubjectMatchRuleSpelling(Rule.first) << Rule.second
+            << FixItHint::CreateRemoval(
+                   replacementRangeForListElement(*this, It->second.second));
+        // Keep going but ignore all of the negated sub-rules.
+        IgnoreNegatedSubRules = true;
+        RulesToFirstSpecifiedNegatedSubRule.erase(It);
+      }
+    }
+
+    if (!IgnoreNegatedSubRules) {
+      for (const auto &Rule : Rules)
+        SubjectMatchRules.push_back(Rule.first);
+    } else {
+      for (const auto &Rule : Rules) {
+        if (!isNegatedAttrMatcherSubRule(Rule.first))
+          SubjectMatchRules.push_back(Rule.first);
+      }
+    }
+    Rules.clear();
+  } else {
+    for (const auto &Rule : StrictSubjectMatchRuleSet) {
+      if (Rules.erase(Rule.first)) {
+        // Add the rule to the set of attribute receivers only if it's supported
+        // in the current language mode.
+        if (Rule.second)
+          SubjectMatchRules.push_back(Rule.first);
+      }
+    }
+  }
+
+  if (!Rules.empty()) {
+    auto Diagnostic =
+        Diag(PragmaLoc, diag::err_pragma_attribute_invalid_matchers)
+        << Attribute.getName();
+    SmallVector<attr::SubjectMatchRule, 2> ExtraRules;
+    for (const auto &Rule : Rules) {
+      ExtraRules.push_back(Rule.first);
+      Diagnostic << FixItHint::CreateRemoval(
+          replacementRangeForListElement(*this, Rule.second));
+    }
+    Diagnostic << attrMatcherRuleListToString(ExtraRules);
+  }
+
+  PragmaAttributeStack.push_back(
+      {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false});
+}
+
+void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) {
+  if (PragmaAttributeStack.empty()) {
+    Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch);
+    return;
+  }
+  const PragmaAttributeEntry &Entry = PragmaAttributeStack.back();
+  if (!Entry.IsUsed) {
+    assert(Entry.Attribute && "Expected an attribute");
+    Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused)
+        << Entry.Attribute->getName();
+    Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here);
+  }
+  PragmaAttributeStack.pop_back();
+}
+
+void Sema::AddPragmaAttributes(Scope *S, Decl *D) {
+  if (PragmaAttributeStack.empty())
+    return;
+  for (auto &Entry : PragmaAttributeStack) {
+    const AttributeList *Attribute = Entry.Attribute;
+    assert(Attribute && "Expected an attribute");
+
+    // Ensure that the attribute can be applied to the given declaration.
+    bool Applies = false;
+    for (const auto &Rule : Entry.MatchRules) {
+      if (Attribute->appliesToDecl(D, Rule)) {
+        Applies = true;
+        break;
+      }
+    }
+    if (!Applies)
+      continue;
+    Entry.IsUsed = true;
+    assert(!Attribute->getNext() && "Expected just one attribute");
+    PragmaAttributeCurrentTargetDecl = D;
+    ProcessDeclAttributeList(S, D, Attribute);
+    PragmaAttributeCurrentTargetDecl = nullptr;
+  }
+}
+
+void Sema::PrintPragmaAttributeInstantiationPoint() {
+  assert(PragmaAttributeCurrentTargetDecl && "Expected an active declaration");
+  Diags.Report(PragmaAttributeCurrentTargetDecl->getLocStart(),
+               diag::note_pragma_attribute_applied_decl_here);
+}
+
+void Sema::DiagnoseUnterminatedPragmaAttribute() {
+  if (PragmaAttributeStack.empty())
+    return;
+  Diag(PragmaAttributeStack.back().Loc, diag::err_pragma_attribute_no_pop_eof);
+}
+
 void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) {
   if(On)
     OptimizeOffPragmaLocation = SourceLocation();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c6a0b01..075e87b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13674,6 +13674,7 @@
 
   if (Attr)
     ProcessDeclAttributeList(S, New, Attr);
+  AddPragmaAttributes(S, New);
 
   // If this has an identifier, add it to the scope stack.
   if (TUK == TUK_Friend) {
@@ -15185,6 +15186,7 @@
 
   // Process attributes.
   if (Attr) ProcessDeclAttributeList(S, New, Attr);
+  AddPragmaAttributes(S, New);
 
   // Register this decl in the current scope stack.
   New->setAccess(TheEnumDecl->getAccess());
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a1ba9de..ae941c8 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6676,6 +6676,9 @@
   // Finally, apply any attributes on the decl itself.
   if (const AttributeList *Attrs = PD.getAttributes())
     ProcessDeclAttributeList(S, D, Attrs);
+
+  // Apply additional attributes specified by '#pragma clang attribute'.
+  AddPragmaAttributes(S, D);
 }
 
 /// Is the given declaration allowed to use a forbidden type?
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fd3f266..b543a73 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -8445,6 +8445,7 @@
     Namespc->setInvalidDecl();
   
   ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
+  AddPragmaAttributes(DeclRegionScope, Namespc);
 
   // FIXME: Should we be merging attributes?
   if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
@@ -9931,6 +9932,7 @@
     NewTD->setInvalidDecl();
 
   ProcessDeclAttributeList(S, NewTD, AttrList);
+  AddPragmaAttributes(S, NewTD);
 
   CheckTypedefForVariablyModifiedType(S, NewTD);
   Invalid |= NewTD->isInvalidDecl();
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index e50f8b2..4f51cd3 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -993,6 +993,7 @@
   
   if (AttrList)
     ProcessDeclAttributeList(TUScope, IDecl, AttrList);
+  AddPragmaAttributes(TUScope, IDecl);
   PushOnScopeChains(IDecl, TUScope);
 
   // Start the definition of this class. If we're in a redefinition case, there 
@@ -1176,7 +1177,8 @@
   
   if (AttrList)
     ProcessDeclAttributeList(TUScope, PDecl, AttrList);
-  
+  AddPragmaAttributes(TUScope, PDecl);
+
   // Merge attributes from previous declarations.
   if (PrevDecl)
     mergeDeclAttributes(PDecl, PrevDecl);
@@ -1706,7 +1708,8 @@
     
     if (attrList)
       ProcessDeclAttributeList(TUScope, PDecl, attrList);
-    
+    AddPragmaAttributes(TUScope, PDecl);
+
     if (PrevDecl)
       mergeDeclAttributes(PDecl, PrevDecl);
 
@@ -1805,6 +1808,7 @@
 
   if (AttrList)
     ProcessDeclAttributeList(TUScope, CDecl, AttrList);
+  AddPragmaAttributes(TUScope, CDecl);
 
   CheckObjCDeclScope(CDecl);
   return ActOnObjCContainerStartDefinition(CDecl);
@@ -1954,6 +1958,7 @@
                                       ClassName, /*typeParamList=*/nullptr,
                                       /*PrevDecl=*/nullptr, ClassLoc,
                                       true);
+    AddPragmaAttributes(TUScope, IDecl);
     IDecl->startDefinition();
     if (SDecl) {
       IDecl->setSuperClass(Context.getTrivialTypeSourceInfo(
@@ -3043,7 +3048,7 @@
                                   ClassName, TypeParams, PrevIDecl,
                                   IdentLocs[i]);
     IDecl->setAtEndRange(IdentLocs[i]);
-    
+
     PushOnScopeChains(IDecl, TUScope);
     CheckObjCDeclScope(IDecl);
     DeclsInGroup.push_back(IDecl);
@@ -4399,6 +4404,7 @@
 
     // Apply the attributes to the parameter.
     ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs);
+    AddPragmaAttributes(TUScope, Param);
 
     if (Param->hasAttr<BlocksAttr>()) {
       Diag(Param->getLocation(), diag::err_block_on_nonlocal);
@@ -4429,6 +4435,7 @@
 
   if (AttrList)
     ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
+  AddPragmaAttributes(TUScope, ObjCMethod);
 
   // Add the method now.
   const ObjCMethodDecl *PrevMethod = nullptr;