37 changes: 37 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,43 @@ def err_pragma_optimize_invalid_argument : Error<
"expected 'on' or 'off'">;
def err_pragma_optimize_extra_argument : Error<
"unexpected extra argument '%0' to '#pragma clang optimize'">;
// - #pragma clang attribute
def err_pragma_attribute_expected_push_pop : Error<
"expected 'push' or 'pop' after '#pragma clang attribute'">;
def err_pragma_attribute_invalid_argument : Error<
"unexpected argument '%0' to '#pragma clang attribute'; "
"expected 'push' or 'pop'">;
def err_pragma_attribute_expected_attribute : Error<
"expected an attribute after '('">;
def err_pragma_attribute_expected_attribute_name : Error<
"expected identifier that represents an attribute name">;
def err_pragma_attribute_extra_tokens_after_attribute : Error<
"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'"
" syntax">;
def note_pragma_attribute_use_attribute_kw : Note<"use the GNU '__attribute__' "
"syntax">;
def err_pragma_attribute_invalid_subject_set_specifier : Error<
"expected attribute subject set specifier 'apply_to'">;
def err_pragma_attribute_expected_subject_identifier : Error<
"expected an identifier that corresponds to an attribute subject rule">;
def err_pragma_attribute_unknown_subject_rule : Error<
"unknown attribute subject rule '%0'">;
def err_pragma_attribute_expected_subject_sub_identifier : Error<
"expected an identifier that corresponds to an attribute subject matcher "
"sub-rule; '%0' matcher %select{does not support sub-rules|supports the "
"following sub-rules: %2|}1">;
def err_pragma_attribute_unknown_subject_sub_rule : Error<
"%select{invalid use of|unknown}2 attribute subject matcher sub-rule '%0'; "
"'%1' matcher %select{does not support sub-rules|supports the following "
"sub-rules: %3}2">;
def err_pragma_attribute_duplicate_subject : Error<
"duplicate attribute subject matcher '%0'">;

def err_opencl_unroll_hint_on_non_loop : Error<
"OpenCL only supports 'opencl_unroll_hint' attribute on for, while, and do statements">;
Expand Down
19 changes: 19 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,25 @@ def err_pragma_loop_compatibility : Error<
def err_pragma_loop_precedes_nonloop : Error<
"expected a for, while, or do-while loop to follow '%0'">;

def err_pragma_attribute_matcher_subrule_contradicts_rule : Error<
"redundant attribute subject matcher sub-rule '%0'; '%1' already matches "
"those declarations">;
def err_pragma_attribute_matcher_negated_subrule_contradicts_subrule : Error<
"negated attribute subject matcher sub-rule '%0' contradicts sub-rule '%1'">;
def err_pragma_attribute_invalid_matchers : Error<
"attribute %0 can't be applied to %1">;
def err_pragma_attribute_stack_mismatch : Error<
"'#pragma clang attribute pop' with no matching '#pragma clang attribute push'">;
def warn_pragma_attribute_unused : Warning<
"unused attribute %0 in '#pragma clang attribute push' region">,
InGroup<PragmaClangAttribute>;
def note_pragma_attribute_region_ends_here : Note<
"'#pragma clang attribute push' regions ends here">;
def err_pragma_attribute_no_pop_eof : Error<"unterminated "
"'#pragma clang attribute push' at end of file">;
def note_pragma_attribute_applied_decl_here : Note<
"when applied to this declaration">;

/// Objective-C parser diagnostics
def err_duplicate_class_def : Error<
"duplicate interface definition for class %0">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,9 @@ ANNOTATION(pragma_loop_hint)

ANNOTATION(pragma_fp)

// Annotation for the attribute pragma directives - #pragma clang attribute ...
ANNOTATION(pragma_attribute)

// Annotations for module import translated from #include etc.
ANNOTATION(module_include)
ANNOTATION(module_begin)
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Parse/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ clang_tablegen(AttrParserStringSwitches.inc -gen-clang-attr-parser-string-switch
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE ../Basic/Attr.td
TARGET ClangAttrParserStringSwitches)

clang_tablegen(AttrSubMatchRulesParserStringSwitches.inc
-gen-clang-attr-subject-match-rules-parser-string-switches
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE ../Basic/Attr.td
TARGET ClangAttrSubMatchRulesParserStringSwitches)
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> UnrollHintHandler;
std::unique_ptr<PragmaHandler> NoUnrollHintHandler;
std::unique_ptr<PragmaHandler> FPHandler;
std::unique_ptr<PragmaHandler> AttributePragmaHandler;

std::unique_ptr<CommentHandler> CommentSemaHandler;

Expand Down Expand Up @@ -565,6 +566,12 @@ class Parser : public CodeCompletionHandler {
/// #pragma clang loop and #pragma unroll.
bool HandlePragmaLoopHint(LoopHint &Hint);

bool ParsePragmaAttributeSubjectMatchRuleSet(
attr::ParsedSubjectMatchRuleSet &SubjectMatchRules,
SourceLocation &AnyLoc, SourceLocation &LastMatchRuleEndLoc);

void HandlePragmaAttribute();

/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/AttributeList.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_SEMA_ATTRIBUTELIST_H
#define LLVM_CLANG_SEMA_ATTRIBUTELIST_H

#include "clang/Basic/AttrSubjectMatchRules.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/VersionTuple.h"
Expand Down Expand Up @@ -509,9 +510,14 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
unsigned getMaxArgs() const;
bool hasVariadicArg() const;
bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const;
bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const;
void getMatchRules(const LangOptions &LangOpts,
SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>>
&MatchRules) const;
bool diagnoseLangOpts(class Sema &S) const;
bool existsInTarget(const TargetInfo &Target) const;
bool isKnownToGCC() const;
bool isSupportedByPragmaAttribute() const;

/// \brief If the parsed attribute has a semantic equivalent, and it would
/// have a semantic Spelling enumeration (due to having semantically-distinct
Expand Down Expand Up @@ -774,6 +780,8 @@ class ParsedAttributes {
void clear() { list = nullptr; pool.clear(); }
AttributeList *getList() const { return list; }

void clearListOnly() { list = nullptr; }

/// Returns a reference to the attribute list. Try not to introduce
/// dependencies on this method, it may not be long-lived.
AttributeList *&getListRef() { return list; }
Expand Down
32 changes: 32 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,20 @@ class Sema {
/// VisContext - Manages the stack for \#pragma GCC visibility.
void *VisContext; // Really a "PragmaVisStack*"

/// \brief This represents the stack of attributes that were pushed by
/// \#pragma clang attribute.
struct PragmaAttributeEntry {
SourceLocation Loc;
AttributeList *Attribute;
SmallVector<attr::SubjectMatchRule, 4> MatchRules;
bool IsUsed;
};
SmallVector<PragmaAttributeEntry, 2> PragmaAttributeStack;

/// \brief The declaration that is currently receiving an attribute from the
/// #pragma attribute stack.
const Decl *PragmaAttributeCurrentTargetDecl;

/// \brief This represents the last location of a "#pragma clang optimize off"
/// directive if such a directive has not been closed by an "on" yet. If
/// optimizations are currently "on", this is set to an invalid location.
Expand Down Expand Up @@ -7206,9 +7220,13 @@ class Sema {
PrintInstantiationStack();
LastEmittedCodeSynthesisContextDepth = CodeSynthesisContexts.size();
}
if (PragmaAttributeCurrentTargetDecl)
PrintPragmaAttributeInstantiationPoint();
}
void PrintInstantiationStack();

void PrintPragmaAttributeInstantiationPoint();

/// \brief Determines whether we are currently in a context where
/// template argument substitution failures are not considered
/// errors.
Expand Down Expand Up @@ -8152,6 +8170,20 @@ class Sema {
/// the appropriate attribute.
void AddCFAuditedAttribute(Decl *D);

/// \brief Called on well-formed '\#pragma clang attribute push'.
void ActOnPragmaAttributePush(AttributeList &Attribute,
SourceLocation PragmaLoc,
attr::ParsedSubjectMatchRuleSet Rules);

/// \brief Called on well-formed '\#pragma clang attribute pop'.
void ActOnPragmaAttributePop(SourceLocation PragmaLoc);

/// \brief Adds the attributes that have been specified using the
/// '\#pragma clang attribute push' directives to the given declaration.
void AddPragmaAttributes(Scope *S, Decl *D);

void DiagnoseUnterminatedPragmaAttribute();

/// \brief Called on well formed \#pragma clang optimize.
void ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc);

Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Basic/Attributes.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "clang/Basic/Attributes.h"
#include "clang/Basic/AttrSubjectMatchRules.h"
#include "clang/Basic/IdentifierTable.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
Expand All @@ -15,3 +16,13 @@ int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,

return 0;
}

const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) {
switch (Rule) {
#define ATTR_MATCH_RULE(NAME, SPELLING, IsAbstract) \
case attr::NAME: \
return SPELLING;
#include "clang/Basic/AttrSubMatchRulesList.inc"
}
llvm_unreachable("Invalid subject match rule");
}
534 changes: 534 additions & 0 deletions clang/lib/Parse/ParsePragma.cpp

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ Parser::ParseStatementOrDeclarationAfterAttributes(StmtVector &Stmts,
case tok::annot_pragma_dump:
HandlePragmaDump();
return StmtEmpty();

case tok::annot_pragma_attribute:
HandlePragmaAttribute();
return StmtEmpty();
}

// If we reached this code, the statement must end in a semicolon.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
ConsumeToken();
return false;

case tok::annot_pragma_attribute:
HandlePragmaAttribute();
return false;

case tok::eof:
// Late template parsing can begin.
if (getLangOpts().DelayedTemplateParsing)
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Sema/AttributeList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -160,12 +161,16 @@ struct ParsedAttrInfo {
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 {
Expand All @@ -192,6 +197,18 @@ bool AttributeList::diagnoseAppertainsTo(Sema &S, const Decl *D) const {
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);
}
Expand All @@ -216,6 +233,10 @@ bool AttributeList::isKnownToGCC() const {
return getInfo(*this).IsKnownToGCC;
}

bool AttributeList::isSupportedByPragmaAttribute() const {
return getInfo(*this).IsSupportedByPragmaAttribute;
}

unsigned AttributeList::getSemanticSpelling() const {
return getInfo(*this).SpellingIndexToSemanticSpelling(*this);
}
Expand Down
67 changes: 31 additions & 36 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,42 +71,35 @@ void Sema::ActOnTranslationUnitScope(Scope *S) {
}

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;
Expand Down Expand Up @@ -731,6 +724,8 @@ void Sema::ActOnEndOfTranslationUnit() {
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
Expand Down
211 changes: 211 additions & 0 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,217 @@ void Sema::AddCFAuditedAttribute(Decl *D) {
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();
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13674,6 +13674,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,

if (Attr)
ProcessDeclAttributeList(S, New, Attr);
AddPragmaAttributes(S, New);

// If this has an identifier, add it to the scope stack.
if (TUK == TUK_Friend) {
Expand Down Expand Up @@ -15185,6 +15186,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst,

// Process attributes.
if (Attr) ProcessDeclAttributeList(S, New, Attr);
AddPragmaAttributes(S, New);

// Register this decl in the current scope stack.
New->setAccess(TheEnumDecl->getAccess());
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6676,6 +6676,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) {
// 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?
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8445,6 +8445,7 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
Namespc->setInvalidDecl();

ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
AddPragmaAttributes(DeclRegionScope, Namespc);

// FIXME: Should we be merging attributes?
if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
Expand Down Expand Up @@ -9931,6 +9932,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S,
NewTD->setInvalidDecl();

ProcessDeclAttributeList(S, NewTD, AttrList);
AddPragmaAttributes(S, NewTD);

CheckTypedefForVariablyModifiedType(S, NewTD);
Invalid |= NewTD->isInvalidDecl();
Expand Down
13 changes: 10 additions & 3 deletions clang/lib/Sema/SemaDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,7 @@ ActOnStartClassInterface(Scope *S, SourceLocation AtInterfaceLoc,

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
Expand Down Expand Up @@ -1176,7 +1177,8 @@ Sema::ActOnStartProtocolInterface(SourceLocation AtProtoInterfaceLoc,

if (AttrList)
ProcessDeclAttributeList(TUScope, PDecl, AttrList);

AddPragmaAttributes(TUScope, PDecl);

// Merge attributes from previous declarations.
if (PrevDecl)
mergeDeclAttributes(PDecl, PrevDecl);
Expand Down Expand Up @@ -1706,7 +1708,8 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc,

if (attrList)
ProcessDeclAttributeList(TUScope, PDecl, attrList);

AddPragmaAttributes(TUScope, PDecl);

if (PrevDecl)
mergeDeclAttributes(PDecl, PrevDecl);

Expand Down Expand Up @@ -1805,6 +1808,7 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc,

if (AttrList)
ProcessDeclAttributeList(TUScope, CDecl, AttrList);
AddPragmaAttributes(TUScope, CDecl);

CheckObjCDeclScope(CDecl);
return ActOnObjCContainerStartDefinition(CDecl);
Expand Down Expand Up @@ -1954,6 +1958,7 @@ Decl *Sema::ActOnStartClassImplementation(
ClassName, /*typeParamList=*/nullptr,
/*PrevDecl=*/nullptr, ClassLoc,
true);
AddPragmaAttributes(TUScope, IDecl);
IDecl->startDefinition();
if (SDecl) {
IDecl->setSuperClass(Context.getTrivialTypeSourceInfo(
Expand Down Expand Up @@ -3043,7 +3048,7 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc,
ClassName, TypeParams, PrevIDecl,
IdentLocs[i]);
IDecl->setAtEndRange(IdentLocs[i]);

PushOnScopeChains(IDecl, TUScope);
CheckObjCDeclScope(IDecl);
DeclsInGroup.push_back(IDecl);
Expand Down Expand Up @@ -4399,6 +4404,7 @@ Decl *Sema::ActOnMethodDeclaration(

// 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);
Expand Down Expand Up @@ -4429,6 +4435,7 @@ Decl *Sema::ActOnMethodDeclaration(

if (AttrList)
ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
AddPragmaAttributes(TUScope, ObjCMethod);

// Add the method now.
const ObjCMethodDecl *PrevMethod = nullptr;
Expand Down
6 changes: 6 additions & 0 deletions clang/test/FixIt/fixit-pragma-attribute.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// Verify that the suggested attribute subject match rules don't include the
// 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)"
83 changes: 83 additions & 0 deletions clang/test/FixIt/fixit-pragma-attribute.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -Wno-pragma-clang-attribute %s 2>&1 | FileCheck %s

#pragma clang attribute push (annotate)
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:31}:"__attribute__(("
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:39-[[@LINE-2]]:39}:"))"
#pragma clang attribute push (annotate(("test")))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:31}:"__attribute__(("
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:49-[[@LINE-2]]:49}:"))"

#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( enum, function, function, namespace, function ))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:97-[[@LINE-1]]:107}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:118-[[@LINE-2]]:127}:""

#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_global), function, variable(is_global), variable(is_global) ))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:112-[[@LINE-1]]:133}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:133-[[@LINE-2]]:153}:""

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:108-[[@LINE-1]]:132}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:153-[[@LINE-2]]:172}:""

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions2"))), apply_to = any(function(is_member),function))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:99-[[@LINE-1]]:119}:""

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions1"))), apply_to = any(variable(is_parameter), variable(unless(is_parameter))))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:130-[[@LINE-1]]:160}:""
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:106-[[@LINE-1]]:137}:""
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum, variable))
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:77-[[@LINE-1]]:82}:""
#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)"
#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)
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:60-[[@LINE-1]]:60}:", apply_to"
#pragma clang attribute push (__attribute__((abi_tag("a"))) any(function))
// 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)"
#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)"

#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)"
#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)"
#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)"
#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)"
#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)"

#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)"
#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)"

// Don't give fix-it to attributes without a strict subject set
#pragma clang attribute push (__attribute__((annotate("a"))))
// CHECK-NO: [[@LINE-1]]:61
169 changes: 169 additions & 0 deletions clang/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=namespace" %s | FileCheck --check-prefix=CHECK-NAMESPACE %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=type_alias" %s | FileCheck --check-prefix=CHECK-TYPE_ALIAS %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=enum" %s | FileCheck --check-prefix=CHECK-ENUM %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=enum_constant" %s | FileCheck --check-prefix=CHECK-ENUM_CONSTANT %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=record" %s | FileCheck --check-prefix=CHECK-RECORD %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=record(unless(is_union))" %s | FileCheck --check-prefix=CHECK-RECORD_UNLESS_IS_UNION %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=field" %s | FileCheck --check-prefix=CHECK-FIELD %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=function" %s | FileCheck --check-prefix=CHECK-FUNCTION %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=hasType(functionType)" %s | FileCheck --check-prefix=CHECK-HAS_TYPE_FUNCTION_TYPE %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=function(is_member)" %s | FileCheck --check-prefix=CHECK-FUNCTION_IS_MEMBER %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable" %s | FileCheck --check-prefix=CHECK-VARIABLE %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(is_global)" %s | FileCheck --check-prefix=CHECK-VARIABLE_IS_GLOBAL %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(is_parameter)" %s | FileCheck --check-prefix=CHECK-VARIABLE_IS_PARAMETER %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=variable(unless(is_parameter))" %s | FileCheck --check-prefix=CHECK-VARIABLE_UNLESS_IS_PARAMETER %s

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(SUBJECT))

namespace testNamespace {
// CHECK-NAMESPACE: NamespaceDecl{{.*}} testNamespace
// CHECK-NAMESPACE-NEXT: AnnotateAttr{{.*}} "test"

typedef int testTypedef;
// CHECK-TYPE_ALIAS: TypedefDecl{{.*}} testTypedef
// CHECK-TYPE_ALIAS-NEXT: BuiltinType
// CHECK-TYPE_ALIAS-NEXT: AnnotateAttr{{.*}} "test"

using testTypeAlias = double;
// CHECK-TYPE_ALIAS: TypeAliasDecl{{.*}} testTypeAlias
// CHECK-TYPE_ALIAS-NEXT: BuiltinType
// CHECK-TYPE_ALIAS-NEXT: AnnotateAttr{{.*}} "test"

enum testEnum {
testEnumCase1,
testEnumCase2
};
// CHECK-ENUM: EnumDecl{{.*}} testEnum
// CHECK-ENUM-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-ENUM_CONSTANT: EnumConstantDecl{{.*}} testEnumCase1
// CHECK-ENUM_CONSTANT-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-ENUM_CONSTANT: EnumConstantDecl{{.*}} testEnumCase2
// CHECK-ENUM_CONSTANT-NEXT: AnnotateAttr{{.*}} "test"

struct testStructRecord {
int testStructRecordField;
};
// CHECK-RECORD: CXXRecordDecl{{.*}} testStructRecord
// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testStructRecord
// CHECK-RECORD_UNLESS_IS_UNION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FIELD: FieldDecl{{.*}} testStructRecordField
// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test"

class testClassRecord {
int testClassRecordField;
};
// CHECK-RECORD: CXXRecordDecl{{.*}} testClassRecord
// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testClassRecord
// CHECK-RECORD_UNLESS_IS_UNION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FIELD: FieldDecl{{.*}} testClassRecordField
// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test"

union testUnionRecord {
int testUnionRecordField;
};
// CHECK-RECORD: CXXRecordDecl{{.*}} testUnionRecord
// CHECK-RECORD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl{{.*}} testUnionRecord
// CHECK-RECORD_UNLESS_IS_UNION-NOT: AnnotateAttr{{.*}} "test"
// CHECK-FIELD: FieldDecl{{.*}} testUnionRecordField
// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test"

// CHECK-RECORD_UNLESS_IS_UNION-LABEL: CXXRecordDecl
void testFunctionDecl();
// CHECK-FUNCTION: FunctionDecl{{.*}} testFunctionDecl
// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE: FunctionDecl{{.*}} testFunctionDecl
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"

void testFunctionDecl() { }
// CHECK-FUNCTION: FunctionDecl{{.*}} testFunctionDecl
// CHECK-FUNCTION-NEXT: CompoundStmt
// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE: FunctionDecl{{.*}} testFunctionDecl
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"

void (*testFunctionVar)();
// CHECK-HAS_TYPE_FUNCTION_TYPE: VarDecl{{.*}} testFunctionVar
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"
// 'function' should not apply to variables with a function type!
// CHECK-FUNCTION: VarDecl{{.*}} testFunctionVar
// CHECK-FUNCTION-NOT: AnnotateAttr{{.*}} "test"

class testMethods {
testMethods();
void testMethod();
};
void testMethods::testMethod() { }
void testFunctionNotMethod();
// CHECK-FUNCTION-LABEL: CXXConstructorDecl{{.*}} testMethods
// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION_IS_MEMBER: CXXConstructorDecl{{.*}} testMethods
// CHECK-FUNCTION_IS_MEMBER-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXConstructorDecl{{.*}} testMethods
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION: CXXMethodDecl{{.*}} testMethod
// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION_IS_MEMBER: CXXMethodDecl{{.*}} testMethod
// CHECK-FUNCTION_IS_MEMBER-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXMethodDecl{{.*}} testMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION: CXXMethodDecl{{.*}} testMethod
// CHECK-FUNCTION-NEXT: CompoundStmt
// CHECK-FUNCTION-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION_IS_MEMBER: CXXMethodDecl{{.*}} testMethod
// CHECK-FUNCTION_IS_MEMBER-NEXT: CompoundStmt
// CHECK-CXX_METHOD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE: CXXMethodDecl{{.*}} testMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-FUNCTION_IS_MEMBER: FunctionDecl{{.*}} testFunctionNotMethod
// CHECK-FUNCTION_IS_MEMBER-NOT: AnnotateAttr{{.*}} "test"

int testVariable;
// CHECK-VARIABLE: VarDecl{{.*}} testVariable
// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testVariable
// CHECK-VARIABLE_IS_GLOBAL-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testVariable
// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testVariable
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test"
void testVarFunction(int testParam) {
// CHECK-VARIABLE: VarDecl{{.*}} testParam
// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testParam
// CHECK-VARIABLE_IS_GLOBAL-NOT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testParam
// CHECK-VARIABLE_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testParam
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test"

int testLocalVariable;
// CHECK-VARIABLE: VarDecl{{.*}} testLocalVariable
// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testLocalVariable
// CHECK-VARIABLE_IS_GLOBAL-NOT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testLocalVariable
// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testLocalVariable
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test"
}
class testVarClass {
static int testStaticVar;
};
// CHECK-VARIABLE: VarDecl{{.*}} testStaticVar
// CHECK-VARIABLE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_GLOBAL-LABEL: VarDecl{{.*}} testStaticVar
// CHECK-VARIABLE_IS_GLOBAL-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_IS_PARAMETER-LABEL: VarDecl{{.*}} testStaticVar
// CHECK-VARIABLE_IS_PARAMETER-NOT: AnnotateAttr{{.*}} "test"
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-LABEL: VarDecl{{.*}} testStaticVar
// CHECK-VARIABLE_UNLESS_IS_PARAMETER-NEXT: AnnotateAttr{{.*}} "test"


}

#pragma clang attribute pop
106 changes: 106 additions & 0 deletions clang/test/Misc/pragma-attribute-cxx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fcxx-exceptions %s
// RUN: %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test -std=c++11 -fcxx-exceptions %s | FileCheck %s
// expected-no-diagnostics

class testClass1 {
};
// CHECK-LABEL: CXXRecordDecl{{.*}} testClass1
// CHECK-NOT: AnnotateAttr

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to=any(record, field, variable, function, namespace, type_alias))

class testClass2 {
void testMethod1(int param);

testClass2();

testClass2 *operator -> ();
};
// CHECK-LABEL: CXXRecordDecl{{.*}} testClass2
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK: CXXMethodDecl{{.*}} testMethod1
// CHECK-NEXT: ParmVarDecl{{.*}} param
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: CXXConstructorDecl
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: CXXMethodDecl{{.*}} operator->
// CHECK-NEXT: AnnotateAttr{{.*}} "test"

#pragma clang attribute push (__attribute__((annotate("method"))), apply_to=any(record, field, variable, function, namespace, type_alias))

void testClass2::testMethod1(int param) {

#pragma clang attribute pop
}
// CHECK-LABEL: CXXMethodDecl{{.*}}prev{{.*}} testMethod1
// CHECK-NEXT: ParmVarDecl{{.*}} param
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: AnnotateAttr{{.*}} "method"
// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: AnnotateAttr{{.*}} "method"

namespace testNamespace {
}
// CHECK-LABEL: NamespaceDecl{{.*}} testNamespace
// CHECK-NEXT: AnnotateAttr{{.*}} "test"

class testClassForward;
// CHECK-LABEL: CXXRecordDecl{{.*}} testClassForward
// CHECK-NEXT: AnnotateAttr{{.*}} "test"

namespace testNamespaceAlias = testNamespace;
// CHECK-LABEL: NamespaceAliasDecl{{.*}} testNamespaceAlias
// CHECK-NOT: AnnotateAttr

using testTypeAlias = testClass2;
// CHECK-LABEL: TypeAliasDecl{{.*}} testTypeAlias
// CHECK: AnnotateAttr{{.*}} "test"

void testCatchVariable() {
try {
} catch (int testCatch) {
}
testCatchVariable();
}
// CHECK-LABEL: FunctionDecl{{.*}} testCatchVariable
// CHECK: CXXCatchStmt
// CHECK-NEXT: VarDecl{{.*}} testCatch
// CHECK-NEXT: AnnotateAttr{{.*}} "test"

void testLambdaMethod() {
auto l = [] () { };
testLambdaMethod();
}
// CHECK-LABEL: FunctionDecl{{.*}} testLambdaMethod
// CHECK: LambdaExpr
// CHECK-NEXT: CXXRecordDecl
// CHECK-NEXT: CXXMethodDecl{{.*}} operator()
// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: AnnotateAttr{{.*}} "test"

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((require_constant_initialization)), apply_to=variable(is_global))

int testCI1 = 1;
// CHECK-LABEL: VarDecl{{.*}} testCI1
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: RequireConstantInitAttr

#pragma clang attribute pop

int testNoCI = 0;
// CHECK-LABEL: VarDecl{{.*}} testNoCI
// CHECK-NEXT: IntegerLiteral
// CHECK-NOT: RequireConstantInitAttr

// Check support for CXX11 style attributes
#pragma clang attribute push ([[noreturn]], apply_to = function)

void testNoReturn();
// CHECK-LABEL: FunctionDecl{{.*}} testNoReturn
// CHECK-NEXT: CXX11NoReturnAttr

#pragma clang attribute pop
113 changes: 113 additions & 0 deletions clang/test/Misc/pragma-attribute-objc-subject-match-rules.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump "-DSUBJECT=objc_interface" %s | FileCheck --check-prefix=CHECK-OBJC_INTERFACE %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_protocol" %s | FileCheck --check-prefix=CHECK-OBJC_PROTOCOL %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump "-DSUBJECT=objc_category" %s | FileCheck --check-prefix=CHECK-OBJC_CATEGORY %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_method" %s | FileCheck --check-prefix=CHECK-OBJC_METHOD %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_method(is_instance)" %s | FileCheck --check-prefix=CHECK-OBJC_METHOD_IS_INSTANCE %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=field" %s | FileCheck --check-prefix=CHECK-FIELD %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=objc_property" %s | FileCheck --check-prefix=CHECK-OBJC_PROPERTY %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=block" %s | FileCheck --check-prefix=CHECK-BLOCK %s
// RUN: %clang_cc1 -fblocks -fobjc-arc -Wno-objc-root-class -fsyntax-only -ast-dump -ast-dump-filter test "-DSUBJECT=hasType(functionType)" %s | FileCheck --check-prefix=CHECK-HAS_TYPE_FUNCTION_TYPE %s

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(SUBJECT))

@interface testInterface
@end
// CHECK-OBJC_INTERFACE: ObjCInterfaceDecl{{.*}} testInterface
// CHECK-OBJC_INTERFACE-NEXT: AnnotateAttr{{.*}} "test"

@interface testInterface ()
@end
// CHECK-OBJC_INTERFACE: ObjCCategoryDecl
// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_CATEGORY: ObjCCategoryDecl
// CHECK-OBJC_CATEGORY-NEXT: ObjCInterface
// CHECK-OBJC_CATEGORY-NEXT: AnnotateAttr{{.*}} "test"

@interface testInterface (testCategory)
@end
// CHECK-OBJC_INTERFACE: ObjCCategoryDecl{{.*}} testCategory
// CHECK-OBJC_INTERFACE-NOT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_CATEGORY: ObjCCategoryDecl{{.*}} testCategory
// CHECK-OBJC_CATEGORY-NEXT: ObjCInterface
// CHECK-OBJC_CATEGORY-NEXT: AnnotateAttr{{.*}} "test"

// CHECK-OBJC_INTERFACE-LABEL: ObjCProtocolDecl
@protocol testProtocol
@end
// CHECK-OBJC_PROTOCOL: ObjCProtocolDecl{{.*}} testProtocol
// CHECK-OBJC_PROTOCOL-NEXT: AnnotateAttr{{.*}} "test"

@interface methodContainer
- (void) testInstanceMethod;
+ (void) testClassMethod;
@end
// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD_IS_INSTANCE: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD_IS_INSTANCE-LABEL: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-OBJC_METHOD_IS_INSTANCE-NOT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test"

@implementation methodContainer
- (void) testInstanceMethod { }
+ (void) testClassMethod { }
@end
// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD-NEXT: CompoundStmt
// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD-NEXT: CompoundStmt
// CHECK-OBJC_METHOD-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD_IS_INSTANCE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: ImplicitParamDecl
// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: CompoundStmt
// CHECK-OBJC_METHOD_IS_INSTANCE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-OBJC_METHOD_IS_INSTANCE: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-OBJC_METHOD_IS_INSTANCE-NOT: AnnotateAttr{{.*}} "test"

// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testInstanceMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: ObjCMethodDecl{{.*}} testClassMethod
// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test"
@interface propertyContainer {
int testIvar;
// CHECK-FIELD: ObjCIvarDecl{{.*}} testIvar
// CHECK-FIELD-NEXT: AnnotateAttr{{.*}} "test"

}
@property int testProperty;
// CHECK-OBJC_PROPERTY: ObjCPropertyDecl{{.*}} testProperty
// CHECK-OBJC_PROPERTY-NEXT: AnnotateAttr{{.*}} "test"

@end

void (^testBlockVar)();
// CHECK-BLOCK: VarDecl{{.*}} testBlockVar
// CHECK-BLOCK-NOT: AnnotateAttr{{.*}} "test"

void testBlock() {
(void)(^ { });
}
// CHECK-BLOCK-LABEL: BlockDecl
// CHECK-BLOCK-NEXT: CompoundStmt
// CHECK-BLOCK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE-LABEL: FunctionDecl{{.*}} testBlock
// CHECK-HAS_TYPE_FUNCTION_TYPE: BlockDecl
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: CompoundStmt
// The attribute applies to function, but not to block:
// CHECK-HAS_TYPE_FUNCTION_TYPE-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-HAS_TYPE_FUNCTION_TYPE-NOT: AnnotateAttr{{.*}} "test"


#pragma clang attribute pop
164 changes: 164 additions & 0 deletions clang/test/Misc/pragma-attribute-objc.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -ast-dump -ast-dump-filter test %s | FileCheck %s

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(objc_interface, objc_protocol, objc_property, field, objc_method, variable))
#pragma clang attribute push (__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)

@interface testInterface1
// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface1
// CHECK-NEXT: ObjCImplementation
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: ObjCSubclassingRestrictedAttr{{.*}}

// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

{
int testIvar1;
// CHECK-LABEL: ObjCIvarDecl{{.*}} testIvar1
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
}

@property int testProp1;
// CHECK-LABEL: ObjCPropertyDecl{{.*}} testProp1
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr

- (void)testIm:(int) x;
// CHECK-LABEL: ObjCMethodDecl{{.*}}testIm
// CHECK-NEXT: ParmVarDecl{{.*}} x
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr

+ (void)testCm;
// CHECK-LABEL: ObjCMethodDecl{{.*}}testCm
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr

// Implicit getters/setters shouldn't receive the attributes.
// CHECK-LABEL: ObjCMethodDecl{{.*}}testProp1
// CHECK-NOT: AnnotateAttr
// CHECK-LABEL: ObjCMethodDecl{{.*}}setTestProp1
// CHECK-NOT: AnnotateAttr

@end

// @implementation can't receive explicit attributes, so don't add the pragma
// attributes to them.
@implementation testInterface1
// CHECK-LABEL: ObjCImplementationDecl{{.*}}testInterface1
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

{
int testIvar2;
// CHECK-LABEL: ObjCIvarDecl{{.*}} testIvar2
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
}

// Don't add attributes to implicit parameters!
- (void)testIm:(int) x {
// CHECK-LABEL: ObjCMethodDecl{{.*}}testIm
// CHECK-NEXT: ImplicitParamDecl
// CHECK-NEXT: ImplicitParamDecl
// CHECK-NEXT: ParmVarDecl{{.*}} x
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
}

+ (void)testCm {
// CHECK-LABEL: ObjCMethodDecl{{.*}}testCm
// CHECK: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
// CHECK-NOT: AnnotateAttr
_Pragma("clang attribute push (__attribute__((annotate(\"applied at container start\"))), apply_to=objc_interface)");
}

// Implicit ivars shouldn't receive the attributes.
// CHECK-LABEL: ObjCIvarDecl{{.*}}_testProp1
// CHECK-NOT: AnnotateAttr

@end

@implementation testImplWithoutInterface // expected-warning {{cannot find interface declaration for 'testImplWithoutInterface'}}
// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testImplWithoutInterface
// CHECK-NEXT: ObjCImplementation
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NEXT: ObjCSubclassingRestrictedAttr
// CHECK-NEXT: AnnotateAttr{{.*}} "applied at container start"

