Improve check: Warn about virtual function calls in constructor/destructor
This commit is contained in:
parent
e492932f19
commit
1046ca2120
|
@ -64,11 +64,6 @@ static const char * getFunctionTypeName(Function::Type type)
|
|||
return "";
|
||||
}
|
||||
|
||||
static bool isPureWithoutBody(Function const & func)
|
||||
{
|
||||
return func.isPure() && !func.hasBody();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
|
||||
|
@ -2200,15 +2195,15 @@ void CheckClass::selfInitializationError(const Token* tok, const std::string& va
|
|||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Check for pure virtual function calls
|
||||
// Check for virtual function calls in constructor/destructor
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CheckClass::checkPureVirtualFunctionCall()
|
||||
void CheckClass::checkVirtualFunctionCallInConstructor()
|
||||
{
|
||||
if (! _settings->isEnabled(Settings::WARNING))
|
||||
return;
|
||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||
std::map<const Function *, std::list<const Token *> > callsPureVirtualFunctionMap;
|
||||
std::map<const Function *, std::list<const Token *> > virtualFunctionCallsMap;
|
||||
for (std::size_t i = 0; i < functions; ++i) {
|
||||
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||
if (scope->function == nullptr || !scope->function->hasBody() ||
|
||||
|
@ -2216,106 +2211,127 @@ void CheckClass::checkPureVirtualFunctionCall()
|
|||
scope->function->isDestructor()))
|
||||
continue;
|
||||
|
||||
const std::list<const Token *> & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap);
|
||||
for (std::list<const Token *>::const_iterator pureCallIter=pureVirtualFunctionCalls.begin();
|
||||
pureCallIter!=pureVirtualFunctionCalls.end();
|
||||
++pureCallIter) {
|
||||
const Token & pureCall=**pureCallIter;
|
||||
std::list<const Token *> pureFuncStack;
|
||||
pureFuncStack.push_back(&pureCall);
|
||||
getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, pureCall, pureFuncStack);
|
||||
if (!pureFuncStack.empty())
|
||||
callsPureVirtualFunctionError(*scope->function, pureFuncStack, pureFuncStack.back()->str());
|
||||
const std::list<const Token *> & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap);
|
||||
for (std::list<const Token *>::const_iterator it = virtualFunctionCalls.begin(); it != virtualFunctionCalls.end(); ++it) {
|
||||
const Token * callToken = *it;
|
||||
std::list<const Token *> callstack;
|
||||
callstack.push_back(callToken);
|
||||
getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
|
||||
if (callstack.empty())
|
||||
continue;
|
||||
if (callstack.back()->function()->isPure())
|
||||
pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
|
||||
else
|
||||
virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::list<const Token *> & CheckClass::callsPureVirtualFunction(const Function & function,
|
||||
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap)
|
||||
const std::list<const Token *> & CheckClass::getVirtualFunctionCalls(const Function & function,
|
||||
std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap)
|
||||
{
|
||||
std::pair<std::map<const Function *, std::list<const Token *> >::iterator, bool > found =
|
||||
callsPureVirtualFunctionMap.insert(std::pair<const Function *, std::list< const Token *> >(&function, std::list<const Token *>()));
|
||||
std::list<const Token *> & pureFunctionCalls = found.first->second;
|
||||
if (found.second) {
|
||||
if (function.hasBody()) {
|
||||
for (const Token *tok = function.arg->link();
|
||||
tok && tok != function.functionScope->classEnd;
|
||||
tok = tok->next()) {
|
||||
if (function.type != Function::eConstructor &&
|
||||
function.type != Function::eCopyConstructor &&
|
||||
function.type != Function::eMoveConstructor &&
|
||||
function.type != Function::eDestructor) {
|
||||
if ((Token::simpleMatch(tok, ") {") &&
|
||||
tok->link() &&
|
||||
Token::Match(tok->link()->previous(), "if|switch")) ||
|
||||
Token::simpleMatch(tok, "else {")
|
||||
) {
|
||||
// Assume pure virtual function call is prevented by "if|else|switch" condition
|
||||
tok = tok->linkAt(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (tok->scope()->type == Scope::eLambda)
|
||||
tok = tok->scope()->classEnd->next();
|
||||
const std::map<const Function *, std::list<const Token *> >::const_iterator found = virtualFunctionCallsMap.find(&function);
|
||||
if (found != virtualFunctionCallsMap.end())
|
||||
return found->second;
|
||||
|
||||
const Function * callFunction = tok->function();
|
||||
if (!callFunction ||
|
||||
function.nestedIn != callFunction->nestedIn ||
|
||||
(tok->previous() && tok->previous()->str() == "."))
|
||||
continue;
|
||||
virtualFunctionCallsMap[&function] = std::list<const Token *>();
|
||||
std::list<const Token *> & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second;
|
||||
|
||||
if (tok->previous() &&
|
||||
tok->previous()->str() == "(") {
|
||||
const Token * prev = tok->previous();
|
||||
if (prev->previous() &&
|
||||
(_settings->library.ignorefunction(tok->str())
|
||||
|| _settings->library.ignorefunction(prev->previous()->str())))
|
||||
continue;
|
||||
}
|
||||
if (!function.hasBody())
|
||||
return virtualFunctionCalls;
|
||||
|
||||
if (isPureWithoutBody(*callFunction)) {
|
||||
pureFunctionCalls.push_back(tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::list<const Token *> & pureFunctionCallsOfTok = callsPureVirtualFunction(*callFunction,
|
||||
callsPureVirtualFunctionMap);
|
||||
if (!pureFunctionCallsOfTok.empty()) {
|
||||
pureFunctionCalls.push_back(tok);
|
||||
continue;
|
||||
}
|
||||
for (const Token *tok = function.arg->link(); tok != function.functionScope->classEnd; tok = tok->next()) {
|
||||
if (function.type != Function::eConstructor &&
|
||||
function.type != Function::eCopyConstructor &&
|
||||
function.type != Function::eMoveConstructor &&
|
||||
function.type != Function::eDestructor) {
|
||||
if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) ||
|
||||
Token::simpleMatch(tok, "else {")) {
|
||||
// Assume pure virtual function call is prevented by "if|else|switch" condition
|
||||
tok = tok->linkAt(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (tok->scope()->type == Scope::eLambda)
|
||||
tok = tok->scope()->classEnd->next();
|
||||
|
||||
const Function * callFunction = tok->function();
|
||||
if (!callFunction ||
|
||||
function.nestedIn != callFunction->nestedIn ||
|
||||
(tok->previous() && tok->previous()->str() == "."))
|
||||
continue;
|
||||
|
||||
if (tok->previous() &&
|
||||
tok->previous()->str() == "(") {
|
||||
const Token * prev = tok->previous();
|
||||
if (prev->previous() &&
|
||||
(_settings->library.ignorefunction(tok->str())
|
||||
|| _settings->library.ignorefunction(prev->previous()->str())))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (callFunction->isVirtual()) {
|
||||
virtualFunctionCalls.push_back(tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::list<const Token *> & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap);
|
||||
if (!virtualFunctionCallsOfTok.empty())
|
||||
virtualFunctionCalls.push_back(tok);
|
||||
}
|
||||
return pureFunctionCalls;
|
||||
return virtualFunctionCalls;
|
||||
}
|
||||
|
||||
void CheckClass::getFirstPureVirtualFunctionCallStack(
|
||||
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap,
|
||||
const Token & pureCall,
|
||||
std::list<const Token *> & pureFuncStack)
|
||||
void CheckClass::getFirstVirtualFunctionCallStack(
|
||||
std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap,
|
||||
const Token * callToken,
|
||||
std::list<const Token *> & callstack)
|
||||
{
|
||||
if (isPureWithoutBody(*pureCall.function())) {
|
||||
pureFuncStack.push_back(pureCall.function()->token);
|
||||
const Function *callFunction = callToken->function();
|
||||
if (callFunction->isVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) {
|
||||
callstack.push_back(callFunction->token);
|
||||
return;
|
||||
}
|
||||
std::map<const Function *, std::list<const Token *> >::const_iterator found = callsPureVirtualFunctionMap.find(pureCall.function());
|
||||
if (found == callsPureVirtualFunctionMap.end() ||
|
||||
found->second.empty()) {
|
||||
pureFuncStack.clear();
|
||||
std::map<const Function *, std::list<const Token *> >::const_iterator found = virtualFunctionCallsMap.find(callFunction);
|
||||
if (found == virtualFunctionCallsMap.end() || found->second.empty()) {
|
||||
callstack.clear();
|
||||
return;
|
||||
}
|
||||
const Token & firstPureCall = **found->second.begin();
|
||||
pureFuncStack.push_back(&firstPureCall);
|
||||
getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, firstPureCall, pureFuncStack);
|
||||
const Token * firstCall = *found->second.begin();
|
||||
callstack.push_back(firstCall);
|
||||
getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, callstack);
|
||||
}
|
||||
|
||||
void CheckClass::callsPureVirtualFunctionError(
|
||||
const Function & scopeFunction,
|
||||
void CheckClass::virtualFunctionCallInConstructorError(
|
||||
const Function * scopeFunction,
|
||||
const std::list<const Token *> & tokStack,
|
||||
const std::string &funcname)
|
||||
{
|
||||
const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor";
|
||||
|
||||
ErrorPath errorPath;
|
||||
for (std::list<const Token *>::const_iterator it = tokStack.begin(); it != tokStack.end(); ++it)
|
||||
errorPath.push_back(ErrorPathItem(*it, "Calling " + (*it)->str()));
|
||||
if (!errorPath.empty())
|
||||
errorPath.back().second = funcname + " is a virtual method";
|
||||
|
||||
reportError(errorPath, Severity::warning, "virtualCallInConstructor", "Call of virtual function '" + funcname + "' in " + scopeFunctionTypeName + ".\n"
|
||||
"Call of pure virtual function '" + funcname + "' in " + scopeFunctionTypeName + ". Dynamic binding is not used.", CWE(0U), false);
|
||||
}
|
||||
|
||||
void CheckClass::pureVirtualFunctionCallInConstructorError(
|
||||
const Function * scopeFunction,
|
||||
const std::list<const Token *> & tokStack,
|
||||
const std::string &purefuncname)
|
||||
{
|
||||
const char * scopeFunctionTypeName = getFunctionTypeName(scopeFunction.type);
|
||||
const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor";
|
||||
|
||||
ErrorPath errorPath;
|
||||
for (std::list<const Token *>::const_iterator it = tokStack.begin(); it != tokStack.end(); ++it)
|
||||
errorPath.push_back(ErrorPathItem(*it, "Calling " + (*it)->str()));
|
||||
if (!errorPath.empty())
|
||||
errorPath.back().second = purefuncname + " is a pure virtual method without body";
|
||||
|
||||
reportError(tokStack, Severity::warning, "pureVirtualCall", "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ".\n"
|
||||
"Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), false);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
checkClass.virtualDestructor();
|
||||
checkClass.checkConst();
|
||||
checkClass.copyconstructors();
|
||||
checkClass.checkPureVirtualFunctionCall();
|
||||
checkClass.checkVirtualFunctionCallInConstructor();
|
||||
|
||||
checkClass.checkDuplInheritedMembers();
|
||||
checkClass.checkExplicitConstructors();
|
||||
|
@ -143,8 +143,8 @@ public:
|
|||
|
||||
void copyconstructors();
|
||||
|
||||
/** @brief call of pure virtual function */
|
||||
void checkPureVirtualFunctionCall();
|
||||
/** @brief call of virtual function in constructor/destructor */
|
||||
void checkVirtualFunctionCallInConstructor();
|
||||
|
||||
/** @brief Check duplicated inherited members */
|
||||
void checkDuplInheritedMembers();
|
||||
|
@ -184,7 +184,8 @@ private:
|
|||
void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname);
|
||||
void suggestInitializationList(const Token *tok, const std::string& varname);
|
||||
void selfInitializationError(const Token* tok, const std::string& varname);
|
||||
void callsPureVirtualFunctionError(const Function & scopeFunction, const std::list<const Token *> & tokStack, const std::string &purefuncname);
|
||||
void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list<const Token *> & tokStack, const std::string &purefuncname);
|
||||
void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list<const Token *> & tokStack, const std::string &funcname);
|
||||
void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedname, const std::string &basename, const std::string &variablename, bool derivedIsStruct, bool baseIsStruct);
|
||||
void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor);
|
||||
void unsafeClassDivZeroError(const Token *tok, const std::string &className, const std::string &methodName, const std::string &varName);
|
||||
|
@ -219,6 +220,8 @@ private:
|
|||
c.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false);
|
||||
c.copyCtorAndEqOperatorError(nullptr, "class", false, false);
|
||||
c.unsafeClassDivZeroError(nullptr, "Class", "dostuff", "x");
|
||||
c.pureVirtualFunctionCallInConstructorError(nullptr, std::list<const Token *>(), "f");
|
||||
c.virtualFunctionCallInConstructorError(nullptr, std::list<const Token *>(), "f");
|
||||
}
|
||||
|
||||
static std::string myName() {
|
||||
|
@ -317,22 +320,22 @@ private:
|
|||
/**
|
||||
* @brief gives a list of tokens where pure virtual functions are called directly or indirectly
|
||||
* @param function function to be checked
|
||||
* @param callsPureVirtualFunctionMap map of results for already checked functions
|
||||
* @param virtualFunctionCallsMap map of results for already checked functions
|
||||
* @return list of tokens where pure virtual functions are called
|
||||
*/
|
||||
const std::list<const Token *> & callsPureVirtualFunction(
|
||||
const std::list<const Token *> & getVirtualFunctionCalls(
|
||||
const Function & function,
|
||||
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap);
|
||||
std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap);
|
||||
|
||||
/**
|
||||
* @brief looks for the first pure virtual function call stack
|
||||
* @param callsPureVirtualFunctionMap map of results obtained from callsPureVirtualFunction
|
||||
* @param pureCall token where pure virtual function is called directly or indirectly
|
||||
* @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls
|
||||
* @param callToken token where pure virtual function is called directly or indirectly
|
||||
* @param[in,out] pureFuncStack list to append the stack
|
||||
*/
|
||||
void getFirstPureVirtualFunctionCallStack(
|
||||
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap,
|
||||
const Token & pureCall,
|
||||
void getFirstVirtualFunctionCallStack(
|
||||
std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap,
|
||||
const Token *callToken,
|
||||
std::list<const Token *> & pureFuncStack);
|
||||
|
||||
static bool canNotCopy(const Scope *scope);
|
||||
|
|
|
@ -180,6 +180,7 @@ private:
|
|||
TEST_CASE(initializerListUsage);
|
||||
TEST_CASE(selfInitialization);
|
||||
|
||||
TEST_CASE(virtualFunctionCallInConstructor);
|
||||
TEST_CASE(pureVirtualFunctionCall);
|
||||
TEST_CASE(pureVirtualFunctionCallOtherClass);
|
||||
TEST_CASE(pureVirtualFunctionCallWithBody);
|
||||
|
@ -6297,7 +6298,7 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void checkPureVirtualFunctionCall(const char code[], Settings *s = 0, bool inconclusive = true) {
|
||||
void checkVirtualFunctionCall(const char code[], Settings *s = 0, bool inconclusive = true) {
|
||||
// Clear the error log
|
||||
errout.str("");
|
||||
|
||||
|
@ -6316,200 +6317,211 @@ private:
|
|||
tokenizer.simplifyTokenList2();
|
||||
|
||||
CheckClass checkClass(&tokenizer, s, this);
|
||||
checkClass.checkPureVirtualFunctionCall();
|
||||
checkClass.checkVirtualFunctionCallInConstructor();
|
||||
}
|
||||
|
||||
void virtualFunctionCallInConstructor() {
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual int f() { return 1; }\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{f();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of virtual function 'f' in constructor.\n", errout.str());
|
||||
}
|
||||
|
||||
void pureVirtualFunctionCall() {
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{pure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{pure();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual int pure()=0;\n"
|
||||
" A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::A():m(A::pure())\n"
|
||||
"{}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual int pure()=0;\n"
|
||||
" A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::A():m(A::pure())\n"
|
||||
"{}\n");
|
||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0; \n"
|
||||
" virtual ~A(); \n"
|
||||
" int m; \n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{pure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0; \n"
|
||||
" virtual ~A(); \n"
|
||||
" int m; \n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{pure();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure()\n"
|
||||
" {pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure()\n"
|
||||
" {pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual int pure()=0;\n"
|
||||
" int nonpure()\n"
|
||||
" {return pure();}\n"
|
||||
" A(); \n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::A():m(nonpure())\n"
|
||||
"{}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual int pure()=0;\n"
|
||||
" int nonpure()\n"
|
||||
" {return pure();}\n"
|
||||
" A(); \n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::A():m(nonpure())\n"
|
||||
"{}\n");
|
||||
TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", "", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0; \n"
|
||||
" void nonpure()\n"
|
||||
" {pure();}\n"
|
||||
" virtual ~A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{nonpure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0; \n"
|
||||
" void nonpure()\n"
|
||||
" {pure();}\n"
|
||||
" virtual ~A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{nonpure();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A(bool b);\n"
|
||||
"};\n"
|
||||
"A::A(bool b)\n"
|
||||
"{if (b) pure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A(bool b);\n"
|
||||
"};\n"
|
||||
"A::A(bool b)\n"
|
||||
"{if (b) pure();}\n");
|
||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" virtual ~A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{if (b) pure();}");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" virtual ~A();\n"
|
||||
" int m;\n"
|
||||
"};\n"
|
||||
"A::~A()\n"
|
||||
"{if (b) pure();}");
|
||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str());
|
||||
|
||||
// #5831
|
||||
checkPureVirtualFunctionCall("class abc {\n"
|
||||
"public:\n"
|
||||
" virtual ~abc() throw() {}\n"
|
||||
" virtual void def(void* g) throw () = 0;\n"
|
||||
"};");
|
||||
checkVirtualFunctionCall("class abc {\n"
|
||||
"public:\n"
|
||||
" virtual ~abc() throw() {}\n"
|
||||
" virtual void def(void* g) throw () = 0;\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// #4992
|
||||
checkPureVirtualFunctionCall("class CMyClass {\n"
|
||||
" std::function< void(void) > m_callback;\n"
|
||||
"public:\n"
|
||||
" CMyClass() {\n"
|
||||
" m_callback = [this]() { return VirtualMethod(); };\n"
|
||||
" }\n"
|
||||
" virtual void VirtualMethod() = 0;\n"
|
||||
"};");
|
||||
checkVirtualFunctionCall("class CMyClass {\n"
|
||||
" std::function< void(void) > m_callback;\n"
|
||||
"public:\n"
|
||||
" CMyClass() {\n"
|
||||
" m_callback = [this]() { return VirtualMethod(); };\n"
|
||||
" }\n"
|
||||
" virtual void VirtualMethod() = 0;\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void pureVirtualFunctionCallOtherClass() {
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A(const A & a);\n"
|
||||
"};\n"
|
||||
"A::A(const A & a)\n"
|
||||
"{a.pure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A(const A & a);\n"
|
||||
"};\n"
|
||||
"A::A(const A & a)\n"
|
||||
"{a.pure();}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"class B\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{B b; b.pure();}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"class B\n"
|
||||
"{\n"
|
||||
" virtual void pure()=0;\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{B b; b.pure();}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void pureVirtualFunctionCallWithBody() {
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pureWithBody()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{pureWithBody();}\n"
|
||||
"void A::pureWithBody()\n"
|
||||
"{}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" virtual void pureWithBody()=0;\n"
|
||||
" A();\n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{pureWithBody();}\n"
|
||||
"void A::pureWithBody()\n"
|
||||
"{}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pureWithBody()=0;\n"
|
||||
" void nonpure()\n"
|
||||
" {pureWithBody();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure();}\n"
|
||||
"void A::pureWithBody()\n"
|
||||
"{}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pureWithBody()=0;\n"
|
||||
" void nonpure()\n"
|
||||
" {pureWithBody();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure();}\n"
|
||||
"void A::pureWithBody()\n"
|
||||
"{}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
}
|
||||
|
||||
void pureVirtualFunctionCallPrevented() {
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" { if (bCallPure) pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" { if (bCallPure) pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" { if (!bCallPure) ; else pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" { if (!bCallPure) ; else pure();}\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkPureVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" {\n"
|
||||
" switch (bCallPure) {\n"
|
||||
" case true: pure(); break;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
" {\n"
|
||||
" virtual void pure()=0;\n"
|
||||
" void nonpure(bool bCallPure)\n"
|
||||
" {\n"
|
||||
" switch (bCallPure) {\n"
|
||||
" case true: pure(); break;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" A(); \n"
|
||||
"};\n"
|
||||
"A::A()\n"
|
||||
"{nonpure(false);}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue