Improve STL iterators checking (#1380)

* Improve STL interators checking

* Improve error messages for container iterators from different scopes

* Mini refactoring

* Replace hardcoded pattern to ValueType::Type::ITERATOR

* Error messages improvements, more tests and refactoring

* Refactoring after code review

* Put getting operand data into separate function

* Update getErrorMessages and iterator errors ids

* Refactoring

* Fix error

* Refactoring, early return implementation

* Delete redundant code

* Tiny changes in comments
This commit is contained in:
Igor 2018-10-17 07:36:51 +03:00 committed by Daniel Marjamäki
parent c9f768a915
commit 0a9be3e734
3 changed files with 473 additions and 23 deletions

View File

@ -29,6 +29,7 @@
#include <cstddef> #include <cstddef>
#include <list> #include <list>
#include <map>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
@ -158,12 +159,46 @@ void CheckStl::invalidIteratorError(const Token *tok, const std::string &iterato
reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, false); reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, false);
} }
void CheckStl::iteratorsError(const Token *tok, const std::string &container1, const std::string &container2) void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2)
{ {
reportError(tok, Severity::error, "iterators", reportError(tok, Severity::error, "iterators1",
"$symbol:" + container1 + "\n" "$symbol:" + containerName1 + "\n"
"$symbol:" + container2 + "\n" "$symbol:" + containerName2 + "\n"
"Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", CWE664, false); "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false);
}
void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2)
{
std::list<const Token*> callstack = { tok, containerTok };
reportError(callstack, Severity::error, "iterators2",
"$symbol:" + containerName1 + "\n"
"$symbol:" + containerName2 + "\n"
"Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false);
}
void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName)
{
std::list<const Token*> callstack = { tok, containerTok };
reportError(callstack, Severity::error, "iterators3",
"$symbol:" + containerName + "\n"
"Same iterator is used with containers '" + containerName + "' that are defined in different scopes.", CWE664, false);
}
void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2)
{
std::list<const Token*> callstack = { cmpOperatorTok, containerTok1, containerTok2 };
reportError(callstack, Severity::error, "iteratorsCmp1",
"$symbol:" + containerName1 + "\n"
"$symbol:" + containerName2 + "\n"
"Comparison of iterators from containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false);
}
void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName)
{
std::list<const Token*> callstack = { cmpOperatorTok, containerTok1, containerTok2 };
reportError(callstack, Severity::error, "iteratorsCmp2",
"$symbol:" + containerName + "\n"
"Comparison of iterators from containers '" + containerName + "' that are defined in different scopes.", CWE664, false);
} }
// Error message used when dereferencing an iterator that has been erased.. // Error message used when dereferencing an iterator that has been erased..
@ -229,10 +264,52 @@ static std::string getContainerName(const Token *containerToken)
return ret; return ret;
} }
enum OperandPosition
{
Left,
Right
};
static const Token* findIteratorContainer(const Token* start, const Token* end, unsigned int id)
{
const Token* containerToken = nullptr;
for(const Token* tok = start; tok != end; tok = tok->next())
{
if (Token::Match(tok, "%varid% = %name% . %name% (", id))
{
// Iterator is assigned to value
if (tok->tokAt(5)->valueType() && tok->tokAt(5)->valueType()->type == ValueType::Type::ITERATOR)
{
containerToken = tok->tokAt(2);
}
}
else if (Token::Match(tok, "%varid% = %name% (", id))
{
// Prevent FP: iterator is assigned to something
// TODO: Fix it in future
containerToken = nullptr;
}
}
return containerToken;
}
void CheckStl::iterators() void CheckStl::iterators()
{ {
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
// Filling map of iterators id and their scope begin
std::map<unsigned int, const Token*> iteratorScopeBeginInfo;
for (const Variable* var : symbolDatabase->variableList()) {
bool inconclusiveType=false;
if (!isIterator(var, inconclusiveType))
continue;
const unsigned int iteratorId = var->declarationId();
if (iteratorId != 0)
iteratorScopeBeginInfo[iteratorId] = var->nameToken();
}
// Storage to save found comparison problems to avoid duplicate error messages
std::set<const Token*> foundOperatorErrors;
for (const Variable* var : symbolDatabase->variableList()) { for (const Variable* var : symbolDatabase->variableList()) {
bool inconclusiveType=false; bool inconclusiveType=false;
if (!isIterator(var, inconclusiveType)) if (!isIterator(var, inconclusiveType))
@ -269,15 +346,12 @@ void CheckStl::iterators()
invalidationScope = nullptr; invalidationScope = nullptr;
} }
// Is iterator compared against different container? // Is comparison expression?
if (tok2->isComparisonOp() && containerToken && tok2->astOperand1() && tok2->astOperand2()) { // Check whether iterator compared against different container or iterator of different container?
const Token *other = nullptr; if (tok2->isComparisonOp() && tok2->astOperand1() && tok2->astOperand2() &&
if (tok2->astOperand1()->varId() == iteratorId) (foundOperatorErrors.find(tok2) == foundOperatorErrors.end()) &&
other = tok2->astOperand2()->tokAt(-3); compareIteratorAgainstDifferentContainer(tok2, containerToken, iteratorId, iteratorScopeBeginInfo)) {
else if (tok2->astOperand2()->varId() == iteratorId) foundOperatorErrors.insert(tok2);
other = tok2->astOperand1()->tokAt(-3);
if (Token::Match(other, "%name% . end|rend|cend|crend ( )") && other->varId() != containerToken->varId())
iteratorsError(tok2, getContainerName(containerToken), getContainerName(other));
} }
// Is the iterator used in a insert/erase operation? // Is the iterator used in a insert/erase operation?
@ -397,6 +471,74 @@ void CheckStl::iterators()
} }
} }
bool CheckStl::compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const unsigned int iteratorId, const std::map<unsigned int, const Token*>& iteratorScopeBeginInfo)
{
if (!containerTok)
return false;
const Token *otherOperand = nullptr;
OperandPosition operandPosition;
if (operatorTok->astOperand1()->varId() == iteratorId)
{
otherOperand = operatorTok->astOperand2();
operandPosition = OperandPosition::Right;
}
else if (operatorTok->astOperand2()->varId() == iteratorId)
{
otherOperand = operatorTok->astOperand1();
operandPosition = OperandPosition::Left;
}
if (!otherOperand)
return false;
const Token * const otherExprPart = otherOperand->tokAt(-3);
if (Token::Match(otherExprPart, "%name% . end|rend|cend|crend ( )") && otherExprPart->varId() != containerTok->varId())
{
const std::string& firstContainerName = getContainerName(containerTok);
const std::string& secondContainerName = getContainerName(otherExprPart);
if (firstContainerName != secondContainerName)
{
if (operandPosition == OperandPosition::Right)
iteratorsError(operatorTok, containerTok, firstContainerName, secondContainerName);
else
iteratorsError(operatorTok, containerTok, secondContainerName, firstContainerName);
}
else
{
iteratorsError(operatorTok, containerTok, firstContainerName);
}
return true;
}
else
{
const unsigned int otherId = otherOperand->varId();
auto it = iteratorScopeBeginInfo.find(otherId);
if (it != iteratorScopeBeginInfo.end())
{
const Token* otherContainerToken = findIteratorContainer(it->second, operatorTok->astOperand1(), otherId);
if (otherContainerToken && otherContainerToken->varId() != containerTok->varId())
{
const std::string& firstContainerName = getContainerName(containerTok);
const std::string& secondContainerName = getContainerName(otherContainerToken);
if (firstContainerName != secondContainerName)
{
if (operandPosition == OperandPosition::Right)
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName, secondContainerName);
else
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, secondContainerName, firstContainerName);
}
else
{
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName);
}
return true;
}
}
}
return false;
}
// Error message for bad iterator usage.. // Error message for bad iterator usage..
void CheckStl::mismatchingContainersError(const Token *tok) void CheckStl::mismatchingContainersError(const Token *tok)

