Improve check: Warn about virtual function calls in constructor/destructor

This commit is contained in:
Daniel Marjamäki 2018-04-02 15:31:47 +02:00
parent e492932f19
commit 1046ca2120
3 changed files with 279 additions and 248 deletions

View File

@ -64,11 +64,6 @@ static const char * getFunctionTypeName(Function::Type type)
return ""; return "";
} }
static bool isPureWithoutBody(Function const & func)
{
return func.isPure() && !func.hasBody();
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) 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)) if (! _settings->isEnabled(Settings::WARNING))
return; return;
const std::size_t functions = symbolDatabase->functionScopes.size(); 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) { for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i]; const Scope * scope = symbolDatabase->functionScopes[i];
if (scope->function == nullptr || !scope->function->hasBody() || if (scope->function == nullptr || !scope->function->hasBody() ||
@ -2216,106 +2211,127 @@ void CheckClass::checkPureVirtualFunctionCall()
scope->function->isDestructor())) scope->function->isDestructor()))
continue; continue;
const std::list<const Token *> & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap); const std::list<const Token *> & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap);
for (std::list<const Token *>::const_iterator pureCallIter=pureVirtualFunctionCalls.begin(); for (std::list<const Token *>::const_iterator it = virtualFunctionCalls.begin(); it != virtualFunctionCalls.end(); ++it) {
pureCallIter!=pureVirtualFunctionCalls.end(); const Token * callToken = *it;
++pureCallIter) { std::list<const Token *> callstack;
const Token & pureCall=**pureCallIter; callstack.push_back(callToken);
std::list<const Token *> pureFuncStack; getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
pureFuncStack.push_back(&pureCall); if (callstack.empty())
getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, pureCall, pureFuncStack); continue;
if (!pureFuncStack.empty()) if (callstack.back()->function()->isPure())
callsPureVirtualFunctionError(*scope->function, pureFuncStack, pureFuncStack.back()->str()); 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, const std::list<const Token *> & CheckClass::getVirtualFunctionCalls(const Function & function,
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap) std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap)
{ {
std::pair<std::map<const Function *, std::list<const Token *> >::iterator, bool > found = const std::map<const Function *, std::list<const Token *> >::const_iterator found = virtualFunctionCallsMap.find(&function);
callsPureVirtualFunctionMap.insert(std::pair<const Function *, std::list< const Token *> >(&function, std::list<const Token *>())); if (found != virtualFunctionCallsMap.end())
std::list<const Token *> & pureFunctionCalls = found.first->second; return found->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 Function * callFunction = tok->function(); virtualFunctionCallsMap[&function] = std::list<const Token *>();
if (!callFunction || std::list<const Token *> & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second;
function.nestedIn != callFunction->nestedIn ||
(tok->previous() && tok->previous()->str() == "."))
continue;
if (tok->previous() && if (!function.hasBody())
tok->previous()->str() == "(") { return virtualFunctionCalls;
const Token * prev = tok->previous();
if (prev->previous() &&
(_settings->library.ignorefunction(tok->str())
|| _settings->library.ignorefunction(prev->previous()->str())))
continue;
}
if (isPureWithoutBody(*callFunction)) { for (const Token *tok = function.arg->link(); tok != function.functionScope->classEnd; tok = tok->next()) {
pureFunctionCalls.push_back(tok); if (function.type != Function::eConstructor &&
continue; function.type != Function::eCopyConstructor &&
} function.type != Function::eMoveConstructor &&
function.type != Function::eDestructor) {
const std::list<const Token *> & pureFunctionCallsOfTok = callsPureVirtualFunction(*callFunction, if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) ||
callsPureVirtualFunctionMap); Token::simpleMatch(tok, "else {")) {
if (!pureFunctionCallsOfTok.empty()) { // Assume pure virtual function call is prevented by "if|else|switch" condition
pureFunctionCalls.push_back(tok); tok = tok->linkAt(1);
continue; 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( void CheckClass::getFirstVirtualFunctionCallStack(
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap, std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap,
const Token & pureCall, const Token * callToken,
std::list<const Token *> & pureFuncStack) std::list<const Token *> & callstack)
{ {
if (isPureWithoutBody(*pureCall.function())) { const Function *callFunction = callToken->function();
pureFuncStack.push_back(pureCall.function()->token); if (callFunction->isVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) {
callstack.push_back(callFunction->token);
return; return;
} }
std::map<const Function *, std::list<const Token *> >::const_iterator found = callsPureVirtualFunctionMap.find(pureCall.function()); std::map<const Function *, std::list<const Token *> >::const_iterator found = virtualFunctionCallsMap.find(callFunction);
if (found == callsPureVirtualFunctionMap.end() || if (found == virtualFunctionCallsMap.end() || found->second.empty()) {
found->second.empty()) { callstack.clear();
pureFuncStack.clear();
return; return;
} }
const Token & firstPureCall = **found->second.begin(); const Token * firstCall = *found->second.begin();
pureFuncStack.push_back(&firstPureCall); callstack.push_back(firstCall);
getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, firstPureCall, pureFuncStack); getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, callstack);
} }
void CheckClass::callsPureVirtualFunctionError( void CheckClass::virtualFunctionCallInConstructorError(
const Function & scopeFunction, 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::list<const Token *> & tokStack,
const std::string &purefuncname) 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" 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); "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), false);
} }