// CHECK-LABEL: ObjCImplementationDecl{{.*}}testImplWithoutInterface
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

@end

#pragma clang attribute pop

@protocol testProtocol
// CHECK-LABEL: ObjCProtocolDecl{{.*}}testProtocol
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
// CHECK-NOT: AnnotateAttr

- (void)testProtIm;
// CHECK-LABEL: ObjCMethodDecl{{.*}}testProtIm
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr

@end

@protocol testForwardProtocol;
// CHECK-LABEL: ObjCProtocolDecl{{.*}}testForwardProtocol
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr


// Categories can't receive explicit attributes, so don't add pragma attributes
// to them.
@interface testInterface1(testCat)
// CHECK-LABEL: ObjCCategoryDecl{{.*}}testCat
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

@end

@implementation testInterface1(testCat)
// CHECK-LABEL: ObjCCategoryImplDecl{{.*}}testCat
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

@end

// @class/@compatibility_alias declarations can't receive explicit attributes,
// so don't add pragma attributes to them.
@class testClass;
// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testClass
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

@compatibility_alias testCompat testInterface1;
// CHECK-LABEL: ObjCCompatibleAliasDecl{{.*}}testCompat
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr

#pragma clang attribute pop // objc_subclassing_restricted

@interface testInterface3
// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface3
// CHECK-NEXT: AnnotateAttr{{.*}} "test"
// CHECK-NOT: ObjCSubclassingRestrictedAttr
@end

#pragma clang attribute pop // annotate("test")

@interface testInterface4
// CHECK-LABEL: ObjCInterfaceDecl{{.*}}testInterface4
// CHECK-NOT: AnnotateAttr
// CHECK-NOT: ObjCSubclassingRestrictedAttr
@end
222 changes: 222 additions & 0 deletions clang/test/Misc/pragma-attribute-strict-subjects.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-pragma-clang-attribute -verify %s
// RUN: not %clang_cc1 -fsyntax-only -ast-dump -ast-dump-filter test %s | FileCheck %s

// Check for contradictions in rules for attribute without a strict subject set:

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
// expected-error@-1 {{redundant attribute subject matcher sub-rule 'variable(is_parameter)'; 'variable' already matches those declarations}}
// expected-error@-2 {{redundant attribute subject matcher sub-rule 'variable(is_global)'; 'variable' already matches those declarations}}

// Ensure that we've recovered from the error:
int testRecoverSubRuleContradiction = 0;
// CHECK-LABEL: VarDecl{{.*}} testRecoverSubRuleContradiction
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: AnnotateAttr{{.*}} "subRuleContradictions"

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
// We have just one error, don't error on 'variable(is_global)'

// Ensure that we've recovered from the error:
int testRecoverNegatedContradiction = 0;
// CHECK-LABEL: VarDecl{{.*}} testRecoverNegatedContradiction
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: AnnotateAttr{{.*}} "negatedSubRuleContradictions2"

void testRecoverNegatedContradictionFunc(void);
// CHECK-LABEL: FunctionDecl{{.*}} testRecoverNegatedContradictionFunc
// CHECK-NEXT: AnnotateAttr{{.*}} "negatedSubRuleContradictions2"

#pragma clang attribute pop

// Verify the strict subject set verification.

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))

int testRecoverStrictnessVar = 0;
// CHECK-LABEL: VarDecl{{.*}} testRecoverStrictnessVar
// CHECK-NEXT: IntegerLiteral
// CHECK-NOT: AbiTagAttr

void testRecoverStrictnessFunc(void);
// CHECK-LABEL: FunctionDecl{{.*}} testRecoverStrictnessFunc
// CHECK-NEXT: AbiTagAttr

struct testRecoverStrictnessStruct { };
// CHECK-LABEL: RecordDecl{{.*}} testRecoverStrictnessStruct
// CHECK-NOT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}}

int testRecoverExtraVar = 0;
// CHECK-LABEL: VarDecl{{.*}} testRecoverExtraVar
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: AbiTagAttr

void testRecoverExtraFunc(void);
// CHECK-LABEL: FunctionDecl{{.*}} testRecoverExtraFunc
// CHECK-NEXT: AbiTagAttr

struct testRecoverExtraStruct { };
// CHECK-LABEL: RecordDecl{{.*}} testRecoverExtraStruct
// CHECK-NEXT: AbiTagAttr

enum testNoEnumAbiTag { CaseCase };
// CHECK-LABEL: EnumDecl{{.*}} testNoEnumAbiTag
// CHECK-NO: AbiTagAttr

#pragma clang attribute pop

// Verify the non-strict subject set verification.

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))

int testSubset1Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset1Var
// CHECK-NOT: AbiTagAttr

void testSubset1Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset1Func
// CHECK-NEXT: AbiTagAttr

struct testSubset1Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset1Struct
// CHECK-NOT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = variable)

int testSubset2Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset2Var
// CHECK-NEXT: AbiTagAttr

void testSubset2Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset2Func
// CHECK-NOT: AbiTagAttr

struct testSubset2Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset2Struct
// CHECK-NOT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union))))

int testSubset3Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset3Var
// CHECK-NOT: AbiTagAttr

void testSubset3Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset3Func
// CHECK-NOT: AbiTagAttr

struct testSubset3Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset3Struct
// CHECK-NEXT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, variable))

int testSubset4Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset4Var
// CHECK-NEXT: AbiTagAttr

void testSubset4Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset4Func
// CHECK-NEXT: AbiTagAttr

struct testSubset4Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset4Struct
// CHECK-NOT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(variable, record(unless(is_union))))

int testSubset5Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset5Var
// CHECK-NEXT: AbiTagAttr

void testSubset5Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset5Func
// CHECK-NOT: AbiTagAttr

struct testSubset5Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset5Struct
// CHECK-NEXT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function))

int testSubset6Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset6Var
// CHECK-NOT: AbiTagAttr

void testSubset6Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset6Func
// CHECK-NEXT: AbiTagAttr

struct testSubset6Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset6Struct
// CHECK-NEXT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable))

int testSubset7Var;
// CHECK-LABEL: VarDecl{{.*}} testSubset7Var
// CHECK-NEXT: AbiTagAttr

void testSubset7Func(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubset7Func
// CHECK-NEXT: AbiTagAttr

struct testSubset7Struct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubset7Struct
// CHECK-NEXT: AbiTagAttr

#pragma clang attribute pop


#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}}

int testSubsetRecoverVar;
// CHECK-LABEL: VarDecl{{.*}} testSubsetRecoverVar
// CHECK-NEXT: AbiTagAttr

void testSubsetRecoverFunc(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubsetRecoverFunc
// CHECK-NEXT: AbiTagAttr

struct testSubsetRecoverStruct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubsetRecoverStruct
// CHECK-NEXT: AbiTagAttr

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = enum)
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}}

int testSubsetNoVar;
// CHECK-LABEL: VarDecl{{.*}} testSubsetNoVar
// CHECK-NOT: AbiTagAttr

void testSubsetNoFunc(void);
// CHECK-LABEL: FunctionDecl{{.*}} testSubsetNoFunc
// CHECK-NOT: AbiTagAttr

struct testSubsetNoStruct { };
// CHECK-LABEL: RecordDecl{{.*}} testSubsetNoStruct
// CHECK-NOT: AbiTagAttr

#pragma clang attribute pop
62 changes: 62 additions & 0 deletions clang/test/Misc/pragma-attribute-supported-attributes-list.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// RUN: clang-tblgen -gen-clang-test-pragma-attribute-supported-attributes -I%src_include_dir %src_include_dir/clang/Basic/Attr.td -o - | FileCheck %s

// The number of supported attributes should never go down!

// CHECK: #pragma clang attribute supports 57 attributes:
// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUWavesPerEU (SubjectMatchRule_function)
// CHECK-NEXT: AVRSignal (SubjectMatchRule_function)
// CHECK-NEXT: AbiTag (SubjectMatchRule_record_not_is_union, SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_namespace)
// CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias)
// CHECK-NEXT: AllocSize (SubjectMatchRule_function)
// CHECK-NEXT: Annotate ()
// CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: CXX11NoReturn (SubjectMatchRule_function)
// CHECK-NEXT: CallableWhen (SubjectMatchRule_function_is_member)
// CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Consumable (SubjectMatchRule_record)
// CHECK-NEXT: Convergent (SubjectMatchRule_function)
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: EnableIf (SubjectMatchRule_function)
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
// CHECK-NEXT: Flatten (SubjectMatchRule_function)
// CHECK-NEXT: IFunc (SubjectMatchRule_function)
// CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
// CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record)
// CHECK-NEXT: NoDebug (SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter)
// CHECK-NEXT: NoDuplicate (SubjectMatchRule_function)
// CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global)
// CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global)
// CHECK-NEXT: NoSplitStack (SubjectMatchRule_function)
// CHECK-NEXT: NotTailCalled (SubjectMatchRule_function)
// CHECK-NEXT: ObjCBoxable (SubjectMatchRule_record)
// CHECK-NEXT: ObjCMethodFamily (SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCRequiresSuper (SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCRuntimeName (SubjectMatchRule_objc_interface, SubjectMatchRule_objc_protocol)
// CHECK-NEXT: ObjCRuntimeVisible (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCSubclassingRestricted (SubjectMatchRule_objc_interface)
// CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable)
// CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: Overloadable (SubjectMatchRule_function)
// CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
// CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local)
// CHECK-NEXT: Target (SubjectMatchRule_function)
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
// CHECK-NEXT: XRayInstrument (SubjectMatchRule_function_is_member, SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function_is_member, SubjectMatchRule_objc_method, SubjectMatchRule_function)
13 changes: 13 additions & 0 deletions clang/test/Parser/pragma-attribute-declspec.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -triple i386-pc-win32 -std=c++11 -verify -Wno-pragma-clang-attribute -fms-extensions -fms-compatibility %s

#pragma clang attribute push(__declspec(dllexport), apply_to = function)

void function();

#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(align), apply_to = variable) // expected-error {{attribute 'align' is not supported by '#pragma clang attribute'}}

#pragma clang attribute push(__declspec(), apply_to = variable) // A noop
181 changes: 181 additions & 0 deletions clang/test/Parser/pragma-attribute.cpp

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions clang/test/Sema/pragma-attribute-strict-subjects.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-pragmas -verify %s

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(function, variable))

#pragma clang attribute pop

// Check for contradictions in rules for attribute without a strict subject set:

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
// expected-error@-1 {{redundant attribute subject matcher sub-rule 'variable(is_parameter)'; 'variable' already matches those declarations}}
// expected-error@-2 {{redundant attribute subject matcher sub-rule 'variable(is_global)'; 'variable' already matches those declarations}}

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions2"))), apply_to = any(function(is_member), function))
// expected-error@-1 {{redundant attribute subject matcher sub-rule 'function(is_member)'; 'function' already matches those declarations}}

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("subRuleContradictions3"))), apply_to = any(variable, variable(unless(is_parameter))))
// expected-error@-1 {{redundant attribute subject matcher sub-rule 'variable(unless(is_parameter))'; 'variable' already matches those declarations}}

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions1"))), apply_to = any(variable(is_parameter), variable(unless(is_parameter))))
// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_parameter)'}}

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
// We have just one error, don't error on 'variable(is_global)'

#pragma clang attribute pop

// Verify the strict subject set verification.

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))
// No error
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable))
// No error
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, variable, record(unless(is_union))))
// No error
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(variable, record(unless(is_union)), function))
// No error
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), variable, enum))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}}
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter)))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'variable(is_parameter)', and 'enum_constant'}}
#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), enum))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}}
#pragma clang attribute pop

// Verify the non-strict subject set verification.

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function))

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = variable)

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union))))

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, variable))

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(variable, record(unless(is_union))))

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function))

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable))

#pragma clang attribute pop


#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(record(unless(is_union)), function, variable, enum, enum_constant))
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'enum'}}

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = enum)
// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'enum'}}

#pragma clang attribute pop

// Handle attributes whose subjects are supported only in other language modes:

#pragma clang attribute push(__attribute__((abi_tag("b"))), apply_to = any(namespace, record(unless(is_union)), variable, function))
// 'namespace' is accepted!
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((abi_tag("b"))), apply_to = any(namespace))
// 'namespace' is accepted!
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)
// No error!
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = objc_interface)
// No error!
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol))
// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol))
// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
// Don't report an error about missing 'objc_interface' as we aren't parsing
// Objective-C.
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_interface, objc_protocol))
// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
#pragma clang attribute pop

#pragma clang attribute push(__attribute__((objc_subclassing_restricted)), apply_to = any(objc_protocol))
// expected-error@-1 {{attribute 'objc_subclassing_restricted' can't be applied to 'objc_protocol'}}
// Don't report an error about missing 'objc_interface' as we aren't parsing
// Objective-C.
#pragma clang attribute pop

// Use of matchers from other language modes should not cause for attributes
// without subject list:
#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = objc_method)

#pragma clang attribute pop

#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = any(objc_interface, objc_protocol))

#pragma clang attribute pop
47 changes: 47 additions & 0 deletions clang/test/Sema/pragma-attribute.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}

// Don't verify unused attributes.
#pragma clang attribute push (__attribute__((annotate)), apply_to = function) // expected-warning {{unused attribute 'annotate' in '#pragma clang attribute push' region}}
#pragma clang attribute pop // expected-note {{'#pragma clang attribute push' regions ends here}}

// Ensure we only report any errors once.
#pragma clang attribute push (__attribute__((annotate)), apply_to = function) // expected-error 4 {{'annotate' attribute takes one argument}}

void test5_begin(); // expected-note {{when applied to this declaration}}
void test5_1(); // expected-note {{when applied to this declaration}}

#pragma clang attribute push (__attribute__((annotate())), apply_to = function) // expected-error 2 {{'annotate' attribute takes one argument}}

void test5_2(); // expected-note 2 {{when applied to this declaration}}

#pragma clang attribute push (__attribute__((annotate("hello", "world"))), apply_to = function) // expected-error {{'annotate' attribute takes one argument}}

void test5_3(); // expected-note 3 {{when applied to this declaration}}

#pragma clang attribute pop
#pragma clang attribute pop
#pragma clang attribute pop

// Verify that the warnings are reported for each receiver declaration

#pragma clang attribute push (__attribute__((optnone)), apply_to = function) // expected-note 2 {{conflicting attribute is here}}

__attribute__((always_inline)) void optnone1() { } // expected-warning {{'always_inline' attribute ignored}}
// expected-note@-1 {{when applied to this declaration}}

void optnone2() { }

__attribute__((always_inline)) void optnone3() { } // expected-warning {{'always_inline' attribute ignored}}
// expected-note@-1 {{when applied to this declaration}}

#pragma clang attribute pop

#pragma clang attribute push ([[]], apply_to = function) // A noop

#pragma clang attribute pop // expected-error {{'#pragma clang attribute pop' with no matching '#pragma clang attribute push'}}

#pragma clang attribute push (__attribute__((annotate("func"))), apply_to = function) // expected-error {{unterminated '#pragma clang attribute push' at end of file}}

void function();
2 changes: 2 additions & 0 deletions clang/test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ if config.host_triple and config.host_triple != '@LLVM_HOST_TRIPLE@':
else:
config.substitutions.append( ('%target_itanium_abi_host_triple', '') )

config.substitutions.append( ('%src_include_dir', config.clang_src_dir + '/include') )

# FIXME: Find nicer way to prohibit this.
config.substitutions.append(
(' clang ', """*** Do not use 'clang' in tests, use '%clang'. ***""") )
Expand Down
1 change: 1 addition & 0 deletions clang/test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ config.llvm_shlib_dir = "@SHLIBDIR@"
config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@"
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
config.clang_obj_root = "@CLANG_BINARY_DIR@"
config.clang_src_dir = "@CLANG_SOURCE_DIR@"
config.clang_tools_dir = "@CLANG_TOOLS_DIR@"
config.host_triple = "@LLVM_HOST_TRIPLE@"
config.target_triple = "@TARGET_TRIPLE@"
Expand Down
445 changes: 438 additions & 7 deletions clang/utils/TableGen/ClangAttrEmitter.cpp

Large diffs are not rendered by default.

31 changes: 26 additions & 5 deletions clang/utils/TableGen/TableGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ using namespace clang;
enum ActionType {
GenClangAttrClasses,
GenClangAttrParserStringSwitches,
GenClangAttrSubjectMatchRulesParserStringSwitches,
GenClangAttrImpl,
GenClangAttrList,
GenClangAttrSubjectMatchRuleList,
GenClangAttrPCHRead,
GenClangAttrPCHWrite,
GenClangAttrHasAttributeImpl,
Expand Down Expand Up @@ -54,7 +56,8 @@ enum ActionType {
GenArmNeonTest,
GenAttrDocs,
GenDiagDocs,
GenOptDocs
GenOptDocs,
GenTestPragmaAttributeSupportedAttributes
};

namespace {
Expand All @@ -66,10 +69,17 @@ cl::opt<ActionType> Action(
clEnumValN(GenClangAttrParserStringSwitches,
"gen-clang-attr-parser-string-switches",
"Generate all parser-related attribute string switches"),
clEnumValN(GenClangAttrSubjectMatchRulesParserStringSwitches,
"gen-clang-attr-subject-match-rules-parser-string-switches",
"Generate all parser-related attribute subject match rule"
"string switches"),
clEnumValN(GenClangAttrImpl, "gen-clang-attr-impl",
"Generate clang attribute implementations"),
clEnumValN(GenClangAttrList, "gen-clang-attr-list",
"Generate a clang attribute list"),
clEnumValN(GenClangAttrSubjectMatchRuleList,
"gen-clang-attr-subject-match-rule-list",
"Generate a clang attribute subject match rule list"),
clEnumValN(GenClangAttrPCHRead, "gen-clang-attr-pch-read",
"Generate clang PCH attribute reader"),
clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write",
Expand All @@ -80,8 +90,7 @@ cl::opt<ActionType> Action(
clEnumValN(GenClangAttrSpellingListIndex,
"gen-clang-attr-spelling-index",
"Generate a clang attribute spelling index"),
clEnumValN(GenClangAttrASTVisitor,
"gen-clang-attr-ast-visitor",
clEnumValN(GenClangAttrASTVisitor, "gen-clang-attr-ast-visitor",
"Generate a recursive AST visitor for clang attributes"),
clEnumValN(GenClangAttrTemplateInstantiate,
"gen-clang-attr-template-instantiate",
Expand Down Expand Up @@ -137,8 +146,11 @@ cl::opt<ActionType> Action(
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
"Generate diagnostic documentation"),
clEnumValN(GenOptDocs, "gen-opt-docs",
"Generate option documentation")));
clEnumValN(GenOptDocs, "gen-opt-docs", "Generate option documentation"),
clEnumValN(GenTestPragmaAttributeSupportedAttributes,
"gen-clang-test-pragma-attribute-supported-attributes",
"Generate a list of attributes supported by #pragma clang "
"attribute for testing purposes")));

cl::opt<std::string>
ClangComponent("clang-component",
Expand All @@ -153,12 +165,18 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenClangAttrParserStringSwitches:
EmitClangAttrParserStringSwitches(Records, OS);
break;
case GenClangAttrSubjectMatchRulesParserStringSwitches:
EmitClangAttrSubjectMatchRulesParserStringSwitches(Records, OS);
break;
case GenClangAttrImpl:
EmitClangAttrImpl(Records, OS);
break;
case GenClangAttrList:
EmitClangAttrList(Records, OS);
break;
case GenClangAttrSubjectMatchRuleList:
EmitClangAttrSubjectMatchRuleList(Records, OS);
break;
case GenClangAttrPCHRead:
EmitClangAttrPCHRead(Records, OS);
break;
Expand Down Expand Up @@ -244,6 +262,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenOptDocs:
EmitClangOptDocs(Records, OS);
break;
case GenTestPragmaAttributeSupportedAttributes:
EmitTestPragmaAttributeSupportedAttributes(Records, OS);
break;
}

return false;
Expand Down
6 changes: 6 additions & 0 deletions clang/utils/TableGen/TableGenBackends.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
const std::string &N, const std::string &S);

void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
raw_ostream &OS);
void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS);
Expand Down Expand Up @@ -72,6 +75,9 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS);
void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS);
void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS);

void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records,
raw_ostream &OS);

} // end namespace clang

#endif