diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 3a0221b2d..5b8ce3b91 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -306,3 +306,86 @@ void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok) reportError(tok, Severity::error, "autoVariables", "Wrong assignment of an auto-variable to an effective parameter of a function"); } + +void CheckAutoVariables::returnReference() +{ + // locate function that returns a reference.. + 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, "%type% & %var% (") || + Token::Match(tok, "> & %var% (")) + { + // go to the '(' + const Token *tok2 = tok->tokAt(3); + + // 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 "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()); + else if (Token::Match(tok2, "%type% < %any% > %var% ;")) + localvar.insert(tok2->tokAt(4)->varId()); + } + + // return.. + else if (Token::Match(tok2, "return %var% ;")) + { + // is the returned variable a local variable? + if ((tok2->next()->varId() > 0) && + (localvar.find(tok2->next()->varId()) != localvar.end())) + { + // report error.. + errorReturnReference(tok2); + } + } + } + } + } + } +} + +void CheckAutoVariables::errorReturnReference(const Token *tok) +{ + reportError(tok, Severity::error, "returnReference", "Returning reference to auto variable"); +} + + diff --git a/lib/checkautovariables.h b/lib/checkautovariables.h index af6c02d9a..217f07f6f 100644 --- a/lib/checkautovariables.h +++ b/lib/checkautovariables.h @@ -47,6 +47,7 @@ public: CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); + checkAutoVariables.returnReference(); } /** Check auto variables */ @@ -55,6 +56,9 @@ public: /** Returning pointer to local array */ void returnPointerToLocalArray(); + /** Returning reference to local/temporary variable */ + void returnReference(); + private: std::set fp_list; std::set vd_list; @@ -67,15 +71,15 @@ private: - void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok); - + void errorReturnReference(const Token *tok); void getErrorMessages() { errorAutoVariableAssignment(0); errorReturnPointerToLocalArray(0); + errorReturnReference(0); } std::string name() const @@ -88,7 +92,8 @@ 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" - "* assigning address of an variable to an effective parameter of a function\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 cefbe20c6..09e13cc31 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -57,6 +57,7 @@ private: CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this); checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); + checkAutoVariables.returnReference(); } void run() @@ -69,6 +70,9 @@ private: TEST_CASE(returnLocalVariable1); TEST_CASE(returnLocalVariable2); + + // return reference.. + TEST_CASE(returnReference); } @@ -95,7 +99,8 @@ private: check("void func1(int* arr[2])\n" "{\n" " int num=2;" - "arr[0]=#}"); + " arr[0]=#\n" + "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Wrong assignment of an auto-variable to an effective parameter of a function\n", errout.str()); } @@ -159,6 +164,31 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); } + + void returnReference() + { + check("std::string &foo()\n" + "{\n" + " std::string s;\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Returning reference to auto variable\n", errout.str()); + + check("std::vector &foo()\n" + "{\n" + " std::vector v;\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Returning reference to auto variable\n", errout.str()); + + check("std::vector &foo()\n" + "{\n" + " static std::vector v;\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + } }; REGISTER_TEST(TestAutoVariables)