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:
parent
ba8cca8fa9
commit
188096665c
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1964,10 +1964,21 @@ bool Tokenizer::tokenize(std::istream &code,
|
|||
|
||||
simplifyEmptyNamespaces();
|
||||
|
||||
bool valid = validate();
|
||||
if (valid)
|
||||
if (!validate())
|
||||
return false;
|
||||
|
||||
createSymbolDatabase();
|
||||
return valid;
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue