diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 978b4dc57..391b3833e 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1237,6 +1237,10 @@ void CheckUnusedVar::checkFunctionVariableUsage() while (Token::Match(op1tok, ".|[|*")) op1tok = op1tok->astOperand1(); + // Assignment in macro => do not warn + if (isAssignment && tok->isExpandedMacro() && op1tok && op1tok->isExpandedMacro()) + continue; + const Variable *op1Var = op1tok ? op1tok->variable() : nullptr; if (!op1Var && Token::Match(tok, "(|{") && tok->previous() && tok->previous()->variable()) op1Var = tok->previous()->variable(); @@ -1353,6 +1357,13 @@ void CheckUnusedVar::checkFunctionVariableUsage() bool error = false; if (vnt->next()->isSplittedVarDeclEq()) { const Token* nextStmt = vnt->tokAt(2); + if (nextStmt->isExpandedMacro()) { + const Token* parent = nextStmt; + while (parent->astParent() && parent == parent->astParent()->astOperand1()) + parent = parent->astParent(); + if (parent->isAssignmentOp() && parent->isExpandedMacro()) + continue; + } while (nextStmt && nextStmt->str() != ";") nextStmt = nextStmt->next(); error = precedes(usage._lastAccess, nextStmt); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index bae9864fa..c7a28e35b 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -6435,6 +6435,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co endDecl = endDecl->next(); endDecl->next()->isSplittedVarDeclEq(true); endDecl->insertToken(varName->str()); + endDecl->next()->isExpandedMacro(varName->isExpandedMacro()); continue; } //non-VLA case diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index b1a66424a..f03132101 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -136,6 +136,7 @@ private: TEST_CASE(localvar65); // #9876, #10006 TEST_CASE(localvar66); // #11143 TEST_CASE(localvar67); // #9946 + TEST_CASE(localvar68); TEST_CASE(localvarloops); // loops TEST_CASE(localvaralias1); TEST_CASE(localvaralias2); // ticket #1637 @@ -265,6 +266,31 @@ private: (checkUnusedVar.checkStructMemberUsage)(); } + void checkFunctionVariableUsageP(const char code[], const char* filename = "test.cpp") { + // Raw tokens.. + std::vector files(1, filename); + std::istringstream istr(code); + const simplecpp::TokenList tokens1(istr, files, files[0]); + + // Preprocess.. + simplecpp::TokenList tokens2(files); + std::map filedata; + simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); + + Preprocessor preprocessor(settings, nullptr); + preprocessor.setDirectives(tokens1); + + // Tokenizer.. + Tokenizer tokenizer(&settings, this, &preprocessor); + tokenizer.createTokens(std::move(tokens2)); + tokenizer.simplifyTokens1(""); + + // Check for unused variables.. + CheckUnusedVar checkUnusedVar(&tokenizer, &settings, this); + (checkUnusedVar.checkFunctionVariableUsage)(); + } + + void isRecordTypeWithoutSideEffects() { functionVariableUsage( "class A {};\n" @@ -3639,6 +3665,16 @@ private: ASSERT_EQUALS("", errout.str()); } + void localvar68() { + checkFunctionVariableUsageP("#define X0 int x = 0\n" + "void f() { X0; }\n"); + ASSERT_EQUALS("", errout.str()); + + checkFunctionVariableUsageP("#define X0 int (*x)(int) = 0\n" + "void f() { X0; }\n"); + ASSERT_EQUALS("", errout.str()); + } + void localvarloops() { // loops functionVariableUsage("void fun(int c) {\n"