Add #pragma clang attribute

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: 300539
This commit is contained in:
Alex Lorenz 2017-04-18 09:41:47 +00:00
parent a4e79cca77
commit 0a849f47d2
40 changed files with 3072 additions and 52 deletions

View File

@ -2346,3 +2346,178 @@ statements in C)
The pragma can also be used with ``off`` which turns FP contraction off for a
section of the code. This can be useful when fast contraction is otherwise
enabled for the translation unit with the ``-ffp-contract=fast`` flag.
Specifying an attribute for multiple declarations (#pragma clang attribute)
===========================================================================
The ``#pragma clang attribute`` directive can be used to apply an attribute to
multiple declarations. The ``#pragma clang attribute push`` variation of the
directive pushes a new attribute to the attribute stack. The declarations that
follow the pragma receive the attributes that are on the attribute stack, until
the stack is cleared using a ``#pragma clang attribute pop`` directive. Multiple
push directives can be nested inside each other.
The attributes that are used in the ``#pragma clang attribute`` directives
can be written using the GNU-style syntax:
.. code-block:: c++
#pragma clang attribute push(__attribute__((annotate("custom"))), apply_to = function)
void function(); // The function now has the annotate("custom") attribute
#pragma clang attribute pop
The attributes can also be written using the C++11 style syntax:
.. code-block:: c++
#pragma clang attribute push([[noreturn]], apply_to = function)
void function(); // The function now has the [[noreturn]] attribute
#pragma clang attribute pop
The ``__declspec`` style syntax is also supported:
.. code-block:: c++
#pragma clang attribute push(__declspec(dllexport), apply_to = function)
void function(); // The function now has the __declspec(dllexport) attribute
#pragma clang attribute pop
A single push directive accepts only one attribute regardless of the syntax
used.
Subject Match Rules
-------------------
The set of declarations that receive a single attribute from the attribute stack
depends on the subject match rules that were specified in the pragma. Subject
match rules are specified after the attribute. The compiler expects an
identifier that corresponds to the subject set specifier. The ``apply_to``
specifier is currently the only supported subject set specifier. It allows you
to specify match rules that form a subset of the attribute's allowed subject
set, i.e. the compiler doesn't require all of the attribute's subjects. For
example, an attribute like ``[[nodiscard]]`` whose subject set includes
``enum``, ``record`` and ``hasType(functionType)``, requires the presence of at
least one of these rules after ``apply_to``:
.. code-block:: c++
#pragma clang attribute push([[nodiscard]], apply_to = enum)
enum Enum1 { A1, B1 }; // The enum will receive [[nodiscard]]
struct Record1 { }; // The struct will *not* receive [[nodiscard]]
#pragma clang attribute pop
#pragma clang attribute push([[nodiscard]], apply_to = any(record, enum))
enum Enum2 { A2, B2 }; // The enum will receive [[nodiscard]]
struct Record2 { }; // The struct *will* receive [[nodiscard]]
#pragma clang attribute pop
// This is an error, since [[nodiscard]] can't be applied to namespaces:
#pragma clang attribute push([[nodiscard]], apply_to = any(record, namespace))
#pragma clang attribute pop
Multiple match rules can be specified using the ``any`` match rule, as shown
in the example above. The ``any`` rule applies attributes to all declarations
that are matched by at least one of the rules in the ``any``. It doesn't nest
and can't be used inside the other match rules. Redundant match rules or rules
that conflict with one another should not be used inside of ``any``.
Clang supports the following match rules:
- ``function``: Can be used to apply attributes to functions. This includes C++
member functions, static functions, operators, and constructors/destructors.
- ``function(is_member)``: Can be used to apply attributes to C++ member
functions. This includes members like static functions, operators, and
constructors/destructors.
- ``hasType(functionType)``: Can be used to apply attributes to functions, C++
member functions, and variables/fields whose type is a function pointer. It
does not apply attributes to Objective-C methods or blocks.
- ``type_alias``: Can be used to apply attributes to ``typedef`` declarations
and C++11 type aliases.
- ``record``: Can be used to apply attributes to ``struct``, ``class``, and
``union`` declarations.
- ``record(unless(is_union))``: Can be used to apply attributes only to
``struct`` and ``class`` declarations.
- ``enum``: Can be be used to apply attributes to enumeration declarations.
- ``enum_constant``: Can be used to apply attributes to enumerators.
- ``variable``: Can be used to apply attributes to variables, including
local variables, parameters, global variables, and static member variables.
It does not apply attributes to instance member variables or Objective-C
ivars.
- ``variable(is_thread_local)``: Can be used to apply attributes to thread-local
variables only.
- ``variable(is_global)``: Can be used to apply attributes to global variables
only.
- ``variable(is_parameter)``: Can be used to apply attributes to parameters
only.
- ``variable(unless(is_parameter))``: Can be used to apply attributes to all
the variables that are not parameters.
- ``field``: Can be used to apply attributes to non-static member variables
in a record. This includes Objective-C ivars.
- ``namespace``: Can be used to apply attributes to ``namespace`` declarations.
- ``objc_interface``: Can be used to apply attributes to ``@interface``
declarations.
- ``objc_protocol``: Can be used to apply attributes to ``@protocol``
declarations.
- ``objc_category``: Can be used to apply attributes to category declarations,
including class extensions.
- ``objc_method``: Can be used to apply attributes to Objective-C methods,
including instance and class methods. Implicit methods like implicit property
getters and setters do not receive the attribute.
- ``objc_method(is_instance)``: Can be used to apply attributes to Objective-C
instance methods.
- ``objc_property``: Can be used to apply attributes to ``@property``
declarations.
- ``block``: Can be used to apply attributes to block declarations. This does
not include variables/fields of block pointer type.
The use of ``unless`` in match rules is currently restricted to a strict set of
sub-rules that are used by the supported attributes. That means that even though
``variable(unless(is_parameter))`` is a valid match rule,
``variable(unless(is_thread_local))`` is not.
Supported Attributes
--------------------
Not all attributes can be used with the ``#pragma clang attribute`` directive.
Notably, statement attributes like ``[[fallthrough]]`` or type attributes
like ``address_space`` aren't supported by this directive. You can determine
whether or not an attribute is supported by the pragma by referring to the
:doc:`individual documentation for that attribute <AttributeReference>`.
The attributes are applied to all matching declarations individually, even when
the attribute is semantically incorrect. The attributes that aren't applied to
any declaration are not verified semantically.

View File

@ -248,6 +248,8 @@ def COnly : LangOpt<"CPlusPlus", 1>;
def CPlusPlus : LangOpt<"CPlusPlus">;
def OpenCL : LangOpt<"OpenCL">;
def RenderScript : LangOpt<"RenderScript">;
def ObjC : LangOpt<"ObjC1">;
def BlocksSupported : LangOpt<"Blocks">;
// Defines targets for target-specific attributes. The list of strings should
// specify architectures for which the target applies, based off the ArchType
@ -270,6 +272,102 @@ def TargetMicrosoftCXXABI : TargetArch<["x86", "x86_64", "arm", "thumb"]> {
let CXXABIs = ["Microsoft"];
}
// Attribute subject match rules that are used for #pragma clang attribute.
//
// A instance of AttrSubjectMatcherRule represents an individual match rule.
// An individual match rule can correspond to a number of different attribute
// subjects, e.g. "record" matching rule corresponds to the Record and
// CXXRecord attribute subjects.
//
// Match rules are used in the subject list of the #pragma clang attribute.
// Match rules can have sub-match rules that are instances of
// AttrSubjectMatcherSubRule. A sub-match rule can correspond to a number
// of different attribute subjects, and it can have a negated spelling as well.
// For example, "variable(unless(is_parameter))" matching rule corresponds to
// the NonParmVar attribute subject.
class AttrSubjectMatcherSubRule<string name, list<AttrSubject> subjects,
bit negated = 0> {
string Name = name;
list<AttrSubject> Subjects = subjects;
bit Negated = negated;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, the language options are taken
// from the parent matcher rule.
list<LangOpt> LangOpts = [];
}
class AttrSubjectMatcherRule<string name, list<AttrSubject> subjects,
list<AttrSubjectMatcherSubRule> subrules = []> {
string Name = name;
list<AttrSubject> Subjects = subjects;
list<AttrSubjectMatcherSubRule> Constraints = subrules;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, no language options are required.
list<LangOpt> LangOpts = [];
}
// function(is_member)
def SubRuleForCXXMethod : AttrSubjectMatcherSubRule<"is_member", [CXXMethod]> {
let LangOpts = [CPlusPlus];
}
def SubjectMatcherForFunction : AttrSubjectMatcherRule<"function", [Function], [
SubRuleForCXXMethod
]>;
// hasType is abstract, it should be used with one of the sub-rules.
def SubjectMatcherForType : AttrSubjectMatcherRule<"hasType", [], [
AttrSubjectMatcherSubRule<"functionType", [FunctionLike]>
// FIXME: There's a matcher ambiguity with objc methods and blocks since
// functionType excludes them but functionProtoType includes them.
// AttrSubjectMatcherSubRule<"functionProtoType", [HasFunctionProto]>
]>;
def SubjectMatcherForTypedef : AttrSubjectMatcherRule<"type_alias",
[TypedefName]>;
def SubjectMatcherForRecord : AttrSubjectMatcherRule<"record", [Record,
CXXRecord], [
// unless(is_union)
AttrSubjectMatcherSubRule<"is_union", [Struct], 1>
]>;
def SubjectMatcherForEnum : AttrSubjectMatcherRule<"enum", [Enum]>;
def SubjectMatcherForEnumConstant : AttrSubjectMatcherRule<"enum_constant",
[EnumConstant]>;
def SubjectMatcherForVar : AttrSubjectMatcherRule<"variable", [Var], [
AttrSubjectMatcherSubRule<"is_thread_local", [TLSVar]>,
AttrSubjectMatcherSubRule<"is_global", [GlobalVar]>,
AttrSubjectMatcherSubRule<"is_parameter", [ParmVar]>,
// unless(is_parameter)
AttrSubjectMatcherSubRule<"is_parameter", [NonParmVar], 1>
]>;
def SubjectMatcherForField : AttrSubjectMatcherRule<"field", [Field]>;
def SubjectMatcherForNamespace : AttrSubjectMatcherRule<"namespace",
[Namespace]> {
let LangOpts = [CPlusPlus];
}
def SubjectMatcherForObjCInterface : AttrSubjectMatcherRule<"objc_interface",
[ObjCInterface]> {
let LangOpts = [ObjC];
}
def SubjectMatcherForObjCProtocol : AttrSubjectMatcherRule<"objc_protocol",
[ObjCProtocol]> {
let LangOpts = [ObjC];
}
def SubjectMatcherForObjCCategory : AttrSubjectMatcherRule<"objc_category",
[ObjCCategory]> {
let LangOpts = [ObjC];
}
def SubjectMatcherForObjCMethod : AttrSubjectMatcherRule<"objc_method",
[ObjCMethod], [
AttrSubjectMatcherSubRule<"is_instance", [ObjCInstanceMethod]>
]> {
let LangOpts = [ObjC];
}
def SubjectMatcherForObjCProperty : AttrSubjectMatcherRule<"objc_property",
[ObjCProperty]> {
let LangOpts = [ObjC];
}
def SubjectMatcherForBlock : AttrSubjectMatcherRule<"block", [Block]> {
let LangOpts = [BlocksSupported];
}
class Attr {
// The various ways in which an attribute can be spelled in source
list<Spelling> Spellings;
@ -305,6 +403,14 @@ class Attr {
// Set to true if this attribute meaningful when applied to or inherited
// in a class template definition.
bit MeaningfulToClassTemplateDefinition = 0;
// Set to true if this attribute can be used with '#pragma clang attribute'.
// By default, when this value is false, an attribute is supported by the
// '#pragma clang attribute' only when:
// - It has documentation.
// - It has a subject list whose subjects can be represented using subject
// match rules.
// - It has GNU/CXX11 spelling and doesn't require delayed parsing.
bit ForcePragmaAttributeSupport = 0;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, no language options are required.
list<LangOpt> LangOpts = [];
@ -478,6 +584,9 @@ def AnalyzerNoReturn : InheritableAttr {
def Annotate : InheritableParamAttr {
let Spellings = [GNU<"annotate">];
let Args = [StringArgument<"Annotation">];
// Ensure that the annotate attribute can be used with
// '#pragma clang attribute' even though it has no subject list.
let ForcePragmaAttributeSupport = 1;
let Documentation = [Undocumented];
}

View File

@ -0,0 +1,46 @@
//===-- AttrSubjectMatchRules.h - Attribute subject match rules -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_ATTR_SUBJECT_MATCH_RULES_H
#define LLVM_CLANG_BASIC_ATTR_SUBJECT_MATCH_RULES_H
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
namespace clang {
namespace attr {
/// \brief A list of all the recognized kinds of attributes.
enum SubjectMatchRule {
#define ATTR_MATCH_RULE(X, Spelling, IsAbstract) X,
#include "clang/Basic/AttrSubMatchRulesList.inc"
};
const char *getSubjectMatchRuleSpelling(SubjectMatchRule Rule);
using ParsedSubjectMatchRuleSet = llvm::DenseMap<SubjectMatchRule, SourceRange>;
} // end namespace attr
} // end namespace clang
namespace llvm {
template <>
struct DenseMapInfo<clang::attr::SubjectMatchRule> : DenseMapInfo<int> {
static inline clang::attr::SubjectMatchRule getEmptyKey() {
return (clang::attr::SubjectMatchRule)DenseMapInfo<int>::getEmptyKey();
}
static inline clang::attr::SubjectMatchRule getTombstoneKey() {
return (clang::attr::SubjectMatchRule)DenseMapInfo<int>::getTombstoneKey();
}
};
} // end namespace llvm
#endif

View File

@ -28,6 +28,11 @@ clang_tablegen(AttrList.inc -gen-clang-attr-list
SOURCE Attr.td
TARGET ClangAttrList)
clang_tablegen(AttrSubMatchRulesList.inc -gen-clang-attr-subject-match-rule-list
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE Attr.td
TARGET ClangAttrSubjectMatchRuleList)
clang_tablegen(AttrHasAttributeImpl.inc -gen-clang-attr-has-attribute-impl
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE Attr.td

View File

@ -461,7 +461,9 @@ def Uninitialized : DiagGroup<"uninitialized", [UninitializedSometimes,
def IgnoredPragmaIntrinsic : DiagGroup<"ignored-pragma-intrinsic">;
def UnknownPragmas : DiagGroup<"unknown-pragmas">;
def IgnoredPragmas : DiagGroup<"ignored-pragmas", [IgnoredPragmaIntrinsic]>;
def Pragmas : DiagGroup<"pragmas", [UnknownPragmas, IgnoredPragmas]>;
def PragmaClangAttribute : DiagGroup<"pragma-clang-attribute">;
def Pragmas : DiagGroup<"pragmas", [UnknownPragmas, IgnoredPragmas,
PragmaClangAttribute]>;
def UnknownWarningOption : DiagGroup<"unknown-warning-option">;
def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;

View File

@ -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">;

View File

@ -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">;

View File

@ -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)

View File

@ -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)

View File

@ -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;
@ -565,6 +566,12 @@ private:
/// #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.

View File

@ -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"
@ -509,9 +510,14 @@ public:
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
@ -774,6 +780,8 @@ public:
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; }

View File

@ -437,6 +437,20 @@ public:
/// 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.
@ -7206,9 +7220,13 @@ public:
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.
@ -8152,6 +8170,20 @@ public:
/// 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);

View File

@ -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;
@ -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");
}

View File

@ -183,6 +183,17 @@ private:
Sema &Actions;
};
/// PragmaAttributeHandler - "\#pragma clang attribute ...".
struct PragmaAttributeHandler : public PragmaHandler {
PragmaAttributeHandler(AttributeFactory &AttrFactory)
: PragmaHandler("attribute"), AttributesForPragmaAttribute(AttrFactory) {}
void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &FirstToken) override;
/// A pool of attributes that were parsed in \#pragma clang attribute.
ParsedAttributes AttributesForPragmaAttribute;
};
} // end namespace
void Parser::initializePragmaHandlers() {
@ -275,6 +286,9 @@ void Parser::initializePragmaHandlers() {
FPHandler.reset(new PragmaFPHandler());
PP.AddPragmaHandler("clang", FPHandler.get());
AttributePragmaHandler.reset(new PragmaAttributeHandler(AttrFactory));
PP.AddPragmaHandler("clang", AttributePragmaHandler.get());
}
void Parser::resetPragmaHandlers() {
@ -356,6 +370,9 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", FPHandler.get());
FPHandler.reset();
PP.RemovePragmaHandler("clang", AttributePragmaHandler.get());
AttributePragmaHandler.reset();
}
/// \brief Handle the annotation token produced for #pragma unused(...)
@ -966,6 +983,421 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
return true;
}
namespace {
struct PragmaAttributeInfo {
enum ActionType { Push, Pop };
ParsedAttributes &Attributes;
ActionType Action;
ArrayRef<Token> Tokens;
PragmaAttributeInfo(ParsedAttributes &Attributes) : Attributes(Attributes) {}
};
#include "clang/Parse/AttrSubMatchRulesParserStringSwitches.inc"
} // end anonymous namespace
static StringRef getIdentifier(const Token &Tok) {
if (Tok.is(tok::identifier))
return Tok.getIdentifierInfo()->getName();
const char *S = tok::getKeywordSpelling(Tok.getKind());
if (!S)
return "";
return S;
}
static bool isAbstractAttrMatcherRule(attr::SubjectMatchRule Rule) {
using namespace attr;
switch (Rule) {
#define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) \
case Value: \
return IsAbstract;
#include "clang/Basic/AttrSubMatchRulesList.inc"
}
}
static void diagnoseExpectedAttributeSubjectSubRule(
Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
SourceLocation SubRuleLoc) {
auto Diagnostic =
PRef.Diag(SubRuleLoc,
diag::err_pragma_attribute_expected_subject_sub_identifier)
<< PrimaryRuleName;
if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
Diagnostic << /*SubRulesSupported=*/1 << SubRules;
else
Diagnostic << /*SubRulesSupported=*/0;
}
static void diagnoseUnknownAttributeSubjectSubRule(
Parser &PRef, attr::SubjectMatchRule PrimaryRule, StringRef PrimaryRuleName,
StringRef SubRuleName, SourceLocation SubRuleLoc) {
auto Diagnostic =
PRef.Diag(SubRuleLoc, diag::err_pragma_attribute_unknown_subject_sub_rule)
<< SubRuleName << PrimaryRuleName;
if (const char *SubRules = validAttributeSubjectMatchSubRules(PrimaryRule))
Diagnostic << /*SubRulesSupported=*/1 << SubRules;
else
Diagnostic << /*SubRulesSupported=*/0;
}
bool Parser::ParsePragmaAttributeSubjectMatchRuleSet(
attr::ParsedSubjectMatchRuleSet &SubjectMatchRules, SourceLocation &AnyLoc,
SourceLocation &LastMatchRuleEndLoc) {
bool IsAny = false;
BalancedDelimiterTracker AnyParens(*this, tok::l_paren);
if (getIdentifier(Tok) == "any") {
AnyLoc = ConsumeToken();
IsAny = true;
if (AnyParens.expectAndConsume())
return true;
}
do {
// Parse the subject matcher rule.
StringRef Name = getIdentifier(Tok);
if (Name.empty()) {
Diag(Tok, diag::err_pragma_attribute_expected_subject_identifier);
return true;
}
std::pair<
Optional<attr::SubjectMatchRule>,
llvm::function_ref<Optional<attr::SubjectMatchRule>(StringRef, bool)>>
Rule = isAttributeSubjectMatchRule(Name);
if (!Rule.first) {
Diag(Tok, diag::err_pragma_attribute_unknown_subject_rule) << Name;
return true;
}
attr::SubjectMatchRule PrimaryRule = *Rule.first;
SourceLocation RuleLoc = ConsumeToken();
BalancedDelimiterTracker Parens(*this, tok::l_paren);
if (isAbstractAttrMatcherRule(PrimaryRule)) {
if (Parens.expectAndConsume())
return true;
} else if (Parens.consumeOpen()) {
if (!SubjectMatchRules
.insert(
std::make_pair(PrimaryRule, SourceRange(RuleLoc, RuleLoc)))
.second)
Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject)
<< Name
<< FixItHint::CreateRemoval(SourceRange(
RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleLoc));
LastMatchRuleEndLoc = RuleLoc;
continue;
}
// Parse the sub-rules.
StringRef SubRuleName = getIdentifier(Tok);
if (SubRuleName.empty()) {
diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
Tok.getLocation());
return true;
}
attr::SubjectMatchRule SubRule;
if (SubRuleName == "unless") {
SourceLocation SubRuleLoc = ConsumeToken();
BalancedDelimiterTracker Parens(*this, tok::l_paren);
if (Parens.expectAndConsume())
return true;
SubRuleName = getIdentifier(Tok);
if (SubRuleName.empty()) {
diagnoseExpectedAttributeSubjectSubRule(*this, PrimaryRule, Name,
SubRuleLoc);
return true;
}
auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/true);
if (!SubRuleOrNone) {
std::string SubRuleUnlessName = "unless(" + SubRuleName.str() + ")";
diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
SubRuleUnlessName, SubRuleLoc);
return true;
}
SubRule = *SubRuleOrNone;
ConsumeToken();
if (Parens.consumeClose())
return true;
} else {
auto SubRuleOrNone = Rule.second(SubRuleName, /*IsUnless=*/false);
if (!SubRuleOrNone) {
diagnoseUnknownAttributeSubjectSubRule(*this, PrimaryRule, Name,
SubRuleName, Tok.getLocation());
return true;
}
SubRule = *SubRuleOrNone;
ConsumeToken();
}
SourceLocation RuleEndLoc = Tok.getLocation();
LastMatchRuleEndLoc = RuleEndLoc;
if (Parens.consumeClose())
return true;
if (!SubjectMatchRules
.insert(std::make_pair(SubRule, SourceRange(RuleLoc, RuleEndLoc)))
.second) {
Diag(RuleLoc, diag::err_pragma_attribute_duplicate_subject)
<< attr::getSubjectMatchRuleSpelling(SubRule)
<< FixItHint::CreateRemoval(SourceRange(
RuleLoc, Tok.is(tok::comma) ? Tok.getLocation() : RuleEndLoc));
continue;
}
} while (IsAny && TryConsumeToken(tok::comma));
if (IsAny)
if (AnyParens.consumeClose())
return true;
return false;
}
namespace {
/// Describes the stage at which attribute subject rule parsing was interruped.
enum class MissingAttributeSubjectRulesRecoveryPoint {
Comma,
ApplyTo,
Equals,
Any,
None,
};
MissingAttributeSubjectRulesRecoveryPoint
getAttributeSubjectRulesRecoveryPointForToken(const Token &Tok) {
if (const auto *II = Tok.getIdentifierInfo()) {
if (II->isStr("apply_to"))
return MissingAttributeSubjectRulesRecoveryPoint::ApplyTo;
if (II->isStr("any"))
return MissingAttributeSubjectRulesRecoveryPoint::Any;
}
if (Tok.is(tok::equal))
return MissingAttributeSubjectRulesRecoveryPoint::Equals;
return MissingAttributeSubjectRulesRecoveryPoint::None;
}
/// Creates a diagnostic for the attribute subject rule parsing diagnostic that
/// suggests the possible attribute subject rules in a fix-it together with
/// any other missing tokens.
DiagnosticBuilder createExpectedAttributeSubjectRulesTokenDiagnostic(
unsigned DiagID, AttributeList &Attribute,
MissingAttributeSubjectRulesRecoveryPoint Point, Parser &PRef) {
SourceLocation Loc = PRef.getEndOfPreviousToken();
if (Loc.isInvalid())
Loc = PRef.getCurToken().getLocation();
auto Diagnostic = PRef.Diag(Loc, DiagID);
std::string FixIt;
MissingAttributeSubjectRulesRecoveryPoint EndPoint =
getAttributeSubjectRulesRecoveryPointForToken(PRef.getCurToken());
if (Point == MissingAttributeSubjectRulesRecoveryPoint::Comma)
FixIt = ", ";
if (Point <= MissingAttributeSubjectRulesRecoveryPoint::ApplyTo &&
EndPoint > MissingAttributeSubjectRulesRecoveryPoint::ApplyTo)
FixIt += "apply_to";
if (Point <= MissingAttributeSubjectRulesRecoveryPoint::Equals &&
EndPoint > MissingAttributeSubjectRulesRecoveryPoint::Equals)
FixIt += " = ";
SourceRange FixItRange(Loc);
if (EndPoint == MissingAttributeSubjectRulesRecoveryPoint::None) {
// Gather the subject match rules that are supported by the attribute.
SmallVector<std::pair<attr::SubjectMatchRule, bool>, 4> SubjectMatchRuleSet;
Attribute.getMatchRules(PRef.getLangOpts(), SubjectMatchRuleSet);
if (SubjectMatchRuleSet.empty()) {
// FIXME: We can emit a "fix-it" with a subject list placeholder when
// placeholders will be supported by the fix-its.
return Diagnostic;
}
FixIt += "any(";
bool NeedsComma = false;
for (const auto &I : SubjectMatchRuleSet) {
// Ensure that the missing rule is reported in the fix-it only when it's
// supported in the current language mode.
if (!I.second)
continue;
if (NeedsComma)
FixIt += ", ";
else
NeedsComma = true;
FixIt += attr::getSubjectMatchRuleSpelling(I.first);
}
FixIt += ")";
// Check if we need to remove the range
PRef.SkipUntil(tok::eof, Parser::StopBeforeMatch);
FixItRange.setEnd(PRef.getCurToken().getLocation());
}
if (FixItRange.getBegin() == FixItRange.getEnd())
Diagnostic << FixItHint::CreateInsertion(FixItRange.getBegin(), FixIt);
else
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FixItRange), FixIt);
return Diagnostic;
}
} // end anonymous namespace
void Parser::HandlePragmaAttribute() {
assert(Tok.is(tok::annot_pragma_attribute) &&
"Expected #pragma attribute annotation token");
SourceLocation PragmaLoc = Tok.getLocation();
auto *Info = static_cast<PragmaAttributeInfo *>(Tok.getAnnotationValue());
if (Info->Action == PragmaAttributeInfo::Pop) {
ConsumeToken();
Actions.ActOnPragmaAttributePop(PragmaLoc);
return;
}
// Parse the actual attribute with its arguments.
assert(Info->Action == PragmaAttributeInfo::Push &&
"Unexpected #pragma attribute command");
PP.EnterTokenStream(Info->Tokens, /*DisableMacroExpansion=*/false);
ConsumeToken();
ParsedAttributes &Attrs = Info->Attributes;
Attrs.clearListOnly();
auto SkipToEnd = [this]() {
SkipUntil(tok::eof, StopBeforeMatch);
ConsumeToken();
};
if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
// Parse the CXX11 style attribute.
ParseCXX11AttributeSpecifier(Attrs);
} else if (Tok.is(tok::kw___attribute)) {
ConsumeToken();
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
"attribute"))
return SkipToEnd();
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, "("))
return SkipToEnd();
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_pragma_attribute_expected_attribute_name);
SkipToEnd();
return;
}
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
if (Tok.isNot(tok::l_paren))
Attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
AttributeList::AS_GNU);
else
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, /*EndLoc=*/nullptr,
/*ScopeName=*/nullptr,
/*ScopeLoc=*/SourceLocation(),
AttributeList::AS_GNU,
/*Declarator=*/nullptr);
if (ExpectAndConsume(tok::r_paren))
return SkipToEnd();
if (ExpectAndConsume(tok::r_paren))
return SkipToEnd();
} else if (Tok.is(tok::kw___declspec)) {
ParseMicrosoftDeclSpecs(Attrs);
} else {
Diag(Tok, diag::err_pragma_attribute_expected_attribute_syntax);
if (Tok.getIdentifierInfo()) {
// If we suspect that this is an attribute suggest the use of
// '__attribute__'.
if (AttributeList::getKind(Tok.getIdentifierInfo(), /*ScopeName=*/nullptr,
AttributeList::AS_GNU) !=
AttributeList::UnknownAttribute) {
SourceLocation InsertStartLoc = Tok.getLocation();
ConsumeToken();
if (Tok.is(tok::l_paren)) {
ConsumeAnyToken();
SkipUntil(tok::r_paren, StopBeforeMatch);
if (Tok.isNot(tok::r_paren))
return SkipToEnd();
}
Diag(Tok, diag::note_pragma_attribute_use_attribute_kw)
<< FixItHint::CreateInsertion(InsertStartLoc, "__attribute__((")
<< FixItHint::CreateInsertion(Tok.getEndLoc(), "))");
}
}
SkipToEnd();
return;
}
if (!Attrs.getList() || Attrs.getList()->isInvalid()) {
SkipToEnd();
return;
}
// Ensure that we don't have more than one attribute.
if (Attrs.getList()->getNext()) {
SourceLocation Loc = Attrs.getList()->getNext()->getLoc();
Diag(Loc, diag::err_pragma_attribute_multiple_attributes);
SkipToEnd();
return;
}
if (!Attrs.getList()->isSupportedByPragmaAttribute()) {
Diag(PragmaLoc, diag::err_pragma_attribute_unsupported_attribute)
<< Attrs.getList()->getName();
SkipToEnd();
return;
}
AttributeList &Attribute = *Attrs.getList();
// Parse the subject-list.
if (!TryConsumeToken(tok::comma)) {
createExpectedAttributeSubjectRulesTokenDiagnostic(
diag::err_expected, Attribute,
MissingAttributeSubjectRulesRecoveryPoint::Comma, *this)
<< tok::comma;
SkipToEnd();
return;
}
if (Tok.isNot(tok::identifier)) {
createExpectedAttributeSubjectRulesTokenDiagnostic(
diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
SkipToEnd();
return;
}
const IdentifierInfo *II = Tok.getIdentifierInfo();
if (!II->isStr("apply_to")) {
createExpectedAttributeSubjectRulesTokenDiagnostic(
diag::err_pragma_attribute_invalid_subject_set_specifier, Attribute,
MissingAttributeSubjectRulesRecoveryPoint::ApplyTo, *this);
SkipToEnd();
return;
}
ConsumeToken();
if (!TryConsumeToken(tok::equal)) {
createExpectedAttributeSubjectRulesTokenDiagnostic(
diag::err_expected, Attribute,
MissingAttributeSubjectRulesRecoveryPoint::Equals, *this)
<< tok::equal;
SkipToEnd();
return;
}
attr::ParsedSubjectMatchRuleSet SubjectMatchRules;
SourceLocation AnyLoc, LastMatchRuleEndLoc;
if (ParsePragmaAttributeSubjectMatchRuleSet(SubjectMatchRules, AnyLoc,
LastMatchRuleEndLoc)) {
SkipToEnd();
return;
}
// Tokens following an ill-formed attribute will remain in the token stream
// and must be removed.
if (Tok.isNot(tok::eof)) {
Diag(Tok, diag::err_pragma_attribute_extra_tokens_after_attribute);
SkipToEnd();
return;
}
// Consume the eof terminator token.
ConsumeToken();
Actions.ActOnPragmaAttributePush(Attribute, PragmaLoc,
std::move(SubjectMatchRules));
}
// #pragma GCC visibility comes in two variants:
// 'push' '(' [visibility] ')'
// 'pop'
@ -2395,3 +2827,104 @@ void PragmaForceCUDAHostDeviceHandler::HandlePragma(
PP.Diag(FirstTok.getLocation(),
diag::warn_pragma_force_cuda_host_device_bad_arg);
}
/// \brief Handle the #pragma clang attribute directive.
///
/// The syntax is:
/// \code
/// #pragma clang attribute push(attribute, subject-set)
/// #pragma clang attribute pop
/// \endcode
///
/// The subject-set clause defines the set of declarations which receive the
/// attribute. Its exact syntax is described in the LanguageExtensions document
/// in Clang's documentation.
///
/// This directive instructs the compiler to begin/finish applying the specified
/// attribute to the set of attribute-specific declarations in the active range
/// of the pragma.
void PragmaAttributeHandler::HandlePragma(Preprocessor &PP,
PragmaIntroducerKind Introducer,
Token &FirstToken) {
Token Tok;
PP.Lex(Tok);
auto *Info = new (PP.getPreprocessorAllocator())
PragmaAttributeInfo(AttributesForPragmaAttribute);
// Parse the 'push' or 'pop'.
if (Tok.isNot(tok::identifier)) {
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_push_pop);
return;
}
const auto *II = Tok.getIdentifierInfo();
if (II->isStr("push"))
Info->Action = PragmaAttributeInfo::Push;
else if (II->isStr("pop"))
Info->Action = PragmaAttributeInfo::Pop;
else {
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_invalid_argument)
<< PP.getSpelling(Tok);
return;
}
PP.Lex(Tok);
// Parse the actual attribute.
if (Info->Action == PragmaAttributeInfo::Push) {
if (Tok.isNot(tok::l_paren)) {
PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
return;
}
PP.Lex(Tok);
// Lex the attribute tokens.
SmallVector<Token, 16> AttributeTokens;
int OpenParens = 1;
while (Tok.isNot(tok::eod)) {
if (Tok.is(tok::l_paren))
OpenParens++;
else if (Tok.is(tok::r_paren)) {
OpenParens--;
if (OpenParens == 0)
break;
}
AttributeTokens.push_back(Tok);
PP.Lex(Tok);
}
if (AttributeTokens.empty()) {
PP.Diag(Tok.getLocation(), diag::err_pragma_attribute_expected_attribute);
return;
}
if (Tok.isNot(tok::r_paren)) {
PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
return;
}
SourceLocation EndLoc = Tok.getLocation();
PP.Lex(Tok);
// Terminate the attribute for parsing.
Token EOFTok;
EOFTok.startToken();
EOFTok.setKind(tok::eof);
EOFTok.setLocation(EndLoc);
AttributeTokens.push_back(EOFTok);
Info->Tokens =
llvm::makeArrayRef(AttributeTokens).copy(PP.getPreprocessorAllocator());
}
if (Tok.isNot(tok::eod))
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< "clang attribute";
// Generate the annotated pragma token.
auto TokenArray = llvm::make_unique<Token[]>(1);
TokenArray[0].startToken();
TokenArray[0].setKind(tok::annot_pragma_attribute);
TokenArray[0].setLocation(FirstToken.getLocation());
TokenArray[0].setAnnotationEndLoc(FirstToken.getLocation());
TokenArray[0].setAnnotationValue(static_cast<void *>(Info));
PP.EnterTokenStream(std::move(TokenArray), 1,
/*DisableMacroExpansion=*/false);
}

View File

@ -382,6 +382,10 @@ Retry:
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.

View File

@ -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)

View File

@ -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 @@ 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 {
@ -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);
}
@ -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);
}

View File

@ -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;
@ -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

View File

@ -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();

View File

@ -13674,6 +13674,7 @@ CreateNewDecl:
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 @@ 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());

View File

@ -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?

View File

@ -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>())
@ -9931,6 +9932,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S,
NewTD->setInvalidDecl();
ProcessDeclAttributeList(S, NewTD, AttrList);
AddPragmaAttributes(S, NewTD);
CheckTypedefForVariablyModifiedType(S, NewTD);
Invalid |= NewTD->isInvalidDecl();

View File

@ -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
@ -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);
@ -1706,7 +1708,8 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc,
if (attrList)
ProcessDeclAttributeList(TUScope, PDecl, attrList);
AddPragmaAttributes(TUScope, PDecl);
if (PrevDecl)
mergeDeclAttributes(PDecl, PrevDecl);
@ -1805,6 +1808,7 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc,
if (AttrList)
ProcessDeclAttributeList(TUScope, CDecl, AttrList);
AddPragmaAttributes(TUScope, CDecl);
CheckObjCDeclScope(CDecl);
return ActOnObjCContainerStartDefinition(CDecl);
@ -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(
@ -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);
@ -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);
@ -4429,6 +4435,7 @@ Decl *Sema::ActOnMethodDeclaration(
if (AttrList)
ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
AddPragmaAttributes(TUScope, ObjCMethod);
// Add the method now.
const ObjCMethodDecl *PrevMethod = nullptr;

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,181 @@
// RUN: %clang_cc1 -Wno-pragma-clang-attribute -verify -std=c++11 %s
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = function)
void function();
#pragma clang attribute pop
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(variable(is_parameter), function))
#pragma clang attribute pop
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = variable(unless(is_parameter)))
#pragma clang attribute pop
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(variable(unless(is_parameter))))
#pragma clang attribute pop
#pragma clang attribute push (__attribute__((abi_tag("a")))) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) apply_to=function) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) = function) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) any(function)) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) 22) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) function) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((abi_tag("a"))) (function)) // expected-error {{expected ','}}
#pragma clang attribute push(__attribute__((annotate("test"))), ) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), = any(function)) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), any(function)) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply = any(function )) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), to = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_only_to = function) // expected-error {{expected attribute subject set specifier 'apply_to'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to any(function)) // expected-error {{expected '='}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to function) // expected-error {{expected '='}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to) // expected-error {{expected '='}}
#pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to 41 (22)) // expected-error {{expected '='}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any {) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any function) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = { function, enum }) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(function ) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(function, )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, enum ) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = () ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( + ) ) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any()) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, 42 )) // expected-error {{expected an identifier that corresponds to an attribute subject rule}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( diag )) // expected-error {{unknown attribute subject rule 'diag'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( a )) // expected-error {{unknown attribute subject rule 'a'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, for)) // expected-error {{unknown attribute subject rule 'for'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function42, for )) // expected-error {{unknown attribute subject rule 'function42'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(hasType)) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = hasType) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = hasType(functionType)) // OK
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable( )) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable( ) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is ) )) // expected-error {{unknown attribute subject matcher sub-rule 'is'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_parameter, not) )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable ( ) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = variable ( // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, variable (is ()) )) // expected-error {{unknown attribute subject matcher sub-rule 'is'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, variable (42) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, namespace("test") )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'namespace' matcher does not support sub-rules}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, variable ("test" )) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = enum(is_parameter)) // expected-error {{invalid use of attribute subject matcher sub-rule 'is_parameter'; 'enum' matcher does not support sub-rules}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any(enum(is_parameter))) // expected-error {{invalid use of attribute subject matcher sub-rule 'is_parameter'; 'enum' matcher does not support sub-rules}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any (function, variable (unless) )) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any (function, variable (unless() )) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any ( function, variable (unless(is)) )) // expected-error {{unknown attribute subject matcher sub-rule 'unless(is)'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter, not)) )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter), not) ) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable unless is_parameter )) // expected-error {{expected ')'}} expected-note {{to match this '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless is_parameter) )) // expected-error {{expected '('}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, variable (unless(42)) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, enum(unless("test")) )) // expected-error {{expected an identifier that corresponds to an attribute subject matcher sub-rule; 'enum' matcher does not support sub-rules}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, variable (unless(is_global)) )) // expected-error {{unknown attribute subject matcher sub-rule 'unless(is_global)'; 'variable' matcher supports the following sub-rules: 'is_thread_local', 'is_global', 'is_parameter', 'unless(is_parameter)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( enum(unless(is_parameter)) )) // expected-error {{invalid use of attribute subject matcher sub-rule 'unless(is_parameter)'; 'enum' matcher does not support sub-rules}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, function )) // expected-error {{duplicate attribute subject matcher 'function'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, function, function )) // expected-error 2 {{duplicate attribute subject matcher 'function'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( function, enum, function )) // expected-error {{duplicate attribute subject matcher 'function'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( enum, enum, function )) // expected-error {{duplicate attribute subject matcher 'enum'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_global), variable(is_global) )) // expected-error {{duplicate attribute subject matcher 'variable(is_global)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(is_global), function, variable(is_global), variable(is_global) )) // expected-error 2 {{duplicate attribute subject matcher 'variable(is_global)'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)) )) // expected-error {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = any( variable(unless(is_parameter)), variable(unless(is_parameter)), enum, variable(unless(is_parameter)) )) // expected-error 2 {{duplicate attribute subject matcher 'variable(unless(is_parameter))'}}
#pragma clang attribute // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
#pragma clang attribute 42 // expected-error {{expected 'push' or 'pop' after '#pragma clang attribute'}}
#pragma clang attribute pushpop // expected-error {{unexpected argument 'pushpop' to '#pragma clang attribute'; expected 'push' or 'pop'}}
#pragma clang attribute push // expected-error {{expected '('}}
#pragma clang attribute push ( // expected-error {{expected an attribute after '('}}
#pragma clang attribute push (__attribute__((annotate)) // expected-error {{expected ')'}}
#pragma clang attribute push () // expected-error {{expected an attribute after '('}}
#pragma clang attribute push (__attribute__((annotate("test"))), apply_to = function) () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
// expected-error@-1 {{expected unqualified-id}}
// expected-error@-2 {{unterminated '#pragma clang attribute push' at end of file}}
#pragma clang attribute pop () // expected-warning {{extra tokens at end of '#pragma clang attribute'}}
;
#pragma clang attribute push (__attribute__((42))) // expected-error {{expected identifier that represents an attribute name}}
#pragma clang attribute push (__attribute__((annotate)) foo) // expected-error {{expected ','}}
#pragma clang attribute push (__attribute__((annotate)), apply_to=function foo) // expected-error {{extra tokens after attribute in a '#pragma clang attribute push'}}
#pragma clang attribute push (__attribute__((availability(macos, foo=1))), apply_to=function) // expected-error {{'foo' is not an availability stage; use 'introduced', 'deprecated', or 'obsoleted'}}
// expected-error@-1 {{attribute 'availability' is not supported by '#pragma clang attribute'}}
#pragma clang attribute push (__attribute__((availability(macos, 1))), apply_to=function) // expected-error {{expected 'introduced', 'deprecated', or 'obsoleted'}}
#pragma clang attribute push (__attribute__((used)), apply_to=function) // expected-error {{attribute 'used' is not supported by '#pragma clang attribute'}}
void statementPragmasAndPragmaExpression() {
#pragma clang attribute push (__attribute__((annotate("hello"))), apply_to=variable)
#pragma clang attribute pop
int x = 0;
_Pragma("clang attribute push (__attribute__((annotate(\"hi\"))), apply_to = function)");
_Pragma("clang attribute push (__attribute__((annotate(\"hi\"))), apply_to = any(function(is_method ))"); // expected-error {{expected ')'}}
}
_Pragma("clang attribute pop");
#pragma clang attribute push (__attribute__((address_space(0))), apply_to=variable) // expected-error {{attribute 'address_space' is not supported by '#pragma clang attribute'}}
// Check support for CXX11 style attributes
#pragma clang attribute push ([[noreturn]], apply_to = any(function))
#pragma clang attribute pop
#pragma clang attribute push ([[clang::disable_tail_calls]], apply_to = function)
#pragma clang attribute pop
#pragma clang attribute push ([[gnu::abi_tag]], apply_to=any(function))
#pragma clang attribute pop
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]], apply_to = function) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
#pragma clang attribute push ([[clang::disable_tail_calls, noreturn]]) // expected-error {{more than one attribute specified in '#pragma clang attribute push'}}
#pragma clang attribute push ([[gnu::abi_tag]], apply_to=namespace)
#pragma clang attribute pop
#pragma clang attribute push ([[fallthrough]], apply_to=function) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}}
#pragma clang attribute push ([[clang::fallthrough]], apply_to=function) // expected-error {{attribute 'fallthrough' is not supported by '#pragma clang attribute'}}
#pragma clang attribute push ([[]], apply_to = function) // A noop
#pragma clang attribute push ([[noreturn ""]], apply_to=function) // expected-error {{expected ']'}}
#pragma clang attribute pop
#pragma clang attribute push ([[noreturn 42]]) // expected-error {{expected ']'}} expected-error {{expected ','}}
#pragma clang attribute push(__attribute__, apply_to=function) // expected-error {{expected '(' after 'attribute'}}
#pragma clang attribute push(__attribute__(), apply_to=function) // expected-error {{expected '(' after '('}}
#pragma clang attribute push(__attribute__(()), apply_to=function) // expected-error {{expected identifier that represents an attribute name}}
#pragma clang attribute push(__attribute__((annotate, apply_to=function))) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate("test"), apply_to=function))) // expected-error {{expected ')'}}
#pragma clang attribute push(__attribute__((annotate), apply_to=function)) // expected-error {{expected ')'}}
#pragma clang attribute push (42) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
#pragma clang attribute push (test) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
#pragma clang attribute push (annotate) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
// expected-note@-1 {{use the GNU '__attribute__' syntax}}
#pragma clang attribute push (annotate("test")) // expected-error {{expected an attribute that is specified using the GNU, C++11 or '__declspec' syntax}}
// expected-note@-1 {{use the GNU '__attribute__' syntax}}

View File

@ -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

View File

@ -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();

View File

@ -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'. ***""") )

View File

@ -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@"

View File

@ -12,13 +12,15 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
@ -1522,6 +1524,334 @@ static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS)
OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n";
}
static bool hasGNUorCXX11Spelling(const Record &Attribute) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute);
for (const auto &I : Spellings) {
if (I.variety() == "GNU" || I.variety() == "CXX11")
return true;
}
return false;
}
namespace {
struct AttributeSubjectMatchRule {
const Record *MetaSubject;
const Record *Constraint;
AttributeSubjectMatchRule(const Record *MetaSubject, const Record *Constraint)
: MetaSubject(MetaSubject), Constraint(Constraint) {
assert(MetaSubject && "Missing subject");
}
bool isSubRule() const { return Constraint != nullptr; }
std::vector<Record *> getSubjects() const {
return (Constraint ? Constraint : MetaSubject)
->getValueAsListOfDefs("Subjects");
}
std::vector<Record *> getLangOpts() const {
if (Constraint) {
// Lookup the options in the sub-rule first, in case the sub-rule
// overrides the rules options.
std::vector<Record *> Opts = Constraint->getValueAsListOfDefs("LangOpts");
if (!Opts.empty())
return Opts;
}
return MetaSubject->getValueAsListOfDefs("LangOpts");
}
// Abstract rules are used only for sub-rules
bool isAbstractRule() const { return getSubjects().empty(); }
std::string getName() const {
return (Constraint ? Constraint : MetaSubject)->getValueAsString("Name");
}
bool isNegatedSubRule() const {
assert(isSubRule() && "Not a sub-rule");
return Constraint->getValueAsBit("Negated");
}
std::string getSpelling() const {
std::string Result = MetaSubject->getValueAsString("Name");
if (isSubRule()) {
Result += '(';
if (isNegatedSubRule())
Result += "unless(";
Result += getName();
if (isNegatedSubRule())
Result += ')';
Result += ')';
}
return Result;
}
std::string getEnumValueName() const {
std::string Result =
"SubjectMatchRule_" + MetaSubject->getValueAsString("Name");
if (isSubRule()) {
Result += "_";
if (isNegatedSubRule())
Result += "not_";
Result += Constraint->getValueAsString("Name");
}
if (isAbstractRule())
Result += "_abstract";
return Result;
}
std::string getEnumValue() const { return "attr::" + getEnumValueName(); }
static const char *EnumName;
};
const char *AttributeSubjectMatchRule::EnumName = "attr::SubjectMatchRule";
struct PragmaClangAttributeSupport {
std::vector<AttributeSubjectMatchRule> Rules;
llvm::DenseMap<const Record *, AttributeSubjectMatchRule> SubjectsToRules;
PragmaClangAttributeSupport(RecordKeeper &Records);
bool isAttributedSupported(const Record &Attribute);
void emitMatchRuleList(raw_ostream &OS);
std::string generateStrictConformsTo(const Record &Attr, raw_ostream &OS);
void generateParsingHelpers(raw_ostream &OS);
};
} // end anonymous namespace
PragmaClangAttributeSupport::PragmaClangAttributeSupport(
RecordKeeper &Records) {
std::vector<Record *> MetaSubjects =
Records.getAllDerivedDefinitions("AttrSubjectMatcherRule");
auto MapFromSubjectsToRules = [this](const Record *SubjectContainer,
const Record *MetaSubject,
const Record *Constraint = nullptr) {
Rules.emplace_back(MetaSubject, Constraint);
std::vector<Record *> ApplicableSubjects =
SubjectContainer->getValueAsListOfDefs("Subjects");
for (const auto *Subject : ApplicableSubjects) {
bool Inserted =
SubjectsToRules.try_emplace(Subject, MetaSubject, Constraint).second;
if (!Inserted) {
PrintFatalError("Attribute subject match rules should not represent"
"same attribute subjects.");
}
}
};
for (const auto *MetaSubject : MetaSubjects) {
MapFromSubjectsToRules(MetaSubject, MetaSubject);
std::vector<Record *> Constraints =
MetaSubject->getValueAsListOfDefs("Constraints");
for (const auto *Constraint : Constraints)
MapFromSubjectsToRules(Constraint, MetaSubject, Constraint);
}
}
static PragmaClangAttributeSupport &
getPragmaAttributeSupport(RecordKeeper &Records) {
static PragmaClangAttributeSupport Instance(Records);
return Instance;
}
void PragmaClangAttributeSupport::emitMatchRuleList(raw_ostream &OS) {
OS << "#ifndef ATTR_MATCH_SUB_RULE\n";
OS << "#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, "
"IsNegated) "
<< "ATTR_MATCH_RULE(Value, Spelling, IsAbstract)\n";
OS << "#endif\n";
for (const auto &Rule : Rules) {
OS << (Rule.isSubRule() ? "ATTR_MATCH_SUB_RULE" : "ATTR_MATCH_RULE") << '(';
OS << Rule.getEnumValueName() << ", \"" << Rule.getSpelling() << "\", "
<< Rule.isAbstractRule();
if (Rule.isSubRule())
OS << ", "
<< AttributeSubjectMatchRule(Rule.MetaSubject, nullptr).getEnumValue()
<< ", " << Rule.isNegatedSubRule();
OS << ")\n";
}
OS << "#undef ATTR_MATCH_SUB_RULE\n";
}
bool PragmaClangAttributeSupport::isAttributedSupported(
const Record &Attribute) {
if (Attribute.getValueAsBit("ForcePragmaAttributeSupport"))
return true;
// Opt-out rules:
// FIXME: The documentation check should be moved before
// the ForcePragmaAttributeSupport check after annotate is documented.
// No documentation present.
if (Attribute.isValueUnset("Documentation"))
return false;
std::vector<Record *> Docs = Attribute.getValueAsListOfDefs("Documentation");
if (Docs.empty())
return false;
if (Docs.size() == 1 && Docs[0]->getName() == "Undocumented")
return false;
// An attribute requires delayed parsing (LateParsed is on)
if (Attribute.getValueAsBit("LateParsed"))
return false;
// An attribute has no GNU/CXX11 spelling
if (!hasGNUorCXX11Spelling(Attribute))
return false;
// An attribute subject list has a subject that isn't covered by one of the
// subject match rules or has no subjects at all.
if (Attribute.isValueUnset("Subjects"))
return false;
const Record *SubjectObj = Attribute.getValueAsDef("Subjects");
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
if (Subjects.empty())
return false;
for (const auto *Subject : Subjects) {
if (SubjectsToRules.find(Subject) == SubjectsToRules.end())
return false;
}
return true;
}
std::string
PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr,
raw_ostream &OS) {
if (!isAttributedSupported(Attr))
return "nullptr";
// Generate a function that constructs a set of matching rules that describe
// to which declarations the attribute should apply to.
std::string FnName = "matchRulesFor" + Attr.getName().str();
std::stringstream SS;
SS << "static void " << FnName << "(llvm::SmallVectorImpl<std::pair<"
<< AttributeSubjectMatchRule::EnumName
<< ", bool>> &MatchRules, const LangOptions &LangOpts) {\n";
if (Attr.isValueUnset("Subjects")) {
SS << "}\n\n";
OS << SS.str();
return FnName;
}
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
for (const auto *Subject : Subjects) {
auto It = SubjectsToRules.find(Subject);
assert(It != SubjectsToRules.end() &&
"This attribute is unsupported by #pragma clang attribute");
AttributeSubjectMatchRule Rule = It->getSecond();
// The rule might be language specific, so only subtract it from the given
// rules if the specific language options are specified.
std::vector<Record *> LangOpts = Rule.getLangOpts();
SS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue()
<< ", /*IsSupported=*/";
if (!LangOpts.empty()) {
for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) {
std::string Part = (*I)->getValueAsString("Name");
if ((*I)->getValueAsBit("Negated"))
SS << "!";
SS << "LangOpts." + Part;
if (I + 1 != E)
SS << " || ";
}
} else
SS << "true";
SS << "));\n";
}
SS << "}\n\n";
OS << SS.str();
return FnName;
}
void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) {
// Generate routines that check the names of sub-rules.
OS << "Optional<attr::SubjectMatchRule> "
"defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n";
OS << " return None;\n";
OS << "}\n\n";
std::map<const Record *, std::vector<AttributeSubjectMatchRule>>
SubMatchRules;
for (const auto &Rule : Rules) {
if (!Rule.isSubRule())
continue;
SubMatchRules[Rule.MetaSubject].push_back(Rule);
}
for (const auto &SubMatchRule : SubMatchRules) {
OS << "Optional<attr::SubjectMatchRule> isAttributeSubjectMatchSubRuleFor_"
<< SubMatchRule.first->getValueAsString("Name")
<< "(StringRef Name, bool IsUnless) {\n";
OS << " if (IsUnless)\n";
OS << " return "
"llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n";
for (const auto &Rule : SubMatchRule.second) {
if (Rule.isNegatedSubRule())
OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue()
<< ").\n";
}
OS << " Default(None);\n";
OS << " return "
"llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n";
for (const auto &Rule : SubMatchRule.second) {
if (!Rule.isNegatedSubRule())
OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue()
<< ").\n";
}
OS << " Default(None);\n";
OS << "}\n\n";
}
// Generate the function that checks for the top-level rules.
OS << "std::pair<Optional<attr::SubjectMatchRule>, "
"llvm::function_ref<Optional<attr::SubjectMatchRule> (StringRef, "
"bool)>> isAttributeSubjectMatchRule(StringRef Name) {\n";
OS << " return "
"llvm::StringSwitch<std::pair<Optional<attr::SubjectMatchRule>, "
"llvm::function_ref<Optional<attr::SubjectMatchRule> (StringRef, "
"bool)>>>(Name).\n";
for (const auto &Rule : Rules) {
if (Rule.isSubRule())
continue;
std::string SubRuleFunction;
if (SubMatchRules.count(Rule.MetaSubject))
SubRuleFunction = "isAttributeSubjectMatchSubRuleFor_" + Rule.getName();
else
SubRuleFunction = "defaultIsAttributeSubjectMatchSubRuleFor";
OS << " Case(\"" << Rule.getName() << "\", std::make_pair("
<< Rule.getEnumValue() << ", " << SubRuleFunction << ")).\n";
}
OS << " Default(std::make_pair(None, "
"defaultIsAttributeSubjectMatchSubRuleFor));\n";
OS << "}\n\n";
// Generate the function that checks for the submatch rules.
OS << "const char *validAttributeSubjectMatchSubRules("
<< AttributeSubjectMatchRule::EnumName << " Rule) {\n";
OS << " switch (Rule) {\n";
for (const auto &SubMatchRule : SubMatchRules) {
OS << " case "
<< AttributeSubjectMatchRule(SubMatchRule.first, nullptr).getEnumValue()
<< ":\n";
OS << " return \"'";
bool IsFirst = true;
for (const auto &Rule : SubMatchRule.second) {
if (!IsFirst)
OS << ", '";
IsFirst = false;
if (Rule.isNegatedSubRule())
OS << "unless(";
OS << Rule.getName();
if (Rule.isNegatedSubRule())
OS << ')';
OS << "'";
}
OS << "\";\n";
}
OS << " default: return nullptr;\n";
OS << " }\n";
OS << "}\n\n";
}
template <typename Fn>
static void forEachUniqueSpelling(const Record &Attr, Fn &&F) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
@ -2109,6 +2439,17 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#undef PRAGMA_SPELLING_ATTR\n";
}
// Emits the enumeration list for attributes.
void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader(
"List of all attribute subject matching rules that Clang recognizes", OS);
PragmaClangAttributeSupport &PragmaAttributeSupport =
getPragmaAttributeSupport(Records);
emitDefaultDefine(OS, "ATTR_MATCH_RULE", nullptr);
PragmaAttributeSupport.emitMatchRuleList(OS);
OS << "#undef ATTR_MATCH_RULE\n";
}
// Emits the code to read an attribute from a precompiled header.
void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute deserialization code", OS);
@ -2704,9 +3045,13 @@ static std::string GetSubjectWithSuffix(const Record *R) {
return B + "Decl";
}
static std::string functionNameForCustomAppertainsTo(const Record &Subject) {
return "is" + Subject.getName().str();
}
static std::string GenerateCustomAppertainsTo(const Record &Subject,
raw_ostream &OS) {
std::string FnName = "is" + Subject.getName().str();
std::string FnName = functionNameForCustomAppertainsTo(Subject);
// If this code has already been generated, simply return the previous
// instance of it.
@ -2791,6 +3136,40 @@ static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
return FnName;
}
static void
emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport,
raw_ostream &OS) {
OS << "static bool checkAttributeMatchRuleAppliesTo(const Decl *D, "
<< AttributeSubjectMatchRule::EnumName << " rule) {\n";
OS << " switch (rule) {\n";
for (const auto &Rule : PragmaAttributeSupport.Rules) {
if (Rule.isAbstractRule()) {
OS << " case " << Rule.getEnumValue() << ":\n";
OS << " assert(false && \"Abstract matcher rule isn't allowed\");\n";
OS << " return false;\n";
continue;
}
std::vector<Record *> Subjects = Rule.getSubjects();
assert(!Subjects.empty() && "Missing subjects");
OS << " case " << Rule.getEnumValue() << ":\n";
OS << " return ";
for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) {
// If the subject has custom code associated with it, use the function
// that was generated for GenerateAppertainsTo to check if the declaration
// is valid.
if ((*I)->isSubClassOf("SubsetSubject"))
OS << functionNameForCustomAppertainsTo(**I) << "(D)";
else
OS << "isa<" << GetSubjectWithSuffix(*I) << ">(D)";
if (I + 1 != E)
OS << " || ";
}
OS << ";\n";
}
OS << " }\n}\n\n";
}
static void GenerateDefaultLangOptRequirements(raw_ostream &OS) {
OS << "static bool defaultDiagnoseLangOpts(Sema &, ";
OS << "const AttributeList &) {\n";
@ -2949,6 +3328,9 @@ static bool IsKnownToGCC(const Record &Attr) {
void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Parsed attribute helpers", OS);
PragmaClangAttributeSupport &PragmaAttributeSupport =
getPragmaAttributeSupport(Records);
// Get the list of parsed attributes, and accept the optional list of
// duplicates due to the ParseKind.
ParsedAttrMap Dupes;
@ -2982,10 +3364,13 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
SS << ", " << I->second->isSubClassOf("TypeAttr");
SS << ", " << I->second->isSubClassOf("StmtAttr");
SS << ", " << IsKnownToGCC(*I->second);
SS << ", " << PragmaAttributeSupport.isAttributedSupported(*I->second);
SS << ", " << GenerateAppertainsTo(*I->second, OS);
SS << ", " << GenerateLangOptRequirements(*I->second, OS);
SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS);
SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS);
SS << ", "
<< PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS);
SS << " }";
if (I + 1 != E)
@ -2997,6 +3382,9 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
OS << "static const ParsedAttrInfo AttrInfoMap[AttributeList::UnknownAttribute + 1] = {\n";
OS << SS.str();
OS << "};\n\n";
// Generate the attribute match rules.
emitAttributeMatchRules(PragmaAttributeSupport, OS);
}
// Emits the kind list of parsed attributes
@ -3136,6 +3524,11 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records,
emitClangAttrLateParsedList(Records, OS);
}
void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
raw_ostream &OS) {
getPragmaAttributeSupport(Records).generateParsingHelpers(OS);
}
class DocumentationData {
public:
const Record *Documentation;
@ -3167,8 +3560,8 @@ enum SpellingKind {
Pragma = 1 << 5
};
static void WriteDocumentation(const DocumentationData &Doc,
raw_ostream &OS) {
static void WriteDocumentation(RecordKeeper &Records,
const DocumentationData &Doc, raw_ostream &OS) {
// FIXME: there is no way to have a per-spelling category for the attribute
// documentation. This may not be a limiting factor since the spellings
// should generally be consistently applied across the category.
@ -3250,7 +3643,7 @@ static void WriteDocumentation(const DocumentationData &Doc,
// List what spelling syntaxes the attribute supports.
OS << ".. csv-table:: Supported Syntaxes\n";
OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\",";
OS << " \"Pragma\"\n\n";
OS << " \"Pragma\", \"Pragma clang attribute\"\n\n";
OS << " \"";
if (SupportedSpellings & GNU) OS << "X";
OS << "\",\"";
@ -3261,6 +3654,9 @@ static void WriteDocumentation(const DocumentationData &Doc,
if (SupportedSpellings & Keyword) OS << "X";
OS << "\", \"";
if (SupportedSpellings & Pragma) OS << "X";
OS << "\", \"";
if (getPragmaAttributeSupport(Records).isAttributedSupported(*Doc.Attribute))
OS << "X";
OS << "\"\n\n";
// If the attribute is deprecated, print a message about it, and possibly
@ -3327,7 +3723,40 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) {
// Walk over each of the attributes in the category and write out their
// documentation.
for (const auto &Doc : I.second)
WriteDocumentation(Doc, OS);
WriteDocumentation(Records, Doc, OS);
}
}
void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records,
raw_ostream &OS) {
PragmaClangAttributeSupport Support = getPragmaAttributeSupport(Records);
ParsedAttrMap Attrs = getParsedAttrList(Records);
unsigned NumAttrs = 0;
for (const auto &I : Attrs) {
if (Support.isAttributedSupported(*I.second))
++NumAttrs;
}
OS << "#pragma clang attribute supports " << NumAttrs << " attributes:\n";
for (const auto &I : Attrs) {
if (!Support.isAttributedSupported(*I.second))
continue;
OS << I.first;
if (I.second->isValueUnset("Subjects")) {
OS << " ()\n";
continue;
}
const Record *SubjectObj = I.second->getValueAsDef("Subjects");
std::vector<Record *> Subjects =
SubjectObj->getValueAsListOfDefs("Subjects");
OS << " (";
for (const auto &Subject : llvm::enumerate(Subjects)) {
if (Subject.index())
OS << ", ";
OS << Support.SubjectsToRules.find(Subject.value())
->getSecond()
.getEnumValueName();
}
OS << ")\n";
}
}

View File

@ -25,8 +25,10 @@ using namespace clang;
enum ActionType {
GenClangAttrClasses,
GenClangAttrParserStringSwitches,
GenClangAttrSubjectMatchRulesParserStringSwitches,
GenClangAttrImpl,
GenClangAttrList,
GenClangAttrSubjectMatchRuleList,
GenClangAttrPCHRead,
GenClangAttrPCHWrite,
GenClangAttrHasAttributeImpl,
@ -54,7 +56,8 @@ enum ActionType {
GenArmNeonTest,
GenAttrDocs,
GenDiagDocs,
GenOptDocs
GenOptDocs,
GenTestPragmaAttributeSupportedAttributes
};
namespace {
@ -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",
@ -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",
@ -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",
@ -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;
@ -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;

View File

@ -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);
@ -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