From 901b775471c178b65c24d881a73c9947af7b5657 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 8 Jun 2023 07:47:21 +0200 Subject: [PATCH] Fix scope for function returning rvalue reference, insert std:: in return statement (#5119) * Fix scope for function returning rvalue reference * Insert std:: in return statement * Fix failing test --- lib/checkother.cpp | 2 +- lib/symboldatabase.cpp | 10 +++++----- lib/symboldatabase.h | 2 +- lib/tokenize.cpp | 2 +- test/testsymboldatabase.cpp | 13 +++++++++++++ test/testtokenize.cpp | 11 +++++++++++ 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 6698b96df..39a417101 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2174,7 +2174,7 @@ void CheckOther::checkMisusedScopedObject() } if (tok->isAssignmentOp() && Token::simpleMatch(tok->astOperand1(), "(") && tok->astOperand1()->astOperand1()) { if (const Function* ftok = tok->astOperand1()->astOperand1()->function()) { - if (ftok->retType && Token::Match(ftok->retType->classDef, "class|struct|union") && !Function::returnsReference(ftok)) + if (ftok->retType && Token::Match(ftok->retType->classDef, "class|struct|union") && !Function::returnsReference(ftok, /*unknown*/ false, /*includeRValueRef*/ true)) misusedScopeObjectError(tok->next(), ftok->retType->name(), /*isAssignment*/ true); } } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index febcea819..c1817f301 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1833,7 +1833,7 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const // regular function? else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && tok->previous() && - (Token::Match(tok->previous(), "%name%|>|&|*|::|~") || // Either a return type or scope qualifier in front of tok + (Token::Match(tok->previous(), "%name%|>|&|&&|*|::|~") || // Either a return type or scope qualifier in front of tok outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor const Token* tok1 = tok->previous(); const Token* tok2 = tok->next()->link()->next(); @@ -1874,7 +1874,7 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const // done if constructor or destructor if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) { // skip over pointers and references - while (Token::Match(tok1, "%type%|*|&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const")) + while (Token::Match(tok1, "%type%|*|&|&&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const")) tok1 = tok1->previous(); // skip over decltype @@ -3046,10 +3046,10 @@ bool Function::returnsConst(const Function* function, bool unknown) }); } -bool Function::returnsReference(const Function* function, bool unknown) +bool Function::returnsReference(const Function* function, bool unknown, bool includeRValueRef) { - return checkReturns(function, unknown, false, [](UNUSED const Token* defStart, const Token* defEnd) { - return Token::simpleMatch(defEnd->previous(), "&"); + return checkReturns(function, unknown, false, [includeRValueRef](UNUSED const Token* defStart, const Token* defEnd) { + return includeRValueRef ? Token::Match(defEnd->previous(), "&|&&") : Token::simpleMatch(defEnd->previous(), "&"); }); } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 988769f5b..1846e75f5 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -936,7 +936,7 @@ public: static bool returnsConst(const Function* function, bool unknown = false); static bool returnsPointer(const Function* function, bool unknown = false); - static bool returnsReference(const Function* function, bool unknown = false); + static bool returnsReference(const Function* function, bool unknown = false, bool includeRValueRef = false); static bool returnsStandardType(const Function* function, bool unknown = false); static bool returnsVoid(const Function* function, bool unknown = false); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 177407e7f..cd11989de 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9439,7 +9439,7 @@ void Tokenizer::simplifyNamespaceStd() const Token *start = tok; while (Token::Match(start->previous(), "%type%|*|&")) start = start->previous(); - if (start != tok && start->isName() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) + if (start != tok && start->isName() && !start->isKeyword() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) userFunctions.insert(tok->str()); } if (userFunctions.find(tok->str()) == userFunctions.end() && mSettings->library.matchArguments(tok, "std::" + tok->str())) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index c0f8edcbc..6c664e169 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -390,6 +390,7 @@ private: TEST_CASE(isFunction1); // UNKNOWN_MACRO(a,b) { .. } TEST_CASE(isFunction2); + TEST_CASE(isFunction3); TEST_CASE(findFunction1); TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument @@ -5839,6 +5840,18 @@ private: ASSERT(db->findScopeByName("PTRRELOC") == nullptr); } + void isFunction3() { + GET_SYMBOL_DB("std::vector&& f(std::vector& v) {\n" + " v.push_back(1);\n" + " return std::move(v);\n" + "}"); + ASSERT(db != nullptr); + ASSERT_EQUALS(2, db->scopeList.size()); + const Token* ret = Token::findsimplematch(tokenizer.tokens(), "return"); + ASSERT(ret != nullptr); + ASSERT(ret->scope() && ret->scope()->type == Scope::eFunction); + } + void findFunction1() { GET_SYMBOL_DB("int foo(int x);\n" /* 1 */ diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index a6f5025ad..374a295ec 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4711,6 +4711,17 @@ private: "void f() { string str = to_string(1); }\n"; expected = "void f ( ) { std :: string str ; str = std :: to_string ( 1 ) ; }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); + + code = "using namespace std;\n" + "vector&& f(vector& v) {\n" + " v.push_back(1);\n" + " return move(v);\n" + "}\n"; + expected = "std :: vector < int > && f ( std :: vector < int > & v ) {\n" + "v . push_back ( 1 ) ;\n" + "return std :: move ( v ) ;\n" + "}"; + ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void microsoftMemory() {