diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index f33a9a8ae..10b53cd2b 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -134,178 +134,172 @@ void CheckAutoVariables::addVDA(unsigned int varId) void CheckAutoVariables::autoVariables() { - bool begin_function = false; - bool begin_function_decl = false; - int bindent = 0; + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - // Which variables have an unknown type? - std::set unknown_type; + std::list::const_iterator i; - const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); - - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + for (i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { + const Scope *scope = *i; - if (Token::Match(tok, "%type% *|::| %var% (")) - { - begin_function = true; - fp_list.clear(); - vd_list.clear(); - vda_list.clear(); - } - else if (begin_function && begin_function_decl && Token::Match(tok, "%type% * * %var%")) - { - fp_list.insert(tok->tokAt(3)->str()); - } - else if (begin_function && begin_function_decl && Token::Match(tok, "%type% * %var% [")) - { - fp_list.insert(tok->tokAt(2)->str()); - } - else if (begin_function && tok->str() == "(") - { - begin_function_decl = true; - } - else if (begin_function && tok->str() == ")") - { - begin_function_decl = false; - } - else if (begin_function && tok->str() == "{") - { - bindent++; - } - else if (begin_function && tok->str() == "}") - { - bindent--; - } - else if (bindent <= 0) - { + // only check functions + if (scope->type != Scope::eFunction) continue; - } - // Inside a function body - if (Token::Match(tok, "%type% :: %any%") && !isExternOrStatic(tok)) + fp_list.clear(); + vd_list.clear(); + vda_list.clear(); + + const Token *tok = scope->classDef->next(); + + for (; tok && tok != scope->classDef->next()->link(); tok = tok->next()) { - addVD(tok->tokAt(2)->varId()); - } - else if (Token::Match(tok, "%type% %var% [")) - { - addVDA(tok->next()->varId()); - } - else if (Token::Match(tok, "%var% %var% ;") && !isExternOrStatic(tok) && isTypeName(tok)) - { - addVD(tok->next()->varId()); - if (!tok->isStandardType() && - !symbolDatabase->isClassOrStruct(tok->str())) + if (Token::Match(tok, "%type% * * %var%")) { - unknown_type.insert(tok->next()->varId()); + fp_list.insert(tok->tokAt(3)->str()); + } + else if (Token::Match(tok, "%type% * %var% [")) + { + fp_list.insert(tok->tokAt(2)->str()); } } - else if (Token::Match(tok, "const %var% %var% ;") && !isExternOrStatic(tok) && isTypeName(tok->next())) + + unsigned int indentlevel = 0; + // Which variables have an unknown type? + std::set unknown_type; + for (tok = scope->classDef->next()->link(); tok; tok = tok->next()) { - addVD(tok->tokAt(2)->varId()); + // indentlevel.. + if (tok->str() == "{") + ++indentlevel; + else if (tok->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + + // Inside a function body + if (Token::Match(tok, "%type% :: %any%") && !isExternOrStatic(tok)) + { + addVD(tok->tokAt(2)->varId()); + } + else if (Token::Match(tok, "%type% %var% [")) + { + addVDA(tok->next()->varId()); + } + else if (Token::Match(tok, "%var% %var% ;") && !isExternOrStatic(tok) && isTypeName(tok)) + { + addVD(tok->next()->varId()); + if (!tok->isStandardType() && + !symbolDatabase->isClassOrStruct(tok->str())) + { + unknown_type.insert(tok->next()->varId()); + } + } + else if (Token::Match(tok, "const %var% %var% ;") && !isExternOrStatic(tok) && isTypeName(tok->next())) + { + addVD(tok->tokAt(2)->varId()); + } + + //Critical assignment + else if (Token::Match(tok, "[;{}] %var% = & %var%") && errorAv(tok->tokAt(1), tok->tokAt(4))) + { + errorAutoVariableAssignment(tok); + } + else if (Token::Match(tok, "[;{}] * %var% = & %var%") && errorAv(tok->tokAt(2), tok->tokAt(5)) && + unknown_type.find(tok->tokAt(5)->varId()) == unknown_type.end()) + { + errorAutoVariableAssignment(tok); + } + else if (Token::Match(tok, "[;{}] %var% [ %any% ] = & %var%") && errorAv(tok->tokAt(1), tok->tokAt(7))) + { + errorAutoVariableAssignment(tok); + } + // Critical return + else if (Token::Match(tok, "return & %var% ;") && isAutoVar(tok->tokAt(2)->varId())) + { + reportError(tok, Severity::error, "autoVariables", "Return of the address of an auto-variable"); + } + // Invalid pointer deallocation + else if (Token::Match(tok, "free ( %var% ) ;") && isAutoVarArray(tok->tokAt(2)->varId())) + { + reportError(tok, Severity::error, "autoVariables", "Invalid deallocation"); + } } - //Critical assignment - else if (Token::Match(tok, "[;{}] %var% = & %var%") && errorAv(tok->tokAt(1), tok->tokAt(4))) - { - errorAutoVariableAssignment(tok); - } - else if (Token::Match(tok, "[;{}] * %var% = & %var%") && errorAv(tok->tokAt(2), tok->tokAt(5)) && - unknown_type.find(tok->tokAt(5)->varId()) == unknown_type.end()) - { - errorAutoVariableAssignment(tok); - } - else if (Token::Match(tok, "[;{}] %var% [ %any% ] = & %var%") && errorAv(tok->tokAt(1), tok->tokAt(7))) - { - errorAutoVariableAssignment(tok); - } - // Critical return - else if (Token::Match(tok, "return & %var% ;") && isAutoVar(tok->tokAt(2)->varId())) - { - reportError(tok, Severity::error, "autoVariables", "Return of the address of an auto-variable"); - } - // Invalid pointer deallocation - else if (Token::Match(tok, "free ( %var% ) ;") && isAutoVarArray(tok->tokAt(2)->varId())) - { - reportError(tok, Severity::error, "autoVariables", "Invalid deallocation"); - } + vd_list.clear(); + vda_list.clear(); + fp_list.clear(); } - - vd_list.clear(); - vda_list.clear(); - fp_list.clear(); } + //--------------------------------------------------------------------------- - - void CheckAutoVariables::returnPointerToLocalArray() { - bool infunc = false; - int indentlevel = 0; - std::set arrayVar; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + + std::list::const_iterator i; + + for (i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { - // Is there a function declaration for a function that returns a pointer? - if (!infunc && (Token::Match(tok, "%type% * %var% (") || Token::Match(tok, "%type% * %var% :: %var% ("))) + const Scope *scope = *i; + + // only check functions + if (scope->type != Scope::eFunction) + continue; + + const Token *tok = scope->classDef; + + // skip any qualification + while (Token::Match(tok->tokAt(-2), "%type% ::")) + tok = tok->tokAt(-2); + + // have we reached a function that returns a pointer + if (Token::Match(tok->tokAt(-2), "%type% *")) { - for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == ")") - { - tok = tok2; - break; - } - } - if (Token::simpleMatch(tok, ") {")) - { - infunc = true; - indentlevel = 0; - arrayVar.clear(); - } - } + // go to the '(' + const Token *tok2 = scope->classDef->next(); - // Parsing a function that returns a pointer.. - if (infunc) - { - if (tok->str() == "{") - { - ++indentlevel; - } - else if (tok->str() == "}") - { - --indentlevel; - if (indentlevel <= 0) - { - infunc = false; - } - continue; - } + // go to the ')' + tok2 = tok2->next()->link(); - // Declaring a local array.. - if (Token::Match(tok, "[;{}] %type% %var% [")) + unsigned int indentlevel = 0; + std::set arrayVar; + for (; tok2; tok2 = tok2->next()) { - const unsigned int varid = tok->tokAt(2)->varId(); - if (varid > 0) + // indentlevel.. + if (tok2->str() == "{") + ++indentlevel; + else if (tok2->str() == "}") { - arrayVar.insert(varid); + if (indentlevel <= 1) + break; + --indentlevel; } - } - // Return pointer to local array variable.. - if (Token::Match(tok, "return %var% ;")) - { - const unsigned int varid = tok->next()->varId(); - if (varid > 0 && arrayVar.find(varid) != arrayVar.end()) + // Declaring a local array.. + if (Token::Match(tok2, "[;{}] %type% %var% [")) { - errorReturnPointerToLocalArray(tok); + const unsigned int varid = tok2->tokAt(2)->varId(); + if (varid > 0) + { + arrayVar.insert(varid); + } + } + + // Return pointer to local array variable.. + if (Token::Match(tok2, "return %var% ;")) + { + const unsigned int varid = tok2->next()->varId(); + if (varid > 0 && arrayVar.find(varid) != arrayVar.end()) + { + errorReturnPointerToLocalArray(tok2); + } } } } - - // Declaring array variable.. - - } } @@ -324,6 +318,8 @@ void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok) "is invalid after the function ends."); } +//--------------------------------------------------------------------------- + // return temporary? bool CheckAutoVariables::returnTemporary(const Token *tok) const { @@ -332,89 +328,91 @@ bool CheckAutoVariables::returnTemporary(const Token *tok) const return bool(0 != Token::findmatch(_tokenizer->tokens(), ("std :: string " + tok->next()->str() + " (").c_str())); } +//--------------------------------------------------------------------------- void CheckAutoVariables::returnReference() { - // locate function that returns a reference.. - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + + std::list::const_iterator i; + + for (i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { - // Skip executable scopes.. - if (Token::Match(tok, ") const| {")) - { - tok = tok->next(); - if (tok->str() == "const") - tok = tok->next(); - tok = tok->link(); + const Scope *scope = *i; + + // only check functions + if (scope->type != Scope::eFunction) continue; - } + + const Token *tok = scope->classDef; + + // skip any qualification + while (Token::Match(tok->tokAt(-2), "%type% ::")) + tok = tok->tokAt(-2); // have we reached a function that returns a reference? - if (Token::Match(tok, "%type% & %var% (") || - Token::Match(tok, "> & %var% (")) + if (Token::Match(tok->tokAt(-2), "%type% &") || + Token::Match(tok->tokAt(-2), "> &")) { // go to the '(' - const Token *tok2 = tok->tokAt(3); + const Token *tok2 = scope->classDef->next(); // 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 + for (; tok2; tok2 = tok2->next()) { - 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() == "}") { - // indentlevel.. - if (tok2->str() == "{") - ++indentlevel; - else if (tok2->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } + if (indentlevel <= 1) + break; + --indentlevel; + } - // declare local variable.. - if (Token::Match(tok2, "[{};] %type%") && tok2->next()->str() != "return") - { - // goto next token.. + // 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 "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); - // 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()); + } - // 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); - } - } - - // return reference to temporary.. - else if (returnTemporary(tok2)) + // 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.. - errorReturnTempReference(tok2); + errorReturnReference(tok2); } } + + // return reference to temporary.. + else if (returnTemporary(tok2)) + { + // report error.. + errorReturnTempReference(tok2); + } } } } @@ -430,91 +428,90 @@ void CheckAutoVariables::errorReturnTempReference(const Token *tok) reportError(tok, Severity::error, "returnTempReference", "Returning reference to temporary"); } +//--------------------------------------------------------------------------- // 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; - } + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - // have we reached a function that returns a reference? - if (Token::simpleMatch(tok, "const char *")) + std::list::const_iterator i; + + for (i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) + { + const Scope *scope = *i; + + // only check functions + if (scope->type != Scope::eFunction) + continue; + + const Token *tok = scope->classDef; + + // skip any qualification + while (Token::Match(tok->tokAt(-2), "%type% ::")) + tok = tok->tokAt(-2); + + // have we reached a function that returns a const char * + if (Token::simpleMatch(tok->tokAt(-3), "const char *")) { // go to the '(' - const Token *tok2 = tok->tokAt(3); - while (Token::Match(tok2, "%var% ::")) - tok2 = tok2->tokAt(2); - if (!Token::Match(tok2, "%var% (")) - continue; + const Token *tok2 = scope->classDef->next(); // go to the ')' tok2 = tok2->next()->link(); - // is this a function implementation? - if (Token::Match(tok2, ") const| {")) + unsigned int indentlevel = 0; + std::set localvar; // local variables in function + for (; tok2; tok2 = tok2->next()) { - 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() == "}") { - // indentlevel.. - if (tok2->str() == "{") - ++indentlevel; - else if (tok2->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } + if (indentlevel <= 1) + break; + --indentlevel; + } - // declare local variable.. - if (Token::Match(tok2, "[{};] %type%") && tok2->next()->str() != "return") - { - // goto next token.. + // 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 "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); - // 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()); + } - // 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); - } - } - - // return pointer to temporary.. - else if (returnTemporary(tok2)) + // 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.. - errorReturnTempPointer(tok2); + errorReturnAutocstr(tok2); } } + + // return pointer to temporary.. + else if (returnTemporary(tok2)) + { + // report error.. + errorReturnTempPointer(tok2); + } } } } @@ -529,6 +526,3 @@ void CheckAutoVariables::errorReturnTempPointer(const Token *tok) { reportError(tok, Severity::error, "returnTempPointer", "Returning pointer to temporary"); } - - - diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 1c0824526..221258cc1 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -65,9 +65,12 @@ private: void run() { - TEST_CASE(testautovar); - TEST_CASE(testautovar_array); - TEST_CASE(testautovar_return); + TEST_CASE(testautovar1); + TEST_CASE(testautovar2); + TEST_CASE(testautovar_array1); + TEST_CASE(testautovar_array2); + TEST_CASE(testautovar_return1); + TEST_CASE(testautovar_return2); TEST_CASE(testautovar_extern); TEST_CASE(testinvaliddealloc); TEST_CASE(testassign); // Ticket #1819 @@ -76,15 +79,17 @@ private: TEST_CASE(returnLocalVariable2); // return reference.. - TEST_CASE(returnReference); + TEST_CASE(returnReference1); + TEST_CASE(returnReference2); // return c_str().. - TEST_CASE(returncstr); + TEST_CASE(returncstr1); + TEST_CASE(returncstr2); } - void testautovar() + void testautovar1() { check("void func1(int **res)\n" "{\n" @@ -101,7 +106,30 @@ private: ASSERT_EQUALS("", errout.str()); } - void testautovar_array() + void testautovar2() + { + check("class Fred {\n" + " void func1(int **res);\n" + "}\n" + "void Fred::func1(int **res)\n" + "{\n" + " int num = 2;\n" + " *res = #\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Assigning address of local auto-variable to a function parameter.\n", errout.str()); + + check("class Fred {\n" + " void func1(int **res);\n" + "}\n" + "void Fred::func1(int **res)\n" + "{\n" + " int num = 2;\n" + " foo.res = #\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + + void testautovar_array1() { check("void func1(int* arr[2])\n" "{\n" @@ -111,15 +139,42 @@ private: ASSERT_EQUALS("[test.cpp:3]: (error) Assigning address of local auto-variable to a function parameter.\n", errout.str()); } - void testautovar_return() + void testautovar_array2() + { + check("class Fred {\n" + " void func1(int* arr[2]);\n" + "}\n" + "void Fred::func1(int* arr[2])\n" + "{\n" + " int num=2;" + " arr[0]=#\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Assigning address of local auto-variable to a function parameter.\n", errout.str()); + } + + void testautovar_return1() { check("int* func1()\n" "{\n" " int num=2;" - "return #}"); + " return #" + "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return of the address of an auto-variable\n", errout.str()); } + void testautovar_return2() + { + check("class Fred {\n" + " int* func1()\n" + "}\n" + "int* Fred::func1()\n" + "{\n" + " int num=2;" + " return #" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Return of the address of an auto-variable\n", errout.str()); + } + void testautovar_extern() { check("struct foo *f()\n" @@ -169,6 +224,16 @@ private: " return str;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Returning pointer to local array variable\n", errout.str()); + + check("class Fred {\n" + " char *foo();\n" + "};\n" + "char *Fred::foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Returning pointer to local array variable\n", errout.str()); } void returnLocalVariable2() @@ -179,9 +244,19 @@ private: " return str;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("class Fred {\n" + " std::string foo();\n" + "};\n" + "std::string Fred::foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } - void returnReference() + void returnReference1() { check("std::string &foo()\n" "{\n" @@ -216,7 +291,67 @@ private: ASSERT_EQUALS("[test.cpp:8]: (error) Returning reference to temporary\n", errout.str()); } - void returncstr() + void returnReference2() + { + check("class Fred {\n" + " std::string &foo();\n" + "}\n" + "std::string &Fred::foo()\n" + "{\n" + " std::string s;\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Returning reference to auto variable\n", errout.str()); + + check("class Fred {\n" + " std::vector &foo();\n" + "};\n" + "std::vector &Fred::foo()\n" + "{\n" + " std::vector v;\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Returning reference to auto variable\n", errout.str()); + + check("class Fred {\n" + " std::vector &foo();\n" + "};\n" + "std::vector &Fred::foo()\n" + "{\n" + " static std::vector v;\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("class Fred {\n" + " std::string &f();\n" + "};\n" + "std::string hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "std::string &Fred::f()\n" + "{\n" + " return hello();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:10]: (error) Returning reference to temporary\n", errout.str()); + + check("class Fred {\n" + " std::string hello();\n" + " std::string &f();\n" + "};\n" + "std::string Fred::hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "std::string &Fred::f()\n" + "{\n" + " return hello();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:11]: (error) Returning reference to temporary\n", errout.str()); + } + + void returncstr1() { check("const char *foo()\n" "{\n" @@ -244,6 +379,43 @@ private: ASSERT_EQUALS("[test.cpp:8]: (error) Returning pointer to temporary\n", errout.str()); } + void returncstr2() + { + check("class Fred {\n" + " const char *foo();\n" + "};\n" + "const char *Fred::foo()\n" + "{\n" + " std::string s;\n" + " return s.c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Returning pointer to auto variable\n", errout.str()); + + check("class Fred {\n" + " const char *foo();\n" + "};\n" + "const char *Foo::f()\n" + "{\n" + " std::string s;\n" + " return s.c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Returning pointer to auto variable\n", errout.str()); + + check("class Fred {\n" + " std::string hello();\n" + " const char *f();\n" + "};\n" + "std::string Fred::hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "const char *Fred::f()\n" + "{\n" + " return hello().c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:11]: (error) Returning pointer to temporary\n", errout.str()); + } + }; REGISTER_TEST(TestAutoVariables)