diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2553,7 +2553,7 @@ let Documentation = [SwiftAsyncErrorDocs]; } -def Suppress : StmtAttr { +def Suppress : DeclOrStmtAttr { let Spellings = [CXX11<"gsl", "suppress">]; let Args = [VariadicStringArgument<"DiagnosticIdentifiers">]; let Documentation = [SuppressDocs]; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2654,6 +2654,13 @@ /// clang accepts as an extension. void DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs); + /// Emit diagnostics for C++11 and C2x attributes that aren't declaration + /// attributes. + /// Historically, we've allowed some pure type attributes to be applied to + /// declarations. For these, we emit only a warning. For all others, we emit + /// an error. + void DiagnoseCXX11NonDeclAttrs(ParsedAttributes &Attrs); + /// Parses syntax-generic attribute arguments for attributes which are /// known to the implementation, and adds them to the given ParsedAttributes /// list with the given attribute syntax. Returns the number of arguments diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -61,6 +61,8 @@ unsigned IsType : 1; /// True if this attribute applies to statements. unsigned IsStmt : 1; + /// True if this attribute applies to declarations. + unsigned IsDecl : 1; /// True if this attribute has any spellings that are known to gcc. unsigned IsKnownToGCC : 1; /// True if this attribute is supported by #pragma clang attribute. @@ -79,20 +81,23 @@ AttributeCommonInfo::NoSemaHandlerAttribute) : AttrKind(AttrKind), NumArgs(0), OptArgs(0), NumArgMembers(0), HasCustomParsing(0), AcceptsExprPack(0), IsTargetSpecific(0), IsType(0), - IsStmt(0), IsKnownToGCC(0), IsSupportedByPragmaAttribute(0) {} + IsStmt(0), IsDecl(0), IsKnownToGCC(0), IsSupportedByPragmaAttribute(0) { + } constexpr ParsedAttrInfo(AttributeCommonInfo::Kind AttrKind, unsigned NumArgs, unsigned OptArgs, unsigned NumArgMembers, unsigned HasCustomParsing, unsigned AcceptsExprPack, unsigned IsTargetSpecific, unsigned IsType, - unsigned IsStmt, unsigned IsKnownToGCC, + unsigned IsStmt, unsigned IsDecl, + unsigned IsKnownToGCC, unsigned IsSupportedByPragmaAttribute, ArrayRef Spellings, ArrayRef ArgNames) : AttrKind(AttrKind), NumArgs(NumArgs), OptArgs(OptArgs), NumArgMembers(NumArgMembers), HasCustomParsing(HasCustomParsing), AcceptsExprPack(AcceptsExprPack), IsTargetSpecific(IsTargetSpecific), - IsType(IsType), IsStmt(IsStmt), IsKnownToGCC(IsKnownToGCC), + IsType(IsType), IsStmt(IsStmt), IsDecl(IsDecl), + IsKnownToGCC(IsKnownToGCC), IsSupportedByPragmaAttribute(IsSupportedByPragmaAttribute), Spellings(Spellings), ArgNames(ArgNames) {} diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -10,8 +10,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.h" -#include "clang/Parse/RAIIObjectsForParser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyDeclStackTrace.h" @@ -20,6 +18,8 @@ #include "clang/Basic/CharInfo.h" #include "clang/Basic/TargetInfo.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" @@ -28,6 +28,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include using namespace clang; @@ -1716,6 +1717,61 @@ } } +void Parser::DiagnoseCXX11NonDeclAttrs(ParsedAttributes &Attrs) { + for (ParsedAttr &PA : Attrs) { + if (!(PA.isCXX11Attribute() || PA.isC2xAttribute())) + continue; + + if (PA.getInfo().IsDecl) + continue; + + AttributeCommonInfo::Kind kind = PA.getKind(); + if (kind == ParsedAttr::UnknownAttribute) + continue; + + // Using unordered_set because we don't have DenseMapInfo for + // ParsedAttr::Kind. + static std::unordered_set WarningOnlyAttrs = { + ParsedAttr::AT_AddressSpace, + ParsedAttr::AT_CmseNSCall, + ParsedAttr::AT_OpenCLPrivateAddressSpace, + ParsedAttr::AT_OpenCLGlobalAddressSpace, + ParsedAttr::AT_OpenCLGlobalDeviceAddressSpace, + ParsedAttr::AT_OpenCLGlobalHostAddressSpace, + ParsedAttr::AT_OpenCLLocalAddressSpace, + ParsedAttr::AT_OpenCLConstantAddressSpace, + ParsedAttr::AT_OpenCLGenericAddressSpace, + ParsedAttr::AT_NeonPolyVectorType, + ParsedAttr::AT_NeonVectorType, + ParsedAttr::AT_ArmSveVectorBits, + ParsedAttr::AT_ArmMveStrictPolymorphism, + ParsedAttr::AT_BTFTypeTag, + ParsedAttr::AT_TypeNonNull, + ParsedAttr::AT_TypeNullable, + ParsedAttr::AT_TypeNullableResult, + ParsedAttr::AT_TypeNullUnspecified, + ParsedAttr::AT_ObjCInertUnsafeUnretained, + ParsedAttr::AT_Regparm, + ParsedAttr::AT_NoDeref, + ParsedAttr::AT_ObjCGC, + ParsedAttr::AT_VectorSize, + ParsedAttr::AT_MatrixType, + ParsedAttr::AT_Ptr32, + ParsedAttr::AT_Ptr64, + ParsedAttr::AT_SPtr, + ParsedAttr::AT_UPtr, + }; + + if (WarningOnlyAttrs.count(kind)) { + // FIXME: Emit a warning and change all affected tests to expect this + // warning. + } else { + Diag(PA.getLoc(), diag::err_type_attribute_invalid_on_decl) << PA; + PA.setInvalid(); + } + } +} + // Usually, `__attribute__((attrib)) class Foo {} var` means that attribute // applies to var, not the type Foo. // As an exception to the rule, __declspec(align(...)) before the @@ -1863,6 +1919,7 @@ if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); + DiagnoseCXX11NonDeclAttrs(Attrs); DS.takeAttributesFrom(Attrs); return ParseDeclGroup(DS, Context, &DeclEnd, FRI); } @@ -4314,6 +4371,7 @@ // Parse leading attributes. ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); + DiagnoseCXX11NonDeclAttrs(Attrs); DS.takeAttributesFrom(Attrs); // Parse the common specifier-qualifiers-list piece. @@ -6977,6 +7035,8 @@ // too much hassle. DS.takeAttributesFrom(FirstArgAttrs); + DiagnoseCXX11NonDeclAttrs(DS.getAttributes()); + ParseDeclarationSpecifiers(DS); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyDeclStackTrace.h" @@ -19,10 +18,12 @@ #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/TargetInfo.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/TimeProfiler.h" @@ -213,6 +214,8 @@ Diag(FirstNestedInlineLoc, diag::ext_inline_nested_namespace_definition); } + DiagnoseCXX11NonDeclAttrs(attrs); + // If we're still good, complain about inline namespaces in non-C++0x now. if (InlineLoc.isValid()) Diag(InlineLoc, getLangOpts().CPlusPlus11 ? @@ -2004,6 +2007,8 @@ TParams = MultiTemplateParamsArg(&(*TemplateParams)[0], TemplateParams->size()); + DiagnoseCXX11NonDeclAttrs(attrs); + stripTypeAttributesOffDeclSpec(attrs, DS, TUK); // Declaration or definition of a class type @@ -2726,6 +2731,7 @@ // decl-specifier-seq: // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this, TemplateDiags); + DiagnoseCXX11NonDeclAttrs(attrs); DS.takeAttributesFrom(attrs); if (MalformedTypeSpec) DS.SetTypeSpecError(); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2079,6 +2079,7 @@ // type-specifier-seq DeclSpec DS(AttrFactory); + DiagnoseCXX11NonDeclAttrs(attrs); DS.takeAttributesFrom(attrs); ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition); diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -2584,6 +2584,7 @@ MaybeParseCXX11Attributes(Attributes); DeclSpec DS(AttrFactory); + DiagnoseCXX11NonDeclAttrs(Attributes); DS.takeAttributesFrom(Attributes); if (ParseCXXTypeSpecifierSeq(DS)) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -241,8 +241,10 @@ // Move the attributes from the prefix into the DS. if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) ProhibitAttributes(prefixAttrs); - else + else { + DiagnoseCXX11NonDeclAttrs(prefixAttrs); DS.takeAttributesFrom(prefixAttrs); + } // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1115,6 +1115,7 @@ return Actions.ConvertDeclToDeclGroup(TheDecl); } + DiagnoseCXX11NonDeclAttrs(attrs); DS.takeAttributesFrom(attrs); // ObjC2 allows prefix attributes on class interfaces and protocols. diff --git a/clang/test/Sema/annotate-type.c b/clang/test/Sema/annotate-type.c --- a/clang/test/Sema/annotate-type.c +++ b/clang/test/Sema/annotate-type.c @@ -17,10 +17,7 @@ int *__attribute__((annotate_type("bar"))) y2; // expected-warning {{unknown attribute 'annotate_type' ignored}} // Various error cases - // FIXME: We would want to prohibit the attribute in the following location. - // However, Clang currently generally doesn't prohibit type-only C++11 - // attributes on declarations. This should be fixed more generally. - [[clang::annotate_type("bar")]] int *z1; + [[clang::annotate_type("bar")]] int *z1; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} int *z2 [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a statement}} int *[[clang::annotate_type(1)]] z3; // expected-error {{'annotate_type' attribute requires a string}} @@ -33,7 +30,5 @@ } // More error cases: Prohibit adding the attribute to declarations. // Different declarations hit different code paths, so they need separate tests. -// FIXME: Clang currently generally doesn't prohibit type-only C++11 -// attributes on declarations. -[[clang::annotate_type("bar")]] int *global; -void g([[clang::annotate_type("bar")]] int); +[[clang::annotate_type("bar")]] int *global; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +void g([[clang::annotate_type("bar")]] int); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} diff --git a/clang/test/SemaCXX/annotate-type.cpp b/clang/test/SemaCXX/annotate-type.cpp --- a/clang/test/SemaCXX/annotate-type.cpp +++ b/clang/test/SemaCXX/annotate-type.cpp @@ -2,10 +2,7 @@ struct S1 { void f() [[clang::annotate_type("foo")]]; - // FIXME: We would want to prohibit the attribute in the following location. - // However, Clang currently generally doesn't prohibit type-only C++11 - // attributes on declarations. This should be fixed more generally. - [[clang::annotate_type("foo")]] void g(); + [[clang::annotate_type("foo")]] void g(); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} }; template struct is_same { @@ -50,10 +47,8 @@ // More error cases: Prohibit adding the attribute to declarations. // Different declarations hit different code paths, so they need separate tests. -// FIXME: Clang currently generally doesn't prohibit type-only C++11 -// attributes on declarations. -namespace [[clang::annotate_type("foo")]] my_namespace {} -struct [[clang::annotate_type("foo")]] S3{}; +namespace [[clang::annotate_type("foo")]] my_namespace {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +struct [[clang::annotate_type("foo")]] S3{}; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} void f4() { - for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {} + for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -4220,6 +4220,9 @@ OS << " /*IsStmt=*/"; OS << (Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr")) << ",\n"; + OS << " /*IsDecl=*/"; + OS << (!Attr.isSubClassOf("TypeAttr") && !Attr.isSubClassOf("StmtAttr")) + << ",\n"; OS << " /*IsKnownToGCC=*/"; OS << IsKnownToGCC(Attr) << ",\n"; OS << " /*IsSupportedByPragmaAttribute=*/";