Fixed #7331: Detect copy and move constructors with default parameters (#1018)

This commit is contained in:
Iván Matellanes 2018-01-07 14:20:19 +01:00 committed by Daniel Marjamäki
parent 8c33a95b49
commit cfeea3d35c
3 changed files with 72 additions and 26 deletions

View File

@ -50,6 +50,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
createSymbolDatabaseFindAllScopes();
createSymbolDatabaseClassInfo();
createSymbolDatabaseVariableInfo();
createSymbolDatabaseCopyAndMoveConstructors();
createSymbolDatabaseFunctionScopes();
createSymbolDatabaseClassAndStructScopes();
createSymbolDatabaseFunctionReturnTypes();
@ -421,29 +422,7 @@ void SymbolDatabase::createSymbolDatabaseFindAllScopes()
// destructor
if (function.tokenDef->previous()->str() == "~")
function.type = Function::eDestructor;
// copy/move constructor?
else if (Token::Match(function.tokenDef, "%name% ( const| %name% &|&& &| %name%| )") ||
Token::Match(function.tokenDef, "%name% ( const| %name% <")) {
const Token* typeTok = function.tokenDef->tokAt(2);
if (typeTok->str() == "const")
typeTok = typeTok->next();
if (typeTok->strAt(1) == "<") { // TODO: Remove this branch (#4710)
if (Token::Match(typeTok->linkAt(1), "> & %name%| )"))
function.type = Function::eCopyConstructor;
else if (Token::Match(typeTok->linkAt(1), "> &&|& & %name%| )"))
function.type = Function::eMoveConstructor;
else
function.type = Function::eConstructor;
} else if (typeTok->strAt(1) == "&&" || typeTok->strAt(2) == "&")
function.type = Function::eMoveConstructor;
else
function.type = Function::eCopyConstructor;
if (typeTok->str() != function.tokenDef->str())
function.type = Function::eConstructor; // Overwrite, if types are not identical
}
// regular constructor
// constructor of any kind
else
function.type = Function::eConstructor;
@ -504,9 +483,6 @@ void SymbolDatabase::createSymbolDatabaseFindAllScopes()
// count the number of constructors
if (function.isConstructor())
scope->numConstructors++;
if (function.type == Function::eCopyConstructor ||
function.type == Function::eMoveConstructor)
scope->numCopyOrMoveConstructors++;
// assume implementation is inline (definition and implementation same)
function.token = function.tokenDef;
@ -917,6 +893,33 @@ void SymbolDatabase::createSymbolDatabaseVariableInfo()
}
}
void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors()
{
// fill in class and struct copy/move constructors
for (std::list<Scope>::iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) {
if (!scope->isClassOrStruct())
continue;
std::list<Function>::iterator func;
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (!func->isConstructor() || func->minArgCount() != 1)
continue;
const Variable* firstArg = func->getArgumentVar(0);
if (firstArg->type() == scope->definedType) {
if (firstArg->isRValueReference())
func->type = Function::eMoveConstructor;
else if (firstArg->isReference())
func->type = Function::eCopyConstructor;
}
if (func->type == Function::eCopyConstructor ||
func->type == Function::eMoveConstructor)
scope->numCopyOrMoveConstructors++;
}
}
}
void SymbolDatabase::createSymbolDatabaseFunctionScopes()
{
// fill in function scopes

View File

@ -1163,6 +1163,7 @@ private:
void createSymbolDatabaseFindAllScopes();
void createSymbolDatabaseClassInfo();
void createSymbolDatabaseVariableInfo();
void createSymbolDatabaseCopyAndMoveConstructors();
void createSymbolDatabaseFunctionScopes();
void createSymbolDatabaseClassAndStructScopes();
void createSymbolDatabaseFunctionReturnTypes();

View File

@ -1270,6 +1270,12 @@ private:
}
void testConstructors() {
{
GET_SYMBOL_DB("class Foo { Foo(); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo f); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
@ -1282,18 +1288,54 @@ private:
ASSERT(db && ctor && ctor->type == Function::eConstructor && ctor->isExplicit());
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Bar& f); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo& f); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eCopyConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(const Foo &f); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eCopyConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("template <T> class Foo { Foo(Foo<T>& f); };");
const Function* ctor = tokenizer.tokens()->tokAt(7)->function();
ASSERT(db && ctor && ctor->type == Function::eCopyConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo& f, int default = 0); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eCopyConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo& f, char noDefault); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo&& f); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eMoveConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
{
GET_SYMBOL_DB("class Foo { Foo(Foo & & f, int default = 1, bool defaultToo = true); };");
const Function* ctor = tokenizer.tokens()->tokAt(3)->function();
ASSERT(db && ctor && ctor->type == Function::eMoveConstructor);
ASSERT(ctor && ctor->retDef == 0);
}
}
void functionDeclarationTemplate() {