Update symbol database such that the override keyword implies that the function is also virtual (#1907)
* Update symbol database such that the override keyword implies that the function is also virtual * Add test case for implicit override * change isVirtual to hasVirtualSpecifier * fix method documentation for getVirtualFunctionCalls and getFirstVirtualFunctionCallStack * Fix isImplicitlyVirtual to consider the override keyword and document logic * Fix getFirstVirtualFunctionCallStack and getVirtualFunctionCalls to use isImplicitlyVirtual instead of isVirtual so new test case passes
This commit is contained in:
parent
999aa407f4
commit
7e54f989f9
|
@ -1228,7 +1228,7 @@ void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Sco
|
|||
|
||||
// Warn if type is a class that contains any virtual functions
|
||||
for (const Function &func : type->functionList) {
|
||||
if (func.isVirtual()) {
|
||||
if (func.hasVirtualSpecifier()) {
|
||||
if (allocation)
|
||||
mallocOnClassError(tok, tok->str(), type->classDef, "virtual function");
|
||||
else
|
||||
|
@ -1651,9 +1651,9 @@ void CheckClass::virtualDestructor()
|
|||
if (scope->definedType->derivedFrom.empty()) {
|
||||
if (printInconclusive) {
|
||||
const Function *destructor = scope->getDestructor();
|
||||
if (destructor && !destructor->isVirtual()) {
|
||||
if (destructor && !destructor->hasVirtualSpecifier()) {
|
||||
for (const Function &func : scope->functionList) {
|
||||
if (func.isVirtual()) {
|
||||
if (func.hasVirtualSpecifier()) {
|
||||
inconclusiveErrors.push_back(destructor);
|
||||
break;
|
||||
}
|
||||
|
@ -1736,7 +1736,7 @@ void CheckClass::virtualDestructor()
|
|||
if (derivedFrom->derivedFrom.empty()) {
|
||||
virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false);
|
||||
}
|
||||
} else if (!baseDestructor->isVirtual()) {
|
||||
} else if (!baseDestructor->hasVirtualSpecifier()) {
|
||||
// TODO: This is just a temporary fix, better solution is needed.
|
||||
// Skip situations where base class has base classes of its own, because
|
||||
// some of the base classes might have virtual destructor.
|
||||
|
@ -1827,7 +1827,7 @@ void CheckClass::checkConst()
|
|||
if (func.type != Function::eFunction || !func.hasBody())
|
||||
continue;
|
||||
// don't warn for friend/static/virtual functions
|
||||
if (func.isFriend() || func.isStatic() || func.isVirtual())
|
||||
if (func.isFriend() || func.isStatic() || func.hasVirtualSpecifier())
|
||||
continue;
|
||||
// get last token of return type
|
||||
const Token *previous = func.tokenDef->previous();
|
||||
|
@ -2369,7 +2369,7 @@ const std::list<const Token *> & CheckClass::getVirtualFunctionCalls(const Funct
|
|||
continue;
|
||||
}
|
||||
|
||||
if (callFunction->isVirtual()) {
|
||||
if (callFunction->isImplicitlyVirtual()) {
|
||||
if (!callFunction->isPure() && Token::simpleMatch(tok->previous(), "::"))
|
||||
continue;
|
||||
virtualFunctionCalls.push_back(tok);
|
||||
|
@ -2389,7 +2389,7 @@ void CheckClass::getFirstVirtualFunctionCallStack(
|
|||
std::list<const Token *> & pureFuncStack)
|
||||
{
|
||||
const Function *callFunction = callToken->function();
|
||||
if (callFunction->isVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) {
|
||||
if (callFunction->isImplicitlyVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) {
|
||||
pureFuncStack.push_back(callFunction->tokenDef);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -317,7 +317,7 @@ private:
|
|||
void initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage);
|
||||
|
||||
/**
|
||||
* @brief gives a list of tokens where pure virtual functions are called directly or indirectly
|
||||
* @brief gives a list of tokens where virtual functions are called directly or indirectly
|
||||
* @param function function to be checked
|
||||
* @param virtualFunctionCallsMap map of results for already checked functions
|
||||
* @return list of tokens where pure virtual functions are called
|
||||
|
@ -327,7 +327,7 @@ private:
|
|||
std::map<const Function *, std::list<const Token *> > & virtualFunctionCallsMap);
|
||||
|
||||
/**
|
||||
* @brief looks for the first pure virtual function call stack
|
||||
* @brief looks for the first virtual function call stack
|
||||
* @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
|
||||
|
|
|
@ -1239,7 +1239,7 @@ void CheckOther::checkPassByReference()
|
|||
}
|
||||
|
||||
// Check if variable could be const
|
||||
if (!var->scope() || var->scope()->function->isVirtual())
|
||||
if (!var->scope() || var->scope()->function->hasVirtualSpecifier())
|
||||
continue;
|
||||
|
||||
if (canBeConst(var)) {
|
||||
|
|
|
@ -1757,7 +1757,7 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *s
|
|||
|
||||
// virtual function
|
||||
else if (tok1->str() == "virtual") {
|
||||
isVirtual(true);
|
||||
hasVirtualSpecifier(true);
|
||||
}
|
||||
|
||||
// static function
|
||||
|
@ -2695,7 +2695,7 @@ void SymbolDatabase::printOut(const char *title) const
|
|||
std::cout << " hasBody: " << func->hasBody() << std::endl;
|
||||
std::cout << " isInline: " << func->isInline() << std::endl;
|
||||
std::cout << " isConst: " << func->isConst() << std::endl;
|
||||
std::cout << " isVirtual: " << func->isVirtual() << std::endl;
|
||||
std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl;
|
||||
std::cout << " isPure: " << func->isPure() << std::endl;
|
||||
std::cout << " isStatic: " << func->isStatic() << std::endl;
|
||||
std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl;
|
||||
|
@ -2951,8 +2951,8 @@ void SymbolDatabase::printXml(std::ostream &out) const
|
|||
function->type == Function::eFunction ? "Function" :
|
||||
"Unknown") << '\"';
|
||||
if (function->nestedIn->definedType) {
|
||||
if (function->isVirtual())
|
||||
out << " isVirtual=\"true\"";
|
||||
if (function->hasVirtualSpecifier())
|
||||
out << " hasVirtualSpecifier=\"true\"";
|
||||
else if (function->isImplicitlyVirtual())
|
||||
out << " isImplicitlyVirtual=\"true\"";
|
||||
}
|
||||
|
@ -3144,14 +3144,16 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s
|
|||
|
||||
bool Function::isImplicitlyVirtual(bool defaultVal) const
|
||||
{
|
||||
if (isVirtual())
|
||||
if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual
|
||||
return true;
|
||||
if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile
|
||||
return true;
|
||||
bool foundAllBaseClasses = true;
|
||||
if (getOverriddenFunction(&foundAllBaseClasses))
|
||||
if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual
|
||||
return true;
|
||||
if (foundAllBaseClasses)
|
||||
if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual
|
||||
return false;
|
||||
return defaultVal;
|
||||
return defaultVal; //If we can't see all the bases classes then we can't say conclusively
|
||||
}
|
||||
|
||||
const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const
|
||||
|
@ -3180,7 +3182,7 @@ const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType
|
|||
// check if function defined in base class
|
||||
for (std::multimap<std::string, const Function *>::const_iterator it = parent->functionMap.find(tokenDef->str()); it != parent->functionMap.end() && it->first == tokenDef->str(); ++it) {
|
||||
const Function * func = it->second;
|
||||
if (func->isVirtual()) { // Base is virtual and of same name
|
||||
if (func->hasVirtualSpecifier()) { // Base is virtual and of same name
|
||||
const Token *temp1 = func->tokenDef->previous();
|
||||
const Token *temp2 = tokenDef->previous();
|
||||
bool match = true;
|
||||
|
|
|
@ -665,7 +665,7 @@ class CPPCHECKLIB Function {
|
|||
fHasBody = (1 << 0), ///< @brief has implementation
|
||||
fIsInline = (1 << 1), ///< @brief implementation in class definition
|
||||
fIsConst = (1 << 2), ///< @brief is const
|
||||
fIsVirtual = (1 << 3), ///< @brief is virtual
|
||||
fHasVirtualSpecifier = (1 << 3), ///< @brief does declaration contain 'virtual' specifier
|
||||
fIsPure = (1 << 4), ///< @brief is pure virtual
|
||||
fIsStatic = (1 << 5), ///< @brief is static
|
||||
fIsStaticLocal = (1 << 6), ///< @brief is static local
|
||||
|
@ -771,8 +771,8 @@ public:
|
|||
bool isConst() const {
|
||||
return getFlag(fIsConst);
|
||||
}
|
||||
bool isVirtual() const {
|
||||
return getFlag(fIsVirtual);
|
||||
bool hasVirtualSpecifier() const {
|
||||
return getFlag(fHasVirtualSpecifier);
|
||||
}
|
||||
bool isPure() const {
|
||||
return getFlag(fIsPure);
|
||||
|
@ -878,8 +878,8 @@ private:
|
|||
void isConst(bool state) {
|
||||
setFlag(fIsConst, state);
|
||||
}
|
||||
void isVirtual(bool state) {
|
||||
setFlag(fIsVirtual, state);
|
||||
void hasVirtualSpecifier(bool state) {
|
||||
setFlag(fHasVirtualSpecifier, state);
|
||||
}
|
||||
void isPure(bool state) {
|
||||
setFlag(fIsPure, state);
|
||||
|
|
|
@ -4836,7 +4836,7 @@ static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogg
|
|||
&error);
|
||||
if (!error) {
|
||||
ValueFlow::Value v(result);
|
||||
if (function->isVirtual())
|
||||
if (function->hasVirtualSpecifier())
|
||||
v.setPossible();
|
||||
else
|
||||
v.setKnown();
|
||||
|
|
|
@ -6828,6 +6828,23 @@ private:
|
|||
"int A::f() { return 1; }\n");
|
||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str());
|
||||
|
||||
checkVirtualFunctionCall("class A : B {\n"
|
||||
" int f() override;\n"
|
||||
" A() {f();}\n"
|
||||
"};\n"
|
||||
"int A::f() { return 1; }\n");
|
||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str());
|
||||
|
||||
checkVirtualFunctionCall("class B {\n"
|
||||
" virtual int f() = 0;\n"
|
||||
"};\n"
|
||||
"class A : B {\n"
|
||||
" int f();\n"
|
||||
" A() {f();}\n"
|
||||
"};\n"
|
||||
"int A::f() { return 1; }\n");
|
||||
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (warning) Virtual function 'f' is called from constructor 'A()' at line 6. Dynamic binding is not used.\n", errout.str());
|
||||
|
||||
checkVirtualFunctionCall("class A\n"
|
||||
"{\n"
|
||||
" A() { A::f(); }\n"
|
||||
|
|
|
@ -4070,7 +4070,7 @@ private:
|
|||
const Token *f = db ? Token::findsimplematch(tokenizer.tokens(), "get_endpoint_url ( ) const noexcept ;") : nullptr;
|
||||
ASSERT(f != nullptr);
|
||||
ASSERT(f && f->function() && f->function()->token->linenr() == 2);
|
||||
ASSERT(f && f->function() && f->function()->isVirtual());
|
||||
ASSERT(f && f->function() && f->function()->hasVirtualSpecifier());
|
||||
ASSERT(f && f->function() && !f->function()->hasOverrideSpecifier());
|
||||
ASSERT(f && f->function() && !f->function()->hasFinalSpecifier());
|
||||
ASSERT(f && f->function() && f->function()->isConst());
|
||||
|
@ -4078,7 +4078,7 @@ private:
|
|||
f = db ? Token::findsimplematch(tokenizer.tokens(), "get_endpoint_url ( ) const noexcept override final ;") : nullptr;
|
||||
ASSERT(f != nullptr);
|
||||
ASSERT(f && f->function() && f->function()->token->linenr() == 5);
|
||||
ASSERT(f && f->function() && f->function()->isVirtual());
|
||||
ASSERT(f && f->function() && f->function()->hasVirtualSpecifier());
|
||||
ASSERT(f && f->function() && f->function()->hasOverrideSpecifier());
|
||||
ASSERT(f && f->function() && f->function()->hasFinalSpecifier());
|
||||
ASSERT(f && f->function() && f->function()->isConst());
|
||||
|
|
Loading…
Reference in New Issue