diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-12-22 01:34:09 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-12-22 01:34:09 +0000 |
commit | a734a0eab27678262bea07786b6ff30d5c7a6356 (patch) | |
tree | 37d3481ac48decdf526e66b47882ffeccb5434c7 /lib/Sema/SemaDecl.cpp | |
parent | a05d2741c40c71b59cf6d2f8bbc5d433a5d0e6de (diff) |
Add back -Wduplicate-enum which I mistakenly removed.
This was removed with -Wunique-enum, which is still removed. The
corresponding thread on cfe-comments for that warning is here:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-September/024224.html
If we get specific user feedback for -Wduplicate-enum we can evaluate
whether or not to keep it.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@170974 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaDecl.cpp')
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ebfb4b1e78..2bf0387d84 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -10640,6 +10640,182 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, return New; } +// Returns true when the enum initial expression does not trigger the +// duplicate enum warning. A few common cases are exempted as follows: +// Element2 = Element1 +// Element2 = Element1 + 1 +// Element2 = Element1 - 1 +// Where Element2 and Element1 are from the same enum. +static bool ValidDuplicateEnum(EnumConstantDecl *ECD, EnumDecl *Enum) { + Expr *InitExpr = ECD->getInitExpr(); + if (!InitExpr) + return true; + InitExpr = InitExpr->IgnoreImpCasts(); + + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) { + if (!BO->isAdditiveOp()) + return true; + IntegerLiteral *IL = dyn_cast<IntegerLiteral>(BO->getRHS()); + if (!IL) + return true; + if (IL->getValue() != 1) + return true; + + InitExpr = BO->getLHS(); + } + + // This checks if the elements are from the same enum. + DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(InitExpr); + if (!DRE) + return true; + + EnumConstantDecl *EnumConstant = dyn_cast<EnumConstantDecl>(DRE->getDecl()); + if (!EnumConstant) + return true; + + if (cast<EnumDecl>(TagDecl::castFromDeclContext(ECD->getDeclContext())) != + Enum) + return true; + + return false; +} + +struct DupKey { + int64_t val; + bool isTombstoneOrEmptyKey; + DupKey(int64_t val, bool isTombstoneOrEmptyKey) + : val(val), isTombstoneOrEmptyKey(isTombstoneOrEmptyKey) {} +}; + +static DupKey GetDupKey(const llvm::APSInt& Val) { + return DupKey(Val.isSigned() ? Val.getSExtValue() : Val.getZExtValue(), + false); +} + +struct DenseMapInfoDupKey { + static DupKey getEmptyKey() { return DupKey(0, true); } + static DupKey getTombstoneKey() { return DupKey(1, true); } + static unsigned getHashValue(const DupKey Key) { + return (unsigned)(Key.val * 37); + } + static bool isEqual(const DupKey& LHS, const DupKey& RHS) { + return LHS.isTombstoneOrEmptyKey == RHS.isTombstoneOrEmptyKey && + LHS.val == RHS.val; + } +}; + +// Emits a warning when an element is implicitly set a value that +// a previous element has already been set to. +static void CheckForDuplicateEnumValues(Sema &S, Decl **Elements, + unsigned NumElements, EnumDecl *Enum, + QualType EnumType) { + if (S.Diags.getDiagnosticLevel(diag::warn_duplicate_enum_values, + Enum->getLocation()) == + DiagnosticsEngine::Ignored) + return; + // Avoid anonymous enums + if (!Enum->getIdentifier()) + return; + + // Only check for small enums. + if (Enum->getNumPositiveBits() > 63 || Enum->getNumNegativeBits() > 64) + return; + + typedef llvm::SmallVector<EnumConstantDecl*, 3> ECDVector; + typedef llvm::SmallVector<ECDVector*, 3> DuplicatesVector; + + typedef llvm::PointerUnion<EnumConstantDecl*, ECDVector*> DeclOrVector; + typedef llvm::DenseMap<DupKey, DeclOrVector, DenseMapInfoDupKey> + ValueToVectorMap; + + DuplicatesVector DupVector; + ValueToVectorMap EnumMap; + + // Populate the EnumMap with all values represented by enum constants without + // an initialier. + for (unsigned i = 0; i < NumElements; ++i) { + EnumConstantDecl *ECD = cast<EnumConstantDecl>(Elements[i]); + + // Null EnumConstantDecl means a previous diagnostic has been emitted for + // this constant. Skip this enum since it may be ill-formed. + if (!ECD) { + return; + } + + if (ECD->getInitExpr()) + continue; + + DupKey Key = GetDupKey(ECD->getInitVal()); + DeclOrVector &Entry = EnumMap[Key]; + + // First time encountering this value. + if (Entry.isNull()) + Entry = ECD; + } + + // Create vectors for any values that has duplicates. + for (unsigned i = 0; i < NumElements; ++i) { + EnumConstantDecl *ECD = cast<EnumConstantDecl>(Elements[i]); + if (!ValidDuplicateEnum(ECD, Enum)) + continue; + + DupKey Key = GetDupKey(ECD->getInitVal()); + + DeclOrVector& Entry = EnumMap[Key]; + if (Entry.isNull()) + continue; + + if (EnumConstantDecl *D = Entry.dyn_cast<EnumConstantDecl*>()) { + // Ensure constants are different. + if (D == ECD) + continue; + + // Create new vector and push values onto it. + ECDVector *Vec = new ECDVector(); + Vec->push_back(D); + Vec->push_back(ECD); + + // Update entry to point to the duplicates vector. + Entry = Vec; + + // Store the vector somewhere we can consult later for quick emission of + // diagnostics. + DupVector.push_back(Vec); + continue; + } + + ECDVector *Vec = Entry.get<ECDVector*>(); + // Make sure constants are not added more than once. + if (*Vec->begin() == ECD) + continue; + + Vec->push_back(ECD); + } + + // Emit diagnostics. + for (DuplicatesVector::iterator DupVectorIter = DupVector.begin(), + DupVectorEnd = DupVector.end(); + DupVectorIter != DupVectorEnd; ++DupVectorIter) { + ECDVector *Vec = *DupVectorIter; + assert(Vec->size() > 1 && "ECDVector should have at least 2 elements."); + + // Emit warning for one enum constant. + ECDVector::iterator I = Vec->begin(); + S.Diag((*I)->getLocation(), diag::warn_duplicate_enum_values) + << (*I)->getName() << (*I)->getInitVal().toString(10) + << (*I)->getSourceRange(); + ++I; + + // Emit one note for each of the remaining enum constants with + // the same value. + for (ECDVector::iterator E = Vec->end(); I != E; ++I) + S.Diag((*I)->getLocation(), diag::note_duplicate_element) + << (*I)->getName() << (*I)->getInitVal().toString(10) + << (*I)->getSourceRange(); + delete Vec; + } +} + void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, SourceLocation RBraceLoc, Decl *EnumDeclX, Decl **Elements, unsigned NumElements, @@ -10862,6 +11038,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, // it needs to go into the function scope. if (InFunctionDeclarator) DeclsInPrototypeScope.push_back(Enum); + + CheckForDuplicateEnumValues(*this, Elements, NumElements, Enum, EnumType); } Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr, |