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];
}
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];
}
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;
}
}];
let Documentation = [LoopHintDocs, UnrollHintDocs];
+ let HasCustomParsing = 1;
}
def CapturedRecord : InheritableAttr {
class LangOptions;
class ParsedAttr;
class Sema;
+class Stmt;
class TargetInfo;
struct ParsedAttrInfo {
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 {
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<std::pair<attr::SubjectMatchRule, bool>>
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
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);
if (Stack->empty())
FreeVisContext();
}
+
+template <typename Ty>
+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);
+}
<< "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;
return;
}
- if (handleCommonAttributeFeatures(S, D, AL))
+ if (S.checkCommonAttributeFeatures(D, AL))
return;
switch (AL.getKind()) {
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;
static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {
FallThroughAttr Attr(S.Context, A);
- if (!isa<NullStmt>(St)) {
+ if (isa<SwitchCase>(St)) {
S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
- << Attr.getSpelling() << St->getBeginLoc();
- if (isa<SwitchCase>(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();
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<StringRef> DiagnosticIdentifiers;
for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
StringRef RuleName;
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<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(St)) {
std::string Pragma = "#pragma " + std::string(PragmaName);
S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
return nullptr;
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()) {
// opencl_unroll_hint can have 0 arguments (compiler
// determines unrolling factor) or 1 argument (the unroll factor provided
// by the user).
-
- if (!isa<ForStmt, CXXForRangeStmt, DoStmt, WhileStmt>(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<llvm::APSInt> ArgVal;
}
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<unsigned>(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:
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;
CurType = State.getAttributedType(
createSimpleAttr<LifetimeBoundAttr>(State.getSema().Context, Attr),
CurType, CurType);
- } else {
- Attr.diagnoseAppertainsTo(State.getSema(), nullptr);
}
}
[[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.
__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}}
}
[[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.
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;
}
} // 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)
return false;
const Record *SubjectObj = Attribute.getValueAsDef("Subjects");
std::vector<Record *> 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<Record *> LangOpts) {
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
std::vector<Record *> 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");
return;
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
- std::vector<Record*> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
+ std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
// If the list of subjects is empty, it is assumed that the attribute
// appertains to everything.
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<Record *> 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
std::vector<Record *> 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()) {