Use library to track container lifetimes
This commit is contained in:
parent
9f7f446c59
commit
ef714225bb
|
@ -106,7 +106,7 @@ bool astIsIterator(const Token *tok)
|
||||||
|
|
||||||
bool astIsContainer(const Token *tok)
|
bool astIsContainer(const Token *tok)
|
||||||
{
|
{
|
||||||
return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::CONTAINER;
|
return getLibraryContainer(tok) != nullptr && tok->valueType()->type != ValueType::Type::ITERATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string astCanonicalType(const Token *expr)
|
std::string astCanonicalType(const Token *expr)
|
||||||
|
@ -214,6 +214,22 @@ const Token * nextAfterAstRightmostLeaf(const Token * tok)
|
||||||
return rightmostLeaf->next();
|
return rightmostLeaf->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Token* astParentSkipParens(const Token* tok)
|
||||||
|
{
|
||||||
|
return astParentSkipParens(const_cast<Token*>(tok));
|
||||||
|
}
|
||||||
|
Token* astParentSkipParens(Token* tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
Token * parent = tok->astParent();
|
||||||
|
if (!Token::simpleMatch(parent, "("))
|
||||||
|
return parent;
|
||||||
|
if (parent->link() != nextAfterAstRightmostLeaf(tok))
|
||||||
|
return parent;
|
||||||
|
return astParentSkipParens(parent);
|
||||||
|
}
|
||||||
|
|
||||||
static const Token * getVariableInitExpression(const Variable * var)
|
static const Token * getVariableInitExpression(const Variable * var)
|
||||||
{
|
{
|
||||||
if (!var || !var->declEndToken())
|
if (!var || !var->declEndToken())
|
||||||
|
|
|
@ -81,6 +81,9 @@ const Token * astIsVariableComparison(const Token *tok, const std::string &comp,
|
||||||
|
|
||||||
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
||||||
|
|
||||||
|
Token* astParentSkipParens(Token* tok);
|
||||||
|
const Token* astParentSkipParens(const Token* tok);
|
||||||
|
|
||||||
bool precedes(const Token * tok1, const Token * tok2);
|
bool precedes(const Token * tok1, const Token * tok2);
|
||||||
|
|
||||||
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr);
|
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr);
|
||||||
|
|
|
@ -51,22 +51,6 @@ static const struct CWE CWE788(788U); // Access of Memory Location After End o
|
||||||
static const struct CWE CWE825(825U); // Expired Pointer Dereference
|
static const struct CWE CWE825(825U); // Expired Pointer Dereference
|
||||||
static const struct CWE CWE834(834U); // Excessive Iteration
|
static const struct CWE CWE834(834U); // Excessive Iteration
|
||||||
|
|
||||||
static const Library::Container * getLibraryContainer(const Token * tok)
|
|
||||||
{
|
|
||||||
if (!tok)
|
|
||||||
return nullptr;
|
|
||||||
if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) {
|
|
||||||
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
|
|
||||||
if (!v.isLocalLifetimeValue())
|
|
||||||
continue;
|
|
||||||
return getLibraryContainer(v.tokvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tok->valueType())
|
|
||||||
return nullptr;
|
|
||||||
return tok->valueType()->container;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckStl::outOfBounds()
|
void CheckStl::outOfBounds()
|
||||||
{
|
{
|
||||||
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||||
|
@ -74,9 +58,7 @@ void CheckStl::outOfBounds()
|
||||||
const Library::Container *container = getLibraryContainer(tok);
|
const Library::Container *container = getLibraryContainer(tok);
|
||||||
if (!container)
|
if (!container)
|
||||||
continue;
|
continue;
|
||||||
const Token * parent = tok->astParent();
|
const Token * parent = astParentSkipParens(tok);
|
||||||
while (Token::simpleMatch(parent, "(") && !Token::Match(parent->previous(), "%name%"))
|
|
||||||
parent = parent->astParent();
|
|
||||||
for (const ValueFlow::Value &value : tok->values()) {
|
for (const ValueFlow::Value &value : tok->values()) {
|
||||||
if (!value.isContainerSizeValue())
|
if (!value.isContainerSizeValue())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1361,3 +1361,23 @@ bool Library::isSmartPointer(const Token *tok) const
|
||||||
return smartPointers.find(typestr) != smartPointers.end();
|
return smartPointers.find(typestr) != smartPointers.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
// TODO: Support dereferencing iterators
|
||||||
|
// TODO: Support dereferencing with ->
|
||||||
|
if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) {
|
||||||
|
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
|
||||||
|
if (!v.isLocalLifetimeValue())
|
||||||
|
continue;
|
||||||
|
if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
|
||||||
|
continue;
|
||||||
|
return getLibraryContainer(v.tokvalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tok->valueType())
|
||||||
|
return nullptr;
|
||||||
|
return tok->valueType()->container;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -565,6 +565,8 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok);
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
#endif // libraryH
|
#endif // libraryH
|
||||||
|
|
|
@ -3445,31 +3445,25 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
|
||||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||||
}
|
}
|
||||||
// container lifetimes
|
// container lifetimes
|
||||||
else if (tok->variable() &&
|
else if (astIsContainer(tok)) {
|
||||||
Token::Match(tok, "%var% . begin|cbegin|rbegin|crbegin|end|cend|rend|crend|data|c_str|find|insert (") &&
|
Token * parent = astParentSkipParens(tok);
|
||||||
tok->next()->originalName() != "->") {
|
if (!Token::Match(parent, ". %name% ("))
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "find") && !astIsIterator(tok->tokAt(3)))
|
|
||||||
continue;
|
|
||||||
ErrorPath errorPath;
|
|
||||||
const Library::Container * container = settings->library.detectContainer(tok->variable()->typeStartToken());
|
|
||||||
if (!container)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool isIterator = !Token::Match(tok->tokAt(2), "data|c_str");
|
LifetimeStore ls;
|
||||||
if (isIterator)
|
|
||||||
errorPath.emplace_back(tok, "Iterator to container is created here.");
|
if (astIsIterator(parent->tokAt(2)))
|
||||||
|
ls = LifetimeStore{tok, "Iterator to container is created here.", ValueFlow::Value::LifetimeKind::Iterator};
|
||||||
|
else if (astIsPointer(parent->tokAt(2)) || Token::Match(parent->next(), "data|c_str"))
|
||||||
|
ls = LifetimeStore{tok, "Pointer to container is created here.", ValueFlow::Value::LifetimeKind::Object};
|
||||||
else
|
else
|
||||||
errorPath.emplace_back(tok, "Pointer to container is created here.");
|
continue;
|
||||||
|
|
||||||
ValueFlow::Value value;
|
// Dereferencing
|
||||||
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
|
if (tok->isUnaryOp("*") || parent->originalName() == "->")
|
||||||
value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local;
|
ls.byDerefCopy(parent->tokAt(2), tokenlist, errorLogger, settings);
|
||||||
value.tokvalue = tok;
|
else
|
||||||
value.errorPath = errorPath;
|
ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings);
|
||||||
value.lifetimeKind = isIterator ? ValueFlow::Value::LifetimeKind::Iterator : ValueFlow::Value::LifetimeKind::Object;
|
|
||||||
setTokenValue(tok->tokAt(3), value, tokenlist->getSettings());
|
|
||||||
|
|
||||||
valueFlowForwardLifetime(tok->tokAt(3), tokenlist, errorLogger, settings);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// Check constructors
|
// Check constructors
|
||||||
|
|
|
@ -1745,7 +1745,7 @@ private:
|
||||||
" ints.erase(iter);\n"
|
" ints.erase(iter);\n"
|
||||||
" ints.erase(iter);\n"
|
" ints.erase(iter);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:1] -> [test.cpp:6]: (error) Using iterator to local container 'ints' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4] -> [test.cpp:5] -> [test.cpp:1] -> [test.cpp:6]: (error) Using iterator to local container 'ints' that may be invalid.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void eraseByValue() {
|
void eraseByValue() {
|
||||||
|
@ -1818,14 +1818,14 @@ private:
|
||||||
" m_ImplementationMap.erase(something(unknown));\n" // All iterators become invalidated when erasing from std::vector
|
" m_ImplementationMap.erase(something(unknown));\n" // All iterators become invalidated when erasing from std::vector
|
||||||
" m_ImplementationMap.erase(aIt);\n"
|
" m_ImplementationMap.erase(aIt);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("void f(const std::vector<int>& m_ImplementationMap) {\n"
|
check("void f(const std::vector<int>& m_ImplementationMap) {\n"
|
||||||
" std::vector<int>::iterator aIt = m_ImplementationMap.begin();\n"
|
" std::vector<int>::iterator aIt = m_ImplementationMap.begin();\n"
|
||||||
" m_ImplementationMap.erase(*aIt);\n" // All iterators become invalidated when erasing from std::vector
|
" m_ImplementationMap.erase(*aIt);\n" // All iterators become invalidated when erasing from std::vector
|
||||||
" m_ImplementationMap.erase(aIt);\n"
|
" m_ImplementationMap.erase(aIt);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("void f(const std::vector<int>& m_ImplementationMap) {\n"
|
check("void f(const std::vector<int>& m_ImplementationMap) {\n"
|
||||||
" std::vector<int>::iterator aIt = m_ImplementationMap.begin();\n"
|
" std::vector<int>::iterator aIt = m_ImplementationMap.begin();\n"
|
||||||
|
@ -1833,7 +1833,7 @@ private:
|
||||||
" m_ImplementationMap.erase(*bIt);\n" // All iterators become invalidated when erasing from std::vector
|
" m_ImplementationMap.erase(*bIt);\n" // All iterators become invalidated when erasing from std::vector
|
||||||
" aIt = m_ImplementationMap.erase(aIt);\n"
|
" aIt = m_ImplementationMap.erase(aIt);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'm_ImplementationMap' that may be invalid.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushback1() {
|
void pushback1() {
|
||||||
|
@ -1843,7 +1843,7 @@ private:
|
||||||
" foo.push_back(123);\n"
|
" foo.push_back(123);\n"
|
||||||
" *it;\n"
|
" *it;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'foo' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'foo' that may be invalid.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushback2() {
|
void pushback2() {
|
||||||
|
@ -1988,7 +1988,7 @@ private:
|
||||||
" foo.reserve(100);\n"
|
" foo.reserve(100);\n"
|
||||||
" *it = 0;\n"
|
" *it = 0;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'foo' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'foo' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
// in loop
|
// in loop
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
|
@ -2051,7 +2051,7 @@ private:
|
||||||
" ints.insert(ints.begin(), 1);\n"
|
" ints.insert(ints.begin(), 1);\n"
|
||||||
" ++iter;\n"
|
" ++iter;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'ints' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5]: (error) Using iterator to local container 'ints' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -2076,14 +2076,14 @@ private:
|
||||||
" void* v = &i->foo;\n"
|
" void* v = &i->foo;\n"
|
||||||
" return v;\n"
|
" return v;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("Foo f(const std::vector<Bar>& bars) {\n"
|
check("Foo f(const std::vector<Bar>& bars) {\n"
|
||||||
" std::vector<Bar>::iterator i = bars.begin();\n"
|
" std::vector<Bar>::iterator i = bars.begin();\n"
|
||||||
" bars.insert(Bar());\n"
|
" bars.insert(Bar());\n"
|
||||||
" return i->foo;\n"
|
" return i->foo;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("void f(const std::vector<Bar>& bars) {\n"
|
check("void f(const std::vector<Bar>& bars) {\n"
|
||||||
" for(std::vector<Bar>::iterator i = bars.begin(); i != bars.end(); ++i) {\n"
|
" for(std::vector<Bar>::iterator i = bars.begin(); i != bars.end(); ++i) {\n"
|
||||||
|
@ -2098,7 +2098,7 @@ private:
|
||||||
" i = bars.insert(i, bar);\n"
|
" i = bars.insert(i, bar);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:2]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:2]: (error) Using iterator to local container 'bars' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("void* f(const std::vector<Bar>& bars) {\n"
|
check("void* f(const std::vector<Bar>& bars) {\n"
|
||||||
" std::vector<Bar>::iterator i = bars.begin();\n"
|
" std::vector<Bar>::iterator i = bars.begin();\n"
|
||||||
|
@ -2107,7 +2107,7 @@ private:
|
||||||
" void* v = &i->foo;\n"
|
" void* v = &i->foo;\n"
|
||||||
" return v;\n"
|
" return v;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:6]: (error) Using pointer to local variable 'bars' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:1] -> [test.cpp:6]: (error) Using pointer to local variable 'bars' that may be invalid.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert2() {
|
void insert2() {
|
||||||
|
@ -3877,7 +3877,7 @@ private:
|
||||||
" std::cout << *v0 << std::endl;\n"
|
" std::cout << *v0 << std::endl;\n"
|
||||||
"}\n",
|
"}\n",
|
||||||
true);
|
true);
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'v' that may be invalid.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4]: (error) Using iterator to local container 'v' that may be invalid.\n", errout.str());
|
||||||
|
|
||||||
check("std::string e();\n"
|
check("std::string e();\n"
|
||||||
"void a() {\n"
|
"void a() {\n"
|
||||||
|
|
Loading…
Reference in New Issue