diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 2349c6440..5ede3d9ae 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -839,10 +839,6 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti // add all function parameters std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { - // ignore function without implementations - if (!func->hasBody) - continue; - std::list::const_iterator arg; for (arg = func->argumentList.begin(); arg != func->argumentList.end(); ++arg) { // check for named parameters @@ -997,8 +993,14 @@ void Variable::evaluate() else if (tok->str() == "*") { setFlag(fIsPointer, true); setFlag(fIsConst, false); // Points to const, isn't necessarily const itself - } else if (tok->str() == "&") + } else if (tok->str() == "&") { + if (isReference()) + setFlag(fIsRValueRef, true); setFlag(fIsReference, true); + } else if (tok->str() == "&&") { // Before simplification, && isn't split up + setFlag(fIsRValueRef, true); + setFlag(fIsReference, true); // Set also fIsReference + } if (tok->str() == "<") tok->findClosingBracket(tok); @@ -1567,6 +1569,7 @@ void SymbolDatabase::printVariable(const Variable *var, const char *indent) cons std::cout << indent << " isArray: " << (var->isArray() ? "true" : "false") << std::endl; std::cout << indent << " isPointer: " << (var->isPointer() ? "true" : "false") << std::endl; std::cout << indent << " isReference: " << (var->isReference() ? "true" : "false") << std::endl; + std::cout << indent << " isRValueRef: " << (var->isRValueReference() ? "true" : "false") << std::endl; std::cout << indent << " hasDefault: " << (var->hasDefault() ? "true" : "false") << std::endl; std::cout << indent << "_type: "; if (var->type()) { @@ -2281,7 +2284,7 @@ static const Token* skipScopeIdentifiers(const Token* tok) static const Token* skipPointers(const Token* tok) { - while (Token::Match(tok, "*|&")) { + while (Token::Match(tok, "*|&|&&")) { tok = tok->next(); } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index b710c3329..6b69555a4 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -111,7 +111,8 @@ class CPPCHECKLIB Variable { fIsArray = (1 << 5), /** @brief array variable */ fIsPointer = (1 << 6), /** @brief pointer variable */ fIsReference = (1 << 7), /** @brief reference variable */ - fHasDefault = (1 << 8) /** @brief function argument with default value */ + fIsRValueRef = (1 << 8), /** @brief rvalue reference variable */ + fHasDefault = (1 << 9) /** @brief function argument with default value */ }; /** @@ -341,6 +342,14 @@ public: return getFlag(fIsReference); } + /** + * Is reference variable. + * @return true if reference, false otherwise + */ + bool isRValueReference() const { + return getFlag(fIsRValueRef); + } + /** * Does variable have a default value. * @return true if has a default falue, false if not diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index e05f73e5e..1f7ca0984 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1964,10 +1964,21 @@ bool Tokenizer::tokenize(std::istream &code, simplifyEmptyNamespaces(); - bool valid = validate(); - if (valid) - createSymbolDatabase(); - return valid; + if (!validate()) + return false; + + createSymbolDatabase(); + + // Use symbol database to identify rvalue references. Split && to & &. This is safe, since it doesn't delete any tokens (which might be referenced by symbol database) + for (std::size_t i = 0; i < _symbolDatabase->getVariableListSize(); i++) { + const Variable* var = _symbolDatabase->getVariableFromVarId(i); + if (var && var->isRValueReference()) { + const_cast(var->typeEndToken())->str("&"); + const_cast(var->typeEndToken())->insertToken("&"); + } + } + + return true; } //--------------------------------------------------------------------------- @@ -2555,7 +2566,7 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::mapfindClosingBracket(tok2); if (!ok || !tok2) break; - } else if (tok2->str() == "&") { + } else if (tok2->str() == "&" || tok2->str() == "&&") { ref = true; } else if (tok2->str() != "*" && tok2->str() != "::") { break; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 07b5a32d8..403ccec39 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -118,6 +118,7 @@ private: TEST_CASE(isVariableDeclarationIdentifiesReference); TEST_CASE(isVariableDeclarationDoesNotIdentifyTemplateClass); TEST_CASE(isVariableDeclarationPointerConst); + TEST_CASE(isVariableDeclarationRValueRef); TEST_CASE(staticMemberVar); @@ -527,6 +528,18 @@ private: ASSERT(false == v.isReference()); } + void isVariableDeclarationRValueRef() { + reset(); + givenACodeSampleToTokenize var("int&& i;"); + bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0); + ASSERT(false == v.isArray()); + ASSERT(false == v.isPointer()); + ASSERT(true == v.isReference()); + ASSERT(true == v.isRValueReference()); + } + void staticMemberVar() { GET_SYMBOL_DB("class Foo {\n" " static const double d;\n" @@ -756,6 +769,8 @@ private: ASSERT(foo_int && foo_int->tokenDef->str() == "foo"); ASSERT(foo_int && !foo_int->hasBody); ASSERT(foo_int && foo_int->tokenDef->strAt(2) == "int"); + + ASSERT(&foo_int->argumentList.front() == db->getVariableFromVarId(1)); } } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 6e3c5031e..484e84b25 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -272,6 +272,7 @@ private: TEST_CASE(varid_templateNamespaceFuncPtr); // #4172 TEST_CASE(varid_variadicFunc); TEST_CASE(varid_typename); // #4644 + TEST_CASE(varid_rvalueref); TEST_CASE(varidclass1); TEST_CASE(varidclass2); @@ -4312,6 +4313,22 @@ private: "1: typename A a@1 ;\n", tokenizeDebugListing("typename A a;")); } + void varid_rvalueref() { + ASSERT_EQUALS("\n\n##file 0\n" + "1: int & & a@1 ;\n", tokenizeDebugListing("int&& a;")); + + ASSERT_EQUALS("\n\n##file 0\n" + "1: void foo ( int & & a@1 ) { }\n", tokenizeDebugListing("void foo(int&& a) {}")); + + ASSERT_EQUALS("\n\n##file 0\n" + "1: class C {\n" + "2: C ( int & & a@1 ) ;\n" + "3: } ;\n", + tokenizeDebugListing("class C {\n" + " C(int&& a);\n" + "};")); + } + void varidclass1() { const std::string actual = tokenizeDebugListing( "class Fred\n"