CheckClass: If class has memory management it should have copy constructor, operator= and destructor
This commit is contained in:
parent
eb1571af81
commit
73b41455dd
|
@ -305,6 +305,27 @@ void CheckClass::copyconstructors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!allocatedVars.empty()) {
|
||||||
|
bool hasCopyCtor = false;
|
||||||
|
bool hasOperatorEq = false;
|
||||||
|
bool hasDestructor = false;
|
||||||
|
for (const Function &func : scope->functionList) {
|
||||||
|
if (func.type == Function::eCopyConstructor)
|
||||||
|
hasCopyCtor = true;
|
||||||
|
else if (func.type == Function::eOperatorEqual)
|
||||||
|
hasOperatorEq = true;
|
||||||
|
else if (func.type == Function::eDestructor)
|
||||||
|
hasDestructor = true;
|
||||||
|
}
|
||||||
|
bool derived = !scope->definedType->derivedFrom.empty();
|
||||||
|
if (!hasCopyCtor && !derived)
|
||||||
|
noCopyConstructorError(scope, derived);
|
||||||
|
if (!hasOperatorEq)
|
||||||
|
noOperatorEqError(scope);
|
||||||
|
if (!hasDestructor)
|
||||||
|
noDestructorError(scope);
|
||||||
|
}
|
||||||
|
|
||||||
std::set<const Token*> copiedVars;
|
std::set<const Token*> copiedVars;
|
||||||
const Token* copyCtor = nullptr;
|
const Token* copyCtor = nullptr;
|
||||||
for (const Function &func : scope->functionList) {
|
for (const Function &func : scope->functionList) {
|
||||||
|
@ -337,14 +358,9 @@ void CheckClass::copyconstructors()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!copyCtor) {
|
if (copyCtor && !copiedVars.empty()) {
|
||||||
if (!allocatedVars.empty() && scope->definedType->derivedFrom.empty()) // TODO: Check if base class is non-copyable
|
for (std::set<const Token*>::const_iterator it = copiedVars.begin(); it != copiedVars.end(); ++it) {
|
||||||
noCopyConstructorError(scope->classDef, scope->className, scope->type == Scope::eStruct);
|
copyConstructorShallowCopyError(*it, (*it)->str());
|
||||||
} else {
|
|
||||||
if (!copiedVars.empty()) {
|
|
||||||
for (std::set<const Token*>::const_iterator it = copiedVars.begin(); it != copiedVars.end(); ++it) {
|
|
||||||
copyConstructorShallowCopyError(*it, (*it)->str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// throw error if count mismatch
|
// throw error if count mismatch
|
||||||
/* FIXME: This doesn't work. See #4154
|
/* FIXME: This doesn't work. See #4154
|
||||||
|
@ -372,12 +388,34 @@ void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::st
|
||||||
"$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false);
|
"$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct)
|
void CheckClass::noCopyConstructorError(const Scope *scope, bool inconclusive)
|
||||||
{
|
{
|
||||||
// The constructor might be intentionally missing. Therefore this is not a "warning"
|
const Token *tok = scope ? scope->classDef : nullptr;
|
||||||
|
const std::string &classname = scope ? scope->className : "class";
|
||||||
|
bool isStruct = scope ? (scope->type == Scope::eStruct) : false;
|
||||||
reportError(tok, Severity::style, "noCopyConstructor",
|
reportError(tok, Severity::style, "noCopyConstructor",
|
||||||
"$symbol:" + classname + "\n" +
|
"$symbol:" + classname + "\n" +
|
||||||
std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.", CWE398, false);
|
std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.", CWE398, inconclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckClass::noOperatorEqError(const Scope *scope)
|
||||||
|
{
|
||||||
|
const Token *tok = scope ? scope->classDef : nullptr;
|
||||||
|
const std::string &classname = scope ? scope->className : "class";
|
||||||
|
bool isStruct = scope ? (scope->type == Scope::eStruct) : false;
|
||||||
|
reportError(tok, Severity::style, "noOperatorEq",
|
||||||
|
"$symbol:" + classname + "\n" +
|
||||||
|
std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a assignment operator which is recommended since the class contains a pointer to allocated memory.", CWE398, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckClass::noDestructorError(const Scope *scope)
|
||||||
|
{
|
||||||
|
const Token *tok = scope ? scope->classDef : nullptr;
|
||||||
|
const std::string &classname = scope ? scope->className : "class";
|
||||||
|
bool isStruct = scope ? (scope->type == Scope::eStruct) : false;
|
||||||
|
reportError(tok, Severity::style, "noDestructor",
|
||||||
|
"$symbol:" + classname + "\n" +
|
||||||
|
std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a destructor which is recommended since the class contains a pointer to allocated memory.", CWE398, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckClass::canNotCopy(const Scope *scope)
|
bool CheckClass::canNotCopy(const Scope *scope)
|
||||||
|
|
|
@ -168,7 +168,9 @@ private:
|
||||||
void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct);
|
void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct);
|
||||||
//void copyConstructorMallocError(const Token *cctor, const Token *alloc, const 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& varname);
|
void copyConstructorShallowCopyError(const Token *tok, const std::string& varname);
|
||||||
void noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct);
|
void noCopyConstructorError(const Scope *scope, bool inconclusive);
|
||||||
|
void noOperatorEqError(const Scope *scope);
|
||||||
|
void noDestructorError(const Scope *scope);
|
||||||
void uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool inconclusive);
|
void uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool inconclusive);
|
||||||
void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive);
|
void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive);
|
||||||
void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname);
|
void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname);
|
||||||
|
@ -202,7 +204,9 @@ private:
|
||||||
c.noExplicitConstructorError(nullptr, "classname", false);
|
c.noExplicitConstructorError(nullptr, "classname", false);
|
||||||
//c.copyConstructorMallocError(nullptr, 0, "var");
|
//c.copyConstructorMallocError(nullptr, 0, "var");
|
||||||
c.copyConstructorShallowCopyError(nullptr, "var");
|
c.copyConstructorShallowCopyError(nullptr, "var");
|
||||||
c.noCopyConstructorError(nullptr, "class", false);
|
c.noCopyConstructorError(nullptr, false);
|
||||||
|
c.noOperatorEqError(nullptr);
|
||||||
|
c.noDestructorError(nullptr);
|
||||||
c.uninitVarError(nullptr, false, "classname", "varname", false);
|
c.uninitVarError(nullptr, false, "classname", "varname", false);
|
||||||
c.operatorEqVarError(nullptr, "classname", emptyString, false);
|
c.operatorEqVarError(nullptr, "classname", emptyString, false);
|
||||||
c.unusedPrivateFunctionError(nullptr, "classname", "funcname");
|
c.unusedPrivateFunctionError(nullptr, "classname", "funcname");
|
||||||
|
|
|
@ -68,6 +68,8 @@ private:
|
||||||
|
|
||||||
TEST_CASE(copyConstructor1);
|
TEST_CASE(copyConstructor1);
|
||||||
TEST_CASE(copyConstructor2); // ticket #4458
|
TEST_CASE(copyConstructor2); // ticket #4458
|
||||||
|
TEST_CASE(noOperatorEq); // class with memory management should have operator eq
|
||||||
|
TEST_CASE(noDestructor); // class with memory management should have destructor
|
||||||
|
|
||||||
TEST_CASE(operatorEq1);
|
TEST_CASE(operatorEq1);
|
||||||
TEST_CASE(operatorEq2);
|
TEST_CASE(operatorEq2);
|
||||||
|
@ -564,6 +566,8 @@ private:
|
||||||
" p=(char *)malloc(strlen(str)+1);\n"
|
" p=(char *)malloc(strlen(str)+1);\n"
|
||||||
" strcpy(p,str);\n"
|
" strcpy(p,str);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" F&operator=(const F&);\n"
|
||||||
|
" ~F();\n"
|
||||||
"};");
|
"};");
|
||||||
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());
|
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());
|
||||||
|
|
||||||
|
@ -575,6 +579,8 @@ private:
|
||||||
" F(char *str) {\n"
|
" F(char *str) {\n"
|
||||||
" p = malloc(strlen(str)+1);\n"
|
" p = malloc(strlen(str)+1);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
TODO_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"
|
TODO_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",
|
"[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",
|
||||||
|
@ -593,6 +599,8 @@ private:
|
||||||
" p=(char *)malloc(strlen(str)+1);\n"
|
" p=(char *)malloc(strlen(str)+1);\n"
|
||||||
" strcpy(p,str);\n"
|
" strcpy(p,str);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
TODO_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"
|
TODO_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",
|
"[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",
|
||||||
|
@ -621,6 +629,8 @@ private:
|
||||||
" d=(char *)malloc(strlen(str)+1);\n"
|
" d=(char *)malloc(strlen(str)+1);\n"
|
||||||
" strcpy(p,f.p);\n"
|
" strcpy(p,f.p);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~kalci();\n"
|
||||||
|
" kalci& operator=(const kalci&kalci);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -644,6 +654,8 @@ private:
|
||||||
" 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"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
TODO_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());
|
TODO_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());
|
||||||
|
|
||||||
|
@ -656,6 +668,8 @@ private:
|
||||||
" : p(malloc(size))\n"
|
" : p(malloc(size))\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -668,6 +682,8 @@ private:
|
||||||
" F(const F &f)\n"
|
" F(const F &f)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\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());
|
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());
|
||||||
|
|
||||||
|
@ -681,6 +697,8 @@ private:
|
||||||
" c=(char *)malloc(100);\n"
|
" c=(char *)malloc(100);\n"
|
||||||
" d=(char*)malloc(100);\n"
|
" d=(char*)malloc(100);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
||||||
|
|
||||||
|
@ -703,6 +721,8 @@ private:
|
||||||
" c=(char *)malloc(strlen(str)+1);\n"
|
" c=(char *)malloc(strlen(str)+1);\n"
|
||||||
" strcpy(d,f.p);\n"
|
" strcpy(d,f.p);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -712,6 +732,8 @@ private:
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" p = malloc(100);\n"
|
" p = malloc(100);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -722,6 +744,8 @@ private:
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" p = malloc(100);\n"
|
" p = malloc(100);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -731,6 +755,8 @@ private:
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" p = malloc(100);\n"
|
" p = malloc(100);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:2]: (style) 'class F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", "", errout.str());
|
TODO_ASSERT_EQUALS("[test.cpp:2]: (style) 'class F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", "", errout.str());
|
||||||
|
|
||||||
|
@ -739,13 +765,17 @@ private:
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" p = malloc(100);\n"
|
" p = malloc(100);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" F(F& f);\n" // non-copyable
|
" F(F& f);\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
checkCopyConstructor("class F {\n"
|
checkCopyConstructor("class F {\n"
|
||||||
" char *p;\n"
|
" char *p;\n"
|
||||||
" F() : p(malloc(100)) {}\n"
|
" F() : p(malloc(100)) {}\n"
|
||||||
|
" ~F();\n"
|
||||||
|
" F& operator=(const F&f);\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
||||||
|
|
||||||
|
@ -770,11 +800,32 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" Vector( const Vector<_Tp>& v ) {\n"
|
" Vector( const Vector<_Tp>& v ) {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" ~Vector();\n"
|
||||||
|
" Vector& operator=(const Vector&v);\n"
|
||||||
" _Tp* _M_finish;\n"
|
" _Tp* _M_finish;\n"
|
||||||
"};");
|
"};");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void noOperatorEq() {
|
||||||
|
checkCopyConstructor("struct F {\n"
|
||||||
|
" char* c;\n"
|
||||||
|
" F() { c = malloc(100); }\n"
|
||||||
|
" F(const F &f);\n"
|
||||||
|
" ~F();\n"
|
||||||
|
"};");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (style) struct 'F' does not have a assignment operator which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void noDestructor() {
|
||||||
|
checkCopyConstructor("struct F {\n"
|
||||||
|
" char* c;\n"
|
||||||
|
" F() { c = malloc(100); }\n"
|
||||||
|
" F(const F &f);\n"
|
||||||
|
" F&operator=(const F&);"
|
||||||
|
"};");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (style) struct 'F' does not have a destructor which is recommended since the class contains a pointer to allocated memory.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check the operator Equal
|
// Check the operator Equal
|
||||||
void checkOpertorEq(const char code[]) {
|
void checkOpertorEq(const char code[]) {
|
||||||
|
|
Loading…
Reference in New Issue