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 @@ -2316,6 +2316,7 @@ SourceLocation *DeclSpecStart = nullptr); bool MightBeDeclarator(DeclaratorContext Context); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &Attrs, SourceLocation *DeclEnd = nullptr, ForRangeInit *FRI = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, @@ -2611,6 +2612,10 @@ void stripTypeAttributesOffDeclSpec(ParsedAttributes &Attrs, DeclSpec &DS, Sema::TagUseKind TUK); + /// TODO: Add documentation. + void ExtractDefiniteDeclAttrs(ParsedAttributes &Attrs, + ParsedAttributes &DeclAttrs); + // FixItLoc = possible correct location for the attributes void ProhibitAttributes(ParsedAttributes &Attrs, SourceLocation FixItLoc = SourceLocation()) { diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -459,6 +459,27 @@ } void skipToEnd(); }; + + // TODO: Document and explain how this isn't actually a RAII object. + class AttributeLender { + public: + void Lend(ParsedAttributes &Attrs, Declarator &D) { + for (ParsedAttr &AL : Attrs) { + LentAttrs.push_back(&AL); + } + D.takeAttributes(Attrs); + } + + void Return(ParsedAttributes &Attrs, Declarator &D) { + for (ParsedAttr *AL : LentAttrs) { + Attrs.takeOneFrom(D.getAttributes(), AL); + } + LentAttrs.clear(); + } + + private: + llvm::SmallVector LentAttrs; + }; } // end namespace clang #endif 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 @@ -650,6 +650,8 @@ bool existsInTarget(const TargetInfo &Target) const; bool isKnownToGCC() const; bool isSupportedByPragmaAttribute() const; + // TODO: Document + bool slidesFromDeclToDeclSpec() const; /// If the parsed attribute has a semantic equivalent, and it would /// have a semantic Spelling enumeration (due to having semantically-distinct @@ -906,6 +908,22 @@ ParsedAttr &operator[](SizeType pos) { return *AttrList[pos]; } const ParsedAttr &operator[](SizeType pos) const { return *AttrList[pos]; } + void updateRange() { + Range = SourceRange(); + for (const ParsedAttr &PA : *this) { + if (Range.isInvalid()) { + Range = PA.getRange(); + continue; + } + if (PA.getRange().getBegin() < Range.getBegin()) { + Range.setBegin(PA.getRange().getBegin()); + } + if (PA.getRange().getEnd() > Range.getEnd()) { + Range.setEnd(PA.getRange().getEnd()); + } + } + } + void addAtEnd(ParsedAttr *newAttr) { assert(newAttr); AttrList.push_back(newAttr); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4423,8 +4423,26 @@ // Helper for delayed processing of attributes. void ProcessDeclAttributeDelayed(Decl *D, const ParsedAttributesView &AttrList); - void ProcessDeclAttributeList(Scope *S, Decl *D, const ParsedAttributesView &AL, - bool IncludeCXX11Attributes = true); + struct ProcessDeclAttributeOptions { + ProcessDeclAttributeOptions() + : IncludeCXX11Attributes(true), IgnoreTypeAttributes(false) {} + ProcessDeclAttributeOptions WithIncludeCXX11Attributes(bool Val) { + ProcessDeclAttributeOptions Result = *this; + Result.IncludeCXX11Attributes = Val; + return Result; + } + ProcessDeclAttributeOptions WithIgnoreTypeAttributes(bool Val) { + ProcessDeclAttributeOptions Result = *this; + Result.IgnoreTypeAttributes = Val; + return Result; + } + bool IncludeCXX11Attributes; + bool IgnoreTypeAttributes; + }; + void ProcessDeclAttributeList(Scope *S, Decl *D, + const ParsedAttributesView &AL, + const ProcessDeclAttributeOptions &Options = + ProcessDeclAttributeOptions()); bool ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList); 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 @@ -1732,6 +1732,20 @@ } } +// TODO: Documentation +void Parser::ExtractDefiniteDeclAttrs(ParsedAttributes &Attrs, + ParsedAttributes &DeclAttrs) { + llvm::SmallVector ToBeMoved; + for (ParsedAttr &AL : Attrs) { + if (!AL.slidesFromDeclToDeclSpec()) { + ToBeMoved.push_back(&AL); + } + } + for (ParsedAttr *AL : ToBeMoved) { + DeclAttrs.takeOneFrom(Attrs, AL); + } +} + /// ParseDeclaration - Parse a full 'declaration', which consists of /// declaration-specifiers, some number of declarators, and a semicolon. /// 'Context' should be a DeclaratorContext value. This returns the @@ -1850,8 +1864,10 @@ if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(Attrs, DeclAttrs); DS.takeAttributesFrom(Attrs); - return ParseDeclGroup(DS, Context, &DeclEnd, FRI); + return ParseDeclGroup(DS, Context, DeclAttrs, &DeclEnd, FRI); } /// Returns true if this might be the start of a declarator, or a common typo @@ -2006,10 +2022,13 @@ /// result. Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &Attrs, SourceLocation *DeclEnd, ForRangeInit *FRI) { // Parse the first declarator. ParsingDeclarator D(*this, DS, Context); + AttributeLender AttrLender; + AttrLender.Lend(Attrs, D); ParseDeclarator(D); // Bail out if the first declarator didn't seem well-formed. @@ -2172,7 +2191,9 @@ } // Parse the next declarator. + AttrLender.Return(Attrs, D); D.clear(); + AttrLender.Lend(Attrs, D); D.setCommaLoc(CommaLoc); // Accept attributes in an init-declarator. In the first declarator in a @@ -4300,6 +4321,8 @@ // Parse leading attributes. ParsedAttributes Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(Attrs, DeclAttrs); DS.takeAttributesFrom(Attrs); // Parse the common specifier-qualifiers-list piece. @@ -4309,6 +4332,7 @@ // specifier. Let the actions module cope with it. if (Tok.is(tok::semi)) { RecordDecl *AnonRecord = nullptr; + // TODO: Pass on DeclAttrs Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS, AnonRecord); assert(!AnonRecord && "Did not expect anonymous struct or union here"); @@ -4321,6 +4345,8 @@ SourceLocation CommaLoc; while (true) { ParsingFieldDeclarator DeclaratorInfo(*this, DS); + AttributeLender AttrLender; + AttrLender.Lend(DeclAttrs, DeclaratorInfo.D); DeclaratorInfo.D.setCommaLoc(CommaLoc); // Attributes are only allowed here on successive declarators. @@ -4361,6 +4387,8 @@ return; FirstDeclarator = false; + + AttrLender.Return(DeclAttrs, DeclaratorInfo.D); } } @@ -6953,20 +6981,26 @@ // Just use the ParsingDeclaration "scope" of the declarator. DeclSpec DS(AttrFactory); - // Parse any C++11 attributes. - MaybeParseCXX11Attributes(DS.getAttributes()); - - // Skip any Microsoft attributes before a param. - MaybeParseMicrosoftAttributes(DS.getAttributes()); - - SourceLocation DSStart = Tok.getLocation(); + ParsedAttributes ArgAttrs(AttrFactory); // If the caller parsed attributes for the first argument, add them now. // Take them so that we only apply the attributes to the first parameter. // FIXME: If we can leave the attributes in the token stream somehow, we can // get rid of a parameter (FirstArgAttrs) and this statement. It might be // too much hassle. - DS.takeAttributesFrom(FirstArgAttrs); + ArgAttrs.takeAllFrom(FirstArgAttrs); + + // Parse any C++11 attributes. + MaybeParseCXX11Attributes(ArgAttrs); + + // Skip any Microsoft attributes before a param. + MaybeParseMicrosoftAttributes(ArgAttrs); + + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(ArgAttrs, DeclAttrs); + DS.takeAttributesFrom(ArgAttrs); + + SourceLocation DSStart = Tok.getLocation(); ParseDeclarationSpecifiers(DS); @@ -6980,6 +7014,7 @@ : DeclaratorCtx == DeclaratorContext::LambdaExpr ? DeclaratorContext::LambdaExprParameter : DeclaratorContext::Prototype); + ParmDeclarator.takeAttributes(DeclAttrs); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. 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 @@ -2687,11 +2687,6 @@ if (Tok.is(tok::annot_attr_openmp)) return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, attrs); - // We need to keep these attributes for future diagnostic - // before they are taken over by declaration specifier. - FnAttrs.addAll(attrs.begin(), attrs.end()); - FnAttrs.Range = attrs.Range; - MaybeParseMicrosoftAttributes(attrs); if (Tok.is(tok::kw_using)) { @@ -2722,6 +2717,19 @@ // decl-specifier-seq: // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this, TemplateDiags); + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(attrs, DeclAttrs); + + // We need to keep these attributes for future diagnostic + // before they are taken over by declaration specifier. + FnAttrs.addAll(attrs.begin(), attrs.end()); + // TODO: Explain the reason for this. + if (DeclAttrs.empty()) { + FnAttrs.Range = attrs.Range; + } else { + FnAttrs.updateRange(); + } + DS.takeAttributesFrom(attrs); if (MalformedTypeSpec) DS.SetTypeSpecError(); @@ -2774,6 +2782,8 @@ } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::Member); + AttributeLender AttrLender; + AttrLender.Lend(DeclAttrs, DeclaratorInfo); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; @@ -3072,7 +3082,9 @@ } // Parse the next declarator. + AttrLender.Return(DeclAttrs, DeclaratorInfo); DeclaratorInfo.clear(); + AttrLender.Lend(DeclAttrs, DeclaratorInfo); VS.clear(); BitfieldSize = ExprResult(/*Invalid=*/false); EqualLoc = PureSpecLoc = SourceLocation(); 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 @@ -2077,6 +2077,9 @@ // If this is a for loop, we're entering its condition. ForConditionScope.enter(/*IsConditionVariable=*/true); + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(attrs, DeclAttrs); + // type-specifier-seq DeclSpec DS(AttrFactory); DS.takeAttributesFrom(attrs); @@ -2084,6 +2087,7 @@ // declarator Declarator DeclaratorInfo(DS, DeclaratorContext::Condition); + DeclaratorInfo.takeAttributes(DeclAttrs); ParseDeclarator(DeclaratorInfo); // simple-asm-expr[opt] 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 @@ -2583,6 +2583,9 @@ ParsedAttributes Attributes(AttrFactory); MaybeParseCXX11Attributes(Attributes); + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(Attributes, DeclAttrs); + DeclSpec DS(AttrFactory); DS.takeAttributesFrom(Attributes); @@ -2590,6 +2593,7 @@ return StmtError(); Declarator ExDecl(DS, DeclaratorContext::CXXCatch); + ExDecl.takeAttributes(DeclAttrs); ParseDeclarator(ExDecl); ExceptionDecl = Actions.ActOnExceptionDeclarator(getCurScope(), ExDecl); } else 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 @@ -239,6 +239,9 @@ return Decl; } + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(prefixAttrs, DeclAttrs); + // Move the attributes from the prefix into the DS. if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) ProhibitAttributes(prefixAttrs); @@ -247,6 +250,7 @@ // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + DeclaratorInfo.takeAttributes(DeclAttrs); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); 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 @@ -1112,8 +1112,11 @@ return Actions.ConvertDeclToDeclGroup(TheDecl); } + ParsedAttributes DeclAttrs(AttrFactory); + ExtractDefiniteDeclAttrs(Attrs, DeclAttrs); DS.takeAttributesFrom(Attrs); + // TODO: Plumb attributes through to here? // ObjC2 allows prefix attributes on class interfaces and protocols. // FIXME: This still needs better diagnostics. We should only accept // attributes here, no types, etc. @@ -1145,6 +1148,7 @@ ParseObjCAtInterfaceDeclaration(AtLoc, DS.getAttributes())); } + // TODO: Plumb attributes through to here? // If the declspec consisted only of 'extern' and we have a string // literal following it, this must be a C++ linkage specifier like // 'extern "C"'. @@ -1155,7 +1159,7 @@ return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, DeclaratorContext::File); + return ParseDeclGroup(DS, DeclaratorContext::File, DeclAttrs); } Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -212,6 +212,46 @@ return getInfo().IsSupportedByPragmaAttribute; } +bool ParsedAttr::slidesFromDeclToDeclSpec() const { + if (!isStandardAttributeSyntax()) + return true; + + // TODO: Document that these are due to legacy behavior. + switch (getParsedKind()) { + case AT_AddressSpace: + case AT_CmseNSCall: + case AT_OpenCLPrivateAddressSpace: + case AT_OpenCLGlobalAddressSpace: + case AT_OpenCLGlobalDeviceAddressSpace: + case AT_OpenCLGlobalHostAddressSpace: + case AT_OpenCLLocalAddressSpace: + case AT_OpenCLConstantAddressSpace: + case AT_OpenCLGenericAddressSpace: + case AT_NeonPolyVectorType: + case AT_NeonVectorType: + case AT_ArmSveVectorBits: + case AT_ArmMveStrictPolymorphism: + case AT_BTFTypeTag: + case AT_TypeNonNull: + case AT_TypeNullable: + case AT_TypeNullableResult: + case AT_TypeNullUnspecified: + case AT_ObjCInertUnsafeUnretained: + case AT_Regparm: + case AT_NoDeref: + case AT_ObjCGC: + case AT_VectorSize: + case AT_MatrixType: + case AT_Ptr32: + case AT_Ptr64: + case AT_SPtr: + case AT_UPtr: + return true; + default: + return false; + } +} + bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; } unsigned ParsedAttr::getSemanticSpelling() const { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8312,15 +8312,15 @@ /// ProcessDeclAttribute - Apply the specific attribute to the specified decl if /// the attribute applies to decls. If the attribute is a type attribute, just /// silently ignore it if a GNU attribute. -static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, - const ParsedAttr &AL, - bool IncludeCXX11Attributes) { +static void +ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, + const Sema::ProcessDeclAttributeOptions &Options) { if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute) return; // Ignore C++11 attributes on declarator chunks: they appertain to the type // instead. - if (AL.isCXX11Attribute() && !IncludeCXX11Attributes) + if (AL.isCXX11Attribute() && !Options.IncludeCXX11Attributes) return; // Unknown attributes are automatically warned on. Target-specific attributes @@ -8353,9 +8353,21 @@ if (AL.getInfo().handleDeclAttribute(S, D, AL) != ParsedAttrInfo::NotHandled) break; if (!AL.isStmtAttr()) { - // Type attributes are handled elsewhere; silently move on. assert(AL.isTypeAttr() && "Non-type attribute not handled"); - break; + } + if (AL.isTypeAttr()) { + if (Options.IgnoreTypeAttributes) + break; + if (AL.slidesFromDeclToDeclSpec()) { + if (AL.isStandardAttributeSyntax()) { + // TODO: We're looking at one of a number of type attributes that we + // have historically allowed to be applied to a declaration in C++11 + // syntax, "sliding" them to the DeclSpec. Emit a warning that type + // attributes aren't allowed in this position. + } + // GNU type attributes are handled elsewhere; silently move on. + break; + } } // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a // statement attribute is not written on a declaration, but this code is @@ -9004,14 +9016,14 @@ /// ProcessDeclAttributeList - Apply all the decl attributes in the specified /// attribute list to the specified decl, ignoring any type attributes. -void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, - const ParsedAttributesView &AttrList, - bool IncludeCXX11Attributes) { +void Sema::ProcessDeclAttributeList( + Scope *S, Decl *D, const ParsedAttributesView &AttrList, + const ProcessDeclAttributeOptions &Options) { if (AttrList.empty()) return; for (const ParsedAttr &AL : AttrList) - ProcessDeclAttribute(*this, S, D, AL, IncludeCXX11Attributes); + ProcessDeclAttribute(*this, S, D, AL, Options); // FIXME: We should be able to handle these cases in TableGen. // GCC accepts @@ -9099,7 +9111,9 @@ AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList) { for (const ParsedAttr &AL : AttrList) { if (AL.getKind() == ParsedAttr::AT_Annotate) { - ProcessDeclAttribute(*this, nullptr, ASDecl, AL, AL.isCXX11Attribute()); + ProcessDeclAttributeOptions Options; + Options.IncludeCXX11Attributes = AL.isCXX11Attribute(); + ProcessDeclAttribute(*this, nullptr, ASDecl, AL, Options); } else { Diag(AL.getLoc(), diag::err_only_annotate_after_access_spec); return true; @@ -9241,16 +9255,22 @@ /// specified in many different places, and we need to find and apply them all. void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Apply decl attributes from the DeclSpec if present. - if (!PD.getDeclSpec().getAttributes().empty()) - ProcessDeclAttributeList(S, D, PD.getDeclSpec().getAttributes()); + if (!PD.getDeclSpec().getAttributes().empty()) { + ProcessDeclAttributeList( + S, D, PD.getDeclSpec().getAttributes(), + ProcessDeclAttributeOptions().WithIgnoreTypeAttributes(true)); + } // Walk the declarator structure, applying decl attributes that were in a type // position to the decl itself. This handles cases like: // int *__attr__(x)** D; // when X is a decl attribute. - for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) + for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) { ProcessDeclAttributeList(S, D, PD.getTypeObject(i).getAttrs(), - /*IncludeCXX11Attributes=*/false); + ProcessDeclAttributeOptions() + .WithIncludeCXX11Attributes(false) + .WithIgnoreTypeAttributes(true)); + } // Finally, apply any attributes on the decl itself. ProcessDeclAttributeList(S, D, PD.getAttributes()); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8439,13 +8439,6 @@ break; } case ParsedAttr::AT_AnnotateType: { - if (TAL == TAL_DeclName) { - state.getSema().Diag(attr.getLoc(), - diag::err_type_attribute_invalid_on_decl) - << attr; - return; - } - HandleAnnotateTypeAttr(state, type, attr); attr.setUsedAsTypeAttr(); break; 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,6 @@ } // 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}} +[[clang::annotate_type("bar")]] void annotated_function(); // 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 @@ -1,11 +1,10 @@ -// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -fcxx-exceptions -verify 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 { @@ -48,11 +47,31 @@ // 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 { +} // namespace my_namespace +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) { + } // expected-error {{'annotate_type' attribute cannot be applied to a + // declaration}} + for (; [[clang::annotate_type("foo")]] bool b = false;) { + } // expected-error {{'annotate_type' attribute cannot be applied to a + // declaration}} + while ([[clang::annotate_type("foo")]] bool b = false) { + } // expected-error {{'annotate_type' attribute cannot be applied to a + // declaration}} + if ([[clang::annotate_type("foo")]] bool b = false) { + } // expected-error {{'annotate_type' attribute cannot be applied to a + // declaration}} + try { + } catch ([[clang::annotate_type( + "foo")]] int i) { // expected-error {{'annotate_type' attribute cannot be + // applied to a declaration}} } } +template +[[clang::annotate_type( + "foo")]] T var_template; // expected-error {{'annotate_type' attribute + // cannot be applied to a declaration}}