Fix 10984: False positive: invalidContainer when explictly capturing vector by reference (#4064)
This commit is contained in:
parent
4e7125554f
commit
3e686103ae
|
@ -1035,6 +1035,37 @@ static const Token* getLoopContainer(const Token* tok)
|
||||||
return sepTok->astOperand2();
|
return sepTok->astOperand2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const ValueFlow::Value* getInnerLifetime(const Token* tok,
|
||||||
|
nonneg int id,
|
||||||
|
ErrorPath* errorPath = nullptr,
|
||||||
|
int depth = 4)
|
||||||
|
{
|
||||||
|
if (depth < 0)
|
||||||
|
return nullptr;
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
for (const ValueFlow::Value& val : tok->values()) {
|
||||||
|
if (!val.isLocalLifetimeValue())
|
||||||
|
continue;
|
||||||
|
if (contains({ValueFlow::Value::LifetimeKind::Address,
|
||||||
|
ValueFlow::Value::LifetimeKind::SubObject,
|
||||||
|
ValueFlow::Value::LifetimeKind::Lambda},
|
||||||
|
val.lifetimeKind)) {
|
||||||
|
if (val.capturetok)
|
||||||
|
return getInnerLifetime(val.capturetok, id, errorPath, depth - 1);
|
||||||
|
if (errorPath)
|
||||||
|
errorPath->insert(errorPath->end(), val.errorPath.begin(), val.errorPath.end());
|
||||||
|
return getInnerLifetime(val.tokvalue, id, errorPath, depth - 1);
|
||||||
|
}
|
||||||
|
if (!val.tokvalue->variable())
|
||||||
|
continue;
|
||||||
|
if (val.tokvalue->varId() != id)
|
||||||
|
continue;
|
||||||
|
return &val;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckStl::invalidContainer()
|
void CheckStl::invalidContainer()
|
||||||
{
|
{
|
||||||
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||||
|
@ -1121,25 +1152,14 @@ void CheckStl::invalidContainer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const ValueFlow::Value& val : info.tok->values()) {
|
|
||||||
if (!val.isLocalLifetimeValue())
|
|
||||||
continue;
|
|
||||||
if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address)
|
|
||||||
continue;
|
|
||||||
if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::SubObject)
|
|
||||||
continue;
|
|
||||||
if (!val.tokvalue->variable())
|
|
||||||
continue;
|
|
||||||
if (val.tokvalue->varId() != r.tok->varId())
|
|
||||||
continue;
|
|
||||||
ErrorPath ep;
|
ErrorPath ep;
|
||||||
|
const ValueFlow::Value* val = getInnerLifetime(info.tok, r.tok->varId(), &ep);
|
||||||
// Check the iterator is created before the change
|
// Check the iterator is created before the change
|
||||||
if (val.tokvalue != tok && reaches(val.tokvalue, tok, library, &ep)) {
|
if (val && val->tokvalue != tok && reaches(val->tokvalue, tok, library, &ep)) {
|
||||||
v = &val;
|
v = val;
|
||||||
errorPath = ep;
|
errorPath = ep;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (!info.tok)
|
if (!info.tok)
|
||||||
|
|
|
@ -3758,6 +3758,7 @@ struct LifetimeStore {
|
||||||
ValueFlow::Value value;
|
ValueFlow::Value value;
|
||||||
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
|
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
|
||||||
value.tokvalue = lt.token;
|
value.tokvalue = lt.token;
|
||||||
|
value.capturetok = argtok;
|
||||||
value.errorPath = er;
|
value.errorPath = er;
|
||||||
value.lifetimeKind = type;
|
value.lifetimeKind = type;
|
||||||
value.setInconclusive(inconclusive || lt.inconclusive);
|
value.setInconclusive(inconclusive || lt.inconclusive);
|
||||||
|
@ -3796,6 +3797,7 @@ struct LifetimeStore {
|
||||||
value.lifetimeScope = v.lifetimeScope;
|
value.lifetimeScope = v.lifetimeScope;
|
||||||
value.path = v.path;
|
value.path = v.path;
|
||||||
value.tokvalue = lt.token;
|
value.tokvalue = lt.token;
|
||||||
|
value.capturetok = argtok;
|
||||||
value.errorPath = std::move(er);
|
value.errorPath = std::move(er);
|
||||||
value.lifetimeKind = type;
|
value.lifetimeKind = type;
|
||||||
value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive);
|
value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive);
|
||||||
|
@ -8272,6 +8274,7 @@ ValueFlow::Value::Value(const Token* c, long long val, Bound b)
|
||||||
path(0),
|
path(0),
|
||||||
wideintvalue(0),
|
wideintvalue(0),
|
||||||
subexpressions(),
|
subexpressions(),
|
||||||
|
capturetok(nullptr),
|
||||||
lifetimeKind(LifetimeKind::Object),
|
lifetimeKind(LifetimeKind::Object),
|
||||||
lifetimeScope(LifetimeScope::Local),
|
lifetimeScope(LifetimeScope::Local),
|
||||||
valueKind(ValueKind::Possible)
|
valueKind(ValueKind::Possible)
|
||||||
|
|
|
@ -105,6 +105,7 @@ namespace ValueFlow {
|
||||||
path(0),
|
path(0),
|
||||||
wideintvalue(val),
|
wideintvalue(val),
|
||||||
subexpressions(),
|
subexpressions(),
|
||||||
|
capturetok(nullptr),
|
||||||
lifetimeKind(LifetimeKind::Object),
|
lifetimeKind(LifetimeKind::Object),
|
||||||
lifetimeScope(LifetimeScope::Local),
|
lifetimeScope(LifetimeScope::Local),
|
||||||
valueKind(ValueKind::Possible)
|
valueKind(ValueKind::Possible)
|
||||||
|
@ -364,6 +365,9 @@ namespace ValueFlow {
|
||||||
|
|
||||||
std::vector<std::string> subexpressions;
|
std::vector<std::string> subexpressions;
|
||||||
|
|
||||||
|
// Set to where a lifetime is captured by value
|
||||||
|
const Token* capturetok;
|
||||||
|
|
||||||
enum class LifetimeKind {
|
enum class LifetimeKind {
|
||||||
// Pointer points to a member of lifetime
|
// Pointer points to a member of lifetime
|
||||||
Object,
|
Object,
|
||||||
|
|
|
@ -5274,6 +5274,68 @@ private:
|
||||||
"}\n",
|
"}\n",
|
||||||
true);
|
true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// #10984
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::vector<int> v;\n"
|
||||||
|
" auto g = [&v]{};\n"
|
||||||
|
" v.push_back(1);\n"
|
||||||
|
" g();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f(std::vector<int> v) {\n"
|
||||||
|
" auto it = v.begin();\n"
|
||||||
|
" auto g = [&]{ std::cout << *it << std::endl;};\n"
|
||||||
|
" v.push_back(1);\n"
|
||||||
|
" g();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:2] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'v' that may be invalid.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("void f(std::vector<int> v) {\n"
|
||||||
|
" auto it = v.begin();\n"
|
||||||
|
" auto g = [=]{ std::cout << *it << std::endl;};\n"
|
||||||
|
" v.push_back(1);\n"
|
||||||
|
" g();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:2] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'v' that may be invalid.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" int* p;\n"
|
||||||
|
" void g();\n"
|
||||||
|
"};\n"
|
||||||
|
"void f(std::vector<int> v) {\n"
|
||||||
|
" auto it = v.begin();\n"
|
||||||
|
" A a{v.data()};\n"
|
||||||
|
" v.push_back(1);\n"
|
||||||
|
" a.g();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:7] -> [test.cpp:8] -> [test.cpp:5] -> [test.cpp:9]: (error) Using object that points to local variable 'v' that may be invalid.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" int*& p;\n"
|
||||||
|
" void g();\n"
|
||||||
|
"};\n"
|
||||||
|
"void f(std::vector<int> v) {\n"
|
||||||
|
" auto* p = v.data();\n"
|
||||||
|
" A a{p};\n"
|
||||||
|
" v.push_back(1);\n"
|
||||||
|
" a.g();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:6] -> [test.cpp:7] -> [test.cpp:8] -> [test.cpp:5] -> [test.cpp:9]: (error) Using object that points to local variable 'v' that may be invalid.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidContainerLoop() {
|
void invalidContainerLoop() {
|
||||||
|
|
Loading…
Reference in New Issue