Skip to content

Commit 3e09503

Browse files
authored
Fix 11087: false negative: usage of reference to member of temporary object not detected (#4217)
* Fix 11087: false negative: usage of reference to member of temporary object not detected * Format * Add another test case * Fix FP with pointer * Format
1 parent 9cecc84 commit 3e09503

2 files changed

Lines changed: 90 additions & 1 deletion

File tree

lib/valueflow.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,31 @@ std::vector<ValueFlow::Value> getLifetimeObjValues(const Token* tok, bool inconc
32473247
return result;
32483248
}
32493249

3250+
static bool hasUniqueOwnership(const Token* tok)
3251+
{
3252+
if (astIsPointer(tok))
3253+
return false;
3254+
if (astIsUniqueSmartPointer(tok))
3255+
return true;
3256+
if (astIsContainerOwned(tok))
3257+
return true;
3258+
return false;
3259+
}
3260+
3261+
// Check if dereferencing an object that doesn't have unique ownership
3262+
static bool derefShared(const Token* tok)
3263+
{
3264+
if (!tok)
3265+
return false;
3266+
if (tok->str() == "." && tok->originalName() != "->") {
3267+
return false;
3268+
} else if (!tok->isUnaryOp("*") && tok->str() == "[") {
3269+
return false;
3270+
}
3271+
const Token* ptrTok = tok->astOperand1();
3272+
return !hasUniqueOwnership(ptrTok);
3273+
}
3274+
32503275
ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive)
32513276
{
32523277
std::vector<ValueFlow::Value> values = getLifetimeObjValues(tok, inconclusive);
@@ -3320,6 +3345,7 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
33203345
const Variable* argvar = argvarTok->variable();
33213346
if (!argvar)
33223347
continue;
3348+
const Token* argTok = nullptr;
33233349
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
33243350
int n = getArgumentPos(argvar, f);
33253351
if (n < 0)
@@ -3328,9 +3354,17 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
33283354
// TODO: Track lifetimes of default parameters
33293355
if (n >= args.size())
33303356
return std::vector<LifetimeToken> {};
3331-
const Token* argTok = args[n];
3357+
argTok = args[n];
33323358
lt.errorPath.emplace_back(returnTok, "Return reference.");
33333359
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
3360+
} else if (Token::Match(tok->tokAt(-2), ". %name% (") && !derefShared(tok->tokAt(-2)) &&
3361+
exprDependsOnThis(argvarTok)) {
3362+
argTok = tok->tokAt(-2)->astOperand1();
3363+
lt.errorPath.emplace_back(returnTok, "Return reference that depends on 'this'.");
3364+
lt.errorPath.emplace_back(tok->previous(),
3365+
"Calling member function on '" + argTok->expressionString() + "'.");
3366+
}
3367+
if (argTok) {
33343368
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
33353369
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), pred, depth - returns.size()),
33363370
returns.size() > 1);

test/testautovariables.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,6 +1940,61 @@ class TestAutoVariables : public TestFixture {
19401940
" g(std::move(v));\n"
19411941
"}\n");
19421942
ASSERT_EQUALS("", errout.str());
1943+
1944+
// #11087
1945+
check("struct S1 {\n"
1946+
" int& get() { return val; }\n"
1947+
" int val{42};\n"
1948+
"};\n"
1949+
"void f() {\n"
1950+
" int& v = S1().get();\n"
1951+
" v += 1;\n"
1952+
"}\n");
1953+
ASSERT_EQUALS(
1954+
"[test.cpp:6] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:7]: (error) Using reference to dangling temporary.\n",
1955+
errout.str());
1956+
1957+
check("struct A {\n"
1958+
" const int& g() const { return i; }\n"
1959+
" int i;\n"
1960+
"};\n"
1961+
"A* a();\n"
1962+
"int f() {\n"
1963+
" const int& i = a()->g();\n"
1964+
" return i;\n"
1965+
"}\n");
1966+
ASSERT_EQUALS("", errout.str());
1967+
1968+
check("struct A {\n"
1969+
" const int& g() const { return i; }\n"
1970+
" int i;\n"
1971+
"};\n"
1972+
"std::unique_ptr<A> a();\n"
1973+
"int f() {\n"
1974+
" const int& i = a()->g();\n"
1975+
" return i;\n"
1976+
"}\n");
1977+
ASSERT_EQUALS(
1978+
"[test.cpp:7] -> [test.cpp:2] -> [test.cpp:7] -> [test.cpp:8]: (error) Using reference to dangling temporary.\n",
1979+
errout.str());
1980+
1981+
check("struct S1 {\n"
1982+
" auto get() -> auto& { return val; }\n"
1983+
" int val{42};\n"
1984+
"};\n"
1985+
"struct S2 {\n"
1986+
" auto get() -> S1 { return s; }\n"
1987+
" S1 s;\n"
1988+
"};\n"
1989+
"auto main() -> int {\n"
1990+
" S2 c{};\n"
1991+
" auto& v = c.get().get();\n"
1992+
" v += 1;\n"
1993+
" return c.s.val;\n"
1994+
"}\n");
1995+
ASSERT_EQUALS(
1996+
"[test.cpp:11] -> [test.cpp:2] -> [test.cpp:11] -> [test.cpp:12]: (error) Using reference to dangling temporary.\n",
1997+
errout.str());
19431998
}
19441999

19452000
void testglobalnamespace() {

0 commit comments

Comments
 (0)