From 0a849f47d2f76a09be9d30d3f7809663efaf87e8 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 18 Apr 2017 09:41:47 +0000 Subject: [PATCH] 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 --- clang/docs/LanguageExtensions.rst | 175 ++++++ clang/include/clang/Basic/Attr.td | 109 ++++ .../clang/Basic/AttrSubjectMatchRules.h | 46 ++ clang/include/clang/Basic/CMakeLists.txt | 5 + clang/include/clang/Basic/DiagnosticGroups.td | 4 +- .../clang/Basic/DiagnosticParseKinds.td | 37 ++ .../clang/Basic/DiagnosticSemaKinds.td | 19 + clang/include/clang/Basic/TokenKinds.def | 3 + clang/include/clang/Parse/CMakeLists.txt | 6 + clang/include/clang/Parse/Parser.h | 7 + clang/include/clang/Sema/AttributeList.h | 8 + clang/include/clang/Sema/Sema.h | 32 ++ clang/lib/Basic/Attributes.cpp | 11 + clang/lib/Parse/ParsePragma.cpp | 533 ++++++++++++++++++ clang/lib/Parse/ParseStmt.cpp | 4 + clang/lib/Parse/Parser.cpp | 4 + clang/lib/Sema/AttributeList.cpp | 21 + clang/lib/Sema/Sema.cpp | 67 +-- clang/lib/Sema/SemaAttr.cpp | 211 +++++++ clang/lib/Sema/SemaDecl.cpp | 2 + clang/lib/Sema/SemaDeclAttr.cpp | 3 + clang/lib/Sema/SemaDeclCXX.cpp | 2 + clang/lib/Sema/SemaDeclObjC.cpp | 13 +- clang/test/FixIt/fixit-pragma-attribute.c | 6 + clang/test/FixIt/fixit-pragma-attribute.cpp | 83 +++ ...agma-attribute-cxx-subject-match-rules.cpp | 169 ++++++ clang/test/Misc/pragma-attribute-cxx.cpp | 106 ++++ ...ragma-attribute-objc-subject-match-rules.m | 113 ++++ clang/test/Misc/pragma-attribute-objc.m | 164 ++++++ .../Misc/pragma-attribute-strict-subjects.c | 222 ++++++++ ...a-attribute-supported-attributes-list.test | 62 ++ .../test/Parser/pragma-attribute-declspec.cpp | 13 + clang/test/Parser/pragma-attribute.cpp | 181 ++++++ .../Sema/pragma-attribute-strict-subjects.c | 153 +++++ clang/test/Sema/pragma-attribute.c | 47 ++ clang/test/lit.cfg | 2 + clang/test/lit.site.cfg.in | 1 + clang/utils/TableGen/ClangAttrEmitter.cpp | 443 ++++++++++++++- clang/utils/TableGen/TableGen.cpp | 31 +- clang/utils/TableGen/TableGenBackends.h | 6 + 40 files changed, 3072 insertions(+), 52 deletions(-) create mode 100644 clang/include/clang/Basic/AttrSubjectMatchRules.h create mode 100644 clang/test/FixIt/fixit-pragma-attribute.c create mode 100644 clang/test/FixIt/fixit-pragma-attribute.cpp create mode 100644 clang/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp create mode 100644 clang/test/Misc/pragma-attribute-cxx.cpp create mode 100644 clang/test/Misc/pragma-attribute-objc-subject-match-rules.m create mode 100644 clang/test/Misc/pragma-attribute-objc.m create mode 100644 clang/test/Misc/pragma-attribute-strict-subjects.c create mode 100644 clang/test/Misc/pragma-attribute-supported-attributes-list.test create mode 100644 clang/test/Parser/pragma-attribute-declspec.cpp create mode 100644 clang/test/Parser/pragma-attribute.cpp create mode 100644 clang/test/Sema/pragma-attribute-strict-subjects.c create mode 100644 clang/test/Sema/pragma-attribute.c diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a8fb4623b637..187dae751be1 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -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 `. + +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. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index c5d2c7fc618b..bf62408db2cc 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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 subjects, + bit negated = 0> { + string Name = name; + list 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 LangOpts = []; +} +class AttrSubjectMatcherRule subjects, + list subrules = []> { + string Name = name; + list Subjects = subjects; + list 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 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 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 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]; } diff --git a/clang/include/clang/Basic/AttrSubjectMatchRules.h b/clang/include/clang/Basic/AttrSubjectMatchRules.h new file mode 100644 index 000000000000..955f495ad67d --- /dev/null +++ b/clang/include/clang/Basic/AttrSubjectMatchRules.h @@ -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; + +} // end namespace attr +} // end namespace clang + +namespace llvm { + +template <> +struct DenseMapInfo : DenseMapInfo { + static inline clang::attr::SubjectMatchRule getEmptyKey() { + return (clang::attr::SubjectMatchRule)DenseMapInfo::getEmptyKey(); + } + static inline clang::attr::SubjectMatchRule getTombstoneKey() { + return (clang::attr::SubjectMatchRule)DenseMapInfo::getTombstoneKey(); + } +}; + +} // end namespace llvm + +#endif diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index e4929b5b52b9..3e0fb8728c48 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -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 diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 4bcf3be0a8eb..4cde1c81fd4d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -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">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index aebf8a9f3574..d95e43c10c55 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8afe61645ae0..3912a8549188 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; +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">; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 48e0c33f0e86..968b203a3827 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/clang/include/clang/Parse/CMakeLists.txt b/clang/include/clang/Parse/CMakeLists.txt index ec75f7b96b14..2cc7e54b3b01 100644 --- a/clang/include/clang/Parse/CMakeLists.txt +++ b/clang/include/clang/Parse/CMakeLists.txt @@ -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) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5f4e5fb4b215..8d0935dec1b6 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -184,6 +184,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr UnrollHintHandler; std::unique_ptr NoUnrollHintHandler; std::unique_ptr FPHandler; + std::unique_ptr AttributePragmaHandler; std::unique_ptr 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. diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index 7c1678086c2f..f3b042c9ce79 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -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> + &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; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5a3cdfb77c9c..bd68842c9f73 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 MatchRules; + bool IsUsed; + }; + SmallVector 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); diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index c215366fc398..b7570d03c85a 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -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"); +} diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index c8de6b35f9ef..aa915fa3ba1c 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -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 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, + llvm::function_ref(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, 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(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 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(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(Info)); + PP.EnterTokenStream(std::move(TokenArray), 1, + /*DisableMacroExpansion=*/false); +} diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index eaff9fe8eedf..7d78046d0684 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -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. diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index aa8ed91d382f..265c12d7d5e7 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -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) diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp index 55e9601bf5e5..724db456785f 100644 --- a/clang/lib/Sema/AttributeList.cpp +++ b/clang/lib/Sema/AttributeList.cpp @@ -16,6 +16,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/SemaInternal.h" @@ -160,12 +161,16 @@ 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> &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> &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); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 294b56059b33..950f04088822 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -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 diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index c6e3cc886316..dfc5d6cd0151 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -368,6 +368,217 @@ void Sema::AddCFAuditedAttribute(Decl *D) { D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Loc)); } +namespace { + +Optional +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 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 SubjectMatchRules; + // Gather the subject match rules that are supported by the attribute. + SmallVector, 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, 2> + RulesToFirstSpecifiedNegatedSubRule; + for (const auto &Rule : Rules) { + Optional 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 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 ExtraRules; + for (const auto &Rule : Rules) { + ExtraRules.push_back(Rule.first); + Diagnostic << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, Rule.second)); + } + Diagnostic << attrMatcherRuleListToString(ExtraRules); + } + + PragmaAttributeStack.push_back( + {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false}); +} + +void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc) { + if (PragmaAttributeStack.empty()) { + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch); + return; + } + const PragmaAttributeEntry &Entry = PragmaAttributeStack.back(); + if (!Entry.IsUsed) { + assert(Entry.Attribute && "Expected an attribute"); + Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused) + << Entry.Attribute->getName(); + Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here); + } + PragmaAttributeStack.pop_back(); +} + +void Sema::AddPragmaAttributes(Scope *S, Decl *D) { + if (PragmaAttributeStack.empty()) + return; + for (auto &Entry : PragmaAttributeStack) { + const AttributeList *Attribute = Entry.Attribute; + assert(Attribute && "Expected an attribute"); + + // Ensure that the attribute can be applied to the given declaration. + bool Applies = false; + for (const auto &Rule : Entry.MatchRules) { + if (Attribute->appliesToDecl(D, Rule)) { + Applies = true; + break; + } + } + if (!Applies) + continue; + Entry.IsUsed = true; + assert(!Attribute->getNext() && "Expected just one attribute"); + PragmaAttributeCurrentTargetDecl = D; + ProcessDeclAttributeList(S, D, Attribute); + PragmaAttributeCurrentTargetDecl = nullptr; + } +} + +void Sema::PrintPragmaAttributeInstantiationPoint() { + assert(PragmaAttributeCurrentTargetDecl && "Expected an active declaration"); + Diags.Report(PragmaAttributeCurrentTargetDecl->getLocStart(), + diag::note_pragma_attribute_applied_decl_here); +} + +void Sema::DiagnoseUnterminatedPragmaAttribute() { + if (PragmaAttributeStack.empty()) + return; + Diag(PragmaAttributeStack.back().Loc, diag::err_pragma_attribute_no_pop_eof); +} + void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) { if(On) OptimizeOffPragmaLocation = SourceLocation(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c6a0b0101d37..075e87b75cdc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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()); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a1ba9de368db..ae941c88ca3e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -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? diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index fd3f266c9a08..b543a731641f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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()) @@ -9931,6 +9932,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, NewTD->setInvalidDecl(); ProcessDeclAttributeList(S, NewTD, AttrList); + AddPragmaAttributes(S, NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index e50f8b206779..4f51cd399c0c 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -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()) { 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; diff --git a/clang/test/FixIt/fixit-pragma-attribute.c b/clang/test/FixIt/fixit-pragma-attribute.c new file mode 100644 index 000000000000..f166eb2978e3 --- /dev/null +++ b/clang/test/FixIt/fixit-pragma-attribute.c @@ -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)" diff --git a/clang/test/FixIt/fixit-pragma-attribute.cpp b/clang/test/FixIt/fixit-pragma-attribute.cpp new file mode 100644 index 000000000000..8e3f6d9392f0 --- /dev/null +++ b/clang/test/FixIt/fixit-pragma-attribute.cpp @@ -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 diff --git a/clang/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp b/clang/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp new file mode 100644 index 000000000000..b7741343ad45 --- /dev/null +++ b/clang/test/Misc/pragma-attribute-cxx-subject-match-rules.cpp @@ -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 diff --git a/clang/test/Misc/pragma-attribute-cxx.cpp b/clang/test/Misc/pragma-attribute-cxx.cpp new file mode 100644 index 000000000000..c241c4e4bdba --- /dev/null +++ b/clang/test/Misc/pragma-attribute-cxx.cpp @@ -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 diff --git a/clang/test/Misc/pragma-attribute-objc-subject-match-rules.m b/clang/test/Misc/pragma-attribute-objc-subject-match-rules.m new file mode 100644 index 000000000000..09ab5e1f33a0 --- /dev/null +++ b/clang/test/Misc/pragma-attribute-objc-subject-match-rules.m @@ -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 diff --git a/clang/test/Misc/pragma-attribute-objc.m b/clang/test/Misc/pragma-attribute-objc.m new file mode 100644 index 000000000000..541cfa9ad3bc --- /dev/null +++ b/clang/test/Misc/pragma-attribute-objc.m @@ -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 diff --git a/clang/test/Misc/pragma-attribute-strict-subjects.c b/clang/test/Misc/pragma-attribute-strict-subjects.c new file mode 100644 index 000000000000..ecd551bee6c7 --- /dev/null +++ b/clang/test/Misc/pragma-attribute-strict-subjects.c @@ -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 diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test new file mode 100644 index 000000000000..b1f2e66ab339 --- /dev/null +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -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) diff --git a/clang/test/Parser/pragma-attribute-declspec.cpp b/clang/test/Parser/pragma-attribute-declspec.cpp new file mode 100644 index 000000000000..28785ba90063 --- /dev/null +++ b/clang/test/Parser/pragma-attribute-declspec.cpp @@ -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 diff --git a/clang/test/Parser/pragma-attribute.cpp b/clang/test/Parser/pragma-attribute.cpp new file mode 100644 index 000000000000..b28ec8c2c86d --- /dev/null +++ b/clang/test/Parser/pragma-attribute.cpp @@ -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}} diff --git a/clang/test/Sema/pragma-attribute-strict-subjects.c b/clang/test/Sema/pragma-attribute-strict-subjects.c new file mode 100644 index 000000000000..a84e2bde38d5 --- /dev/null +++ b/clang/test/Sema/pragma-attribute-strict-subjects.c @@ -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 diff --git a/clang/test/Sema/pragma-attribute.c b/clang/test/Sema/pragma-attribute.c new file mode 100644 index 000000000000..d321f2ce4be7 --- /dev/null +++ b/clang/test/Sema/pragma-attribute.c @@ -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(); diff --git a/clang/test/lit.cfg b/clang/test/lit.cfg index e4a13054ba81..a5e8b9de994b 100644 --- a/clang/test/lit.cfg +++ b/clang/test/lit.cfg @@ -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'. ***""") ) diff --git a/clang/test/lit.site.cfg.in b/clang/test/lit.site.cfg.in index 8fdfbfc4842e..63d713987482 100644 --- a/clang/test/lit.site.cfg.in +++ b/clang/test/lit.site.cfg.in @@ -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@" diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 8aaa28beaac2..6dc391122eb7 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -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 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 getSubjects() const { + return (Constraint ? Constraint : MetaSubject) + ->getValueAsListOfDefs("Subjects"); + } + + std::vector getLangOpts() const { + if (Constraint) { + // Lookup the options in the sub-rule first, in case the sub-rule + // overrides the rules options. + std::vector 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 Rules; + llvm::DenseMap 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 MetaSubjects = + Records.getAllDerivedDefinitions("AttrSubjectMatcherRule"); + auto MapFromSubjectsToRules = [this](const Record *SubjectContainer, + const Record *MetaSubject, + const Record *Constraint = nullptr) { + Rules.emplace_back(MetaSubject, Constraint); + std::vector 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 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 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 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> &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 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 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 " + "defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n"; + OS << " return None;\n"; + OS << "}\n\n"; + + std::map> + SubMatchRules; + for (const auto &Rule : Rules) { + if (!Rule.isSubRule()) + continue; + SubMatchRules[Rule.MetaSubject].push_back(Rule); + } + + for (const auto &SubMatchRule : SubMatchRules) { + OS << "Optional isAttributeSubjectMatchSubRuleFor_" + << SubMatchRule.first->getValueAsString("Name") + << "(StringRef Name, bool IsUnless) {\n"; + OS << " if (IsUnless)\n"; + OS << " return " + "llvm::StringSwitch>(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>(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, " + "llvm::function_ref (StringRef, " + "bool)>> isAttributeSubjectMatchRule(StringRef Name) {\n"; + OS << " return " + "llvm::StringSwitch, " + "llvm::function_ref (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 static void forEachUniqueSpelling(const Record &Attr, Fn &&F) { std::vector 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 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 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"; } } diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index fd7999be3877..781518ddbc31 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -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 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 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 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 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; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h index 033cb78f36f3..e1b7d0ec63be 100644 --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -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