Refactorization/Partial rewrite of CheckClass::copyconstructors():

- Reformatted check code and some test cases
- Fixed false positives #4148 (non-copyable/unknown base classes) and #4178 (copy ctor implementation not seen)
- Proper usage of STL containers
- Better support for initializer list
- Rephrased error messages
This commit is contained in:
PKEuS 2012-09-10 13:31:30 +02:00
parent fa0414250d
commit 4e59e55229
3 changed files with 200 additions and 160 deletions

View File

@ -157,125 +157,92 @@ void CheckClass::copyconstructors()
{ {
if (!_settings->isEnabled("style")) if (!_settings->isEnabled("style"))
return; return;
std::vector<unsigned int> var_id;
std::vector<std::string> var_name; for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
std::list<Scope>::const_iterator scope; if (!scope->isClassOrStruct()) // scope is class or structure
std::list<Function>::const_iterator func;
unsigned int flag=0,varid=0;
for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
if (!scope->isClassOrStruct()) /*scope is class or structure*/
continue; continue;
int count_no_variables=0,count_copy_constructor=0,count_no_allocated_variables=0,count_no_of_pointer_variable=0;
std::list<Variable>::const_iterator var; std::map<unsigned int, const Token*> allocatedVars;
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) {
if (var->isPointer()) { for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
count_no_of_pointer_variable ++; if (func->type == Function::eConstructor && func->functionScope) {
} for (const Token* tok = func->functionScope->classStart; tok!=func->functionScope->classEnd; tok=tok->next()) {
} if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) {
if (count_no_of_pointer_variable>0) { const Variable* var = symbolDatabase->getVariableFromVarId(tok->varId());
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (var && var->isPointer() && var->scope() == &*scope)
if (func->type == Function::eConstructor && func->functionScope) { allocatedVars[tok->varId()] = tok;
for (const Token* tok = func->functionScope->classStart; tok!=func->functionScope->classEnd; tok=tok->next()) {
const char pattern[] = "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc";
if (Token::Match(tok, pattern)) {
std::vector<std::string>::iterator it=var_name.begin();
std::vector<unsigned int>::iterator itr;
for (itr=var_id.begin(); itr!=var_id.end(); ++itr,++it) {
if (*itr==tok->varId()) {
break;
}
}
if (itr==var_id.end()) {
var_id.push_back(tok->varId());
var_name.push_back(tok->str());
count_no_allocated_variables++;
}
}
} }
} }
} }
} }
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
std::set<const Token*> copiedVars;
const Token* copyCtor = 0;
for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (func->type == Function::eCopyConstructor) { if (func->type == Function::eCopyConstructor) {
count_copy_constructor++; copyCtor = func->tokenDef;
break; if (func->functionScope) {
}
}
if (!var_id.empty() && count_copy_constructor==0) {
noCopyConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct");
}
if (count_copy_constructor==1) {
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (func->type == Function::eCopyConstructor && func->functionScope) {
const Token* tok = func->tokenDef->linkAt(1)->next(); const Token* tok = func->tokenDef->linkAt(1)->next();
if (tok->str()==":") { if (tok->str()==":") {
tok=tok->next(); tok=tok->next();
if (Token::Match(tok,"%var% ( %var% . %var% )")) { while (Token::Match(tok, "%var% (")) {
if (allocatedVars.find(tok->varId()) != allocatedVars.end()) {
for (std::vector<unsigned int>::iterator itr=var_id.begin(); itr!=var_id.end(); ++itr) { if (tok->varId() && Token::Match(tok->tokAt(2), "%var% . %var% )"))
if (*itr==tok->varId()) { copiedVars.insert(tok);
flag=1; else if (!Token::Match(tok->tokAt(2), "%any% )"))
varid=tok->varId(); allocatedVars.erase(tok->varId()); // Assume memory is allocated
}
} }
tok = tok->linkAt(1)->tokAt(2);
} }
} }
for (tok=func->functionScope->classStart; tok!=func->functionScope->classEnd; tok=tok->next()) { for (tok=func->functionScope->classStart; tok!=func->functionScope->classEnd; tok=tok->next()) {
const char pattern[] = "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc"; if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) {
if (Token::Match(tok, pattern)) { allocatedVars.erase(tok->varId());
std::vector<std::string>::iterator it=var_name.begin(); } else if (Token::Match(tok, "%var% = %var% . %var% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) {
copiedVars.insert(tok);
for (std::vector<unsigned int>::iterator itr=var_id.begin(); itr!=var_id.end(); ++itr,++it) {
if (*itr==tok->varId()) {
if (varid==tok->varId()) {
flag=0;
}
count_no_variables++;
var_id.erase(itr);
var_name.erase(it);
break;
}
}
} }
} }
} } else // non-copyable or implementation not seen
} allocatedVars.clear();
if (flag==1) { break;
copyConstructorShallowCopyError(scope->classDef, scope->className, scope->classDef->str() == "struct"); }
} }
/*if count mismatch throw error*/ if (!copyCtor) {
if (count_no_variables!=count_no_allocated_variables) { if (!allocatedVars.empty() && scope->derivedFrom.empty()) // TODO: Check if base class is non-copyable
copyConstructorMallocError(scope->classDef, scope->className, scope->classDef->str() == "struct",var_name); noCopyConstructorError(scope->classDef, scope->className, scope->type == Scope::eStruct);
} else {
if (!copiedVars.empty()) {
for (std::set<const Token*>::const_iterator i = copiedVars.begin(); i != copiedVars.end(); ++i) {
copyConstructorShallowCopyError(*i, (*i)->str());
}
}
// throw error if count mismatch
for (std::map<unsigned int, const Token*>::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) {
copyConstructorMallocError(copyCtor, i->second, i->second->str());
} }
} }
var_id.clear();
var_name.clear();
} }
} }
void CheckClass::copyConstructorMallocError(const Token *tok, const std::string &classname, bool isStruct, const std::vector<std::string>& var_name) void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname)
{ {
for (std::vector<std::string>::const_iterator itr=var_name.begin(); itr!=var_name.end(); ++itr) { std::list<const Token*> callstack;
reportError(tok, Severity::style, "copyConstructorNoAllocation", "The copy constructor of " + std::string(isStruct ? "struct" : "class") + " '" + classname +"' does not allocate memory for class member " + *itr+"."); callstack.push_back(cctor);
} callstack.push_back(alloc);
reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors.");
} }
void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string &classname, bool isStruct) void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname)
{ {
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning" reportError(tok, Severity::style, "copyCtorPointerCopying", "Value of pointer '" + varname + "', which points to allocated memory, is copied in copy constructor instead of allocating new memory.");
reportError(tok, Severity::style, "copyConstructorPointerCopying", "The copy constructor of " + std::string(isStruct ? "struct" : "class") + " '" + classname +"' does not allocate memory for pointer class member and copying pointer values.");
} }
void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct) void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct)
{ {
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning" // The constructor might be intentionally missing. Therefore this is not a "warning"
reportError(tok, Severity::style, "noCopyConstructor", reportError(tok, Severity::style, "noCopyConstructor",
"The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "'" + std::string(isStruct ? "struct" : "class") + " " + classname +
"' does not have a copy constructor which is required since the class contains a pointer member."); "' does not have a copy constructor which is required since the class contains a pointer to allocated memory.");
} }
bool CheckClass::canNotCopy(const Scope *scope) bool CheckClass::canNotCopy(const Scope *scope)
@ -1299,7 +1266,7 @@ void CheckClass::thisSubtractionError(const Token *tok)
void CheckClass::checkConst() void CheckClass::checkConst()
{ {
// This is an inconclusive check. False positives: #2340, #3322. // This is an inconclusive check. False positives: #3322.
if (!_settings->inconclusive) if (!_settings->inconclusive)
return; return;

View File

@ -117,8 +117,8 @@ private:
// Reporting errors.. // Reporting errors..
void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); void noConstructorError(const Token *tok, const std::string &classname, bool isStruct);
void copyConstructorMallocError(const Token *tok, const std::string &classname, bool isStruct, const std::vector<std::string>& var_name); void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name);
void copyConstructorShallowCopyError(const Token *tok, const std::string &classname, bool isStruct); void copyConstructorShallowCopyError(const Token *tok, const std::string& varname);
void noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct); void noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct);
void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname); void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname);
void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname); void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname);
@ -137,9 +137,8 @@ private:
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
CheckClass c(0, settings, errorLogger); CheckClass c(0, settings, errorLogger);
c.noConstructorError(0, "classname", false); c.noConstructorError(0, "classname", false);
std::vector<std::string> temp; c.copyConstructorMallocError(0, 0, "var");
c.copyConstructorMallocError(0, "class", false, temp); c.copyConstructorShallowCopyError(0, "var");
c.copyConstructorShallowCopyError(0, "class", false);
c.noCopyConstructorError(0, "class", false); c.noCopyConstructorError(0, "class", false);
c.uninitVarError(0, "classname", "varname"); c.uninitVarError(0, "classname", "varname");
c.operatorEqVarError(0, "classname", ""); c.operatorEqVarError(0, "classname", "");

View File

@ -185,18 +185,30 @@ private:
"{\n" "{\n"
" public:\n" " public:\n"
" char *c,*p,*d;\n" " char *c,*p,*d;\n"
" F(const F &f) :p(f.p)\n" " F(const F &f) : p(f.p), c(f.c)\n"
" {\n"
" p=(char *)malloc(strlen(f.p)+1);\n"
" strcpy(p,f.p);\n"
" }\n"
" F(char *str)\n"
" {\n" " {\n"
" p=(char *)malloc(strlen(f.p)+1);\n"
" strcpy(p,f.p);\n"
" }\n"
" F(char *str)\n"
" {\n"
" p=(char *)malloc(strlen(str)+1);\n" " p=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,str);\n" " strcpy(p,str);\n"
" }\n" " }\n"
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (style) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n", errout.str());
checkCopyConstructor("class F {\n"
" char *p;\n"
" F(const F &f) {\n"
" p = f.p;\n"
" }\n"
" F(char *str) {\n"
" p = malloc(strlen(str)+1);\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (style) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n"
"[test.cpp:3] -> [test.cpp:7]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", errout.str());
checkCopyConstructor("class F\n" checkCopyConstructor("class F\n"
"{\n" "{\n"
@ -205,13 +217,14 @@ private:
" F(const F &f) :p(f.p)\n" " F(const F &f) :p(f.p)\n"
" {\n" " {\n"
" }\n" " }\n"
" F(char *str)\n" " F(char *str)\n"
" {\n" " {\n"
" p=(char *)malloc(strlen(str)+1);\n" " p=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,str);\n" " strcpy(p,str);\n"
" }\n" " }\n"
"};"); "};");
ASSERT_EQUALS("[test.cpp:1]: (style) The copy constructor of class 'F' does not allocate memory for pointer class member and copying pointer values.\n[test.cpp:1]: (style) The copy constructor of class 'F' does not allocate memory for class member p.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (style) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n"
"[test.cpp:5] -> [test.cpp:10]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", errout.str());
checkCopyConstructor("class kalci\n" checkCopyConstructor("class kalci\n"
"{\n" "{\n"
@ -219,22 +232,22 @@ private:
" char *c,*p,*d;\n" " char *c,*p,*d;\n"
" kalci()\n" " kalci()\n"
" {\n" " {\n"
" p=(char *)malloc(100);\n" " p=(char *)malloc(100);\n"
" strcpy(p,\"hello\");\n" " strcpy(p,\"hello\");\n"
" c=(char *)malloc(100);\n" " c=(char *)malloc(100);\n"
" strcpy(p,\"hello\");\n" " strcpy(p,\"hello\");\n"
" d=(char *)malloc(100);\n" " d=(char *)malloc(100);\n"
" strcpy(p,\"hello\");\n" " strcpy(p,\"hello\");\n"
" }\n" " }\n"
" kalci(const kalci &f)\n" " kalci(const kalci &f)\n"
" {\n" " {\n"
" p=(char *)malloc(strlen(str)+1);\n" " p=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n"
" c=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n"
" d=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n" " strcpy(p,f.p);\n"
"}\n" " c=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n"
" d=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n"
" }\n"
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
@ -242,24 +255,48 @@ private:
"{\n" "{\n"
" public:\n" " public:\n"
" char *c,*p,*d;\n" " char *c,*p,*d;\n"
"F(char *str,char *st,char *string)\n" " F(char *str,char *st,char *string)\n"
"{\n" " {\n"
" p=(char *)malloc(100);\n" " p=(char *)malloc(100);\n"
" strcpy(p,str);\n" " strcpy(p,str);\n"
" c=(char *)malloc(100);\n" " c=(char *)malloc(100);\n"
" strcpy(p,st);\n" " strcpy(p,st);\n"
" d=(char *)malloc(100);\n" " d=(char *)malloc(100);\n"
" strcpy(p,string);\n" " strcpy(p,string);\n"
"}\n" " }\n"
"F(const F &f)\n" " F(const F &f)\n"
"{\n" " {\n"
" p=(char *)malloc(strlen(str)+1);\n" " p=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n" " strcpy(p,f.p);\n"
" c=(char *)malloc(strlen(str)+1);\n" " c=(char *)malloc(strlen(str)+1);\n"
" strcpy(p,f.p);\n" " strcpy(p,f.p);\n"
"}\n" " }\n"
"};"); "};");
ASSERT_EQUALS("[test.cpp:1]: (style) The copy constructor of class 'F' does not allocate memory for class member d.\n", errout.str()); ASSERT_EQUALS("[test.cpp:14] -> [test.cpp:11]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", errout.str());
checkCopyConstructor("class F {\n"
" char *c;\n"
" F(char *str,char *st,char *string) {\n"
" p=(char *)malloc(100);\n"
" }\n"
" F(const F &f)\n"
" : p(malloc(size))\n"
" {\n"
" }\n"
"};");
ASSERT_EQUALS("", errout.str());
checkCopyConstructor("class F {\n"
" char *c;\n"
" F(char *str,char *st,char *string)\n"
" : p(malloc(size))\n"
" {\n"
" }\n"
" F(const F &f)\n"
" {\n"
" }\n"
"};");
TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout.str());
checkCopyConstructor("class F\n" checkCopyConstructor("class F\n"
"{\n" "{\n"
@ -267,32 +304,69 @@ private:
" char *c,*p,*d;\n" " char *c,*p,*d;\n"
" F()\n" " F()\n"
" {\n" " {\n"
" p=(char *)malloc(100);\n" " p=(char *)malloc(100);\n"
" c=(char *)malloc(100);\n" " c=(char *)malloc(100);\n"
" d=(char*)malloc(100);\n" " d=(char*)malloc(100);\n"
" }\n" " }\n"
"};"); "};");
ASSERT_EQUALS("[test.cpp:1]: (style) The class 'F' does not have a copy constructor which is required since the class contains a pointer member.\n", errout.str()); ASSERT_EQUALS("[test.cpp:1]: (style) 'class F' does not have a copy constructor which is required since the class contains a pointer to allocated memory.\n", errout.str());
checkCopyConstructor("class F\n" checkCopyConstructor("class F\n"
"{\n" "{\n"
" public:\n" " public:\n"
" char *c;\n" " char *c;\n"
" const char *p,*d;\n" " const char *p,*d;\n"
" F(char *str,char *st,char *string)\n" " F(char *str,char *st,char *string)\n"
" {\n" " {\n"
" p=str;\n" " p=str;\n"
" d=st;\n" " d=st;\n"
" c=(char *)malloc(strlen(string)+1);\n" " c=(char *)malloc(strlen(string)+1);\n"
" strcpy(d,string);\n" " strcpy(d,string);\n"
" }\n" " }\n"
"F(const F &f)\n" " F(const F &f)\n"
" {\n"
" p=f.p;\n"
" d=f.d;\n"
" c=(char *)malloc(strlen(str)+1);\n"
" strcpy(d,f.p);\n"
" }\n"
"};");
ASSERT_EQUALS("", errout.str());
checkCopyConstructor("class F : E\n"
"{\n" "{\n"
" p=f.p;\n" " char *p;\n"
" d=f.d;\n" " F() {\n"
" c=(char *)malloc(strlen(str)+1);\n" " p = malloc(100);\n"
" strcpy(d,f.p);\n" " }\n"
"}\n" "};");
ASSERT_EQUALS("", errout.str());
checkCopyConstructor("class E { E(E&); };\n" // non-copyable
"class F : E\n"
"{\n"
" char *p;\n"
" F() {\n"
" p = malloc(100);\n"
" }\n"
"};");
ASSERT_EQUALS("", errout.str());
checkCopyConstructor("class E {};\n"
"class F : E {\n"
" char *p;\n"
" F() {\n"
" p = malloc(100);\n"
" }\n"
"};");
TODO_ASSERT_EQUALS("[test.cpp:2]: (style) 'class F' does not have a copy constructor which is required since the class contains a pointer to allocated memory.\n", "", errout.str());
checkCopyConstructor("class F {\n"
" char *p;\n"
" F() {\n"
" p = malloc(100);\n"
" }\n"
" F(F& f);\n" // non-copyable
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }