diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng index 3b4760083..4949ef15c 100644 --- a/cfg/cppcheck-cfg.rng +++ b/cfg/cppcheck-cfg.rng @@ -625,6 +625,9 @@ + + + diff --git a/cfg/gnu.cfg b/cfg/gnu.cfg index e6e3a138a..565bc35ca 100644 --- a/cfg/gnu.cfg +++ b/cfg/gnu.cfg @@ -1731,4 +1731,6 @@ + + diff --git a/cfg/windows.cfg b/cfg/windows.cfg index 2c0db5b23..8b4b19d6a 100644 --- a/cfg/windows.cfg +++ b/cfg/windows.cfg @@ -17938,4 +17938,17 @@ HFONT CreateFont( + + + + + + + + + + + + + diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 87b35c549..ef2280222 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -322,9 +322,7 @@ void CheckExceptionSafety::unhandledExceptionSpecification() for (const Scope * scope : symbolDatabase->functionScopes) { // only check functions without exception specification - if (scope->function && !scope->function->isThrow() && - scope->className != "main" && scope->className != "wmain" && - scope->className != "_tmain" && scope->className != "WinMain") { + if (scope->function && !scope->function->isThrow() && !mSettings->library.isentrypoint(scope->className)) { for (const Token *tok = scope->function->functionScope->bodyStart->next(); tok != scope->function->functionScope->bodyEnd; tok = tok->next()) { if (tok->str() == "try") { diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index caedaaf8a..236561a72 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -305,8 +305,6 @@ static bool isOperatorFunction(const std::string & funcName) return std::find(additionalOperators.cbegin(), additionalOperators.cend(), funcName.substr(operatorPrefix.length())) != additionalOperators.cend(); } - - bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings) const { using ErrorParams = std::tuple; @@ -316,8 +314,7 @@ bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings const FunctionUsage &func = it->second; if (func.usedOtherFile || func.filename.empty()) continue; - if (it->first == "main" || - (settings.isWindowsPlatform() && (it->first == "WinMain" || it->first == "_tmain"))) + if (settings.library.isentrypoint(it->first)) continue; if (!func.usedSameFile) { if (isOperatorFunction(it->first)) @@ -401,7 +398,7 @@ namespace { }; } -void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir) +void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLogger * const errorLogger, const std::string &buildDir) { std::map decls; std::set calls; @@ -453,11 +450,7 @@ void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger, for (std::map::const_iterator decl = decls.cbegin(); decl != decls.cend(); ++decl) { const std::string &functionName = decl->first; - // TODO: move to configuration files - // TODO: WinMain, wmain and _tmain only apply to Windows code - // TODO: also skip other known entry functions i.e. annotated with "constructor" and "destructor" attributes - if (functionName == "main" || functionName == "WinMain" || functionName == "wmain" || functionName == "_tmain" || - functionName == "if") + if (settings.library.isentrypoint(functionName)) continue; if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) { diff --git a/lib/checkunusedfunctions.h b/lib/checkunusedfunctions.h index d219e6afb..c937a55ff 100644 --- a/lib/checkunusedfunctions.h +++ b/lib/checkunusedfunctions.h @@ -76,7 +76,7 @@ public: std::string analyzerInfo() const; /** @brief Combine and analyze all analyzerInfos for all TUs */ - static void analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir); + static void analyseWholeProgram(const Settings &settings, ErrorLogger * const errorLogger, const std::string &buildDir); private: diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 0cf3635d0..ca7ddc5d1 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1764,7 +1764,7 @@ void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map fileInfoList; CTU::FileInfo ctuFileInfo; diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 74af0bd79..0f1d616d5 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -533,28 +533,37 @@ namespace { if (condAttr) condition = condAttr; for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) { - if (std::strcmp(e1->Name(), "ClCompile") != 0) - continue; - enhancedInstructionSet = "StreamingSIMDExtensions2"; - for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { - if (e->GetText()) { - if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) - preprocessorDefinitions = e->GetText(); - else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { - if (!additionalIncludePaths.empty()) - additionalIncludePaths += ';'; - additionalIncludePaths += e->GetText(); - } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) { - if (std::strcmp(e->GetText(), "stdcpp14") == 0) - cppstd = Standards::CPP14; - else if (std::strcmp(e->GetText(), "stdcpp17") == 0) - cppstd = Standards::CPP17; - else if (std::strcmp(e->GetText(), "stdcpp20") == 0) - cppstd = Standards::CPP20; - else if (std::strcmp(e->GetText(), "stdcpplatest") == 0) - cppstd = Standards::CPPLatest; - } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) { - enhancedInstructionSet = e->GetText(); + if (std::strcmp(e1->Name(), "ClCompile") == 0) { + enhancedInstructionSet = "StreamingSIMDExtensions2"; + for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (e->GetText()) { + if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) + preprocessorDefinitions = e->GetText(); + else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { + if (!additionalIncludePaths.empty()) + additionalIncludePaths += ';'; + additionalIncludePaths += e->GetText(); + } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) { + if (std::strcmp(e->GetText(), "stdcpp14") == 0) + cppstd = Standards::CPP14; + else if (std::strcmp(e->GetText(), "stdcpp17") == 0) + cppstd = Standards::CPP17; + else if (std::strcmp(e->GetText(), "stdcpp20") == 0) + cppstd = Standards::CPP20; + else if (std::strcmp(e->GetText(), "stdcpplatest") == 0) + cppstd = Standards::CPPLatest; + } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) { + enhancedInstructionSet = e->GetText(); + } + } + } + } + else if (std::strcmp(e1->Name(), "Link") == 0) { + for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (!e->GetText()) + continue; + if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) { + entryPointSymbol = e->GetText(); } } } @@ -595,6 +604,7 @@ namespace { std::string enhancedInstructionSet; std::string preprocessorDefinitions; std::string additionalIncludePaths; + std::string entryPointSymbol; // TODO: use this Standards::cppstd_t cppstd = Standards::CPPLatest; }; } diff --git a/lib/library.cpp b/lib/library.cpp index 6f90ddb66..cb464a241 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -643,6 +643,13 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc) } } + else if (nodename == "entrypoint") { + const char * const type_name = node->Attribute("name"); + if (type_name == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + mEntrypoints.emplace(type_name); + } + else unknown_elements.insert(nodename); } diff --git a/lib/library.h b/lib/library.h index 0a64e6f5b..aef027f14 100644 --- a/lib/library.h +++ b/lib/library.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -476,6 +477,10 @@ public: return -1; } + bool isentrypoint(const std::string &func) const { + return func == "main" || mEntrypoints.find(func) != mEntrypoints.end(); + } + std::vector defines; // to provide some library defines struct SmartPointer { @@ -643,6 +648,7 @@ private: std::map mPlatforms; // platform dependent typedefs std::map, TypeCheck> mTypeChecks; std::unordered_map mNonOverlappingData; + std::unordered_set mEntrypoints; const ArgumentChecks * getarg(const Token *ftok, int argnr) const; diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 964da3775..040fd8e79 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -49,6 +49,7 @@ private: TEST_CASE(nothrowThrow); TEST_CASE(unhandledExceptionSpecification1); // #4800 TEST_CASE(unhandledExceptionSpecification2); + TEST_CASE(unhandledExceptionSpecification3); TEST_CASE(nothrowAttributeThrow); TEST_CASE(nothrowAttributeThrow2); // #5703 TEST_CASE(nothrowDeclspecThrow); @@ -380,6 +381,32 @@ private: ASSERT_EQUALS("", errout.str()); } + void unhandledExceptionSpecification3() { + const char code[] = "void f() const throw (std::runtime_error);\n" + "int _init() {\n" + " f();\n" + "}\n" + "int _fini() {\n" + " f();\n" + "}\n" + "int main()\n" + "{\n" + " f();\n" + "}\n"; + + check(code, true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function f().\n" + "[test.cpp:6] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function f().\n", errout.str()); + + Settings settingsOld = settings; + LOAD_LIB_2(settings.library, "gnu.cfg"); + + check(code, true); + ASSERT_EQUALS("", errout.str()); + + settings = settingsOld; + } + void nothrowAttributeThrow() { check("void func1() throw(int) { throw 1; }\n" "void func2() __attribute((nothrow)); void func2() { throw 1; }\n" diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index 7db3871bb..21701227f 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -72,6 +72,10 @@ private: TEST_CASE(ignore_declaration); // ignore declaration TEST_CASE(operatorOverload); + + TEST_CASE(entrypointsWin); + TEST_CASE(entrypointsWinU); + TEST_CASE(entrypointsUnix); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) @@ -354,12 +358,6 @@ private: void unusedMain() { check("int main() { }"); ASSERT_EQUALS("", errout.str()); - - check("int _tmain() { }", Settings::Win32A); - ASSERT_EQUALS("", errout.str()); - - check("int WinMain() { }", Settings::Win32A); - ASSERT_EQUALS("", errout.str()); } void initializationIsNotAFunction() { @@ -609,6 +607,59 @@ private: ASSERT_EQUALS("", errout.str()); } + void entrypointsWin() { + check("int WinMain() { }"); + ASSERT_EQUALS("[test.cpp:1]: (style) The function 'WinMain' is never used.\n", errout.str()); + + check("int _tmain() { }"); + ASSERT_EQUALS("[test.cpp:1]: (style) The function '_tmain' is never used.\n", errout.str()); + + Settings settingsOld = settings; + LOAD_LIB_2(settings.library, "windows.cfg"); + + check("int WinMain() { }"); + ASSERT_EQUALS("", errout.str()); + + check("int _tmain() { }"); + ASSERT_EQUALS("", errout.str()); + + settings = settingsOld; + } + + void entrypointsWinU() { + check("int wWinMain() { }"); + ASSERT_EQUALS("[test.cpp:1]: (style) The function 'wWinMain' is never used.\n", errout.str()); + + check("int _tmain() { }"); + ASSERT_EQUALS("[test.cpp:1]: (style) The function '_tmain' is never used.\n", errout.str()); + + Settings settingsOld = settings; + LOAD_LIB_2(settings.library, "windows.cfg"); + + check("int wWinMain() { }"); + ASSERT_EQUALS("", errout.str()); + + check("int _tmain() { }"); + ASSERT_EQUALS("", errout.str()); + + settings = settingsOld; + } + + void entrypointsUnix() { + check("int _init() { }\n" + "int _fini() { }\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) The function '_init' is never used.\n" + "[test.cpp:2]: (style) The function '_fini' is never used.\n", errout.str()); + + Settings settingsOld = settings; + LOAD_LIB_2(settings.library, "gnu.cfg"); + + check("int _init() { }\n" + "int _fini() { }\n"); + ASSERT_EQUALS("", errout.str()); + + settings = settingsOld; + } }; REGISTER_TEST(TestUnusedFunctions)