From: Alexis Hunt Date: Fri, 28 Nov 2014 00:53:20 +0000 (+0000) Subject: Create a new 'flag_enum' attribute. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=724f14e75c50c6839408509b2fede5464fa9111e;p=platform%2Fupstream%2Fllvm.git Create a new 'flag_enum' attribute. This attribute serves as a hint to improve warnings about the ranges of enumerators used as flag types. It currently has no working C++ implementation due to different semantics for enums in C++. For more explanation, see the docs and testcases. Reviewed by Aaron Ballman. llvm-svn: 222906 --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a152c54..a03d0c9 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -220,12 +220,14 @@ class SubjectList subjects, SubjectDiag diag = WarnDiag, string CustomDiag = customDiag; } -class LangOpt { +class LangOpt { string Name = name; + bit Negated = negated; } def MicrosoftExt : LangOpt<"MicrosoftExt">; def Borland : LangOpt<"Borland">; def CUDA : LangOpt<"CUDA">; +def COnly : LangOpt<"CPlusPlus", 1>; // Defines targets for target-specific attributes. The list of strings should // specify architectures for which the target applies, based off the ArchType @@ -703,6 +705,25 @@ def MinSize : InheritableAttr { let Documentation = [Undocumented]; } +def FlagEnum : InheritableAttr { + let Spellings = [GNU<"flag_enum">]; + let Subjects = SubjectList<[Enum]>; + let Documentation = [FlagEnumDocs]; + let LangOpts = [COnly]; + let AdditionalMembers = [{ +private: + llvm::APInt FlagBits; +public: + llvm::APInt &getFlagBits() { + return FlagBits; + } + + const llvm::APInt &getFlagBits() const { + return FlagBits; + } +}]; +} + def Flatten : InheritableAttr { let Spellings = [GCC<"flatten">]; let Subjects = SubjectList<[Function], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a336ff1..c4571fa 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1136,6 +1136,16 @@ behavior of the program is undefined. }]; } +def FlagEnumDocs : Documentation { + let Category = DocCatType; + let Content = [{ +This attribute can be added to an enumerator to signal to the compiler that it +is intended to be used as a flag type. This will cause the compiler to assume +that the range of the type includes all of the values that you can get by +manipulating bits of the enumerator when issuing warnings. + }]; +} + def MSInheritanceDocs : Documentation { let Category = DocCatType; let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance"; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 76d7a66..8184546 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -190,6 +190,7 @@ def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">; +def FlagEnum : DiagGroup<"flag-enum">; def InfiniteRecursion : DiagGroup<"infinite-recursion">; def GNUImaginaryConstant : DiagGroup<"gnu-imaginary-constant">; def IgnoredQualifiers : DiagGroup<"ignored-qualifiers">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 901ba95..820f5a4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2234,7 +2234,7 @@ def warn_attribute_wrong_decl_type : Warning< "%0 attribute only applies to %select{functions|unions|" "variables and functions|functions and methods|parameters|" "functions, methods and blocks|functions, methods, and classes|" - "functions, methods, and parameters|classes|variables|methods|" + "functions, methods, and parameters|classes|enums|variables|methods|" "variables, functions and labels|fields and global variables|structs|" "variables and typedefs|thread-local variables|" "variables and fields|variables, data members and tag types|" @@ -4053,6 +4053,9 @@ def ext_enum_too_large : ExtWarn< def ext_enumerator_increment_too_large : ExtWarn< "incremented enumerator value %0 is not representable in the " "largest integer type">, InGroup; +def warn_flag_enum_constant_out_of_range : Warning< + "enumeration value %0 is out of range of flags in enumeration type %1">, + InGroup; def warn_illegal_constant_array_size : Extension< "size of static array must be an integer constant expression">; diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index c8e8625..3a6df70 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -822,6 +822,7 @@ enum AttributeDeclKind { ExpectedFunctionMethodOrClass, ExpectedFunctionMethodOrParameter, ExpectedClass, + ExpectedEnum, ExpectedVariable, ExpectedMethod, ExpectedVariableFunctionOrLabel, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 06292d25..0ad86a4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7950,6 +7950,12 @@ public: Expr *SrcExpr, AssignmentAction Action, bool *Complained = nullptr); + /// IsValueInFlagEnum - Determine if a value is allowed as part of a flag + /// enum. If AllowMask is true, then we also allow the complement of a valid + /// value, to be used as a mask. + bool IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val, + bool AllowMask) const; + /// DiagnoseAssignmentEnum - Warn if assignment to enum is a constant /// integer not in the range of enum values. void DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d299ec1..f75a2ce 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13501,6 +13501,49 @@ static void CheckForDuplicateEnumValues(Sema &S, ArrayRef Elements, } } +bool +Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val, + bool AllowMask) const { + FlagEnumAttr *FEAttr = ED->getAttr(); + assert(FEAttr && "looking for value in non-flag enum"); + + llvm::APInt FlagMask = ~FEAttr->getFlagBits(); + unsigned Width = FlagMask.getBitWidth(); + + // We will try a zero-extended value for the regular check first. + llvm::APInt ExtVal = Val.zextOrSelf(Width); + + // A value is in a flag enum if either its bits are a subset of the enum's + // flag bits (the first condition) or we are allowing masks and the same is + // true of its complement (the second condition). When masks are allowed, we + // allow the common idiom of ~(enum1 | enum2) to be a valid enum value. + // + // While it's true that any value could be used as a mask, the assumption is + // that a mask will have all of the insignificant bits set. Anything else is + // likely a logic error. + if (!(FlagMask & ExtVal)) + return true; + + if (AllowMask) { + // Try a one-extended value instead. This can happen if the enum is wider + // than the constant used, in C with extensions to allow for wider enums. + // The mask will still have the correct behaviour, so we give the user the + // benefit of the doubt. + // + // FIXME: This heuristic can cause weird results if the enum was extended + // to a larger type and is signed, because then bit-masks of smaller types + // that get extended will fall out of range (e.g. ~0x1u). We currently don't + // detect that case and will get a false positive for it. In most cases, + // though, it can be fixed by making it a signed type (e.g. ~0x1), so it may + // be fine just to accept this as a warning. + ExtVal |= llvm::APInt::getHighBitsSet(Width, Width - Val.getBitWidth()); + if (!(FlagMask & ~ExtVal)) + return true; + } + + return false; +} + void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, SourceLocation RBraceLoc, Decl *EnumDeclX, ArrayRef Elements, @@ -13586,10 +13629,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, BestPromotionType = Context.getPromotedIntegerType(BestType); else BestPromotionType = BestType; - // We don't need to set BestWidth, because BestType is going to be the type - // of the enumerators, but we do anyway because otherwise some compilers - // warn that it might be used uninitialized. - BestWidth = CharWidth; + + BestWidth = Context.getIntWidth(BestType); } else if (NumNegativeBits) { // If there is a negative value, figure out the smallest integer type (of @@ -13654,10 +13695,15 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, } } + FlagEnumAttr *FEAttr = Enum->getAttr(); + if (FEAttr) + FEAttr->getFlagBits() = llvm::APInt(BestWidth, 0); + // Loop over all of the enumerator constants, changing their types to match - // the type of the enum if needed. - for (unsigned i = 0, e = Elements.size(); i != e; ++i) { - EnumConstantDecl *ECD = cast_or_null(Elements[i]); + // the type of the enum if needed. If we have a flag type, we also prepare the + // FlagBits cache. + for (auto *D : Elements) { + auto *ECD = cast_or_null(D); if (!ECD) continue; // Already issued a diagnostic. // Standard C says the enumerators have int type, but we allow, as an @@ -13687,7 +13733,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, // enum-specifier, each enumerator has the type of its // enumeration. ECD->setType(EnumType); - continue; + goto flagbits; } else { NewTy = BestType; NewWidth = BestWidth; @@ -13714,8 +13760,32 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, ECD->setType(EnumType); else ECD->setType(NewTy); + +flagbits: + // Check to see if we have a constant with exactly one bit set. Note that x + // & (x - 1) will be nonzero if and only if x has more than one bit set. + if (FEAttr) { + llvm::APInt ExtVal = InitVal.zextOrSelf(BestWidth); + if (ExtVal != 0 && !(ExtVal & (ExtVal - 1))) { + FEAttr->getFlagBits() |= ExtVal; + } + } } + if (FEAttr) { + for (Decl *D : Elements) { + EnumConstantDecl *ECD = cast_or_null(D); + if (!ECD) continue; // Already issued a diagnostic. + + llvm::APSInt InitVal = ECD->getInitVal(); + if (InitVal != 0 && !IsValueInFlagEnum(Enum, InitVal, true)) + Diag(ECD->getLocation(), diag::warn_flag_enum_constant_out_of_range) + << ECD << Enum; + } + } + + + Enum->completeDefinition(BestType, BestPromotionType, NumPositiveBits, NumNegativeBits); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 00f9f80..d3f28af 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4313,6 +4313,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_OptimizeNone: handleOptimizeNoneAttr(S, D, Attr); break; + case AttributeList::AT_FlagEnum: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_Flatten: handleSimpleAttribute(S, D, Attr); break; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 14f7092..78e33c3 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -687,26 +687,39 @@ static void checkCaseValue(Sema &S, SourceLocation Loc, const llvm::APSInt &Val, } } +typedef SmallVector, 64> EnumValsTy; + /// Returns true if we should emit a diagnostic about this case expression not /// being a part of the enum used in the switch controlling expression. -static bool ShouldDiagnoseSwitchCaseNotInEnum(const ASTContext &Ctx, +static bool ShouldDiagnoseSwitchCaseNotInEnum(const Sema &S, const EnumDecl *ED, - const Expr *CaseExpr) { - // Don't warn if the 'case' expression refers to a static const variable of - // the enum type. - CaseExpr = CaseExpr->IgnoreParenImpCasts(); - if (const DeclRefExpr *DRE = dyn_cast(CaseExpr)) { + const Expr *CaseExpr, + EnumValsTy::iterator &EI, + EnumValsTy::iterator &EIEnd, + const llvm::APSInt &Val) { + bool FlagType = ED->hasAttr(); + + if (const DeclRefExpr *DRE = + dyn_cast(CaseExpr->IgnoreParenImpCasts())) { if (const VarDecl *VD = dyn_cast(DRE->getDecl())) { - if (!VD->hasGlobalStorage()) - return true; QualType VarType = VD->getType(); - if (!VarType.isConstQualified()) - return true; - QualType EnumType = Ctx.getTypeDeclType(ED); - if (Ctx.hasSameUnqualifiedType(EnumType, VarType)) + QualType EnumType = S.Context.getTypeDeclType(ED); + if (VD->hasGlobalStorage() && VarType.isConstQualified() && + S.Context.hasSameUnqualifiedType(EnumType, VarType)) return false; } } + + if (FlagType) { + return !S.IsValueInFlagEnum(ED, Val, false); + } else { + while (EI != EIEnd && EI->first < Val) + EI++; + + if (EI != EIEnd && EI->first == Val) + return false; + } + return true; } @@ -1045,8 +1058,6 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, // If switch has default case, then ignore it. if (!CaseListIsErroneous && !HasConstantCond && ET) { const EnumDecl *ED = ET->getDecl(); - typedef SmallVector, 64> - EnumValsTy; EnumValsTy EnumVals; // Gather all enum values, set their type and sort them, @@ -1057,57 +1068,48 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, EnumVals.push_back(std::make_pair(Val, EDI)); } std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals); - EnumValsTy::iterator EIend = + auto EI = EnumVals.begin(), EIEnd = std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals); // See which case values aren't in enum. - EnumValsTy::const_iterator EI = EnumVals.begin(); for (CaseValsTy::const_iterator CI = CaseVals.begin(); - CI != CaseVals.end(); CI++) { - while (EI != EIend && EI->first < CI->first) - EI++; - if (EI == EIend || EI->first > CI->first) { - Expr *CaseExpr = CI->second->getLHS(); - if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr)) - Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) - << CondTypeBeforePromotion; - } + CI != CaseVals.end(); CI++) { + Expr *CaseExpr = CI->second->getLHS(); + if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd, + CI->first)) + Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) + << CondTypeBeforePromotion; } + // See which of case ranges aren't in enum EI = EnumVals.begin(); for (CaseRangesTy::const_iterator RI = CaseRanges.begin(); - RI != CaseRanges.end() && EI != EIend; RI++) { - while (EI != EIend && EI->first < RI->first) - EI++; - - if (EI == EIend || EI->first != RI->first) { - Expr *CaseExpr = RI->second->getLHS(); - if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr)) - Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) - << CondTypeBeforePromotion; - } + RI != CaseRanges.end(); RI++) { + Expr *CaseExpr = RI->second->getLHS(); + if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd, + RI->first)) + Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) + << CondTypeBeforePromotion; llvm::APSInt Hi = RI->second->getRHS()->EvaluateKnownConstInt(Context); AdjustAPSInt(Hi, CondWidth, CondIsSigned); - while (EI != EIend && EI->first < Hi) - EI++; - if (EI == EIend || EI->first != Hi) { - Expr *CaseExpr = RI->second->getRHS(); - if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr)) - Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) - << CondTypeBeforePromotion; - } + + CaseExpr = RI->second->getRHS(); + if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd, + Hi)) + Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum) + << CondTypeBeforePromotion; } // Check which enum vals aren't in switch - CaseValsTy::const_iterator CI = CaseVals.begin(); - CaseRangesTy::const_iterator RI = CaseRanges.begin(); + auto CI = CaseVals.begin(); + auto RI = CaseRanges.begin(); bool hasCasesNotInSwitch = false; SmallVector UnhandledNames; - for (EI = EnumVals.begin(); EI != EIend; EI++){ + for (EI = EnumVals.begin(); EI != EIEnd; EI++){ // Drop unneeded case values while (CI != CaseVals.end() && CI->first < EI->first) CI++; @@ -1194,30 +1196,37 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, llvm::APSInt RhsVal = SrcExpr->EvaluateKnownConstInt(Context); AdjustAPSInt(RhsVal, DstWidth, DstIsSigned); const EnumDecl *ED = ET->getDecl(); - typedef SmallVector, 64> - EnumValsTy; - EnumValsTy EnumVals; - - // Gather all enum values, set their type and sort them, - // allowing easier comparison with rhs constant. - for (auto *EDI : ED->enumerators()) { - llvm::APSInt Val = EDI->getInitVal(); - AdjustAPSInt(Val, DstWidth, DstIsSigned); - EnumVals.push_back(std::make_pair(Val, EDI)); - } - if (EnumVals.empty()) - return; - std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals); - EnumValsTy::iterator EIend = - std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals); - - // See which values aren't in the enum. - EnumValsTy::const_iterator EI = EnumVals.begin(); - while (EI != EIend && EI->first < RhsVal) - EI++; - if (EI == EIend || EI->first != RhsVal) { - Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) + + if (ED->hasAttr()) { + if (!IsValueInFlagEnum(ED, RhsVal, true)) + Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) << DstType.getUnqualifiedType(); + } else { + typedef SmallVector, 64> + EnumValsTy; + EnumValsTy EnumVals; + + // Gather all enum values, set their type and sort them, + // allowing easier comparison with rhs constant. + for (auto *EDI : ED->enumerators()) { + llvm::APSInt Val = EDI->getInitVal(); + AdjustAPSInt(Val, DstWidth, DstIsSigned); + EnumVals.push_back(std::make_pair(Val, EDI)); + } + if (EnumVals.empty()) + return; + std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals); + EnumValsTy::iterator EIend = + std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals); + + // See which values aren't in the enum. + EnumValsTy::const_iterator EI = EnumVals.begin(); + while (EI != EIend && EI->first < RhsVal) + EI++; + if (EI == EIend || EI->first != RhsVal) { + Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) + << DstType.getUnqualifiedType(); + } } } } diff --git a/clang/test/Sema/attr-flag-enum.c b/clang/test/Sema/attr-flag-enum.c new file mode 100644 index 0000000..a53c1dc --- /dev/null +++ b/clang/test/Sema/attr-flag-enum.c @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -std=c11 -Wassign-enum %s + +enum __attribute__((flag_enum)) flag { + ea = 0x1, + eb = 0x2, + ec = 0x8, +}; + +enum __attribute__((flag_enum)) flag2 { + ga = 0x1, + gb = 0x4, + + gc = 0x5, // no-warning + gd = 0x7, // expected-warning {{enumeration value 'gd' is out of range}} + ge = ~0x2, // expected-warning {{enumeration value 'ge' is out of range}} + gf = ~0x4, // no-warning + gg = ~0x1, // no-warning + gh = ~0x5, // no-warning + gi = ~0x11, // expected-warning {{enumeration value 'gi' is out of range}} +}; + +enum __attribute__((flag_enum)) flag3 { + fa = 0x1, + fb = ~0x1u, // no-warning +}; + +// What happens here is that ~0x2 is negative, and so the enum must be signed. +// But ~0x1u is unsigned and has the high bit set, so the enum must be 64-bit. +// The result is that ~0x1u does not have high bits set, and so it is considered +// to be an invalid value. See Sema::IsValueInFlagEnum in SemaDecl.cpp for more +// discussion. +enum __attribute__((flag_enum)) flag4 { + ha = 0x1, + hb = 0x2, + + hc = ~0x1u, // expected-warning {{enumeration value 'hc' is out of range}} + hd = ~0x2, // no-warning +}; + +void f(void) { + enum flag e = 0; // no-warning + e = 0x1; // no-warning + e = 0x3; // no-warning + e = 0xa; // no-warning + e = 0x4; // expected-warning {{integer constant not in range of enumerated type}} + e = 0xf; // expected-warning {{integer constant not in range of enumerated type}} + e = ~0; // no-warning + e = ~0x1; // no-warning + e = ~0x2; // no-warning + e = ~0x3; // no-warning + e = ~0x4; // expected-warning {{integer constant not in range of enumerated type}} + + switch (e) { + case 0: break; // no-warning + case 0x1: break; // no-warning + case 0x3: break; // no-warning + case 0xa: break; // no-warning + case 0x4: break; // expected-warning {{case value not in enumerated type}} + case 0xf: break; // expected-warning {{case value not in enumerated type}} + case ~0: break; // expected-warning {{case value not in enumerated type}} + case ~0x1: break; // expected-warning {{case value not in enumerated type}} + case ~0x2: break; // expected-warning {{case value not in enumerated type}} + case ~0x3: break; // expected-warning {{case value not in enumerated type}} + case ~0x4: break; // expected-warning {{case value not in enumerated type}} + default: break; + } + + enum flag2 f = ~0x1; // no-warning + f = ~0x1u; // no-warning + + enum flag4 h = ~0x1; // no-warning + h = ~0x1u; // expected-warning {{integer constant not in range of enumerated type}} +} diff --git a/clang/test/SemaCXX/attr-flag-enum-reject.cpp b/clang/test/SemaCXX/attr-flag-enum-reject.cpp new file mode 100644 index 0000000..f28d60c --- /dev/null +++ b/clang/test/SemaCXX/attr-flag-enum-reject.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -Wassign-enum %s + +enum __attribute__((flag_enum)) flag { // expected-warning {{ignored}} +}; diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 10b70fc..7847b4f 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2187,7 +2187,8 @@ static std::string CalculateDiagnostic(const Record &S) { Namespace = 1U << 11, Field = 1U << 12, CXXMethod = 1U << 13, - ObjCProtocol = 1U << 14 + ObjCProtocol = 1U << 14, + Enum = 1U << 15 }; uint32_t SubMask = 0; @@ -2221,6 +2222,7 @@ static std::string CalculateDiagnostic(const Record &S) { .Case("Namespace", Namespace) .Case("Field", Field) .Case("CXXMethod", CXXMethod) + .Case("Enum", Enum) .Default(0); if (!V) { // Something wasn't in our mapping, so be helpful and let the developer @@ -2239,6 +2241,7 @@ static std::string CalculateDiagnostic(const Record &S) { case Var: return "ExpectedVariable"; case Param: return "ExpectedParameter"; case Class: return "ExpectedClass"; + case Enum: return "ExpectedEnum"; case CXXMethod: // FIXME: Currently, this maps to ExpectedMethod based on existing code, // but should map to something a bit more accurate at some point. @@ -2392,6 +2395,8 @@ static std::string GenerateLangOptRequirements(const Record &R, std::string FnName = "check", Test; for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { std::string Part = (*I)->getValueAsString("Name"); + if ((*I)->getValueAsBit("Negated")) + Test += "!"; Test += "S.LangOpts." + Part; if (I + 1 != E) Test += " || ";