From 491299048bc21ea8e0beb59d113cd44b18774528 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 2 Mar 2023 21:45:15 +0100 Subject: [PATCH] Amend fix for #11449 Function call not recognized (#4838) --- cfg/std.cfg | 14 +++++++ lib/astutils.cpp | 84 +++++++++++++++++++++--------------------- lib/valueflow.cpp | 16 ++++++-- test/cfg/std.cpp | 8 ++++ test/testcondition.cpp | 10 +++++ 5 files changed, 85 insertions(+), 47 deletions(-) diff --git a/cfg/std.cfg b/cfg/std.cfg index dec4d8988..1e2544afb 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -6789,6 +6789,20 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun + + + + + false + + arg1 + + + + + + + diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 07d052b96..cc28af023 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1220,54 +1220,52 @@ SmallVector followAllReferences(const Token* tok, return refs_result; } - } else if (Token::Match(tok->previous(), "%name% (")) { + } else if (tok->previous() && tok->previous()->function() && Token::Match(tok->previous(), "%name% (")) { const Function *f = tok->previous()->function(); - if (f) { - if (!Function::returnsReference(f)) { - refs_result.push_back({tok, std::move(errors)}); - return refs_result; - } - std::set result; - std::vector returns = Function::findReturns(f); - for (const Token* returnTok : returns) { - if (returnTok == tok) - continue; - for (const ReferenceToken& rt : - followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) { - const Variable* argvar = rt.token->variable(); - if (!argvar) { + if (!Function::returnsReference(f)) { + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + std::set result; + std::vector returns = Function::findReturns(f); + for (const Token* returnTok : returns) { + if (returnTok == tok) + continue; + for (const ReferenceToken& rt : + followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) { + const Variable* argvar = rt.token->variable(); + if (!argvar) { + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { + const int n = getArgumentPos(argvar, f); + if (n < 0) { refs_result.push_back({tok, std::move(errors)}); return refs_result; } - if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { - const int n = getArgumentPos(argvar, f); - if (n < 0) { - refs_result.push_back({tok, std::move(errors)}); - return refs_result; - } - std::vector args = getArguments(tok->previous()); - if (n >= args.size()) { - refs_result.push_back({tok, std::move(errors)}); - return refs_result; - } - const Token* argTok = args[n]; - ErrorPath er = errors; - er.emplace_back(returnTok, "Return reference."); - er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); - auto refs = - followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size()); - result.insert(refs.cbegin(), refs.cend()); - if (!inconclusive && result.size() > 1) { - refs_result.push_back({tok, std::move(errors)}); - return refs_result; - } + std::vector args = getArguments(tok->previous()); + if (n >= args.size()) { + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + const Token* argTok = args[n]; + ErrorPath er = errors; + er.emplace_back(returnTok, "Return reference."); + er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); + auto refs = + followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size()); + result.insert(refs.cbegin(), refs.cend()); + if (!inconclusive && result.size() > 1) { + refs_result.push_back({tok, std::move(errors)}); + return refs_result; } } } - if (!result.empty()) { - refs_result.insert(refs_result.end(), result.cbegin(), result.cend()); - return refs_result; - } + } + if (!result.empty()) { + refs_result.insert(refs_result.end(), result.cbegin(), result.cend()); + return refs_result; } } refs_result.push_back({tok, std::move(errors)}); @@ -1583,8 +1581,8 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2 } } // templates/casts - if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) || - (Token::Match(tok2, "%name% <") && tok2->next()->link())) { + if ((tok1->next() && tok1->next()->link() && Token::Match(tok1, "%name% <")) || + (tok2->next() && tok2->next()->link() && Token::Match(tok2, "%name% <"))) { // non-const template function that is not a dynamic_cast => return false if (pure && Token::simpleMatch(tok1->next()->link(), "> (") && diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 524f60478..bfdf1eba6 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -471,9 +471,17 @@ static const Token *getCastTypeStartToken(const Token *parent, const Settings* s return parent->astOperand1(); if (parent->str() != "(") return nullptr; - if (!parent->astOperand2() && Token::Match(parent, "( %name%") && - (parent->next()->isStandardType() || settings->library.isNotLibraryFunction(parent->next()))) - return parent->next(); + if (!parent->astOperand2() && Token::Match(parent, "( %name%|::")) { + const Token* ftok = parent->next(); + if (ftok->isStandardType()) + return ftok; + if (Token::simpleMatch(ftok, "::")) + ftok = ftok->next(); + while (Token::Match(ftok, "%name% ::")) + ftok = ftok->tokAt(2); + if (settings->library.isNotLibraryFunction(ftok)) + return parent->next(); + } if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) return parent->astOperand1()->tokAt(2); return nullptr; @@ -7376,7 +7384,7 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat if (!function) continue; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { - if (!Token::Match(tok, "%name% (")) + if (tok->isKeyword() || !Token::Match(tok, "%name% (")) continue; const Function * const calledFunction = tok->function(); diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 3eb781b13..bc7c3aee5 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #ifdef __cpp_lib_span @@ -4561,6 +4562,13 @@ void stdbind() auto f2 = std::bind(stdbind_helper, 10); } +int stdexchange() { + int i; + // cppcheck-suppress uninitvar + int j = std::exchange(i, 5); + return j; +} + class A { std::vector m_str; diff --git a/test/testcondition.cpp b/test/testcondition.cpp index f46378a7b..f6b261adc 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -4492,6 +4492,16 @@ private: " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (style) Condition 'o[1]=='\\0'' is always false\n", errout.str()); + + check("void f(int x) {\n" // #11449 + " int i = x;\n" + " i = (std::min)(i, 1);\n" + " if (i == 1) {}\n" + " int j = x;\n" + " j = (::std::min)(j, 1);\n" + " if (j == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void alwaysTrueSymbolic()