Skip to content

Commit a40e581

Browse files
Fix #11757 Detect useless overriding functions (#5155)
1 parent 818ebb8 commit a40e581

3 files changed

Lines changed: 135 additions & 0 deletions

File tree

lib/checkclass.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3036,6 +3036,70 @@ void CheckClass::overrideError(const Function *funcInBase, const Function *funcI
30363036
Certainty::normal);
30373037
}
30383038

3039+
void CheckClass::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived)
3040+
{
3041+
const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : "";
3042+
const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function";
3043+
3044+
ErrorPath errorPath;
3045+
if (funcInBase && funcInDerived) {
3046+
errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class");
3047+
errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class");
3048+
}
3049+
3050+
reportError(errorPath, Severity::style, "uselessOverride",
3051+
"$symbol:" + functionName + "\n"
3052+
"The " + funcType + " '$symbol' overrides a " + funcType + " in a base class but just delegates back to the base class.",
3053+
CWE(0U) /* Unknown CWE! */,
3054+
Certainty::normal);
3055+
}
3056+
3057+
static const Token* getSingleFunctionCall(const Scope* scope) {
3058+
const Token* const start = scope->bodyStart->next();
3059+
const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd);
3060+
if (!end || end->next() != scope->bodyEnd)
3061+
return nullptr;
3062+
const Token* ftok = start;
3063+
if (ftok->str() == "return")
3064+
ftok = ftok->astOperand1();
3065+
else {
3066+
while (Token::Match(ftok, "%name%|::"))
3067+
ftok = ftok->next();
3068+
}
3069+
if (Token::simpleMatch(ftok, "(") && ftok->previous()->function())
3070+
return ftok->previous();
3071+
return nullptr;
3072+
}
3073+
3074+
void CheckClass::checkUselessOverride()
3075+
{
3076+
if (!mSettings->severity.isEnabled(Severity::style))
3077+
return;
3078+
3079+
for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) {
3080+
if (!classScope->definedType || classScope->definedType->derivedFrom.size() != 1)
3081+
continue;
3082+
for (const Function& func : classScope->functionList) {
3083+
if (!func.functionScope)
3084+
continue;
3085+
const Function* baseFunc = func.getOverriddenFunction();
3086+
if (!baseFunc || baseFunc->isPure())
3087+
continue;
3088+
if (const Token* const call = getSingleFunctionCall(func.functionScope)) {
3089+
if (call->function() != baseFunc)
3090+
continue;
3091+
std::vector<const Token*> funcArgs = getArguments(func.tokenDef);
3092+
std::vector<const Token*> callArgs = getArguments(call);
3093+
if (!std::equal(funcArgs.begin(), funcArgs.end(), callArgs.begin(), [](const Token* t1, const Token* t2) {
3094+
return t1->str() == t2->str();
3095+
}))
3096+
continue;
3097+
uselessOverrideError(baseFunc, &func);
3098+
}
3099+
}
3100+
}
3101+
}
3102+
30393103
void CheckClass::checkThisUseAfterFree()
30403104
{
30413105
if (!mSettings->severity.isEnabled(Severity::warning))

lib/checkclass.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class CPPCHECKLIB CheckClass : public Check {
8383
checkClass.checkExplicitConstructors();
8484
checkClass.checkCopyCtorAndEqOperator();
8585
checkClass.checkOverride();
86+
checkClass.checkUselessOverride();
8687
checkClass.checkThisUseAfterFree();
8788
checkClass.checkUnsafeClassRefMember();
8889
}
@@ -146,6 +147,9 @@ class CPPCHECKLIB CheckClass : public Check {
146147
/** @brief Check that the override keyword is used when overriding virtual functions */
147148
void checkOverride();
148149

150+
/** @brief Check that the overriden function is not identical to the base function */
151+
void checkUselessOverride();
152+
149153
/** @brief When "self pointer" is destroyed, 'this' might become invalid. */
150154
void checkThisUseAfterFree();
151155

@@ -227,6 +231,7 @@ class CPPCHECKLIB CheckClass : public Check {
227231
void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct);
228232
void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor);
229233
void overrideError(const Function *funcInBase, const Function *funcInDerived);
234+
void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived);
230235
void thisUseAfterFree(const Token *self, const Token *free, const Token *use);
231236
void unsafeClassRefMemberError(const Token *tok, const std::string &varname);
232237
void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase);

test/testclass.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ class TestClass : public TestFixture {
230230
TEST_CASE(override1);
231231
TEST_CASE(overrideCVRefQualifiers);
232232

233+
TEST_CASE(uselessOverride);
234+
233235
TEST_CASE(thisUseAfterFree);
234236

235237
TEST_CASE(unsafeClassRefMember);
@@ -8338,6 +8340,70 @@ class TestClass : public TestFixture {
83388340
ASSERT_EQUALS("", errout.str());
83398341
}
83408342

8343+
#define checkUselessOverride(code) checkUselessOverride_(code, __FILE__, __LINE__)
8344+
void checkUselessOverride_(const char code[], const char* file, int line) {
8345+
// Clear the error log
8346+
errout.str("");
8347+
8348+
const Settings settings = settingsBuilder().severity(Severity::style).build();
8349+
8350+
Preprocessor preprocessor(settings);
8351+
8352+
// Tokenize..
8353+
Tokenizer tokenizer(&settings, this, &preprocessor);
8354+
std::istringstream istr(code);
8355+
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
8356+
8357+
// Check..
8358+
CheckClass checkClass(&tokenizer, &settings, this);
8359+
(checkClass.checkUselessOverride)();
8360+
}
8361+
8362+
void uselessOverride() {
8363+
checkUselessOverride("struct B { virtual int f() { return 5; } };\n" // #11757
8364+
"struct D : B {\n"
8365+
" int f() override { return B::f(); }\n"
8366+
"};");
8367+
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str());
8368+
8369+
checkUselessOverride("struct B { virtual void f(); };\n"
8370+
"struct D : B {\n"
8371+
" void f() override { B::f(); }\n"
8372+
"};");
8373+
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str());
8374+
8375+
checkUselessOverride("struct B { virtual int f() = 0; };\n"
8376+
"int B::f() { return 5; }\n"
8377+
"struct D : B {\n"
8378+
" int f() override { return B::f(); }\n"
8379+
"};");
8380+
ASSERT_EQUALS("", errout.str());
8381+
8382+
checkUselessOverride("struct B { virtual int f(int i); };\n"
8383+
"struct D : B {\n"
8384+
" int f(int i) override { return B::f(i); }\n"
8385+
"};");
8386+
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str());
8387+
8388+
checkUselessOverride("struct B { virtual int f(int i); };\n"
8389+
"struct D : B {\n"
8390+
" int f(int i) override { return B::f(i + 1); }\n"
8391+
"};");
8392+
ASSERT_EQUALS("", errout.str());
8393+
8394+
checkUselessOverride("struct B { virtual int f(int i, int j); };\n"
8395+
"struct D : B {\n"
8396+
" int f(int i, int j) override { return B::f(j, i); }\n"
8397+
"};");
8398+
ASSERT_EQUALS("", errout.str());
8399+
8400+
checkUselessOverride("struct B { virtual int f(); };\n"
8401+
"struct I { virtual int f() = 0; };\n"
8402+
"struct D : B, I {\n"
8403+
" int f() override { return B::f(); }\n"
8404+
"};");
8405+
ASSERT_EQUALS("", errout.str());
8406+
}
83418407

83428408
#define checkUnsafeClassRefMember(code) checkUnsafeClassRefMember_(code, __FILE__, __LINE__)
83438409
void checkUnsafeClassRefMember_(const char code[], const char* file, int line) {

0 commit comments

Comments
 (0)