зеркало из https://github.com/microsoft/clang.git
Warn when cases are missing from a switch on a value of enumeration
type (-Wswitch), from Michal! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95592 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
7dc94d6892
Коммит
ba915af51c
|
@ -96,7 +96,8 @@ def : DiagGroup<"strict-overflow">;
|
||||||
def InvalidOffsetof : DiagGroup<"invalid-offsetof">;
|
def InvalidOffsetof : DiagGroup<"invalid-offsetof">;
|
||||||
def : DiagGroup<"strict-prototypes">;
|
def : DiagGroup<"strict-prototypes">;
|
||||||
def : DiagGroup<"strict-selector-match">;
|
def : DiagGroup<"strict-selector-match">;
|
||||||
def Switch : DiagGroup<"switch">;
|
def SwitchEnum : DiagGroup<"switch-enum">;
|
||||||
|
def Switch : DiagGroup<"switch", [SwitchEnum]>;
|
||||||
def Trigraphs : DiagGroup<"trigraphs">;
|
def Trigraphs : DiagGroup<"trigraphs">;
|
||||||
|
|
||||||
def : DiagGroup<"type-limits">;
|
def : DiagGroup<"type-limits">;
|
||||||
|
|
|
@ -2556,6 +2556,10 @@ def warn_case_value_overflow : Warning<
|
||||||
"overflow converting case value to switch condition type (%0 to %1)">;
|
"overflow converting case value to switch condition type (%0 to %1)">;
|
||||||
def err_duplicate_case : Error<"duplicate case value '%0'">;
|
def err_duplicate_case : Error<"duplicate case value '%0'">;
|
||||||
def warn_case_empty_range : Warning<"empty case range specified">;
|
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<
|
def err_typecheck_statement_requires_scalar : Error<
|
||||||
"statement requires expression of scalar type (%0 invalid)">;
|
"statement requires expression of scalar type (%0 invalid)">;
|
||||||
def err_typecheck_statement_requires_integer : Error<
|
def err_typecheck_statement_requires_integer : Error<
|
||||||
|
|
|
@ -370,6 +370,22 @@ static bool CmpCaseVals(const std::pair<llvm::APSInt, CaseStmt*>& lhs,
|
||||||
return false;
|
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
|
/// GetTypeBeforeIntegralPromotion - Returns the pre-promotion type of
|
||||||
/// potentially integral-promoted expression @p expr.
|
/// potentially integral-promoted expression @p expr.
|
||||||
static QualType GetTypeBeforeIntegralPromotion(const Expr* expr) {
|
static QualType GetTypeBeforeIntegralPromotion(const Expr* expr) {
|
||||||
|
@ -560,7 +576,8 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch,
|
||||||
CaseValsTy CaseVals;
|
CaseValsTy CaseVals;
|
||||||
|
|
||||||
// Keep track of any GNU case ranges we see. The APSInt is the low value.
|
// 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;
|
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
|
// FIXME: If the case list was broken is some way, we don't have a good system
|
||||||
|
|
|
@ -85,3 +85,141 @@ int f0(int var) { // expected-note{{'var' declared here}}
|
||||||
}
|
}
|
||||||
return 2;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче