parent
e5b1a6ceb1
commit
9ad7dfd5fd
|
@ -153,6 +153,131 @@ void CheckClass::constructors()
|
|||
}
|
||||
}
|
||||
|
||||
void CheckClass::copyconstructors()
|
||||
{
|
||||
if (!_settings->isEnabled("style"))
|
||||
return;
|
||||
std::vector<unsigned int> var_id;
|
||||
std::vector<std::string> var_name;
|
||||
std::list<Scope>::const_iterator scope;
|
||||
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;
|
||||
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;
|
||||
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) {
|
||||
if (var->isPointer()) {
|
||||
count_no_of_pointer_variable ++;
|
||||
}
|
||||
}
|
||||
if (count_no_of_pointer_variable>0) {
|
||||
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
|
||||
if (func->type == Function::eConstructor && func->functionScope) {
|
||||
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) {
|
||||
if (func->type == Function::eCopyConstructor) {
|
||||
count_copy_constructor++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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();
|
||||
if (tok->str()==":") {
|
||||
tok=tok->next();
|
||||
if (Token::Match(tok,"%var% ( %var% . %var% )")) {
|
||||
|
||||
for (std::vector<unsigned int>::iterator itr=var_id.begin(); itr!=var_id.end(); ++itr) {
|
||||
if (*itr==tok->varId()) {
|
||||
flag=1;
|
||||
varid=tok->varId();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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, pattern)) {
|
||||
std::vector<std::string>::iterator it=var_name.begin();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag==1) {
|
||||
copyConstructorShallowCopyError(scope->classDef, scope->className, scope->classDef->str() == "struct");
|
||||
}
|
||||
/*if count mismatch throw error*/
|
||||
if (count_no_variables!=count_no_allocated_variables) {
|
||||
copyConstructorMallocError(scope->classDef, scope->className, scope->classDef->str() == "struct",var_name);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator itr=var_name.begin(); itr!=var_name.end(); ++itr) {
|
||||
reportError(tok, Severity::style, "copyConstructorNoAllocation", "The copy constructor of " + std::string(isStruct ? "struct" : "class") + " '" + classname +"' does not allocate memory for class member " + *itr+".");
|
||||
}
|
||||
}
|
||||
|
||||
void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string &classname, bool isStruct)
|
||||
{
|
||||
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning"
|
||||
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)
|
||||
{
|
||||
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning"
|
||||
reportError(tok, Severity::style, "noCopyConstructor",
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '" + classname +
|
||||
"' does not have a copy constructor which is required since the class contains a pointer member.");
|
||||
|
||||
}
|
||||
|
||||
bool CheckClass::canNotCopy(const Scope *scope)
|
||||
{
|
||||
std::list<Function>::const_iterator func;
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
|
||||
checkClass.virtualDestructor();
|
||||
checkClass.checkConst();
|
||||
checkClass.copyconstructors();
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,11 +110,16 @@ public:
|
|||
|
||||
void initializationListUsage();
|
||||
|
||||
void copyconstructors();
|
||||
|
||||
private:
|
||||
const SymbolDatabase *symbolDatabase;
|
||||
|
||||
// Reporting errors..
|
||||
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 copyConstructorShallowCopyError(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 operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname);
|
||||
void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname);
|
||||
|
@ -131,6 +137,10 @@ private:
|
|||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||
CheckClass c(0, settings, errorLogger);
|
||||
c.noConstructorError(0, "classname", false);
|
||||
std::vector<std::string> temp;
|
||||
c.copyConstructorMallocError(0, "class", false, temp);
|
||||
c.copyConstructorShallowCopyError(0, "class", false);
|
||||
c.noCopyConstructorError(0, "class", false);
|
||||
c.uninitVarError(0, "classname", "varname");
|
||||
c.operatorEqVarError(0, "classname", "");
|
||||
c.unusedPrivateFunctionError(0, "classname", "funcname");
|
||||
|
@ -152,7 +162,8 @@ private:
|
|||
|
||||
std::string classInfo() const {
|
||||
return "Check the code for each class.\n"
|
||||
"* Missing constructors\n"
|
||||
"* Missing constructors and copy constructors\n"
|
||||
"* Missing allocation of memory in copy constructor\n"
|
||||
"* Are all variables initialized by the constructors?\n"
|
||||
"* Are all variables assigned by 'operator='?\n"
|
||||
"* Warn if memset, memcpy etc are used on a class\n"
|
||||
|
|
|
@ -43,6 +43,8 @@ private:
|
|||
TEST_CASE(virtualDestructorInherited);
|
||||
TEST_CASE(virtualDestructorTemplate);
|
||||
|
||||
TEST_CASE(copyConstructor);
|
||||
|
||||
TEST_CASE(noConstructor1);
|
||||
TEST_CASE(noConstructor2);
|
||||
TEST_CASE(noConstructor3);
|
||||
|
@ -161,6 +163,141 @@ private:
|
|||
TEST_CASE(initializerListUsage);
|
||||
}
|
||||
|
||||
void checkCopyConstructor(const char code[]) {
|
||||
// Clear the error log
|
||||
errout.str("");
|
||||
Settings settings;
|
||||
settings.addEnabled("style");
|
||||
|
||||
// Tokenize..
|
||||
Tokenizer tokenizer(&settings, this);
|
||||
std::istringstream istr(code);
|
||||
tokenizer.tokenize(istr, "test.cpp");
|
||||
tokenizer.simplifyTokenList();
|
||||
|
||||
// Check..
|
||||
CheckClass checkClass(&tokenizer, &settings, this);
|
||||
checkClass.copyconstructors();
|
||||
}
|
||||
|
||||
void copyConstructor() {
|
||||
checkCopyConstructor("class F\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c,*p,*d;\n"
|
||||
" F(const F &f) :p(f.p)\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"
|
||||
" strcpy(p,str);\n"
|
||||
" }\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkCopyConstructor("class F\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c,*p,*d;\n"
|
||||
" F(const F &f) :p(f.p)\n"
|
||||
" {\n"
|
||||
" }\n"
|
||||
" F(char *str)\n"
|
||||
" {\n"
|
||||
" p=(char *)malloc(strlen(str)+1);\n"
|
||||
" strcpy(p,str);\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());
|
||||
|
||||
checkCopyConstructor("class kalci\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c,*p,*d;\n"
|
||||
" kalci()\n"
|
||||
" {\n"
|
||||
" p=(char *)malloc(100);\n"
|
||||
" strcpy(p,\"hello\");\n"
|
||||
" c=(char *)malloc(100);\n"
|
||||
" strcpy(p,\"hello\");\n"
|
||||
" d=(char *)malloc(100);\n"
|
||||
" strcpy(p,\"hello\");\n"
|
||||
" }\n"
|
||||
" kalci(const kalci &f)\n"
|
||||
" {\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"
|
||||
"}\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkCopyConstructor("class F\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c,*p,*d;\n"
|
||||
"F(char *str,char *st,char *string)\n"
|
||||
"{\n"
|
||||
" p=(char *)malloc(100);\n"
|
||||
" strcpy(p,str);\n"
|
||||
" c=(char *)malloc(100);\n"
|
||||
" strcpy(p,st);\n"
|
||||
" d=(char *)malloc(100);\n"
|
||||
" strcpy(p,string);\n"
|
||||
"}\n"
|
||||
"F(const F &f)\n"
|
||||
"{\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"
|
||||
"}\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (style) The copy constructor of class 'F' does not allocate memory for class member d.\n", errout.str());
|
||||
|
||||
checkCopyConstructor("class F\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c,*p,*d;\n"
|
||||
" F()\n"
|
||||
" {\n"
|
||||
" p=(char *)malloc(100);\n"
|
||||
" c=(char *)malloc(100);\n"
|
||||
" d=(char*)malloc(100);\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());
|
||||
|
||||
checkCopyConstructor("class F\n"
|
||||
"{\n"
|
||||
" public:\n"
|
||||
" char *c;\n"
|
||||
" const char *p,*d;\n"
|
||||
" F(char *str,char *st,char *string)\n"
|
||||
" {\n"
|
||||
" p=str;\n"
|
||||
" d=st;\n"
|
||||
" c=(char *)malloc(strlen(string)+1);\n"
|
||||
" strcpy(d,string);\n"
|
||||
" }\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());
|
||||
}
|
||||
|
||||
|
||||
// Check the operator Equal
|
||||
void checkOpertorEq(const char code[]) {
|
||||
// Clear the error log
|
||||
|
|
Loading…
Reference in New Issue