diff --git a/.selfcheck_unused_suppressions b/.selfcheck_unused_suppressions index 68f296fef3d..ce685d8ab10 100644 --- a/.selfcheck_unused_suppressions +++ b/.selfcheck_unused_suppressions @@ -1,15 +1,8 @@ # we are not using all methods of their interfaces unusedFunction:externals/*/* -# TODO: fix these -# false positive - # 10660 -unusedFunction:gui/mainwindow.cpp -unusedFunction:gui/resultstree.cpp -unusedFunction:gui/codeeditor.* # usage is disabled unusedFunction:lib/symboldatabase.cpp -# false positive - #10661 -unusedFunction:oss-fuzz/main.cpp # Q_OBJECT functions which are not called in our code -unusedFunction:cmake.output.notest/gui/cppcheck-gui_autogen/*/moc_aboutdialog.cpp \ No newline at end of file +unusedFunction:cmake.output.notest/gui/cppcheck-gui_autogen/*/moc_aboutdialog.cpp diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index 3fc19d340d7..fa914523813 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -85,6 +85,15 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const Setting if (func->isExtern()) continue; + bool foundAllBaseClasses{}; + if (const Function* ofunc = func->getOverriddenFunction(&foundAllBaseClasses)) { + if (!foundAllBaseClasses || ofunc->isPure()) + continue; + } + else if (func->isImplicitlyVirtual()) { + continue; + } + mFunctionDecl.emplace_back(func); FunctionUsage &usage = mFunctions[stripTemplateParameters(func->name())]; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 31e54535512..a6d9c9bd928 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -4554,9 +4554,7 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s bool Function::isImplicitlyVirtual(bool defaultVal) const { - 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 + if (hasVirtualSpecifier() || hasOverrideSpecifier() || hasFinalSpecifier()) return true; bool foundAllBaseClasses = true; if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual diff --git a/test/cli/other_test.py b/test/cli/other_test.py index ec4912f929f..a71885a3806 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -831,7 +831,7 @@ def test_showtime_top5_file(tmpdir): elif lines[i].startswith('valueFlowEnumValue'): assert lines[i].endswith(' - 2 result(s))') else: - assert lines[i].endswith(' - 1 result(s))') + assert lines[i].endswith(' result(s))') assert lines[6].startswith('Overall time:') assert stderr == '' diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index 2b1773f2f66..4be243363ac 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -82,6 +82,7 @@ class TestUnusedFunctions : public TestFixture { TEST_CASE(entrypointsUnix); TEST_CASE(includes); + TEST_CASE(virtualFunc); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) @@ -711,6 +712,29 @@ class TestUnusedFunctions : public TestFixture { check(processed); TODO_ASSERT_EQUALS("[test.h:3]: (style) The function 'f' is never used.\n", "[test.cpp:3]: (style) The function 'f' is never used.\n", errout_str()); } + + void virtualFunc() + { + check("struct D : public B {\n" // #10660 + " virtual void f() {}\n" + " void g() override {}\n" + " void h() final {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B {\n" + " virtual void f() = 0;\n" + " void g();\n" + "};\n" + "struct D : B {\n" + " void f() override {}\n" + "};\n" + "int main() {\n" + " D d;\n" + " d.g();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } }; REGISTER_TEST(TestUnusedFunctions)