diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 5b8ce3b91..110c32e80 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -355,6 +355,10 @@ void CheckAutoVariables::returnReference() // goto next token.. tok2 = tok2->next(); + // skip "const" + if (Token::Match(tok2, "const %type%")) + tok2 = tok2->next(); + // skip "std::" if it is seen if (Token::simpleMatch(tok2, "std ::")) tok2 = tok2->tokAt(2); @@ -389,3 +393,88 @@ void CheckAutoVariables::errorReturnReference(const Token *tok) } +// Return c_str +void CheckAutoVariables::returncstr() +{ + // locate function that returns a const char *.. + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Skip executable scopes.. + if (Token::Match(tok, ") const| {")) + { + tok = tok->next(); + if (tok->str() == "const") + tok = tok->next(); + tok = tok->link(); + continue; + } + + // have we reached a function that returns a reference? + if (Token::Match(tok, "const char * %var% (")) + { + // go to the '(' + const Token *tok2 = tok->tokAt(4); + + // go to the ')' + tok2 = tok2->link(); + + // is this a function implementation? + if (Token::Match(tok2, ") const| {")) + { + unsigned int indentlevel = 0; + std::set localvar; // local variables in function + while (0 != (tok2 = tok2->next())) + { + // indentlevel.. + if (tok2->str() == "{") + ++indentlevel; + else if (tok2->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + + // declare local variable.. + if (Token::Match(tok2, "[{};] %type%") && tok2->next()->str() != "return") + { + // goto next token.. + tok2 = tok2->next(); + + // skip "const" + if (Token::Match(tok2, "const %type%")) + tok2 = tok2->next(); + + // skip "std::" if it is seen + if (Token::simpleMatch(tok2, "std ::")) + tok2 = tok2->tokAt(2); + + // is it a variable declaration? + if (Token::Match(tok2, "%type% %var% ;")) + localvar.insert(tok2->next()->varId()); + } + + // return.. + else if (Token::Match(tok2, "return %var% . c_str ( ) ;")) + { + // is the returned variable a local variable? + if ((tok2->next()->varId() > 0) && + (localvar.find(tok2->next()->varId()) != localvar.end())) + { + // report error.. + errorReturnAutocstr(tok2); + } + } + } + } + } + } +} + +void CheckAutoVariables::errorReturnAutocstr(const Token *tok) +{ + reportError(tok, Severity::error, "returnAutocstr", "Returning pointer to auto variable"); +} + + + diff --git a/lib/checkautovariables.h b/lib/checkautovariables.h index 217f07f6f..c63cfdf93 100644 --- a/lib/checkautovariables.h +++ b/lib/checkautovariables.h @@ -59,6 +59,9 @@ public: /** Returning reference to local/temporary variable */ void returnReference(); + /** Returning c_str to local variable */ + void returncstr(); + private: std::set fp_list; std::set vd_list; @@ -74,12 +77,14 @@ private: void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok); void errorReturnReference(const Token *tok); + void errorReturnAutocstr(const Token *tok); void getErrorMessages() { errorAutoVariableAssignment(0); errorReturnPointerToLocalArray(0); errorReturnReference(0); + errorReturnAutocstr(0); } std::string name() const @@ -91,7 +96,7 @@ private: { return "A pointer to a variable is only valid as long as the variable is in scope.\n" "Check:\n" - "* returning a pointer to variable\n" + "* returning a pointer to auto variable\n" "* assigning address of an variable to an effective parameter of a function\n" "* returning reference to local/temporary variable\n"; } diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 09e13cc31..f402eadc6 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -58,6 +58,7 @@ private: checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); checkAutoVariables.returnReference(); + checkAutoVariables.returncstr(); } void run() @@ -73,6 +74,9 @@ private: // return reference.. TEST_CASE(returnReference); + + // return c_str().. + TEST_CASE(returncstr); } @@ -188,7 +192,41 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); + check("std::string hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "\n" + "std::string &f()\n" + "{\n" + " return hello();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Returning reference to temporary\n", errout.str()); } + + void returncstr() + { + check("const char *foo()\n" + "{\n" + " std::string s;\n" + " return s.c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Returning pointer to auto variable\n", errout.str()); + + check("std::string hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "\n" + "const char *f()\n" + "{\n" + " return hello().c_str();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Returning pointer to temporary\n", errout.str()); + } + }; REGISTER_TEST(TestAutoVariables)