[cfe-commits] r95592 - in /cfe/trunk: include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaStmt.cpp test/Sema/switch.c
Douglas Gregor
dgregor at apple.com
Mon Feb 8 14:24:16 PST 2010
Author: dgregor
Date: Mon Feb 8 16:24:16 2010
New Revision: 95592
URL: http://llvm.org/viewvc/llvm-project?rev=95592&view=rev
Log:
Warn when cases are missing from a switch on a value of enumeration
type (-Wswitch), from Michal!
Modified:
cfe/trunk/include/clang/Basic/DiagnosticGroups.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Sema/SemaStmt.cpp
cfe/trunk/test/Sema/switch.c
Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=95592&r1=95591&r2=95592&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Mon Feb 8 16:24:16 2010
@@ -96,7 +96,8 @@
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">;
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=95592&r1=95591&r2=95592&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Feb 8 16:24:16 2010
@@ -2556,6 +2556,10 @@
"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<
Modified: cfe/trunk/lib/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=95592&r1=95591&r2=95592&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmt.cpp Mon Feb 8 16:24:16 2010
@@ -370,6 +370,22 @@
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 @@
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 @@
}
}
}
+
+ // 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
Modified: cfe/trunk/test/Sema/switch.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/switch.c?rev=95592&r1=95591&r2=95592&view=diff
==============================================================================
--- cfe/trunk/test/Sema/switch.c (original)
+++ cfe/trunk/test/Sema/switch.c Mon Feb 8 16:24:16 2010
@@ -85,3 +85,141 @@
}
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;
+ }
+}
More information about the cfe-commits
mailing list