diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 348ea10297f..734d59a1505 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -851,6 +851,30 @@ void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, Certainty::inconclusive); } +static bool isNestedInSwitch(const Scope* scope) +{ + while (scope) { + if (scope->type == Scope::ScopeType::eSwitch) + return true; + if (scope->type == Scope::ScopeType::eUnconditional) { + scope = scope->nestedIn; + continue; + } + break; + } + return false; +} + +static bool isVardeclInSwitch(const Token* tok) +{ + if (!tok) + return false; + if (!isNestedInSwitch(tok->scope())) + return false; + const Token* end = Token::findsimplematch(tok, ";"); + return end && end->previous()->variable() && end->previous()->variable()->nameToken() == end->previous(); +} + //--------------------------------------------------------------------------- // Find consecutive return, break, continue, goto or throw statements. e.g.: // break; break; @@ -958,7 +982,7 @@ void CheckOther::checkUnreachableCode() if (silencedWarning) secondBreak = silencedWarning; - if (!labelInFollowingLoop && !silencedCompilerWarningOnly) + if (!labelInFollowingLoop && !silencedCompilerWarningOnly && !isVardeclInSwitch(secondBreak)) unreachableCodeError(secondBreak, tok, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else if (secondBreak->scope() && secondBreak->scope()->isLoopScope() && secondBreak->str() == "}" && tok->str() == "continue") { diff --git a/test/testother.cpp b/test/testother.cpp index b0a76891dd1..15b1aefc268 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -5677,6 +5677,53 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.cpp:6]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n" "[test.cpp:1]: (style) Parameter 'argv' can be declared as const array\n", errout_str()); + + check("int f(int i) {\n" // #13491 + " switch (i) {\n" + " case 0:\n" + " return 0;\n" + " int j;\n" + " case 1:\n" + " case 2:\n" + " j = 5;\n" + " return j + i;\n" + " }\n" + " return 3;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i) {\n" + " switch (i) {\n" + " {\n" + " case 0:\n" + " return 0;\n" + " }\n" + " {\n" + " int j;\n" + " case 1:\n" + " case 2:\n" + " j = 5;\n" + " return j + i;\n" + " }\n" + " }\n" + " return 3;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i) {\n" + " switch (i) {\n" + " case 0:\n" + " return 0;\n" + " int j;\n" + " dostuff();\n" + " case 1:\n" + " case 2:\n" + " j = 5;\n" + " return j + i;\n" + " }\n" + " return 3;\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:6]: (style) Statements following 'return' will never be executed.\n", "", errout_str()); } void redundantContinue() {