Skip to content

Commit 4912990

Browse files
Amend fix for #11449 Function call not recognized (#4838)
1 parent 7c73ecf commit 4912990

5 files changed

Lines changed: 85 additions & 47 deletions

File tree

cfg/std.cfg

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6789,6 +6789,20 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
67896789
<not-uninit/>
67906790
</arg>
67916791
</function>
6792+
<!-- https://en.cppreference.com/w/cpp/utility/exchange -->
6793+
<!-- template< class T, class U = T > constexpr T exchange( T& obj, U&& new_value ) -->
6794+
<function name="std::exchange">
6795+
<leak-ignore/>
6796+
<noreturn>false</noreturn>
6797+
<use-retval/>
6798+
<returnValue>arg1</returnValue>
6799+
<arg nr="1">
6800+
<not-uninit/>
6801+
</arg>
6802+
<arg nr="2">
6803+
<not-uninit/>
6804+
</arg>
6805+
</function>
67926806
<!-- https://en.cppreference.com/w/cpp/memory/addressof -->
67936807
<!-- template< class T > constexpr T* addressof( T& arg ) noexcept; -->
67946808
<function name="std::addressof">

lib/astutils.cpp

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,54 +1220,52 @@ SmallVector<ReferenceToken> followAllReferences(const Token* tok,
12201220
return refs_result;
12211221
}
12221222

1223-
} else if (Token::Match(tok->previous(), "%name% (")) {
1223+
} else if (tok->previous() && tok->previous()->function() && Token::Match(tok->previous(), "%name% (")) {
12241224
const Function *f = tok->previous()->function();
1225-
if (f) {
1226-
if (!Function::returnsReference(f)) {
1227-
refs_result.push_back({tok, std::move(errors)});
1228-
return refs_result;
1229-
}
1230-
std::set<ReferenceToken, ReferenceTokenLess> result;
1231-
std::vector<const Token*> returns = Function::findReturns(f);
1232-
for (const Token* returnTok : returns) {
1233-
if (returnTok == tok)
1234-
continue;
1235-
for (const ReferenceToken& rt :
1236-
followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) {
1237-
const Variable* argvar = rt.token->variable();
1238-
if (!argvar) {
1225+
if (!Function::returnsReference(f)) {
1226+
refs_result.push_back({tok, std::move(errors)});
1227+
return refs_result;
1228+
}
1229+
std::set<ReferenceToken, ReferenceTokenLess> result;
1230+
std::vector<const Token*> returns = Function::findReturns(f);
1231+
for (const Token* returnTok : returns) {
1232+
if (returnTok == tok)
1233+
continue;
1234+
for (const ReferenceToken& rt :
1235+
followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) {
1236+
const Variable* argvar = rt.token->variable();
1237+
if (!argvar) {
1238+
refs_result.push_back({tok, std::move(errors)});
1239+
return refs_result;
1240+
}
1241+
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
1242+
const int n = getArgumentPos(argvar, f);
1243+
if (n < 0) {
12391244
refs_result.push_back({tok, std::move(errors)});
12401245
return refs_result;
12411246
}
1242-
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
1243-
const int n = getArgumentPos(argvar, f);
1244-
if (n < 0) {
1245-
refs_result.push_back({tok, std::move(errors)});
1246-
return refs_result;
1247-
}
1248-
std::vector<const Token*> args = getArguments(tok->previous());
1249-
if (n >= args.size()) {
1250-
refs_result.push_back({tok, std::move(errors)});
1251-
return refs_result;
1252-
}
1253-
const Token* argTok = args[n];
1254-
ErrorPath er = errors;
1255-
er.emplace_back(returnTok, "Return reference.");
1256-
er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
1257-
auto refs =
1258-
followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size());
1259-
result.insert(refs.cbegin(), refs.cend());
1260-
if (!inconclusive && result.size() > 1) {
1261-
refs_result.push_back({tok, std::move(errors)});
1262-
return refs_result;
1263-
}
1247+
std::vector<const Token*> args = getArguments(tok->previous());
1248+
if (n >= args.size()) {
1249+
refs_result.push_back({tok, std::move(errors)});
1250+
return refs_result;
1251+
}
1252+
const Token* argTok = args[n];
1253+
ErrorPath er = errors;
1254+
er.emplace_back(returnTok, "Return reference.");
1255+
er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
1256+
auto refs =
1257+
followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size());
1258+
result.insert(refs.cbegin(), refs.cend());
1259+
if (!inconclusive && result.size() > 1) {
1260+
refs_result.push_back({tok, std::move(errors)});
1261+
return refs_result;
12641262
}
12651263
}
12661264
}
1267-
if (!result.empty()) {
1268-
refs_result.insert(refs_result.end(), result.cbegin(), result.cend());
1269-
return refs_result;
1270-
}
1265+
}
1266+
if (!result.empty()) {
1267+
refs_result.insert(refs_result.end(), result.cbegin(), result.cend());
1268+
return refs_result;
12711269
}
12721270
}
12731271
refs_result.push_back({tok, std::move(errors)});
@@ -1583,8 +1581,8 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
15831581
}
15841582
}
15851583
// templates/casts
1586-
if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) ||
1587-
(Token::Match(tok2, "%name% <") && tok2->next()->link())) {
1584+
if ((tok1->next() && tok1->next()->link() && Token::Match(tok1, "%name% <")) ||
1585+
(tok2->next() && tok2->next()->link() && Token::Match(tok2, "%name% <"))) {
15881586

15891587
// non-const template function that is not a dynamic_cast => return false
15901588
if (pure && Token::simpleMatch(tok1->next()->link(), "> (") &&

lib/valueflow.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,17 @@ static const Token *getCastTypeStartToken(const Token *parent, const Settings* s
471471
return parent->astOperand1();
472472
if (parent->str() != "(")
473473
return nullptr;
474-
if (!parent->astOperand2() && Token::Match(parent, "( %name%") &&
475-
(parent->next()->isStandardType() || settings->library.isNotLibraryFunction(parent->next())))
476-
return parent->next();
474+
if (!parent->astOperand2() && Token::Match(parent, "( %name%|::")) {
475+
const Token* ftok = parent->next();
476+
if (ftok->isStandardType())
477+
return ftok;
478+
if (Token::simpleMatch(ftok, "::"))
479+
ftok = ftok->next();
480+
while (Token::Match(ftok, "%name% ::"))
481+
ftok = ftok->tokAt(2);
482+
if (settings->library.isNotLibraryFunction(ftok))
483+
return parent->next();
484+
}
477485
if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <"))
478486
return parent->astOperand1()->tokAt(2);
479487
return nullptr;
@@ -7376,7 +7384,7 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat
73767384
if (!function)
73777385
continue;
73787386
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
7379-
if (!Token::Match(tok, "%name% ("))
7387+
if (tok->isKeyword() || !Token::Match(tok, "%name% ("))
73807388
continue;
73817389

73827390
const Function * const calledFunction = tok->function();

test/cfg/std.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <string_view>
4141
#include <unordered_map>
4242
#include <unordered_set>
43+
#include <utility>
4344
#include <vector>
4445
#include <version>
4546
#ifdef __cpp_lib_span
@@ -4561,6 +4562,13 @@ void stdbind()
45614562
auto f2 = std::bind(stdbind_helper, 10);
45624563
}
45634564

4565+
int stdexchange() {
4566+
int i;
4567+
// cppcheck-suppress uninitvar
4568+
int j = std::exchange(i, 5);
4569+
return j;
4570+
}
4571+
45644572
class A
45654573
{
45664574
std::vector<std::string> m_str;

test/testcondition.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4492,6 +4492,16 @@ class TestCondition : public TestFixture {
44924492
" }\n"
44934493
"}\n");
44944494
ASSERT_EQUALS("[test.cpp:6]: (style) Condition 'o[1]=='\\0'' is always false\n", errout.str());
4495+
4496+
check("void f(int x) {\n" // #11449
4497+
" int i = x;\n"
4498+
" i = (std::min)(i, 1);\n"
4499+
" if (i == 1) {}\n"
4500+
" int j = x;\n"
4501+
" j = (::std::min)(j, 1);\n"
4502+
" if (j == 1) {}\n"
4503+
"}\n");
4504+
ASSERT_EQUALS("", errout.str());
44954505
}
44964506

44974507
void alwaysTrueSymbolic()

0 commit comments

Comments
 (0)