/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "tokenize.h" #include "checkstl.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestStl : public TestFixture { public: TestStl() : TestFixture("TestStl") { } private: void run() { TEST_CASE(iterator1); TEST_CASE(iterator2); TEST_CASE(iterator3); TEST_CASE(iterator4); TEST_CASE(iterator5); TEST_CASE(iterator6); TEST_CASE(iterator7); TEST_CASE(iterator8); TEST_CASE(dereference); TEST_CASE(dereference_member); TEST_CASE(STLSize); TEST_CASE(STLSizeNoErr); TEST_CASE(erase1); TEST_CASE(erase2); TEST_CASE(erase3); TEST_CASE(erase4); TEST_CASE(erase5); TEST_CASE(eraseBreak); TEST_CASE(eraseContinue); TEST_CASE(eraseReturn1); TEST_CASE(eraseReturn2); TEST_CASE(eraseReturn3); TEST_CASE(eraseGoto); TEST_CASE(eraseAssign1); TEST_CASE(eraseAssign2); TEST_CASE(eraseErase); TEST_CASE(eraseByValue); TEST_CASE(pushback1); TEST_CASE(pushback2); TEST_CASE(pushback3); TEST_CASE(pushback4); TEST_CASE(pushback5); TEST_CASE(pushback6); TEST_CASE(pushback7); TEST_CASE(pushback8); TEST_CASE(pushback9); TEST_CASE(pushback10); TEST_CASE(insert1); TEST_CASE(invalidcode); TEST_CASE(stlBoundries1); TEST_CASE(stlBoundries2); TEST_CASE(stlBoundries3); // if (str.find("ab")) TEST_CASE(if_find); TEST_CASE(if_str_find); TEST_CASE(size1); // Redundant conditions.. // if (ints.find(123) != ints.end()) ints.remove(123); TEST_CASE(redundantCondition1); TEST_CASE(redundantCondition2); // missing inner comparison when incrementing iterator inside loop TEST_CASE(missingInnerComparison1); TEST_CASE(missingInnerComparison2); // no FP when there is comparison TEST_CASE(missingInnerComparison3); // no FP when there is iterator shadowing TEST_CASE(missingInnerComparison4); // no FP when "break;" is used // catch common problems when using the string::c_str() function TEST_CASE(cstr); } void check(const std::string &code) { // Tokenize.. Tokenizer tokenizer; std::istringstream istr(code.c_str()); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList(); // Clear the error buffer.. errout.str(""); // Check.. Settings settings; settings.inconclusive = true; settings._checkCodingStyle = true; CheckStl checkStl; checkStl.runSimplifiedChecks(&tokenizer, &settings, this); } void iterator1() { check("void f()\n" "{\n" " list l1;\n" " list l2;\n" " for (list::iterator it = l1.begin(); it != l2.end(); ++it)\n" " { }\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with both l1 and l2\n", errout.str()); } void iterator2() { check("void foo()\n" "{\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " while (it != l2.end())\n" " {\n" " ++it;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with both l1 and l2\n", errout.str()); } void iterator3() { check("void foo()\n" "{\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " l2.insert(it, 0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with both l1 and l2\n", errout.str()); } void iterator4() { check("void foo(std::vector &test)\n" "{\n" " std::set result;\n" " for (std::vector::const_iterator cit = test.begin();\n" " cit != test.end();\n" " ++cit)\n" " {\n" " result.insert(cit->size());\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void iterator5() { check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find(ints1.begin(), ints2.end(), 22);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) mismatching containers\n", errout.str()); } void iterator6() { // Ticket #1357 check("void foo(const std::set &ints1)\n" "{\n" " std::set ints2;\n" " std::set::iterator it1 = ints1.begin();\n" " std::set::iterator it2 = ints1.end();\n" " ints2.insert(it1, it2);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void iterator7() { // Ticket #1600 check("void foo(std::vector &r)\n" "{\n" " std::vector::iterator aI = r.begin();\n" " while(aI != r.end())\n" " {\n" " if (*aI == 0)\n" " {\n" " r.insert(aI, 42);\n" " return;\n" " }\n" " ++aI;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Execution path checking.. check("void foo(std::vector &r, int c)\n" "{\n" " std::vector::iterator aI = r.begin();\n" " while(aI != r.end())\n" " {\n" " if (*aI == 0)\n" " {\n" " r.insert(aI, 42);\n" " if (c)\n" " {\n" " return;\n" " }\n" " }\n" " ++aI;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); TODO_ASSERT_EQUALS("[test.cpp:14] (error) After insert, the iterator 'aI' may be invalid", errout.str()); } void iterator8() { // Ticket #1679 check("void foo()\n" "{\n" " std::set s1;\n" " std::set s2;\n" " for (std::set::iterator it = s1.begin(); it != s1.end(); ++it)\n" " {\n" " if (true) { }\n" " if (it != s2.end()) continue;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) Same iterator is used with both s1 and s2\n", errout.str()); } // Dereferencing invalid pointer void dereference() { check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter;\n" " iter = ints.begin() + 2;\n" " ints.erase(iter);\n" " std::cout << (*iter) << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Dereferenced iterator 'iter' has been erased\n", errout.str()); } void dereference_member() { check("void f()\n" "{\n" " std::map ints;\n" " std::map::iterator iter;\n" " iter = ints.begin();\n" " ints.erase(iter);\n" " std::cout << iter->first << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Dereferenced iterator 'iter' has been erased\n", errout.str()); } void STLSize() { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " foo[ii] = 0;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) When ii==foo.size(), foo[ii] is out of bounds\n", errout.str()); check("void foo()\n" "{\n" " std::vector foo;\n" " foo.push_back(1);\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " }\n" " int ii = 0;\n" " foo[ii] = 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void STLSizeNoErr() { { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii < foo.size(); ++ii)\n" " {\n" " foo[ii] = 0;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " if (ii == foo.size())\n" " {\n" " }\n" " else\n" " {\n" " foo[ii] = 0;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } } void erase1() { check("void f()\n" "{\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("for (it = foo.begin(); it != foo.end(); ++it)\n" "{\n" " foo.erase(it);\n" "}\n" "for (it = foo.begin(); it != foo.end(); ++it)\n" "{\n" " foo.erase(it);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n" "[test.cpp:7]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("void f(std::list &ints)\n" "{\n" " std::list::iterator i = ints.begin();\n" " i = ints.erase(i);\n" " *i = 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #2101 check("void f(vector< list > &ints, unsigned int i)\n" "{\n" " list::iterator it;\n" " for(it = ints[i].begin(); it != ints[i].end(); it++) {\n" " if (*it % 2)\n" " it = ints[i].erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void erase2() { check("static void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); it = next)\n" " {\n" " next = it;\n" " next++;\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void erase3() { check("static void f(std::list &foo)\n" "{\n" " std::list::iterator it = foo.begin();\n" " foo.erase(it->a);\n" " if (it->b);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void erase4() { check("void f()\n" "{\n" " std::list::iterator it, it2;\n" " for (it = foo.begin(); it != i2; ++it)\n" " {\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " for (; it != i2; ++it)\n" " {\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " while (it != i2)\n" " {\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " while (it != i2)\n" " {\n" " foo.erase(++it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); } void erase5() { check("void f()\n" "{\n" " std::list foo;\n" " std::list::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (*it == 123)\n" " foo.erase(it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); } void eraseBreak() { check("void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " if (x)" " break;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); check("void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (x) {\n" " foo.erase(it);\n" " break;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseContinue() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator it;\n" " std::vector::iterator jt = ints.begin();\n" " for (it = ints.begin(); it != ints.end(); it = jt) {\n" " ++jt;\n" " if (*it == 1) {\n" " jt = ints.erase(it);\n" " continue;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseReturn1() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " return;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseReturn2() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (*it == 1) {\n" " foo.erase(it);\n" " return;\n" " }\n" " else {\n" " foo.erase(it);\n" " return;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseReturn3() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (somecondition) {\n" " if (*it == 1)\n" " foo.erase(it);\n" " else\n" " *it = 0;\n" " return;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (a) {\n" " if (b)\n" " foo.erase(it);\n" " else\n" " *it = 0;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:9]: (error) Dangerous iterator usage. After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.\n", errout.str()); } void eraseGoto() { check("void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " goto abc;\n" " }\n" "bar:\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseAssign1() { check("void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " it = foo.begin();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseAssign2() { check("void f(list &ints)\n" "{\n" " for (list::iterator it = ints.begin(); it != ints.end();) {\n" " if (*it == 123) {\n" " list::iterator copy = it;\n" " ++copy;\n" " ints.erase(it);\n" " it = copy;\n" " } else {\n" " it->second = 123;\n" " ++it;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void eraseErase() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator iter;\n" " iter = ints.begin() + 2;\n" " ints.erase(iter);\n" " ints.erase(iter);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Invalid iterator: iter\n", errout.str()); } void eraseByValue() { check("void f()\n" "{\n" " std::set foo;\n" " for (std::set it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(*it);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Iterator 'it' becomes invalid when deleted by value from 'foo'\n", errout.str()); } void pushback1() { check("void f(const std::vector &foo)\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.push_back(123);\n" " *it;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) After push_back, the iterator 'it' may be invalid\n", errout.str()); } void pushback2() { check("void f()\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.push_back(123);\n" " {\n" " int *it = &foo[0];\n" " *it = 456;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void pushback3() { check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.push_back(123);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back, the iterator 'it' may be invalid\n", errout.str()); } void pushback4() { check("void f()\n" "{\n" " std::vector ints;\n" " ints.push_back(1);\n" " int *first = &ints[0];\n" " ints.push_back(2);\n" " *first;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Invalid pointer 'first' after push_back / push_front\n", errout.str()); } void pushback5() { check("void f()\n" "{\n" " std::vector::const_iterator i;\n" "\n" " for (i=v.begin(); i!=v.end(); ++i)\n" " {\n" " }\n" "\n" " for (i=rhs.v.begin(); i!=rhs.v.end(); ++i)\n" " {\n" " v.push_back(*i);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void pushback6() { // ticket #735 check("void f()\n" "{\n" " vector v;\n" " vector.push_back(1);\n" " vector.push_back(2);\n" " for (vector::iterator it = v.begin(); it != v.end(); ++it)\n" " {\n" " if (*it == 1)\n" " v.push_back(10);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:9]: (error) After push_back, the iterator 'it' may be invalid\n", errout.str()); check("void f()\n" "{\n" " std::vector v;\n" " vector.push_back(1);\n" " vector.push_back(2);\n" " for (std::vector::iterator it = v.begin(); it != v.end(); ++it)\n" " {\n" " if (*it == 1)\n" " v.push_back(10);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:9]: (error) After push_back, the iterator 'it' may be invalid\n", errout.str()); } void pushback7() { check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); it++)\n" " {\n" " foo.push_back(123);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back, the iterator 'it' may be invalid\n", errout.str()); } void pushback8() { check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::const_iterator end = ints.end();\n" " ints.push_back(10);\n" " std::vector::iterator it;\n" " unsigned int sum = 0;\n" " for (it = ints.begin(); it != end; ++it)\n" " {\n" " sum += *it;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back, the iterator 'end' may be invalid\n", errout.str()); } void pushback9() { check("struct A {\n" " std::vector ints;\n" "};\n" "\n" "void f()\n" "{\n" " std::vector ints;\n" " A a;\n" " std::vector::const_iterator i = ints.begin();\n" " std::vector::const_iterator e = ints.end();\n" " while (i != e)\n" " {\n" " a.ints.push_back(*i);\n" " ++i;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void pushback10() { check("void f(std::vector &foo)\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.reserve(100);\n" " *it = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) After reserve, the iterator 'it' may be invalid\n", errout.str()); // in loop check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.reserve(123);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) After reserve, the iterator 'it' may be invalid\n", errout.str()); } void insert1() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator iter = ints.begin() + 5;\n" " ints.insert(ints.begin(), 1);\n" " ++iter;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) After insert, the iterator 'iter' may be invalid\n", errout.str()); check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter = ints.begin();\n" " ints.insert(iter, 1);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter = ints.begin();\n" " ints.insert(iter, 1);\n" " ints.insert(iter, 2);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) After insert, the iterator 'iter' may be invalid\n", errout.str()); } void invalidcode() { errout.str(""); const std::string src = "void f()\n" "{\n" " for ( \n" "}\n"; Tokenizer tokenizer(0, this); std::istringstream istr(src); ASSERT_EQUALS(false, tokenizer.tokenize(istr, "test.cpp")); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid number of character (() when these macros are defined: ''.\n", errout.str()); } void stlBoundries1() { const int STL_CONTAINER_LIST = 9; const std::string stlCont[STL_CONTAINER_LIST] = { "deque", "list", "set", "multiset", "map", "multimap", "hash_map", "hash_multimap", "hash_set" }; for (int i = 0; i < STL_CONTAINER_LIST; ++i) { check("void f()\n" "{\n" " std::" + stlCont[i] + "::iterator it;\n" " for (it = ab.begin(); it < ab.end(); ++it)\n" " ;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) " + stlCont[i] + " range check should use != and not < since the order of the pointers isn't guaranteed\n", errout.str()); } } void stlBoundries2() { check("void f()\n" "{\n" " std::vector files;\n" " std::vector::const_iterator it;\n" " for (it = files.begin(); it < files.end(); it++) { }\n" " for (it = files.begin(); it < files.end(); it++) { };\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void stlBoundries3() { check("void f()\n" "{\n" " set files;\n" " set::const_iterator current;\n" " for (current = files.begin(); current != files.end(); ++current)\n" " {\n" " assert(*current < 100)\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void if_find() { // --------------------------- // set::find // --------------------------- // error check("void f(std::set s)\n" "{\n" " if (s.find(12)) { }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find is an iterator, but it is not properly checked.\n", errout.str()); // ok check("void f(std::set s)\n" "{\n" " if (s.find(123) != s.end()) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // --------------------------- // std::find // --------------------------- // error check("void f()\n" "{\n" " if (std::find(a,b,c)) { }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find is an iterator, but it is not properly checked.\n", errout.str()); // ok check("void f()\n" "{\n" " if (std::find(a,b,c) != c) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void if_str_find() { // error check("void f(const std::string &s)\n" "{\n" " if (s.find(\"abc\")) { }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. string::find will return 0 if the string is found at position 0. If this is what you want to check then string::compare is a faster alternative because it doesn't scan through the string.\n", errout.str()); } void size1() { check("void f()\n" "{\n" " std::list x;\n" " if (x.size() == 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (performance) Use x.empty() instead of x.size() to guarantee fast code.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() != 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (performance) Use x.empty() instead of x.size() to guarantee fast code.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() > 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (performance) Use x.empty() instead of x.size() to guarantee fast code.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size()) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (performance) Use x.empty() instead of x.size() to guarantee fast code.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " fun(x.size());\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void redundantCondition1() { check("void f()\n" "{\n" " if (haystack.find(needle) != haystack.end())\n" " haystack.remove(needle);" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition. The remove function in the STL will not do anything if element doesn't exist\n", errout.str()); } void redundantCondition2() { check("void f()\n" "{\n" " if (haystack.find(needle) != haystack.end())\n" " {\n" " haystack.remove(needle);\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition. The remove function in the STL will not do anything if element doesn't exist\n", errout.str()); } void missingInnerComparison1() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " if (a) {\n" " it++;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) The iterator is incremented at line 4 and then at line 2. The loop might unintentionally skip an element in the container. There is no comparison between these increments to prevent that the iterator is incremented beyond the end.\n", errout.str()); } void missingInnerComparison2() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " if (a) {\n" " it++;\n" " if (it == ints.end())\n" " return;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison3() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " for (std::set::iterator it = ints2.begin(); it != ints2.end(); ++it)\n" " { }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison4() { check("function f1(std::list &l1) {\n" " for(std::list::iterator i = l1.begin(); i != l1.end(); i++) {\n" " if (*i == 44) {\n" " l1.insert(++i, 55);\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void cstr() { check("void f() {\n" " std::string errmsg;\n" " throw errmsg.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str()\n", errout.str()); check("void f() {\n" " std::ostringstream errmsg;\n" " const char *c = errmsg.str().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str()\n", errout.str()); check("std::string f();\n" "\n" "void foo() {\n" " const char *c = f().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of c_str()\n", errout.str()); } }; REGISTER_TEST(TestStl)