View File

@ -85,7 +85,7 @@ public:
checkClass.virtualDestructor(); checkClass.virtualDestructor();
checkClass.checkConst(); checkClass.checkConst();
checkClass.copyconstructors(); checkClass.copyconstructors();
checkClass.checkPureVirtualFunctionCall(); checkClass.checkVirtualFunctionCallInConstructor();
checkClass.checkDuplInheritedMembers(); checkClass.checkDuplInheritedMembers();
checkClass.checkExplicitConstructors(); checkClass.checkExplicitConstructors();
@ -143,8 +143,8 @@ public:
void copyconstructors(); void copyconstructors();
/** @brief call of pure virtual function */ /** @brief call of virtual function in constructor/destructor */
void checkPureVirtualFunctionCall(); void checkVirtualFunctionCallInConstructor();
/** @brief Check duplicated inherited members */ /** @brief Check duplicated inherited members */
void checkDuplInheritedMembers(); void checkDuplInheritedMembers();
@ -184,7 +184,8 @@ private:
void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); 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 suggestInitializationList(const Token *tok, const std::string& varname);
void selfInitializationError(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 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 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); 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.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false);
c.copyCtorAndEqOperatorError(nullptr, "class", false, false); c.copyCtorAndEqOperatorError(nullptr, "class", false, false);
c.unsafeClassDivZeroError(nullptr, "Class", "dostuff", "x"); 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() { static std::string myName() {
@ -317,22 +320,22 @@ private:
/** /**
* @brief gives a list of tokens where pure virtual functions are called directly or indirectly * @brief gives a list of tokens where pure virtual functions are called directly or indirectly
* @param function function to be checked * @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 * @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, 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 * @brief looks for the first pure virtual function call stack
* @param callsPureVirtualFunctionMap map of results obtained from callsPureVirtualFunction * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls
* @param pureCall token where pure virtual function is called directly or indirectly * @param callToken token where pure virtual function is called directly or indirectly
* @param[in,out] pureFuncStack list to append the stack * @param[in,out] pureFuncStack list to append the stack
*/ */
void getFirstPureVirtualFunctionCallStack( void getFirstVirtualFunctionCallStack(
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap, std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap,
const Token & pureCall, const Token *callToken,
std::list<const Token *> & pureFuncStack); std::list<const Token *> & pureFuncStack);
static bool canNotCopy(const Scope *scope); static bool canNotCopy(const Scope *scope);

View File

@ -180,6 +180,7 @@ private:
TEST_CASE(initializerListUsage); TEST_CASE(initializerListUsage);
TEST_CASE(selfInitialization); TEST_CASE(selfInitialization);
TEST_CASE(virtualFunctionCallInConstructor);
TEST_CASE(pureVirtualFunctionCall); TEST_CASE(pureVirtualFunctionCall);
TEST_CASE(pureVirtualFunctionCallOtherClass); TEST_CASE(pureVirtualFunctionCallOtherClass);
TEST_CASE(pureVirtualFunctionCallWithBody); TEST_CASE(pureVirtualFunctionCallWithBody);
@ -6297,7 +6298,7 @@ private:
ASSERT_EQUALS("", errout.str()); 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 // Clear the error log
errout.str(""); errout.str("");
@ -6316,200 +6317,211 @@ private:
tokenizer.simplifyTokenList2(); tokenizer.simplifyTokenList2();
CheckClass checkClass(&tokenizer, s, this); 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() { void pureVirtualFunctionCall() {
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" A();\n" " A();\n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{pure();}\n"); "{pure();}\n");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual int pure()=0;\n" " virtual int pure()=0;\n"
" A();\n" " A();\n"
" int m;\n" " int m;\n"
"};\n" "};\n"
"A::A():m(A::pure())\n" "A::A():m(A::pure())\n"
"{}\n"); "{}\n");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0; \n" " virtual void pure()=0; \n"
" virtual ~A(); \n" " virtual ~A(); \n"
" int m; \n" " int m; \n"
"};\n" "};\n"
"A::~A()\n" "A::~A()\n"
"{pure();}\n"); "{pure();}\n");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" void nonpure()\n" " void nonpure()\n"
" {pure();}\n" " {pure();}\n"
" A(); \n" " A(); \n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{nonpure();}\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()); 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" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual int pure()=0;\n" " virtual int pure()=0;\n"
" int nonpure()\n" " int nonpure()\n"
" {return pure();}\n" " {return pure();}\n"
" A(); \n" " A(); \n"
" int m;\n" " int m;\n"
"};\n" "};\n"
"A::A():m(nonpure())\n" "A::A():m(nonpure())\n"
"{}\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()); 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" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0; \n" " virtual void pure()=0; \n"
" void nonpure()\n" " void nonpure()\n"
" {pure();}\n" " {pure();}\n"
" virtual ~A();\n" " virtual ~A();\n"
" int m;\n" " int m;\n"
"};\n" "};\n"
"A::~A()\n" "A::~A()\n"
"{nonpure();}\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()); 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" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" A(bool b);\n" " A(bool b);\n"
"};\n" "};\n"
"A::A(bool b)\n" "A::A(bool b)\n"
"{if (b) pure();}\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()); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" virtual ~A();\n" " virtual ~A();\n"
" int m;\n" " int m;\n"
"};\n" "};\n"
"A::~A()\n" "A::~A()\n"
"{if (b) pure();}"); "{if (b) pure();}");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str());
// #5831 // #5831
checkPureVirtualFunctionCall("class abc {\n" checkVirtualFunctionCall("class abc {\n"
"public:\n" "public:\n"
" virtual ~abc() throw() {}\n" " virtual ~abc() throw() {}\n"
" virtual void def(void* g) throw () = 0;\n" " virtual void def(void* g) throw () = 0;\n"
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// #4992 // #4992
checkPureVirtualFunctionCall("class CMyClass {\n" checkVirtualFunctionCall("class CMyClass {\n"
" std::function< void(void) > m_callback;\n" " std::function< void(void) > m_callback;\n"
"public:\n" "public:\n"
" CMyClass() {\n" " CMyClass() {\n"
" m_callback = [this]() { return VirtualMethod(); };\n" " m_callback = [this]() { return VirtualMethod(); };\n"
" }\n" " }\n"
" virtual void VirtualMethod() = 0;\n" " virtual void VirtualMethod() = 0;\n"
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void pureVirtualFunctionCallOtherClass() { void pureVirtualFunctionCallOtherClass() {
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" A(const A & a);\n" " A(const A & a);\n"
"};\n" "};\n"
"A::A(const A & a)\n" "A::A(const A & a)\n"
"{a.pure();}\n"); "{a.pure();}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" A();\n" " A();\n"
"};\n" "};\n"
"class B\n" "class B\n"
"{\n" "{\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{B b; b.pure();}\n"); "{B b; b.pure();}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void pureVirtualFunctionCallWithBody() { void pureVirtualFunctionCallWithBody() {
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
"{\n" "{\n"
" virtual void pureWithBody()=0;\n" " virtual void pureWithBody()=0;\n"
" A();\n" " A();\n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{pureWithBody();}\n" "{pureWithBody();}\n"
"void A::pureWithBody()\n" "void A::pureWithBody()\n"
"{}\n"); "{}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pureWithBody()=0;\n" " virtual void pureWithBody()=0;\n"
" void nonpure()\n" " void nonpure()\n"
" {pureWithBody();}\n" " {pureWithBody();}\n"
" A(); \n" " A(); \n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{nonpure();}\n" "{nonpure();}\n"
"void A::pureWithBody()\n" "void A::pureWithBody()\n"
"{}\n"); "{}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void pureVirtualFunctionCallPrevented() { void pureVirtualFunctionCallPrevented() {
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" void nonpure(bool bCallPure)\n" " void nonpure(bool bCallPure)\n"
" { if (bCallPure) pure();}\n" " { if (bCallPure) pure();}\n"
" A(); \n" " A(); \n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{nonpure(false);}\n"); "{nonpure(false);}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" void nonpure(bool bCallPure)\n" " void nonpure(bool bCallPure)\n"
" { if (!bCallPure) ; else pure();}\n" " { if (!bCallPure) ; else pure();}\n"
" A(); \n" " A(); \n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{nonpure(false);}\n"); "{nonpure(false);}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkPureVirtualFunctionCall("class A\n" checkVirtualFunctionCall("class A\n"
" {\n" " {\n"
" virtual void pure()=0;\n" " virtual void pure()=0;\n"
" void nonpure(bool bCallPure)\n" " void nonpure(bool bCallPure)\n"
" {\n" " {\n"
" switch (bCallPure) {\n" " switch (bCallPure) {\n"
" case true: pure(); break;\n" " case true: pure(); break;\n"
" }\n" " }\n"
" }\n" " }\n"
" A(); \n" " A(); \n"
"};\n" "};\n"
"A::A()\n" "A::A()\n"
"{nonpure(false);}\n"); "{nonpure(false);}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }