initial simplistic implementation of switchCaseFallThrough

This commit is contained in:
Greg Hewgill 2011-02-19 21:33:29 +13:00
parent d7a6e729b8
commit 93ea774484
4 changed files with 164 additions and 1 deletions

View File

@ -306,6 +306,46 @@ void CheckOther::checkRedundantAssignmentInSwitch()
} }
void CheckOther::checkSwitchCaseFallThrough()
{
const char switchPattern[] = "switch ( %any% ) { case";
//const char breakPattern[] = "break|continue|return|exit|goto";
//const char functionPattern[] = "%var% (";
// nested switch
// Find the beginning of a switch. E.g.:
// switch (var) { ...
const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern);
while (tok)
{
// Check the contents of the switch statement
for (const Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next())
{
if (Token::Match(tok2, switchPattern))
{
tok2 = tok2->tokAt(4)->link()->previous();
}
else if (tok2->str() == "case")
{
if (!Token::Match(tok2->previous()->previous(), "break"))
{
switchCaseFallThrough(tok2);
}
}
else if (tok2->str() == "}")
{
// End of the switch block
break;
}
}
tok = Token::findmatch(tok->next(), switchPattern);
}
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// int x = 1; // int x = 1;
// x = x; // <- redundant assignment to self // x = x; // <- redundant assignment to self
@ -2998,6 +3038,12 @@ void CheckOther::redundantAssignmentInSwitchError(const Token *tok, const std::s
"redundantAssignInSwitch", "Redundant assignment of \"" + varname + "\" in switch"); "redundantAssignInSwitch", "Redundant assignment of \"" + varname + "\" in switch");
} }
void CheckOther::switchCaseFallThrough(const Token *tok)
{
reportError(tok, Severity::warning,
"switchCaseFallThrough", "Switch falls through case without comment");
}
void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname)
{ {
reportError(tok, Severity::warning, reportError(tok, Severity::warning,

View File

@ -61,6 +61,7 @@ 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();
@ -162,6 +163,9 @@ public:
/** @brief %Check for assigning to the same variable twice in a switch statement*/ /** @brief %Check for assigning to the same variable twice in a switch statement*/
void checkRedundantAssignmentInSwitch(); void checkRedundantAssignmentInSwitch();
/** @brief %Check for switch case fall through without comment */
void checkSwitchCaseFallThrough();
/** @brief %Check for assigning a variable to itself*/ /** @brief %Check for assigning a variable to itself*/
void checkSelfAssignment(); void checkSelfAssignment();
@ -209,6 +213,7 @@ public:
void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1); void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1);
void fflushOnInputStreamError(const Token *tok, const std::string &varname); void fflushOnInputStreamError(const Token *tok, const std::string &varname);
void redundantAssignmentInSwitchError(const Token *tok, const std::string &varname); void redundantAssignmentInSwitchError(const Token *tok, const std::string &varname);
void switchCaseFallThrough(const Token *tok);
void selfAssignmentError(const Token *tok, const std::string &varname); void selfAssignmentError(const Token *tok, const std::string &varname);
void assignmentInAssertError(const Token *tok, const std::string &varname); void assignmentInAssertError(const Token *tok, const std::string &varname);
void incorrectLogicOperatorError(const Token *tok); void incorrectLogicOperatorError(const Token *tok);
@ -247,6 +252,7 @@ public:
c.sizeofsizeofError(0); c.sizeofsizeofError(0);
c.sizeofCalculationError(0); c.sizeofCalculationError(0);
c.redundantAssignmentInSwitchError(0, "varname"); c.redundantAssignmentInSwitchError(0, "varname");
c.switchCaseFallThrough(0);
c.selfAssignmentError(0, "varname"); c.selfAssignmentError(0, "varname");
c.assignmentInAssertError(0, "varname"); c.assignmentInAssertError(0, "varname");
c.invalidScanfError(0); c.invalidScanfError(0);

View File

@ -377,10 +377,10 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri
i = str.find('\n', i); i = str.find('\n', i);
if (i == std::string::npos) if (i == std::string::npos)
break; break;
std::string comment(str, commentStart, i - commentStart);
if (settings && settings->_inlineSuppressions) if (settings && settings->_inlineSuppressions)
{ {
std::string comment(str, commentStart, i - commentStart);
std::istringstream iss(comment); std::istringstream iss(comment);
std::string word; std::string word;
iss >> word; iss >> word;
@ -392,6 +392,11 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri
} }
} }
if (comment.find("fall through") != std::string::npos)
{
suppressionIDs.push_back("switchCaseFallThrough");
}
code << "\n"; code << "\n";
previous = '\n'; previous = '\n';
++lineno; ++lineno;

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "preprocessor.h"
#include "tokenize.h" #include "tokenize.h"
#include "checkother.h" #include "checkother.h"
#include "testsuite.h" #include "testsuite.h"
@ -74,6 +75,7 @@ private:
TEST_CASE(sizeofCalculation); TEST_CASE(sizeofCalculation);
TEST_CASE(switchRedundantAssignmentTest); TEST_CASE(switchRedundantAssignmentTest);
TEST_CASE(switchFallThroughCase);
TEST_CASE(selfAssignment); TEST_CASE(selfAssignment);
TEST_CASE(testScanf1); TEST_CASE(testScanf1);
@ -148,6 +150,60 @@ private:
checkOther.checkComparisonOfBoolWithInt(); checkOther.checkComparisonOfBoolWithInt();
} }
class SimpleSuppressor: public ErrorLogger
{
public:
SimpleSuppressor(Settings &settings, ErrorLogger *next)
: _settings(settings), _next(next)
{ }
virtual void reportOut(const std::string &outmsg)
{
_next->reportOut(outmsg);
}
virtual void reportErr(const ErrorLogger::ErrorMessage &msg)
{
if (!msg._callStack.empty() && !_settings.nomsg.isSuppressed(msg._id, msg._callStack.begin()->getfile(), msg._callStack.begin()->line))
_next->reportErr(msg);
}
virtual void reportStatus(unsigned int index, unsigned int max)
{
_next->reportStatus(index, max);
}
private:
Settings &_settings;
ErrorLogger *_next;
};
void check_preprocess_suppress(const char precode[], const char *filename = NULL)
{
// Clear the error buffer..
errout.str("");
if (filename == NULL)
filename = "test.cpp";
Settings settings;
settings._checkCodingStyle = true;
// Preprocess file..
Preprocessor preprocessor(&settings, this);
std::list<std::string> configurations;
std::string filedata = "";
std::istringstream fin(precode);
preprocessor.preprocess(fin, filedata, configurations, filename, settings._includePaths);
SimpleSuppressor logger(settings, this);
const std::string code = Preprocessor::getcode(filedata, "", filename, &settings, &logger);
// Tokenize..
Tokenizer tokenizer(&settings, &logger);
std::istringstream istr(code);
tokenizer.tokenize(istr, filename);
// Check..
CheckOther checkOther(&tokenizer, &settings, &logger);
checkOther.checkSwitchCaseFallThrough();
}
void zeroDiv1() void zeroDiv1()
{ {
@ -1146,6 +1202,56 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void switchFallThroughCase()
{
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" break;\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" g();\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
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"
" // fall through\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check_preprocess_suppress(
"void foo() {\n"
" switch (a) {\n"
" case 1:\n"
" g();\n"
" break;\n"
" // fall through\n"
" case 2:\n"
" break;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void selfAssignment() void selfAssignment()
{ {
check("void foo()\n" check("void foo()\n"