diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 3c97405ae..dfc9a246c 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -3728,8 +3728,22 @@ void Tokenizer::setVarIdPass1() else if (tok->str() == ";") { if (!variableMap.leaveScope()) cppcheckError(tok); - } else if (tok->str() == "{") + } else if (tok->str() == "{") { scopeStack.push(VarIdScopeInfo(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/false, *variableMap.getVarId())); + + // check if this '{' is a start of an "if" body + const Token * ifToken = tok->previous(); + if (ifToken && ifToken->str() == ")") + ifToken = ifToken->link(); + else + ifToken = nullptr; + if (ifToken) + ifToken = ifToken->previous(); + if (ifToken && ifToken->str() == "if") { + // open another scope to differentiate between variables declared in the "if" condition and in the "if" body + variableMap.enterScope(); + } + } } else if (!initlist && tok->str()=="(") { const Token * newFunctionDeclEnd = nullptr; if (!scopeStack.top().isExecutable) @@ -3788,6 +3802,17 @@ void Tokenizer::setVarIdPass1() if (!scopeStack.top().isStructInit) { variableMap.leaveScope(); + + // check if this '}' is an end of an "else" body or an "if" body without an "else" part + const Token * ifToken = startToken->previous(); + if (ifToken && ifToken->str() == ")") + ifToken = ifToken->link()->previous(); + else + ifToken = nullptr; + if (startToken->strAt(-1) == "else" || (ifToken && ifToken->str() == "if" && tok->strAt(1) != "else")) { + // leave the extra scope used to differentiate between variables declared in the "if" condition and in the "if" body + variableMap.leaveScope(); + } } scopeStack.pop(); @@ -5096,6 +5121,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) // Split up variable declarations. simplifyVarDecl(false); + elseif(); + validate(); // #6772 "segmentation fault (invalid code) in Tokenizer::setVarId" if (mTimerResults) { @@ -5141,8 +5168,6 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) simplifyEmptyNamespaces(); - elseif(); - simplifyIfSwitchForInit(); simplifyOverloadedOperators(); diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index d83f34f67..312a44d9c 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -179,6 +179,7 @@ private: TEST_CASE(localvarInvert); // Usage with inverted variable TEST_CASE(localvarIf); // Usage in if TEST_CASE(localvarIfElse); // return tmp1 ? tmp2 : tmp3; + TEST_CASE(localvarDeclaredInIf); TEST_CASE(localvarOpAssign); // a |= b; TEST_CASE(localvarFor); // for ( ; var; ) TEST_CASE(localvarForEach); // #4155 - BOOST_FOREACH, hlist_for_each, etc @@ -4593,6 +4594,35 @@ private: ASSERT_EQUALS("", errout.str()); } + void localvarDeclaredInIf() { + functionVariableUsage("int foo(int x)\n" + "{\n" + " if (int y = x % 2)\n" + " return 2;\n" + " else\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'y' is assigned a value that is never used.\n", errout.str()); + + functionVariableUsage("int foo(int x)\n" + "{\n" + " if (int y = x % 2)\n" + " return y;\n" + " else\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int foo(int x)\n" + "{\n" + " if (int y = x % 2)\n" + " return 2;\n" + " else\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void localvarOpAssign() { functionVariableUsage("void foo()\n" "{\n" diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 492050329..7c8b960b3 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -173,6 +173,7 @@ private: TEST_CASE(varid_parameter_pack); // #9383 TEST_CASE(varid_for_auto_cpp17); TEST_CASE(varid_not); // #9689 'not x' + TEST_CASE(varid_declInIfCondition); TEST_CASE(varidclass1); TEST_CASE(varidclass2); @@ -2698,6 +2699,53 @@ private: ASSERT_EQUALS(exp1, tokenize(code1)); } + void varid_declInIfCondition() { + // if + ASSERT_EQUALS("1: void f ( int x@1 ) {\n" + "2: if ( int x@2 = 0 ) { x@2 ; }\n" + "3: x@1 ;\n" + "4: }\n", + tokenize("void f(int x) {\n" + " if (int x = 0) { x; }\n" + " x;\n" + "}")); + // if, else + ASSERT_EQUALS("1: void f ( int x@1 ) {\n" + "2: if ( int x@2 = 0 ) { x@2 ; }\n" + "3: else { x@2 ; }\n" + "4: x@1 ;\n" + "5: }\n", + tokenize("void f(int x) {\n" + " if (int x = 0) { x; }\n" + " else { x; }\n" + " x;\n" + "}")); + // if, else if + ASSERT_EQUALS("1: void f ( int x@1 ) {\n" + "2: if ( int x@2 = 0 ) { x@2 ; }\n" + "3: else { if ( void * x@3 = & x@3 ) { x@3 ; } }\n" + "4: x@1 ;\n" + "5: }\n", + tokenize("void f(int x) {\n" + " if (int x = 0) x;\n" + " else if (void* x = &x) x;\n" + " x;\n" + "}")); + // if, else if, else + ASSERT_EQUALS("1: void f ( int x@1 ) {\n" + "2: if ( int x@2 = 0 ) { x@2 ; }\n" + "3: else { if ( void * x@3 = & x@3 ) { x@3 ; }\n" + "4: else { x@3 ; } }\n" + "5: x@1 ;\n" + "6: }\n", + tokenize("void f(int x) {\n" + " if (int x = 0) x;\n" + " else if (void* x = &x) x;\n" + " else x;\n" + " x;\n" + "}")); + } + void varidclass1() { const std::string actual = tokenize( "class Fred\n"