From 1046ca2120b11b3829b0f08540828b06d01bfa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 2 Apr 2018 15:31:47 +0200 Subject: [PATCH] Improve check: Warn about virtual function calls in constructor/destructor --- lib/checkclass.cpp | 186 +++++++++++++++------------ lib/checkclass.h | 27 ++-- test/testclass.cpp | 314 +++++++++++++++++++++++---------------------- 3 files changed, 279 insertions(+), 248 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 7b7ad48ae..6dda50185 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -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 > callsPureVirtualFunctionMap; + std::map > 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 & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap); - for (std::list::const_iterator pureCallIter=pureVirtualFunctionCalls.begin(); - pureCallIter!=pureVirtualFunctionCalls.end(); - ++pureCallIter) { - const Token & pureCall=**pureCallIter; - std::list pureFuncStack; - pureFuncStack.push_back(&pureCall); - getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, pureCall, pureFuncStack); - if (!pureFuncStack.empty()) - callsPureVirtualFunctionError(*scope->function, pureFuncStack, pureFuncStack.back()->str()); + const std::list & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap); + for (std::list::const_iterator it = virtualFunctionCalls.begin(); it != virtualFunctionCalls.end(); ++it) { + const Token * callToken = *it; + std::list 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 & CheckClass::callsPureVirtualFunction(const Function & function, - std::map > & callsPureVirtualFunctionMap) +const std::list & CheckClass::getVirtualFunctionCalls(const Function & function, + std::map > & virtualFunctionCallsMap) { - std::pair >::iterator, bool > found = - callsPureVirtualFunctionMap.insert(std::pair >(&function, std::list())); - std::list & 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_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(); + std::list & 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 & 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 & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap); + if (!virtualFunctionCallsOfTok.empty()) + virtualFunctionCalls.push_back(tok); } - return pureFunctionCalls; + return virtualFunctionCalls; } -void CheckClass::getFirstPureVirtualFunctionCallStack( - std::map > & callsPureVirtualFunctionMap, - const Token & pureCall, - std::list & pureFuncStack) +void CheckClass::getFirstVirtualFunctionCallStack( + std::map > & virtualFunctionCallsMap, + const Token * callToken, + std::list & 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_iterator found = callsPureVirtualFunctionMap.find(pureCall.function()); - if (found == callsPureVirtualFunctionMap.end() || - found->second.empty()) { - pureFuncStack.clear(); + std::map >::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 & tokStack, + const std::string &funcname) +{ + const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; + + ErrorPath errorPath; + for (std::list::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 & 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_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); } diff --git a/lib/checkclass.h b/lib/checkclass.h index 7956e0c10..af85f7a3f 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -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 & tokStack, const std::string &purefuncname); + void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); + void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & 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(), "f"); + c.virtualFunctionCallInConstructorError(nullptr, std::list(), "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 & callsPureVirtualFunction( + const std::list & getVirtualFunctionCalls( const Function & function, - std::map > & callsPureVirtualFunctionMap); + std::map > & 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 > & callsPureVirtualFunctionMap, - const Token & pureCall, + void getFirstVirtualFunctionCallStack( + std::map > & virtualFunctionCallsMap, + const Token *callToken, std::list & pureFuncStack); static bool canNotCopy(const Scope *scope); diff --git a/test/testclass.cpp b/test/testclass.cpp index 7399f0bf8..6a27040a3 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -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()); }