From 0a9be3e73437975ac57c8269b3083f69e8c6897c Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 17 Oct 2018 07:36:51 +0300 Subject: [PATCH] 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 --- lib/checkstl.cpp | 170 ++++++++++++++++++++++--- lib/checkstl.h | 12 +- test/teststl.cpp | 314 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 473 insertions(+), 23 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 4fc010646..6269e5898 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -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); } -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", - "$symbol:" + container1 + "\n" - "$symbol:" + container2 + "\n" - "Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", CWE664, false); + reportError(tok, Severity::error, "iterators1", + "$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& containerName1, const std::string& containerName2) +{ + std::list 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 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 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 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.. @@ -229,10 +264,52 @@ static std::string getContainerName(const Token *containerToken) 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() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + // Filling map of iterators id and their scope begin + std::map 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 foundOperatorErrors; + for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) @@ -269,15 +346,12 @@ void CheckStl::iterators() invalidationScope = nullptr; } - // Is iterator compared against different container? - if (tok2->isComparisonOp() && containerToken && tok2->astOperand1() && tok2->astOperand2()) { - const Token *other = nullptr; - if (tok2->astOperand1()->varId() == iteratorId) - other = tok2->astOperand2()->tokAt(-3); - else if (tok2->astOperand2()->varId() == iteratorId) - 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 comparison expression? + // Check whether iterator compared against different container or iterator of different container? + if (tok2->isComparisonOp() && tok2->astOperand1() && tok2->astOperand2() && + (foundOperatorErrors.find(tok2) == foundOperatorErrors.end()) && + compareIteratorAgainstDifferentContainer(tok2, containerToken, iteratorId, iteratorScopeBeginInfo)) { + foundOperatorErrors.insert(tok2); } // 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& 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.. void CheckStl::mismatchingContainersError(const Token *tok) diff --git a/lib/checkstl.h b/lib/checkstl.h index 7a75a6ad1..8ea598ef5 100644 --- a/lib/checkstl.h +++ b/lib/checkstl.h @@ -195,7 +195,11 @@ private: 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 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 mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); void sameIteratorExpressionError(const Token *tok); @@ -223,11 +227,17 @@ private: void useStlAlgorithmError(const Token *tok, const std::string &algoName); + bool compareIteratorAgainstDifferentContainer(const Token* tok, const Token* containerToken, const unsigned int iteratorId, const std::map& iteratorScopeBeginInfo); + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { CheckStl c(nullptr, settings, errorLogger); c.outOfBoundsError(nullptr, nullptr, nullptr); c.invalidIteratorError(nullptr, "iterator"); 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.mismatchingContainerExpressionError(nullptr, nullptr); c.sameIteratorExpressionError(nullptr); diff --git a/test/teststl.cpp b/test/teststl.cpp index 35e12480d..3fa762891 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -57,6 +57,12 @@ private: TEST_CASE(iterator13); TEST_CASE(iterator14); // #8191 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(iteratorSameExpression); @@ -246,7 +252,7 @@ private: " for (list::iterator it = l1.begin(); it != l2.end(); ++it)\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" "{\n" @@ -255,7 +261,7 @@ private: " for (list::iterator it = l1.begin(); l2.end() != it; ++it)\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 l1; void func(); };\n" "void C::func() {\n" @@ -274,7 +280,7 @@ private: " for (list::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\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() { @@ -288,7 +294,19 @@ private: " ++it;\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 l1;\n" + " list l2;\n" + " list::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() { @@ -512,7 +530,7 @@ private: " if (it != s2.end()) continue;\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() { @@ -534,7 +552,7 @@ private: " std::map::const_iterator it = map1.find(123);\n" " 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" " std::map map1;\n" @@ -542,7 +560,7 @@ private: " std::map::const_iterator it = map1.find(123);\n" " 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" " int pos = s.find(x);\n" @@ -564,7 +582,7 @@ private: " while (it!=a.end())\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 check("void f() {\n" @@ -613,6 +631,286 @@ private: ASSERT_EQUALS("", errout.str()); } + void iterator16() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it2 = l2.end();\n" + " std::list::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 l1;\n" + " std::list l2(10, 4);\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " while (++it1 != --it2)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " while (it1++ != --it2)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list 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 l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list 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 l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " std::list::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 l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l3;\n" + "std::list::iterator bar()\n" + "{\n" + " return l3.end();\n" + "}\n" + "void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::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 l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::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() { check("std::vector& f();\n" "std::vector& g();\n"