Implemented support for rvalue references (C++11):

- Split up && when it is part of an rvalue reference declaration
- Added support into symbol database
- Current implementation sets Variable::isReference() to true also for rvalue references - they can probably be treated like normal references in many checks.

Changed behaviour of symbol database: Insert argument Variable of functions that are not implemented into SymbolDatabase::_variableList
This commit is contained in:
PKEuS 2013-04-04 09:47:44 -07:00
parent ba8cca8fa9
commit 188096665c
5 changed files with 67 additions and 12 deletions

View File

@ -839,10 +839,6 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
// add all function parameters
std::list<Function>::const_iterator func;
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
// ignore function without implementations
if (!func->hasBody)
continue;
std::list<Variable>::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();
}

View File

@ -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

View File

@ -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<Token*>(var->typeEndToken())->str("&");
const_cast<Token*>(var->typeEndToken())->insertToken("&");
}
}
return true;
}
//---------------------------------------------------------------------------
@ -2555,7 +2566,7 @@ static bool setVarIdParseDeclaration(const Token **tok, const std::map<std::stri
bool ok = tok2->findClosingBracket(tok2);
if (!ok || !tok2)
break;
} else if (tok2->str() == "&") {
} else if (tok2->str() == "&" || tok2->str() == "&&") {
ref = true;
} else if (tok2->str() != "*" && tok2->str() != "::") {
break;

View File

@ -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));
}
}

View File

@ -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"