Skip to content

Commit

Permalink
Implemented support for move constructors:
Browse files Browse the repository at this point in the history
Adapt code to Function::eMoveConstructor
introduced in commit eb29627
  • Loading branch information
zingsheim committed Apr 10, 2013
1 parent 7fdaba4 commit 54e7c8f
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 32 deletions.
64 changes: 47 additions & 17 deletions lib/checkclass.cpp
Expand Up @@ -120,8 +120,7 @@ void CheckClass::constructors()
std::vector<Usage> usage(scope->varlist.size());

for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (!func->hasBody || !(func->type == Function::eConstructor ||
func->type == Function::eCopyConstructor ||
if (!func->hasBody || !(func->isConstructor() ||
func->type == Function::eOperatorEqual))
continue;

Expand Down Expand Up @@ -160,8 +159,15 @@ void CheckClass::constructors()
}

// Check if type can't be copied
if (!var->isPointer() && var->typeScope() && canNotCopy(var->typeScope()))
continue;
if (!var->isPointer() && var->typeScope()) {
if (func->type == Function::eMoveConstructor) {
if (canNotMove(var->typeScope()))
continue;
} else {
if (canNotCopy(var->typeScope()))
continue;
}
}

// Don't warn about unknown types in copy constructors since we
// don't know if they can be copied or not..
Expand Down Expand Up @@ -197,7 +203,7 @@ void CheckClass::constructors()
const Scope *varType = var->typeScope();
if (!varType || varType->type != Scope::eUnion) {
if (func->type == Function::eConstructor &&
func->nestedIn && (func->nestedIn->numConstructors - func->nestedIn->numCopyConstructors) > 1 &&
func->nestedIn && (func->nestedIn->numConstructors - func->nestedIn->numCopyOrMoveConstructors) > 1 &&
func->argCount() == 0 && func->functionScope &&
func->arg && func->arg->link()->next() == func->functionScope->classStart &&
func->functionScope->classStart->link() == func->functionScope->classStart->next()) {
Expand Down Expand Up @@ -324,9 +330,10 @@ bool CheckClass::canNotCopy(const Scope *scope)
bool publicCopy = false;

for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (func->type == Function::eConstructor || func->type == Function::eCopyConstructor)
if (func->isConstructor())
constructor = true;
if (func->type == Function::eCopyConstructor && func->access == Public)
if ((func->type == Function::eCopyConstructor) &&
func->access == Public)
publicCopy = true;
else if (func->type == Function::eOperatorEqual && func->access == Public)
publicAssign = true;
Expand All @@ -335,6 +342,30 @@ bool CheckClass::canNotCopy(const Scope *scope)
return constructor && !(publicAssign || publicCopy);
}

bool CheckClass::canNotMove(const Scope *scope)
{
std::list<Function>::const_iterator func;
bool constructor = false;
bool publicAssign = false;
bool publicCopy = false;
bool publicMove = false;

for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (func->isConstructor())
constructor = true;
if ((func->type == Function::eCopyConstructor) &&
func->access == Public)
publicCopy = true;
else if ((func->type == Function::eMoveConstructor) &&
func->access == Public)
publicMove = true;
else if (func->type == Function::eOperatorEqual && func->access == Public)
publicAssign = true;
}

return constructor && !(publicAssign || publicCopy || publicMove);
}

void CheckClass::assignVar(const std::string &varname, const Scope *scope, std::vector<Usage> &usage)
{
std::list<Variable>::const_iterator var;
Expand Down Expand Up @@ -401,7 +432,7 @@ bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope)

void CheckClass::initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage)
{
bool initList = func.type == Function::eConstructor || func.type == Function::eCopyConstructor;
bool initList = func.isConstructor();
const Token *ftok = func.arg->link()->next();
int level = 0;
for (; ftok != func.functionScope->classEnd; ftok = ftok->next()) {
Expand Down Expand Up @@ -582,7 +613,7 @@ void CheckClass::initializeVarList(const Function &func, std::list<const Functio

// check if member function
if (ftok->function() && ftok->function()->nestedIn == scope &&
ftok->function()->type != Function::eConstructor) {
!ftok->function()->isConstructor()) {
const Function *member = ftok->function();

// recursive call
Expand Down Expand Up @@ -621,7 +652,7 @@ void CheckClass::initializeVarList(const Function &func, std::list<const Functio
// not member function
else {
// could be a base class virtual function, so we assume it initializes everything
if (func.type != Function::eConstructor && isBaseClassFunc(ftok, scope)) {
if (!func.isConstructor() && isBaseClassFunc(ftok, scope)) {
/** @todo False Negative: we should look at the base class functions to see if they
* call any derived class virtual functions that change the derived class state
*/
Expand Down Expand Up @@ -712,7 +743,7 @@ void CheckClass::initializationListUsage()
const Scope * scope = symbolDatabase->functionScopes[i];

// Check every constructor
if (!scope->function || (scope->function->type != Function::eConstructor && scope->function->type != Function::eCopyConstructor))
if (!scope->function || (!scope->function->isConstructor()))
continue;

const Scope* owner = scope->functionOf;
Expand All @@ -728,7 +759,8 @@ void CheckClass::initializationListUsage()
for (const Token* tok2 = tok->tokAt(2); tok2->str() != ";"; tok2 = tok2->next()) {
if (tok2->varId()) {
const Variable* var2 = tok2->variable();
if (var2 && var2->scope() == owner) { // Is there a dependency between two member variables?
if (var2 && var2->scope() == owner &&
tok2->strAt(-1)!=".") { // Is there a dependency between two member variables?
allowed = false;
break;
}
Expand Down Expand Up @@ -1816,7 +1848,7 @@ void CheckClass::initializerListOrder()

// iterate through all member functions looking for constructors
for (func = info->functionList.begin(); func != info->functionList.end(); ++func) {
if ((func->type == Function::eConstructor || func->type == Function::eCopyConstructor) && func->hasBody) {
if ((func->isConstructor()) && func->hasBody) {
// check for initializer list
const Token *tok = func->arg->link()->next();

Expand Down Expand Up @@ -1878,10 +1910,8 @@ void CheckClass::checkPureVirtualFunctionCall()
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
if (scope->function == 0 || !scope->function->hasBody ||
!(scope->function->type==Function::eConstructor ||
scope->function->type==Function::eCopyConstructor ||
scope->function->type==Function::eMoveConstructor ||
scope->function->type==Function::eDestructor))
!(scope->function->isConstructor() ||
scope->function->isDestructor()))
continue;

const std::list<const Token *> & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap);
Expand Down
2 changes: 2 additions & 0 deletions lib/checkclass.h
Expand Up @@ -279,6 +279,8 @@ class CPPCHECKLIB CheckClass : public Check {
std::list<const Token *> & pureFuncStack);

static bool canNotCopy(const Scope *scope);

static bool canNotMove(const Scope *scope);
};
/// @}
//---------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions lib/checkmemoryleak.cpp
Expand Up @@ -2368,8 +2368,8 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam
// Inspect member functions
std::list<Function>::const_iterator func;
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
const bool constructor = func->type == Function::eConstructor;
const bool destructor = func->type == Function::eDestructor;
const bool constructor = func->isConstructor();
const bool destructor = func->isDestructor();
if (!func->hasBody) {
if (destructor) { // implementation for destructor is not seen => assume it deallocates all variables properly
deallocInDestructor = true;
Expand Down
2 changes: 1 addition & 1 deletion lib/checknullpointer.cpp
Expand Up @@ -1131,7 +1131,7 @@ void CheckNullPointer::nullConstantDereference()

const Token *tok = scope->classStart;

if (scope->function && (scope->function->type == Function::eConstructor || scope->function->type == Function::eCopyConstructor))
if (scope->function && scope->function->isConstructor())
tok = scope->function->token; // Check initialization list

for (; tok != scope->classEnd; tok = tok->next()) {
Expand Down
13 changes: 6 additions & 7 deletions lib/symboldatabase.cpp
Expand Up @@ -433,12 +433,11 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
function.isConst = true;

// count the number of constructors
if (function.type == Function::eConstructor)
if (function.isConstructor())
scope->numConstructors++;
else if (function.type == Function::eCopyConstructor) {
scope->numConstructors++;
scope->numCopyConstructors++;
}
if (function.type == Function::eCopyConstructor ||
function.type == Function::eMoveConstructor)
scope->numCopyOrMoveConstructors++;

// assume implementation is inline (definition and implementation same)
function.token = function.tokenDef;
Expand Down Expand Up @@ -1987,7 +1986,7 @@ Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *
classEnd(start_->link()),
nestedIn(nestedIn_),
numConstructors(0),
numCopyConstructors(0),
numCopyOrMoveConstructors(0),
type(type_),
definedType(NULL),
functionOf(NULL),
Expand All @@ -2002,7 +2001,7 @@ Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *
classEnd(NULL),
nestedIn(nestedIn_),
numConstructors(0),
numCopyConstructors(0),
numCopyOrMoveConstructors(0),
definedType(NULL),
functionOf(NULL),
function(NULL)
Expand Down
12 changes: 11 additions & 1 deletion lib/symboldatabase.h
Expand Up @@ -476,6 +476,16 @@ class CPPCHECKLIB Function {
/** @brief check if this function is virtual in the base classes */
bool isImplicitlyVirtual(bool defaultVal = false) const;

bool isConstructor() const {
return type==eConstructor ||
type==eCopyConstructor ||
type==eMoveConstructor;
}

bool isDestructor() const {
return type==eDestructor;
}

const Token *tokenDef; // function name token in class definition
const Token *argDef; // function argument start '(' in class definition
const Token *token; // function name token in implementation
Expand Down Expand Up @@ -530,7 +540,7 @@ class CPPCHECKLIB Scope {
const Scope *nestedIn;
std::list<Scope *> nestedList;
unsigned int numConstructors;
unsigned int numCopyConstructors;
unsigned int numCopyOrMoveConstructors;
std::list<UsingInfo> usingList;
ScopeType type;
Type* definedType;
Expand Down
14 changes: 14 additions & 0 deletions test/testclass.cpp
Expand Up @@ -5544,6 +5544,20 @@ class TestClass : public TestFixture {
"};");
ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str());

checkInitializationListUsage("class C;\n"
"class Fred {\n"
" C c;\n"
" Fred(Fred const & other) { c = other.c; }\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str());

checkInitializationListUsage("class C;\n"
"class Fred {\n"
" C c;\n"
" Fred(Fred && other) { c = other.c; }\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str());

checkInitializationListUsage("class C;\n"
"class Fred {\n"
" C a;\n"
Expand Down

0 comments on commit 54e7c8f

Please sign in to comment.