diff --git a/src/checkstl.cpp b/src/checkstl.cpp index 9c4d20b65..51aa0a998 100644 --- a/src/checkstl.cpp +++ b/src/checkstl.cpp @@ -203,3 +203,60 @@ void CheckStl::eraseCheckLoop(const Token *it) } + + + +void CheckStl::pushback() +{ + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (Token::Match(tok, "vector <")) + { + while (tok && tok->str() != ">") + tok = tok->next(); + if (!tok) + break; + if (Token::Match(tok, "> :: iterator|const_iterator %var% =|;")) + { + const std::string iteratorname(tok->strAt(3)); + std::string vectorname; + int indent = 0; + bool invalidIterator = false; + for (const Token *tok2 = tok;indent >= 0 && tok2; tok2 = tok2->next()) + { + if (tok2->str() == "{" || tok2->str() == "(") + ++indent; + else if (tok2->str() == "}" || tok2->str() == ")") + { + if (indent == 0 && Token::simpleMatch(tok2, ") {")) + tok2 = tok2->next(); + else + --indent; + } + + // Assigning iterator.. + if (Token::Match(tok2, (iteratorname + " = %var% . begin ( )").c_str())) + { + vectorname = tok2->strAt(2); + invalidIterator = false; + } + + // push_back on vector.. + if (vectorname.size() && Token::Match(tok2, (vectorname + " . push_front|push_back").c_str())) + invalidIterator = true; + + // Using invalid iterator.. + if (invalidIterator) + { + if (Token::Match(tok2, ("++|--|*|+|-|(|, " + iteratorname).c_str())) + _errorLogger->pushback(_tokenizer, tok2, iteratorname); + if (Token::Match(tok2, (iteratorname + " ++|--|+|-").c_str())) + _errorLogger->pushback(_tokenizer, tok2, iteratorname); + } + } + } + } + } +} + + diff --git a/src/checkstl.h b/src/checkstl.h index 14094b6aa..bd5b2d7f9 100644 --- a/src/checkstl.h +++ b/src/checkstl.h @@ -51,6 +51,11 @@ public: */ void erase(); + /** + * Dangerous usage of push_back + */ + void pushback(); + private: const Tokenizer *_tokenizer; ErrorLogger *_errorLogger; diff --git a/src/cppcheck.cpp b/src/cppcheck.cpp index f488af4e0..3d1a74706 100644 --- a/src/cppcheck.cpp +++ b/src/cppcheck.cpp @@ -421,6 +421,9 @@ void CppCheck::checkFile(const std::string &code, const char FileName[]) if (ErrorLogger::erase()) checkStl.erase(); + + if (ErrorLogger::pushback()) + checkStl.pushback(); } Settings CppCheck::settings() const diff --git a/src/errorlogger.h b/src/errorlogger.h index 02c8fbb4d..ea2123046 100644 --- a/src/errorlogger.h +++ b/src/errorlogger.h @@ -450,6 +450,15 @@ public: return true; } + void pushback(const Tokenizer *tokenizer, const Token *Location, const std::string &iterator_name) + { + _writemsg(tokenizer, Location, "error", "After push_back or push_front, the iterator '" + iterator_name + "' may be invalid", "pushback"); + } + static bool pushback() + { + return true; + } + static std::string callStackToString(const std::list &callStack); diff --git a/test/teststl.cpp b/test/teststl.cpp index ec8f5d2b5..33f2ea054 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -45,6 +45,8 @@ private: TEST_CASE(eraseReturn); TEST_CASE(eraseGoto); TEST_CASE(eraseAssign); + + TEST_CASE(pushback1); } void check(const char code[]) @@ -215,6 +217,37 @@ private: + + + + void checkPushback(const char code[]) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Clear the error buffer.. + errout.str(""); + + // Check char variable usage.. + CheckStl checkStl(&tokenizer, this); + checkStl.pushback(); + } + + + void pushback1() + { + checkPushback("void f()\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 or push_front, the iterator 'it' may be invalid\n", errout.str()); + } + + }; REGISTER_TEST(TestStl) diff --git a/tools/errmsg.cpp b/tools/errmsg.cpp index 6d8085606..47e0a6e53 100644 --- a/tools/errmsg.cpp +++ b/tools/errmsg.cpp @@ -114,7 +114,7 @@ int main() // checkstl.cpp.. err.push_back(Message("iteratorUsage", Message::error, "Same iterator is used with both %1 and %2", "container1", "container2")); err.push_back(Message("erase", Message::error, "Dangerous usage of erase")); - + err.push_back(Message("pushback", Message::error, "After push_back or push_front, the iterator '%1' may be invalid", "iterator_name")); // Generate code..