diff --git a/lib/checkother.cpp b/lib/checkother.cpp
index 8588e3666..b7e3e6e19 100644
--- a/lib/checkother.cpp
+++ b/lib/checkother.cpp
@@ -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;
// 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");
}
+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)
{
reportError(tok, Severity::warning,
diff --git a/lib/checkother.h b/lib/checkother.h
index d43c4c413..63c7ebed7 100644
--- a/lib/checkother.h
+++ b/lib/checkother.h
@@ -61,6 +61,7 @@ public:
checkOther.sizeofsizeof();
checkOther.sizeofCalculation();
checkOther.checkRedundantAssignmentInSwitch();
+ checkOther.checkSwitchCaseFallThrough();
checkOther.checkAssignmentInAssert();
checkOther.checkSizeofForArrayParameter();
checkOther.checkSelfAssignment();
@@ -162,6 +163,9 @@ public:
/** @brief %Check for assigning to the same variable twice in a switch statement*/
void checkRedundantAssignmentInSwitch();
+ /** @brief %Check for switch case fall through without comment */
+ void checkSwitchCaseFallThrough();
+
/** @brief %Check for assigning a variable to itself*/
void checkSelfAssignment();
@@ -209,6 +213,7 @@ public:
void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1);
void fflushOnInputStreamError(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 assignmentInAssertError(const Token *tok, const std::string &varname);
void incorrectLogicOperatorError(const Token *tok);
@@ -247,6 +252,7 @@ public:
c.sizeofsizeofError(0);
c.sizeofCalculationError(0);
c.redundantAssignmentInSwitchError(0, "varname");
+ c.switchCaseFallThrough(0);
c.selfAssignmentError(0, "varname");
c.assignmentInAssertError(0, "varname");
c.invalidScanfError(0);
diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp
index 45ed8a425..9f4bb0384 100644
--- a/lib/preprocessor.cpp
+++ b/lib/preprocessor.cpp
@@ -377,10 +377,10 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri
i = str.find('\n', i);
if (i == std::string::npos)
break;
+ std::string comment(str, commentStart, i - commentStart);
if (settings && settings->_inlineSuppressions)
{
- std::string comment(str, commentStart, i - commentStart);
std::istringstream iss(comment);
std::string 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";
previous = '\n';
++lineno;
diff --git a/test/testother.cpp b/test/testother.cpp
index 5707fc264..43d07ee8b 100644
--- a/test/testother.cpp
+++ b/test/testother.cpp
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+#include "preprocessor.h"
#include "tokenize.h"
#include "checkother.h"
#include "testsuite.h"
@@ -74,6 +75,7 @@ private:
TEST_CASE(sizeofCalculation);
TEST_CASE(switchRedundantAssignmentTest);
+ TEST_CASE(switchFallThroughCase);
TEST_CASE(selfAssignment);
TEST_CASE(testScanf1);
@@ -148,6 +150,60 @@ private:
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 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()
{
@@ -1146,6 +1202,56 @@ private:
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()
{
check("void foo()\n"