diff options
-rw-r--r-- | include/clang/Basic/DiagnosticGroups.td | 3 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 88 | ||||
-rw-r--r-- | test/Sema/switch.c | 138 |
4 files changed, 231 insertions, 2 deletions
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 03aad86063..e2653017f8 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -96,7 +96,8 @@ def : DiagGroup<"strict-overflow">; def InvalidOffsetof : DiagGroup<"invalid-offsetof">; def : DiagGroup<"strict-prototypes">; def : DiagGroup<"strict-selector-match">; -def Switch : DiagGroup<"switch">; +def SwitchEnum : DiagGroup<"switch-enum">; +def Switch : DiagGroup<"switch", [SwitchEnum]>; def Trigraphs : DiagGroup<"trigraphs">; def : DiagGroup<"type-limits">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 496e0202eb..5849d9b1cc 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2556,6 +2556,10 @@ def warn_case_value_overflow : Warning< "overflow converting case value to switch condition type (%0 to %1)">; def err_duplicate_case : Error<"duplicate case value '%0'">; def warn_case_empty_range : Warning<"empty case range specified">; +def warn_missing_cases : Warning<"enumeration value %0 not handled in switch">, + InGroup<DiagGroup<"switch-enum"> >; +def not_in_enum : Warning<"case value not in enumerated type %0">, + InGroup<DiagGroup<"switch-enum"> >; def err_typecheck_statement_requires_scalar : Error< "statement requires expression of scalar type (%0 invalid)">; def err_typecheck_statement_requires_integer : Error< diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index ad296eb363..50479a9b81 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -370,6 +370,22 @@ static bool CmpCaseVals(const std::pair<llvm::APSInt, CaseStmt*>& lhs, return false; } +/// CmpEnumVals - Comparison predicate for sorting enumeration values. +/// +static bool CmpEnumVals(const std::pair<llvm::APSInt, EnumConstantDecl*>& lhs, + const std::pair<llvm::APSInt, EnumConstantDecl*>& rhs) +{ + return lhs.first < rhs.first; +} + +/// EqEnumVals - Comparison preficate for uniqing enumeration values. +/// +static bool EqEnumVals(const std::pair<llvm::APSInt, EnumConstantDecl*>& lhs, + const std::pair<llvm::APSInt, EnumConstantDecl*>& rhs) +{ + return lhs.first == rhs.first; +} + /// GetTypeBeforeIntegralPromotion - Returns the pre-promotion type of /// potentially integral-promoted expression @p expr. static QualType GetTypeBeforeIntegralPromotion(const Expr* expr) { @@ -560,7 +576,8 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch, CaseValsTy CaseVals; // Keep track of any GNU case ranges we see. The APSInt is the low value. - std::vector<std::pair<llvm::APSInt, CaseStmt*> > CaseRanges; + typedef std::vector<std::pair<llvm::APSInt, CaseStmt*> > CaseRangesTy; + CaseRangesTy CaseRanges; DefaultStmt *TheDefaultStmt = 0; @@ -723,6 +740,75 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch, } } } + + // Check to see if switch is over an Enum and handles all of its + // values + const EnumType* ET = dyn_cast<EnumType>(CondTypeBeforePromotion); + // If switch has default case, then ignore it. + if (!CaseListIsErroneous && !TheDefaultStmt && ET) { + const EnumDecl *ED = ET->getDecl(); + typedef llvm::SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64> EnumValsTy; + EnumValsTy EnumVals; + + // Gather all enum values, set their type and sort them, allowing easier comparison + // with CaseVals. + for (EnumDecl::enumerator_iterator EDI = ED->enumerator_begin(); EDI != ED->enumerator_end(); EDI++) { + llvm::APSInt Val = (*EDI)->getInitVal(); + if(Val.getBitWidth() < CondWidth) + Val.extend(CondWidth); + Val.setIsSigned(CondIsSigned); + EnumVals.push_back(std::make_pair(Val, (*EDI))); + } + std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals); + EnumValsTy::iterator 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) + Diag(CI->second->getLHS()->getExprLoc(), diag::not_in_enum) << ED->getDeclName(); + } + // 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) { + Diag(RI->second->getLHS()->getExprLoc(), diag::not_in_enum) << ED->getDeclName(); + } + + llvm::APSInt Hi = RI->second->getRHS()->EvaluateAsInt(Context); + while (EI != EIend && EI->first < Hi) + EI++; + if (EI == EIend || EI->first != Hi) + Diag(RI->second->getRHS()->getExprLoc(), diag::not_in_enum) << ED->getDeclName(); + } + //Check which enum vals aren't in switch + CaseValsTy::const_iterator CI = CaseVals.begin(); + CaseRangesTy::const_iterator RI = CaseRanges.begin(); + EI = EnumVals.begin(); + for (; EI != EIend; EI++) { + //Drop unneeded case values + llvm::APSInt CIVal; + while (CI != CaseVals.end() && CI->first < EI->first) + CI++; + + if (CI != CaseVals.end() && CI->first == EI->first) + continue; + + //Drop unneeded case ranges + for (; RI != CaseRanges.end(); RI++) { + llvm::APSInt Hi = RI->second->getRHS()->EvaluateAsInt(Context); + if (EI->first <= Hi) + break; + } + + if (RI == CaseRanges.end() || EI->first < RI->first) + Diag(CondExpr->getExprLoc(), diag::warn_missing_cases) << EI->second->getDeclName(); + } + } } // FIXME: If the case list was broken is some way, we don't have a good system diff --git a/test/Sema/switch.c b/test/Sema/switch.c index 08ab0e0ebd..2690ad28e9 100644 --- a/test/Sema/switch.c +++ b/test/Sema/switch.c @@ -85,3 +85,141 @@ int f0(int var) { // expected-note{{'var' declared here}} } return 2; } + +void test7() { + enum { + A = 1, + B + } a; + switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} + case A: + break; + } + switch(a) { + case B: + case A: + break; + } + switch(a) { + case A: + case B: + case 3: // expected-warning{{case value not in enumerated type ''}} + break; + } + switch(a) { + case A: + case B: + case 3 ... //expected-warning{{case value not in enumerated type ''}} + 4: //expected-warning{{case value not in enumerated type ''}} + break; + } + switch(a) { + case 1 ... 2: + break; + } + switch(a) { + case 0 ... 2: //expected-warning{{case value not in enumerated type ''}} + break; + } + switch(a) { + case 1 ... 3: //expected-warning{{case value not in enumerated type ''}} + break; + } + switch(a) { + case 0 ... //expected-warning{{case value not in enumerated type ''}} + 3: //expected-warning{{case value not in enumerated type ''}} + break; + } + +} + +void test8() { + enum { + A, + B, + C = 1 + } a; + switch(a) { + case A: + case B: + break; + } + switch(a) { + case A: + case C: + break; + } + switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} + case A: + break; + } +} + +void test9() { + enum { + A = 3, + C = 1 + } a; + switch(a) { + case 0: //expected-warning{{case value not in enumerated type ''}} + case 1: + case 2: //expected-warning{{case value not in enumerated type ''}} + case 3: + case 4: //expected-warning{{case value not in enumerated type ''}} + break; + } +} + +void test10() { + enum { + A = 10, + C = 2, + B = 4, + D = 12 + } a; + switch(a) { + case 0 ... //expected-warning{{case value not in enumerated type ''}} + 1: //expected-warning{{case value not in enumerated type ''}} + case 2 ... 4: + case 5 ... //expected-warning{{case value not in enumerated type ''}} + 9: //expected-warning{{case value not in enumerated type ''}} + case 10 ... 12: + case 13 ... //expected-warning{{case value not in enumerated type ''}} + 16: //expected-warning{{case value not in enumerated type ''}} + break; + } +} + +void test11() { + enum { + A = -1, + B, + C + } a; + switch(a) { //expected-warning{{enumeration value 'A' not handled in switch}} + case B: + case C: + break; + } + + switch(a) { + case B: + case C: + break; + + default: + break; + } +} + +void test12() { + enum { + A = -1, + B = 4294967286 + } a; + switch(a) { + case A: + case B: + break; + } +} |