full implementation of switch case fall through

This commit is contained in:
Greg Hewgill 2011-02-20 08:02:28 +13:00
parent 93ea774484
commit a532a9690e
3 changed files with 208 additions and 12 deletions

View File

@ -24,6 +24,7 @@
#include <cctype> // std::isupper #include <cctype> // std::isupper
#include <cmath> // fabs() #include <cmath> // fabs()
#include <stack>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it) // Register this check class (by creating a static instance of it)
@ -308,10 +309,8 @@ void CheckOther::checkRedundantAssignmentInSwitch()
void CheckOther::checkSwitchCaseFallThrough() void CheckOther::checkSwitchCaseFallThrough()
{ {
const char switchPattern[] = "switch ( %any% ) { case"; const char switchPattern[] = "switch (";
//const char breakPattern[] = "break|continue|return|exit|goto"; const char breakPattern[] = "break|continue|return|exit|goto";
//const char functionPattern[] = "%var% (";
// nested switch
// Find the beginning of a switch. E.g.: // Find the beginning of a switch. E.g.:
// switch (var) { ... // switch (var) { ...
@ -320,23 +319,99 @@ void CheckOther::checkSwitchCaseFallThrough()
{ {
// Check the contents of the switch statement // Check the contents of the switch statement
for (const Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) std::stack<std::pair<Token *, bool> > ifnest;
std::stack<Token *> loopnest;
std::stack<Token *> scopenest;
bool justbreak = true;
for (const Token *tok2 = tok->tokAt(1)->link()->tokAt(2); tok2; tok2 = tok2->next())
{ {
if (Token::Match(tok2, switchPattern)) if (Token::Match(tok2, "if ("))
{ {
tok2 = tok2->tokAt(4)->link()->previous(); tok2 = tok2->tokAt(1)->link()->next();
ifnest.push(std::make_pair(tok2->link(), false));
justbreak = false;
} }
else if (tok2->str() == "case") else if (Token::Match(tok2, "while ("))
{ {
if (!Token::Match(tok2->previous()->previous(), "break")) tok2 = tok2->tokAt(1)->link()->next();
loopnest.push(tok2->link());
justbreak = false;
}
else if (Token::Match(tok2, "do {"))
{
tok2 = tok2->tokAt(1);
loopnest.push(tok2->link());
justbreak = false;
}
else if (Token::Match(tok2, "for ("))
{
tok2 = tok2->tokAt(1)->link()->next();
loopnest.push(tok2->link());
justbreak = false;
}
else if (Token::Match(tok2, switchPattern))
{
// skip over nested switch, we'll come to that soon
tok2 = tok2->tokAt(1)->link()->next()->link();
}
else if (Token::Match(tok2, breakPattern))
{
if (loopnest.empty())
{
justbreak = true;
}
tok2 = Token::findmatch(tok2, ";");
}
else if (Token::Match(tok2, "case|default"))
{
if (!justbreak)
{ {
switchCaseFallThrough(tok2); switchCaseFallThrough(tok2);
} }
tok2 = Token::findmatch(tok2, ":");
justbreak = true;
}
else if (tok2->str() == "{")
{
scopenest.push(tok2->link());
} }
else if (tok2->str() == "}") else if (tok2->str() == "}")
{ {
// End of the switch block if (!ifnest.empty() && tok2 == ifnest.top().first)
break; {
if (tok2->next()->str() == "else")
{
tok2 = tok2->tokAt(2);
ifnest.pop();
ifnest.push(std::make_pair(tok2->link(), justbreak));
justbreak = false;
}
else
{
justbreak &= ifnest.top().second;
ifnest.pop();
}
}
else if (!loopnest.empty() && tok2 == loopnest.top())
{
loopnest.pop();
}
else if (!scopenest.empty() && tok2 == scopenest.top())
{
scopenest.pop();
}
else
{
assert(ifnest.empty());
assert(loopnest.empty());
assert(scopenest.empty());
// end of switch block
break;
}
}
else if (tok2->str() != ";")
{
justbreak = false;
} }
} }

View File

@ -61,7 +61,6 @@ public:
checkOther.sizeofsizeof(); checkOther.sizeofsizeof();
checkOther.sizeofCalculation(); checkOther.sizeofCalculation();
checkOther.checkRedundantAssignmentInSwitch(); checkOther.checkRedundantAssignmentInSwitch();
checkOther.checkSwitchCaseFallThrough();
checkOther.checkAssignmentInAssert(); checkOther.checkAssignmentInAssert();
checkOther.checkSizeofForArrayParameter(); checkOther.checkSizeofForArrayParameter();
checkOther.checkSelfAssignment(); checkOther.checkSelfAssignment();
@ -91,6 +90,7 @@ public:
checkOther.checkIncorrectStringCompare(); checkOther.checkIncorrectStringCompare();
checkOther.checkIncrementBoolean(); checkOther.checkIncrementBoolean();
checkOther.checkComparisonOfBoolWithInt(); checkOther.checkComparisonOfBoolWithInt();
checkOther.checkSwitchCaseFallThrough();
} }
/** @brief Clarify calculation for ".. a * b ? .." */ /** @brief Clarify calculation for ".. a * b ? .." */

View File

@ -202,6 +202,8 @@ private:
// Check.. // Check..
CheckOther checkOther(&tokenizer, &settings, &logger); CheckOther checkOther(&tokenizer, &settings, &logger);
checkOther.checkSwitchCaseFallThrough(); checkOther.checkSwitchCaseFallThrough();
logger.reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(filename));
} }
@ -1215,6 +1217,18 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 0:\n"
" case 1:\n"
" break;\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check_preprocess_suppress( check_preprocess_suppress(
"void foo() {\n" "void foo() {\n"
" switch (a) {\n" " switch (a) {\n"
@ -1226,6 +1240,17 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:5]: (warning) Switch falls through case without comment\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" g();\n"
" default:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress( check_preprocess_suppress(
"void foo() {\n" "void foo() {\n"
" switch (a) {\n" " switch (a) {\n"
@ -1249,7 +1274,103 @@ private:
" break;\n" " break;\n"
" }\n" " }\n"
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:7]: (information) Unmatched suppression: switchCaseFallThrough\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" {\n"
" break;\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" for (;;) {\n"
" break;\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:7]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" if (b) {\n"
" break;\n"
" } else {\n"
" break;\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" if (b) {\n"
" break;\n"
" } else {\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:8]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" if (b) {\n"
" break;\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:7]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" if (b) {\n"
" } else {\n"
" break;\n"
" }\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:8]: (warning) Switch falls through case without comment\n", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" if (b) {\n"
" case 2:\n"
" } else {\n"
" break;\n"
" }\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (warning) Switch falls through case without comment\n", errout.str());
} }
void selfAssignment() void selfAssignment()