View File

@ -195,7 +195,11 @@ private:
void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at);
void negativeIndexError(const Token* tok, const ValueFlow::Value& index); void negativeIndexError(const Token* tok, const ValueFlow::Value& index);
void invalidIteratorError(const Token* tok, const std::string& iteratorName); void invalidIteratorError(const Token* tok, const std::string& iteratorName);
void iteratorsError(const Token* tok, const std::string& container1, const std::string& container2); 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& containerName);
void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2);
void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName);
void mismatchingContainersError(const Token* tok); void mismatchingContainersError(const Token* tok);
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);
@ -223,11 +227,17 @@ private:
void useStlAlgorithmError(const Token *tok, const std::string &algoName); void useStlAlgorithmError(const Token *tok, const std::string &algoName);
bool compareIteratorAgainstDifferentContainer(const Token* tok, const Token* containerToken, const unsigned int iteratorId, const std::map<unsigned int, const Token*>& iteratorScopeBeginInfo);
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override {
CheckStl c(nullptr, settings, errorLogger); CheckStl c(nullptr, settings, errorLogger);
c.outOfBoundsError(nullptr, nullptr, nullptr); c.outOfBoundsError(nullptr, nullptr, nullptr);
c.invalidIteratorError(nullptr, "iterator"); c.invalidIteratorError(nullptr, "iterator");
c.iteratorsError(nullptr, "container1", "container2"); c.iteratorsError(nullptr, "container1", "container2");
c.iteratorsError(nullptr, nullptr, "container1", "container2");
c.iteratorsError(nullptr, nullptr, "container");
c.iteratorsCmpError(nullptr, nullptr, nullptr, "container1", "container2");
c.iteratorsCmpError(nullptr, nullptr, nullptr, "container");
c.mismatchingContainersError(nullptr); c.mismatchingContainersError(nullptr);
c.mismatchingContainerExpressionError(nullptr, nullptr); c.mismatchingContainerExpressionError(nullptr, nullptr);
c.sameIteratorExpressionError(nullptr); c.sameIteratorExpressionError(nullptr);

View File

@ -57,6 +57,12 @@ private:
TEST_CASE(iterator13); TEST_CASE(iterator13);
TEST_CASE(iterator14); // #8191 TEST_CASE(iterator14); // #8191
TEST_CASE(iterator15); // #8341 TEST_CASE(iterator15); // #8341
TEST_CASE(iterator16);
TEST_CASE(iterator17);
TEST_CASE(iterator18);
TEST_CASE(iterator19);
TEST_CASE(iterator20);
TEST_CASE(iterator21);
TEST_CASE(iteratorExpression); TEST_CASE(iteratorExpression);
TEST_CASE(iteratorSameExpression); TEST_CASE(iteratorSameExpression);
@ -246,7 +252,7 @@ private:
" for (list<int>::iterator it = l1.begin(); it != l2.end(); ++it)\n" " for (list<int>::iterator it = l1.begin(); it != l2.end(); ++it)\n"
" { }\n" " { }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
check("void f()\n" check("void f()\n"
"{\n" "{\n"
@ -255,7 +261,7 @@ private:
" for (list<int>::iterator it = l1.begin(); l2.end() != it; ++it)\n" " for (list<int>::iterator it = l1.begin(); l2.end() != it; ++it)\n"
" { }\n" " { }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str());
check("struct C { std::list<int> l1; void func(); };\n" check("struct C { std::list<int> l1; void func(); };\n"
"void C::func() {\n" "void C::func() {\n"
@ -274,7 +280,7 @@ private:
" for (list<int>::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n" " for (list<int>::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n"
" { }\n" " { }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
} }
void iterator2() { void iterator2() {
@ -288,7 +294,19 @@ private:
" ++it;\n" " ++it;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
check("void foo()\n"
"{\n"
" list<int> l1;\n"
" list<int> l2;\n"
" list<int>::iterator it = l1.begin();\n"
" while (l2.end() != it)\n"
" {\n"
" ++it;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str());
} }
void iterator3() { void iterator3() {
@ -512,7 +530,7 @@ private:
" if (it != s2.end()) continue;\n" " if (it != s2.end()) continue;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:8]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str());
} }
void iterator11() { void iterator11() {
@ -534,7 +552,7 @@ private:
" std::map<int, int>::const_iterator it = map1.find(123);\n" " std::map<int, int>::const_iterator it = map1.find(123);\n"
" if (it == map2.end()) { }" " if (it == map2.end()) { }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str());
check("void f() {\n" check("void f() {\n"
" std::map<int, int> map1;\n" " std::map<int, int> map1;\n"
@ -542,7 +560,7 @@ private:
" std::map<int, int>::const_iterator it = map1.find(123);\n" " std::map<int, int>::const_iterator it = map1.find(123);\n"
" if (map2.end() == it) { }" " if (map2.end() == it) { }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map2' and 'map1'.\n", errout.str());
check("void f(std::string &s) {\n" check("void f(std::string &s) {\n"
" int pos = s.find(x);\n" " int pos = s.find(x);\n"
@ -564,7 +582,7 @@ private:
" while (it!=a.end())\n" " while (it!=a.end())\n"
" ++it;\n" " ++it;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:9]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str()); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:8]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str());
// #4062 // #4062
check("void f() {\n" check("void f() {\n"
@ -613,6 +631,286 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void iterator16() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l2.end();\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.end();\n"
" std::list<int>::iterator it2 = l2.begin();\n"
" while (it2 != it1)\n"
" {\n"
" ++it2;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it2 = l2.end();\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2(10, 4);\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l2.find(4);\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
}
void iterator17() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" { it2 = l2.end(); }\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
" it2 = l2.end();\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" it1 = l2.end();\n"
" it1 = l1.end();\n"
" if (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" { it2 = l2.end(); }\n"
" it2 = l1.end();\n"
" { it2 = l2.end(); }\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:9]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
}
void iterator18() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" while (++it1 != --it2)\n"
" {\n"
" }\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" while (it1++ != --it2)\n"
" {\n"
" }\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l1.end();\n"
" if (--it2 > it1++)\n"
" {\n"
" }\n"
"}");
TODO_ASSERT_EQUALS("", "[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n", errout.str());
}
void iterator19() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" {\n"
" std::list<int> l1;\n"
" if (it1 != l1.end())\n"
" {\n"
" }\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" {\n"
" std::list<int> l1;\n"
" if (l1.end() > it1)\n"
" {\n"
" }\n"
" }\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n",
"[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n",
errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" {\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it2 = l1.begin();\n"
" if (it1 != it2)\n"
" {\n"
" }\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" {\n"
" std::list<int> l1;\n"
" std::list<int>::iterator it2 = l1.begin();\n"
" if (it2 != it1)\n"
" {\n"
" }\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str());
}
void iterator20() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l2.begin();\n"
" it1 = it2;\n"
" while (it1 != l1.end())\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", "", errout.str());
check("std::list<int> l3;\n"
"std::list<int>::iterator bar()\n"
"{\n"
" return l3.end();\n"
"}\n"
"void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.begin();\n"
" std::list<int>::iterator it2 = l2.begin();\n"
" it1 = bar();\n"
" while (it1 != it2)\n"
" {\n"
" ++it1;\n"
" }\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:13] -> [test.cpp:10] -> [test.cpp:11]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", "", errout.str());
}
void iterator21() {
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.end();\n"
" std::list<int>::iterator it2 = l2.begin();\n"
" if (it1 != it2)\n"
" {\n"
" }\n"
" if (it2 != it1)\n"
" {\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n"
"[test.cpp:10] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str());
check("void foo()\n"
"{\n"
" std::list<int> l1;\n"
" std::list<int> l2;\n"
" std::list<int>::iterator it1 = l1.end();\n"
" std::list<int>::iterator it2 = l2.begin();\n"
" if (it1 != it2 && it1 != it2)\n"
" {\n"
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
}
void iteratorExpression() { void iteratorExpression() {
check("std::vector<int>& f();\n" check("std::vector<int>& f();\n"
"std::vector<int>& g();\n" "std::vector<int>& g();\n"