Fix issue 9548: False negative: Mismatching iterators when inserting into a vector (#2595)
This commit is contained in:
parent
99ff04f617
commit
86ed860d26
|
@ -27,6 +27,7 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "astutils.h"
|
#include "astutils.h"
|
||||||
#include "pathanalysis.h"
|
#include "pathanalysis.h"
|
||||||
|
#include "valueflow.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -534,6 +535,18 @@ void CheckStl::iterators()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckStl::mismatchingContainerIteratorError(const Token* tok, const Token* iterTok)
|
||||||
|
{
|
||||||
|
const std::string container(tok ? tok->expressionString() : std::string("v1"));
|
||||||
|
const std::string iter(iterTok ? iterTok->expressionString() : std::string("it"));
|
||||||
|
reportError(tok,
|
||||||
|
Severity::error,
|
||||||
|
"mismatchingContainerIterator",
|
||||||
|
"Iterator '" + iter + "' from different container '" + container + "' are used together.",
|
||||||
|
CWE664,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
// Error message for bad iterator usage..
|
// Error message for bad iterator usage..
|
||||||
void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2)
|
void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2)
|
||||||
{
|
{
|
||||||
|
@ -708,6 +721,49 @@ void CheckStl::mismatchingContainers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckStl::mismatchingContainerIterator()
|
||||||
|
{
|
||||||
|
// Check if different containers are used in various calls of standard functions
|
||||||
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||||
|
for (const Scope * scope : symbolDatabase->functionScopes) {
|
||||||
|
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!astIsContainer(tok))
|
||||||
|
continue;
|
||||||
|
if (!Token::Match(tok, "%var% . %name% ( !!)"))
|
||||||
|
continue;
|
||||||
|
const Token * const ftok = tok->tokAt(2);
|
||||||
|
const std::vector<const Token *> args = getArguments(ftok);
|
||||||
|
|
||||||
|
const Library::Container * c = tok->valueType()->container;
|
||||||
|
Library::Container::Action action = c->getAction(tok->strAt(2));
|
||||||
|
const Token* iterTok = nullptr;
|
||||||
|
if (action == Library::Container::Action::INSERT && args.size() == 2) {
|
||||||
|
// Skip if iterator pair
|
||||||
|
if (astIsIterator(args.back()))
|
||||||
|
continue;
|
||||||
|
if (!astIsIterator(args.front()))
|
||||||
|
continue;
|
||||||
|
iterTok = args.front();
|
||||||
|
} else if (action == Library::Container::Action::ERASE) {
|
||||||
|
if (!astIsIterator(args.front()))
|
||||||
|
continue;
|
||||||
|
iterTok = args.front();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueFlow::Value val = getLifetimeObjValue(iterTok);
|
||||||
|
if (!val.tokvalue)
|
||||||
|
continue;
|
||||||
|
if (val.lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator)
|
||||||
|
continue;
|
||||||
|
if (isSameExpression(true, false, tok, val.tokvalue, mSettings->library, false, false))
|
||||||
|
continue;
|
||||||
|
mismatchingContainerIteratorError(tok, iterTok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool isInvalidMethod(const Token * tok)
|
static bool isInvalidMethod(const Token * tok)
|
||||||
{
|
{
|
||||||
if (Token::Match(tok->next(), ". assign|clear|swap"))
|
if (Token::Match(tok->next(), ". assign|clear|swap"))
|
||||||
|
|
|
@ -80,6 +80,7 @@ public:
|
||||||
checkStl.invalidContainer();
|
checkStl.invalidContainer();
|
||||||
checkStl.invalidContainerLoop();
|
checkStl.invalidContainerLoop();
|
||||||
checkStl.mismatchingContainers();
|
checkStl.mismatchingContainers();
|
||||||
|
checkStl.mismatchingContainerIterator();
|
||||||
|
|
||||||
checkStl.stlBoundaries();
|
checkStl.stlBoundaries();
|
||||||
checkStl.checkDereferenceInvalidIterator();
|
checkStl.checkDereferenceInvalidIterator();
|
||||||
|
@ -123,6 +124,8 @@ public:
|
||||||
*/
|
*/
|
||||||
void mismatchingContainers();
|
void mismatchingContainers();
|
||||||
|
|
||||||
|
void mismatchingContainerIterator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dangerous usage of erase. The iterator is invalidated by erase so
|
* Dangerous usage of erase. The iterator is invalidated by erase so
|
||||||
* it is bad to dereference it after the erase.
|
* it is bad to dereference it after the erase.
|
||||||
|
@ -201,6 +204,7 @@ private:
|
||||||
void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2);
|
void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2);
|
||||||
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2);
|
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2);
|
||||||
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName);
|
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName);
|
||||||
|
void mismatchingContainerIteratorError(const Token* tok, const Token* iterTok);
|
||||||
void mismatchingContainersError(const Token* tok1, const Token* tok2);
|
void mismatchingContainersError(const Token* tok1, const Token* tok2);
|
||||||
void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2);
|
void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2);
|
||||||
void sameIteratorExpressionError(const Token *tok);
|
void sameIteratorExpressionError(const Token *tok);
|
||||||
|
@ -235,6 +239,7 @@ private:
|
||||||
c.iteratorsError(nullptr, nullptr, "container");
|
c.iteratorsError(nullptr, nullptr, "container");
|
||||||
c.invalidContainerLoopError(nullptr, nullptr);
|
c.invalidContainerLoopError(nullptr, nullptr);
|
||||||
c.invalidContainerError(nullptr, nullptr, nullptr, errorPath);
|
c.invalidContainerError(nullptr, nullptr, nullptr, errorPath);
|
||||||
|
c.mismatchingContainerIteratorError(nullptr, nullptr);
|
||||||
c.mismatchingContainersError(nullptr, nullptr);
|
c.mismatchingContainersError(nullptr, nullptr);
|
||||||
c.mismatchingContainerExpressionError(nullptr, nullptr);
|
c.mismatchingContainerExpressionError(nullptr, nullptr);
|
||||||
c.sameIteratorExpressionError(nullptr);
|
c.sameIteratorExpressionError(nullptr);
|
||||||
|
|
|
@ -70,6 +70,7 @@ private:
|
||||||
TEST_CASE(iterator25); // #9742
|
TEST_CASE(iterator25); // #9742
|
||||||
TEST_CASE(iteratorExpression);
|
TEST_CASE(iteratorExpression);
|
||||||
TEST_CASE(iteratorSameExpression);
|
TEST_CASE(iteratorSameExpression);
|
||||||
|
TEST_CASE(mismatchingContainerIterator);
|
||||||
|
|
||||||
TEST_CASE(dereference);
|
TEST_CASE(dereference);
|
||||||
TEST_CASE(dereference_break); // #3644 - handle "break"
|
TEST_CASE(dereference_break); // #3644 - handle "break"
|
||||||
|
@ -1277,6 +1278,21 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:2]: (style) Same iterators expression are used for algorithm.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:2]: (style) Same iterators expression are used for algorithm.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mismatchingContainerIterator() {
|
||||||
|
check("std::vector<int> to_vector(int value) {\n"
|
||||||
|
" std::vector<int> a, b;\n"
|
||||||
|
" a.insert(b.end(), value);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Iterator 'b.end()' from different container 'a' are used together.\n", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<int> f(std::vector<int> a, std::vector<int> b) {\n"
|
||||||
|
" a.erase(b.begin());\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (error) Iterator 'b.begin()' from different container 'a' are used together.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
// Dereferencing invalid pointer
|
// Dereferencing invalid pointer
|
||||||
void dereference() {
|
void dereference() {
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
|
|
Loading…
Reference in New Issue