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)