Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1183,9 +1183,9 @@ def OpenCLUnrollHint : StmtAttr { let Spellings = [GNU<"opencl_unroll_hint">]; -// let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt], -// ErrorDiag, "'for', 'while', and 'do' statements">; - let Args = [UnsignedArgument<"UnrollHint">]; + let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt], + ErrorDiag, "'for', 'while', and 'do' statements">; + let Args = [UnsignedArgument<"UnrollHint", /*opt*/1>]; let Documentation = [OpenCLUnrollHintDocs]; } @@ -1326,7 +1326,10 @@ let Spellings = [CXX11<"", "fallthrough", 201603>, C2x<"", "fallthrough", 201904>, CXX11<"clang", "fallthrough">, GCC<"fallthrough">]; -// let Subjects = [NullStmt]; + // The attribute only applies to a NullStmt, but we have special fix-it + // behavior if applied to a case label. + let Subjects = SubjectList<[NullStmt, SwitchCase], ErrorDiag, + "empty statements">; let Documentation = [FallthroughDocs]; } @@ -1344,7 +1347,8 @@ let Spellings = [Clang<"nomerge">]; let Documentation = [NoMergeDocs]; let InheritEvenIfAlreadyPresent = 1; - let Subjects = SubjectList<[Function], ErrorDiag, "functions and statements">; + let Subjects = SubjectList<[Function, Stmt], ErrorDiag, + "functions and statements">; let SimpleHandler = 1; } @@ -3467,6 +3471,7 @@ }]; let Documentation = [LoopHintDocs, UnrollHintDocs]; + let HasCustomParsing = 1; } def CapturedRecord : InheritableAttr { Index: clang/include/clang/Sema/ParsedAttr.h =================================================================== --- clang/include/clang/Sema/ParsedAttr.h +++ clang/include/clang/Sema/ParsedAttr.h @@ -39,6 +39,7 @@ class LangOptions; class ParsedAttr; class Sema; +class Stmt; class TargetInfo; struct ParsedAttrInfo { @@ -80,6 +81,11 @@ const Decl *D) const { return true; } + /// Check if this attribute appertains to St, and issue a diagnostic if not. + virtual bool diagAppertainsToStmt(Sema &S, const ParsedAttr &Attr, + const Stmt *St) const { + return true; + } /// Check if this attribute is allowed by the language we are compiling, and /// issue a diagnostic if not. virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const { @@ -592,6 +598,7 @@ unsigned getMaxArgs() const; bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; + bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const; bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const; void getMatchRules(const LangOptions &LangOpts, SmallVectorImpl> Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4260,6 +4260,13 @@ void checkUnusedDeclAttributes(Declarator &D); + /// Handles semantic checking for features that are common to all attributes, + /// such as checking whether a parameter was properly specified, or the + /// correct number of arguments were passed, etc. Returns true if the + /// attribute has been diagnosed. + bool checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A); + bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference Index: clang/lib/Sema/ParsedAttr.cpp =================================================================== --- clang/lib/Sema/ParsedAttr.cpp +++ clang/lib/Sema/ParsedAttr.cpp @@ -159,6 +159,10 @@ return getInfo().diagAppertainsToDecl(S, *this, D); } +bool ParsedAttr::diagnoseAppertainsTo(Sema &S, const Stmt *St) const { + return getInfo().diagAppertainsToStmt(S, *this, St); +} + bool ParsedAttr::appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const { return checkAttributeMatchRuleAppliesTo(D, MatchRule); Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -1188,3 +1188,51 @@ if (Stack->empty()) FreeVisContext(); } + +template +static bool checkCommonAttributeFeatures(Sema& S, const Ty* Node, + const ParsedAttr& A) { + // Several attributes carry different semantics than the parsing requires, so + // those are opted out of the common argument checks. + // + // We also bail on unknown and ignored attributes because those are handled + // as part of the target-specific handling logic. + if (A.getKind() == ParsedAttr::UnknownAttribute) + return false; + // Check whether the attribute requires specific language extensions to be + // enabled. + if (!A.diagnoseLangOpts(S)) + return true; + // Check whether the attribute appertains to the given subject. + if (!A.diagnoseAppertainsTo(S, Node)) + return true; + // Check whether the attribute exists in the target architecture. + if (S.CheckAttrTarget(A)) + return true; + + if (A.hasCustomParsing()) + return false; + + if (A.getMinArgs() == A.getMaxArgs()) { + // If there are no optional arguments, then checking for the argument count + // is trivial. + if (!A.checkExactlyNumArgs(S, A.getMinArgs())) + return true; + } else { + // There are optional arguments, so checking is slightly more involved. + if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs())) + return true; + else if (!A.hasVariadicArg() && A.getMaxArgs() && + !A.checkAtMostNumArgs(S, A.getMaxArgs())) + return true; + } + + return false; +} + +bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A) { + return ::checkCommonAttributeFeatures(*this, D, A); +} +bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A) { + return ::checkCommonAttributeFeatures(*this, S, A); +} Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -7371,48 +7371,6 @@ << "2.0"; } -/// Handles semantic checking for features that are common to all attributes, -/// such as checking whether a parameter was properly specified, or the correct -/// number of arguments were passed, etc. -static bool handleCommonAttributeFeatures(Sema &S, Decl *D, - const ParsedAttr &AL) { - // Several attributes carry different semantics than the parsing requires, so - // those are opted out of the common argument checks. - // - // We also bail on unknown and ignored attributes because those are handled - // as part of the target-specific handling logic. - if (AL.getKind() == ParsedAttr::UnknownAttribute) - return false; - // Check whether the attribute requires specific language extensions to be - // enabled. - if (!AL.diagnoseLangOpts(S)) - return true; - // Check whether the attribute appertains to the given subject. - if (!AL.diagnoseAppertainsTo(S, D)) - return true; - if (AL.hasCustomParsing()) - return false; - - if (AL.getMinArgs() == AL.getMaxArgs()) { - // If there are no optional arguments, then checking for the argument count - // is trivial. - if (!AL.checkExactlyNumArgs(S, AL.getMinArgs())) - return true; - } else { - // There are optional arguments, so checking is slightly more involved. - if (AL.getMinArgs() && !AL.checkAtLeastNumArgs(S, AL.getMinArgs())) - return true; - else if (!AL.hasVariadicArg() && AL.getMaxArgs() && - !AL.checkAtMostNumArgs(S, AL.getMaxArgs())) - return true; - } - - if (S.CheckAttrTarget(AL)) - return true; - - return false; -} - static void handleOpenCLAccessAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (D->isInvalidDecl()) return; @@ -7766,7 +7724,7 @@ return; } - if (handleCommonAttributeFeatures(S, D, AL)) + if (S.checkCommonAttributeFeatures(D, AL)) return; switch (AL.getKind()) { @@ -7778,6 +7736,9 @@ assert(AL.isTypeAttr() && "Non-type attribute not handled"); break; } + // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a + // statement attribute is not written on a declaration, but this code is + // needed for attributes in Attr.td that do not list any subjects. S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl) << AL << D->getLocation(); break; Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -26,14 +26,12 @@ static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { FallThroughAttr Attr(S.Context, A); - if (!isa(St)) { + if (isa(St)) { S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) - << Attr.getSpelling() << St->getBeginLoc(); - if (isa(St)) { - SourceLocation L = S.getLocForEndOfToken(Range.getEnd()); - S.Diag(L, diag::note_fallthrough_insert_semi_fixit) - << FixItHint::CreateInsertion(L, ";"); - } + << A << St->getBeginLoc(); + SourceLocation L = S.getLocForEndOfToken(Range.getEnd()); + S.Diag(L, diag::note_fallthrough_insert_semi_fixit) + << FixItHint::CreateInsertion(L, ";"); return nullptr; } auto *FnScope = S.getCurFunction(); @@ -54,11 +52,6 @@ static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { - if (A.getNumArgs() < 1) { - S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1; - return nullptr; - } - std::vector DiagnosticIdentifiers; for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) { StringRef RuleName; @@ -88,10 +81,10 @@ PragmaNameLoc->Ident->getName()) .Default("clang loop"); - if (St->getStmtClass() != Stmt::DoStmtClass && - St->getStmtClass() != Stmt::ForStmtClass && - St->getStmtClass() != Stmt::CXXForRangeStmtClass && - St->getStmtClass() != Stmt::WhileStmtClass) { + // This could be handled automatically by adding a Subjects definition in + // Attr.td, but that would make the diagnostic behavior worse in this case + // because the user spells this attribute as a pragma. + if (!isa(St)) { std::string Pragma = "#pragma " + std::string(PragmaName); S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; return nullptr; @@ -205,9 +198,6 @@ static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { NoMergeAttr NMA(S.Context, A); - if (S.CheckAttrNoArgs(A)) - return nullptr; - CallExprFinder CEF(S, St); if (!CEF.foundCallExpr()) { @@ -377,23 +367,8 @@ // opencl_unroll_hint can have 0 arguments (compiler // determines unrolling factor) or 1 argument (the unroll factor provided // by the user). - - if (!isa(St)) { - S.Diag(A.getLoc(), diag::err_attribute_wrong_decl_type_str) - << A << "'for', 'while', and 'do' statements"; - return nullptr; - } - - unsigned NumArgs = A.getNumArgs(); - - if (NumArgs > 1) { - S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1; - return nullptr; - } - unsigned UnrollFactor = 0; - - if (NumArgs == 1) { + if (A.getNumArgs() == 1) { Expr *E = A.getArgAsExpr(0); Optional ArgVal; @@ -404,28 +379,42 @@ } int Val = ArgVal->getSExtValue(); - if (Val <= 0) { S.Diag(A.getRange().getBegin(), diag::err_attribute_requires_positive_integer) << A << /* positive */ 0; return nullptr; } - UnrollFactor = Val; + UnrollFactor = static_cast(Val); } - return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor); + return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor); } static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { - switch (A.getKind()) { - case ParsedAttr::UnknownAttribute: + if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute) + return nullptr; + + // Unknown attributes are automatically warned on. Target-specific attributes + // which do not apply to the current target architecture are treated as + // though they were unknown attributes. + const TargetInfo *Aux = S.Context.getAuxTargetInfo(); + if (A.getKind() == ParsedAttr::UnknownAttribute || + !(A.existsInTarget(S.Context.getTargetInfo()) || + (S.Context.getLangOpts().SYCLIsDevice && Aux && + A.existsInTarget(*Aux)))) { S.Diag(A.getLoc(), A.isDeclspecAttribute() ? (unsigned)diag::warn_unhandled_ms_attribute_ignored : (unsigned)diag::warn_unknown_attribute_ignored) << A << A.getRange(); return nullptr; + } + + if (S.checkCommonAttributeFeatures(St, A)) + return nullptr; + + switch (A.getKind()) { case ParsedAttr::AT_FallThrough: return handleFallThroughAttr(S, St, A, Range); case ParsedAttr::AT_LoopHint: @@ -441,8 +430,9 @@ case ParsedAttr::AT_Unlikely: return handleUnlikely(S, St, A, Range); default: - // if we're here, then we parsed a known attribute, but didn't recognize - // it as a statement attribute => it is declaration attribute + // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a + // declaration attribute is not written on a statement, but this code is + // needed for attributes in Attr.td that do not list any subjects. S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt) << A << St->getBeginLoc(); return nullptr; Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -7968,8 +7968,6 @@ CurType = State.getAttributedType( createSimpleAttr(State.getSema().Context, Attr), CurType, CurType); - } else { - Attr.diagnoseAppertainsTo(State.getSema(), nullptr); } } Index: clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.fallthrough/p1.cpp =================================================================== --- clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.fallthrough/p1.cpp +++ clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.fallthrough/p1.cpp @@ -53,7 +53,7 @@ [[fallthrough]] // expected-error {{'fallthrough' attribute cannot be applied to a declaration}} void g() { [[fallthrough]] int n; // expected-error {{'fallthrough' attribute cannot be applied to a declaration}} - [[fallthrough]] ++n; // expected-error-re {{{{^}}fallthrough attribute is only allowed on empty statements}} + [[fallthrough]] ++n; // expected-error {{'fallthrough' attribute only applies to empty statements}} switch (n) { // FIXME: This should be an error. Index: clang/test/Parser/stmt-attributes.c =================================================================== --- clang/test/Parser/stmt-attributes.c +++ clang/test/Parser/stmt-attributes.c @@ -40,7 +40,7 @@ __attribute__((unused)) switch (i) { // expected-error {{'unused' attribute cannot be applied to a statement}} __attribute__((uuid)) case 0: // expected-warning {{unknown attribute 'uuid' ignored}} - __attribute__((visibility)) default: // expected-error {{'visibility' attribute cannot be applied to a statement}} + __attribute__((visibility(""))) default: // expected-error {{'visibility' attribute cannot be applied to a statement}} __attribute__((carries_dependency)) break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} } Index: clang/test/Sema/c2x-fallthrough.c =================================================================== --- clang/test/Sema/c2x-fallthrough.c +++ clang/test/Sema/c2x-fallthrough.c @@ -57,7 +57,7 @@ [[fallthrough]] // expected-error {{'fallthrough' attribute cannot be applied to a declaration}} void g(void) { [[fallthrough]] int n; // expected-error {{'fallthrough' attribute cannot be applied to a declaration}} - [[fallthrough]] ++n; // expected-error-re {{{{^}}fallthrough attribute is only allowed on empty statements}} + [[fallthrough]] ++n; // expected-error {{'fallthrough' attribute only applies to empty statements}} switch (n) { // FIXME: This should be an error. Index: clang/test/SemaCXX/switch-implicit-fallthrough.cpp =================================================================== --- clang/test/SemaCXX/switch-implicit-fallthrough.cpp +++ clang/test/SemaCXX/switch-implicit-fallthrough.cpp @@ -299,16 +299,16 @@ int fallthrough_targets(int n) { [[clang::fallthrough]]; // expected-error{{fallthrough annotation is outside switch statement}} - [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} + [[clang::fallthrough]] // expected-error{{'fallthrough' attribute only applies to empty statements}} switch (n) { case 121: n += 400; [[clang::fallthrough]]; // no warning here, correct target case 123: - [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} + [[clang::fallthrough]] // expected-error{{'fallthrough' attribute only applies to empty statements}} n += 800; break; - [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} expected-note{{did you forget ';'?}} + [[clang::fallthrough]] // expected-error{{'fallthrough' attribute is only allowed on empty statements}} expected-note{{did you forget ';'?}} case 125: n += 1600; } Index: clang/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangAttrEmitter.cpp +++ clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1828,6 +1828,22 @@ } // end anonymous namespace +static bool isSupportedPragmaClangAttributeSubject(const Record &Subject) { + // FIXME: #pragma clang attribute does not currently support statement + // attributes, so test whether the subject is one that appertains to a + // declaration node. However, it may be reasonable for support for statement + // attributes to be added. + if (Subject.isSubClassOf("DeclNode") || Subject.isSubClassOf("DeclBase") || + Subject.getName() == "DeclBase") + return true; + + if (Subject.isSubClassOf("SubsetSubject")) + return isSupportedPragmaClangAttributeSubject( + *Subject.getValueAsDef("Base")); + + return false; +} + static bool doesDeclDeriveFrom(const Record *D, const Record *Base) { const Record *CurrentBase = D->getValueAsOptionalDef(BaseFieldName); if (!CurrentBase) @@ -1949,13 +1965,15 @@ return false; const Record *SubjectObj = Attribute.getValueAsDef("Subjects"); std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); - if (Subjects.empty()) - return false; + bool HasAtLeastOneValidSubject = false; for (const auto *Subject : Subjects) { + if (!isSupportedPragmaClangAttributeSubject(*Subject)) + continue; if (SubjectsToRules.find(Subject) == SubjectsToRules.end()) return false; + HasAtLeastOneValidSubject = true; } - return true; + return HasAtLeastOneValidSubject; } static std::string GenerateTestExpression(ArrayRef LangOpts) { @@ -2001,6 +2019,8 @@ const Record *SubjectObj = Attr.getValueAsDef("Subjects"); std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); for (const auto *Subject : Subjects) { + if (!isSupportedPragmaClangAttributeSubject(*Subject)) + continue; auto It = SubjectsToRules.find(Subject); assert(It != SubjectsToRules.end() && "This attribute is unsupported by #pragma clang attribute"); @@ -3503,7 +3523,7 @@ return; const Record *SubjectObj = Attr.getValueAsDef("Subjects"); - std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); // If the list of subjects is empty, it is assumed that the attribute // appertains to everything. @@ -3512,42 +3532,99 @@ bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn"); - // Otherwise, generate an appertainsTo check specific to this attribute which - // checks all of the given subjects against the Decl passed in. - // - // If D is null, that means the attribute was not applied to a declaration - // at all (for instance because it was applied to a type), or that the caller - // has determined that the check should fail (perhaps prior to the creation - // of the declaration). - OS << "bool diagAppertainsToDecl(Sema &S, "; - OS << "const ParsedAttr &Attr, const Decl *D) const override {\n"; - OS << " if ("; - for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) { - // If the subject has custom code associated with it, use the generated - // function for it. The function cannot be inlined into this check (yet) - // because it requires the subject to be of a specific type, and were that - // information inlined here, it would not support an attribute with multiple - // custom subjects. - if ((*I)->isSubClassOf("SubsetSubject")) { - OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)"; - } else { - OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; + // Split the subjects into declaration subjects and statement subjects. + // FIXME: subset subjects are added to the declaration list until there are + // enough statement attributes with custom subject needs to warrant + // the implementation effort. + std::vector DeclSubjects, StmtSubjects; + llvm::copy_if( + Subjects, std::back_inserter(DeclSubjects), [](const Record *R) { + return R->isSubClassOf("SubsetSubject") || !R->isSubClassOf("StmtNode"); + }); + llvm::copy_if(Subjects, std::back_inserter(StmtSubjects), + [](const Record *R) { return R->isSubClassOf("StmtNode"); }); + + // We should have sorted all of the subjects into two lists. + // FIXME: this assertion will be wrong if we ever add type attribute subjects. + assert(DeclSubjects.size() + StmtSubjects.size() == Subjects.size()); + + if (DeclSubjects.empty()) { + // If there are no decl subjects but there are stmt subjects, diagnose + // trying to apply a statement attribute to a declaration. + if (!StmtSubjects.empty()) { + OS << "bool diagAppertainsToDecl(Sema &S, const ParsedAttr &AL, "; + OS << "const Decl *D) const override {\n"; + OS << " S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl)\n"; + OS << " << AL << D->getLocation();\n"; + OS << " return false;\n"; + OS << "}\n\n"; } + } else { + // Otherwise, generate an appertainsTo check specific to this attribute + // which checks all of the given subjects against the Decl passed in. + OS << "bool diagAppertainsToDecl(Sema &S, "; + OS << "const ParsedAttr &Attr, const Decl *D) const override {\n"; + OS << " if ("; + for (auto I = DeclSubjects.begin(), E = DeclSubjects.end(); I != E; ++I) { + // If the subject has custom code associated with it, use the generated + // function for it. The function cannot be inlined into this check (yet) + // because it requires the subject to be of a specific type, and were that + // information inlined here, it would not support an attribute with + // multiple custom subjects. + if ((*I)->isSubClassOf("SubsetSubject")) + OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)"; + else + OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; - if (I + 1 != E) - OS << " && "; + if (I + 1 != E) + OS << " && "; + } + OS << ") {\n"; + OS << " S.Diag(Attr.getLoc(), diag::"; + OS << (Warn ? "warn_attribute_wrong_decl_type_str" + : "err_attribute_wrong_decl_type_str"); + OS << ")\n"; + OS << " << Attr << "; + OS << CalculateDiagnostic(*SubjectObj) << ";\n"; + OS << " return false;\n"; + OS << " }\n"; + OS << " return true;\n"; + OS << "}\n\n"; + } + + if (StmtSubjects.empty()) { + // If there are no stmt subjects but there are decl subjects, diagnose + // trying to apply a declaration attribute to a statement. + if (!DeclSubjects.empty()) { + OS << "bool diagAppertainsToStmt(Sema &S, const ParsedAttr &AL, "; + OS << "const Stmt *St) const override {\n"; + OS << " S.Diag(AL.getLoc(), diag::err_decl_attribute_invalid_on_stmt)\n"; + OS << " << AL << St->getBeginLoc();\n"; + OS << " return false;\n"; + OS << "}\n\n"; + } + } else { + // Now, do the same for statements. + OS << "bool diagAppertainsToStmt(Sema &S, "; + OS << "const ParsedAttr &Attr, const Stmt *St) const override {\n"; + OS << " if ("; + for (auto I = StmtSubjects.begin(), E = StmtSubjects.end(); I != E; ++I) { + OS << "!isa<" << (*I)->getName() << ">(St)"; + if (I + 1 != E) + OS << " && "; + } + OS << ") {\n"; + OS << " S.Diag(Attr.getLoc(), diag::"; + OS << (Warn ? "warn_attribute_wrong_decl_type_str" + : "err_attribute_wrong_decl_type_str"); + OS << ")\n"; + OS << " << Attr << "; + OS << CalculateDiagnostic(*SubjectObj) << ";\n"; + OS << " return false;\n"; + OS << " }\n"; + OS << " return true;\n"; + OS << "}\n\n"; } - OS << ") {\n"; - OS << " S.Diag(Attr.getLoc(), diag::"; - OS << (Warn ? "warn_attribute_wrong_decl_type_str" : - "err_attribute_wrong_decl_type_str"); - OS << ")\n"; - OS << " << Attr << "; - OS << CalculateDiagnostic(*SubjectObj) << ";\n"; - OS << " return false;\n"; - OS << " }\n"; - OS << " return true;\n"; - OS << "}\n\n"; } static void @@ -4214,9 +4291,13 @@ std::vector Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); OS << " ("; + bool PrintComma = false; for (const auto &Subject : llvm::enumerate(Subjects)) { - if (Subject.index()) + if (!isSupportedPragmaClangAttributeSubject(*Subject.value())) + continue; + if (PrintComma) OS << ", "; + PrintComma = true; PragmaClangAttributeSupport::RuleOrAggregateRuleSet &RuleSet = Support.SubjectsToRules.find(Subject.value())->getSecond(); if (RuleSet.isRule()) {