When building SwitchStmts in Sema, record whether all the enum values of a switch(enum) where

covered by individual case statements.  Flow-based analyses may wish to consult this information,
and recording this in the AST allows us to obviate reconstructing this information later when
we build the CFG.

llvm-svn: 113447
This commit is contained in:
Ted Kremenek 2010-09-09 00:05:53 +00:00
parent a5614c5fe8
commit c42f345157
4 changed files with 68 additions and 34 deletions

View File

@ -662,6 +662,11 @@ class SwitchStmt : public Stmt {
SwitchCase *FirstCase;
SourceLocation SwitchLoc;
/// If the SwitchStmt is a switch on an enum value, this records whether
/// all the enum values were covered by CaseStmts. This value is meant to
/// be a hint for possible clients.
unsigned AllEnumCasesCovered : 1;
public:
SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond);
@ -709,6 +714,19 @@ public:
SC->setNextSwitchCase(FirstCase);
FirstCase = SC;
}
/// Set a flag in the SwitchStmt indicating that if the 'switch (X)' is a
/// switch over an enum value then all cases have been explicitly covered.
void setAllEnumCasesCovered() {
AllEnumCasesCovered = 1;
}
/// Returns true if the SwitchStmt is a switch of an enum value and all cases
/// have been explicitly covered.
bool isAllEnumCasesCovered() const {
return (bool) AllEnumCasesCovered;
}
virtual SourceRange getSourceRange() const {
return SourceRange(SwitchLoc, SubExprs[BODY]->getLocEnd());
}

View File

@ -696,14 +696,14 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
}
// Check to see if switch is over an Enum and handles all of its
// values. We don't need to do this if there's a default
// statement or if we have a constant condition.
// values. We only issue a warning if there is not 'default:', but
// we still do the analysis to preserve this information in the AST
// (which can be used by flow-based analyes).
//
// TODO: we might want to check whether case values are out of the
// enum even if we don't want to check whether all cases are handled.
const EnumType* ET = CondTypeBeforePromotion->getAs<EnumType>();
// If switch has default case, then ignore it.
if (!CaseListIsErroneous && !TheDefaultStmt && !HasConstantCond && ET) {
if (!CaseListIsErroneous && !HasConstantCond && ET) {
const EnumDecl *ED = ET->getDecl();
typedef llvm::SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64> EnumValsTy;
EnumValsTy EnumVals;
@ -723,40 +723,46 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
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();
// See which case values aren't in enum.
// TODO: we might want to check whether case values are out of the
// enum even if we don't want to check whether all cases are handled.
if (!TheDefaultStmt) {
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)
while (EI != EIend && EI->first < CI->first)
EI++;
if (EI == EIend || EI->first > CI->first)
Diag(CI->second->getLHS()->getExprLoc(), diag::warn_not_in_enum)
<< ED->getDeclName();
}
// See which of case ranges aren't in enum
EI = EnumVals.begin();
for (CaseRangesTy::const_iterator RI = CaseRanges.begin();
}
// 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++;
while (EI != EIend && EI->first < RI->first)
EI++;
if (EI == EIend || EI->first != RI->first) {
Diag(RI->second->getLHS()->getExprLoc(), diag::warn_not_in_enum)
<< ED->getDeclName();
}
if (EI == EIend || EI->first != RI->first) {
Diag(RI->second->getLHS()->getExprLoc(), diag::warn_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::warn_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::warn_not_in_enum)
<< ED->getDeclName();
}
}
//Check which enum vals aren't in switch
// 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++) {
bool hasCasesNotInSwitch = false;
for (EnumValsTy::const_iterator EI = EnumVals.begin(); EI != EIend; EI++){
//Drop unneeded case values
llvm::APSInt CIVal;
while (CI != CaseVals.end() && CI->first < EI->first)
@ -765,17 +771,23 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
if (CI != CaseVals.end() && CI->first == EI->first)
continue;
//Drop unneeded case ranges
// 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();
if (RI == CaseRanges.end() || EI->first < RI->first) {
hasCasesNotInSwitch = true;
if (!TheDefaultStmt)
Diag(CondExpr->getExprLoc(), diag::warn_missing_cases)
<< EI->second->getDeclName();
}
}
if (!hasCasesNotInSwitch)
SS->setAllEnumCasesCovered();
}
}

View File

@ -239,6 +239,9 @@ void ASTStmtReader::VisitSwitchStmt(SwitchStmt *S) {
S->setCond(Reader.ReadSubExpr());
S->setBody(Reader.ReadSubStmt());
S->setSwitchLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
if (Record[Idx++])
S->setAllEnumCasesCovered();
SwitchCase *PrevSC = 0;
for (unsigned N = Record.size(); Idx != N; ++Idx) {
SwitchCase *SC = Reader.getSwitchCaseWithID(Record[Idx]);

View File

@ -233,6 +233,7 @@ void ASTStmtWriter::VisitSwitchStmt(SwitchStmt *S) {
Writer.AddStmt(S->getCond());
Writer.AddStmt(S->getBody());
Writer.AddSourceLocation(S->getSwitchLoc(), Record);
Record.push_back(S->isAllEnumCasesCovered());
for (SwitchCase *SC = S->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase())
Record.push_back(Writer.RecordSwitchCaseID(SC));