added `entrypoint` to library configuration and got rid of hard-coded ones (#4691)
This commit is contained in:
parent
3076f9def1
commit
a8fd5cbaf4
|
@ -625,6 +625,9 @@
|
|||
</oneOrMore>
|
||||
</element>
|
||||
|
||||
<element name="entrypoint">
|
||||
<attribute name="name"><text/></attribute>
|
||||
</element>
|
||||
</choice>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
|
|
|
@ -1731,4 +1731,6 @@
|
|||
<define name="STDIN_FILENO" value="0"/>
|
||||
<define name="STDOUT_FILENO" value="1"/>
|
||||
<define name="STDERR_FILENO" value="2"/>
|
||||
<entrypoint name="_init"/>
|
||||
<entrypoint name="_fini"/>
|
||||
</def>
|
||||
|
|
|
@ -17938,4 +17938,17 @@ HFONT CreateFont(
|
|||
<define name="UNREFERENCED_PARAMETER(P)" value="((void)(P))"/>
|
||||
<define name="DBG_UNREFERENCED_PARAMETER(P)" value="((void)(P))"/>
|
||||
<define name="DBG_UNREFERENCED_LOCAL_VARIABLE(P)" value="((void)(P))"/>
|
||||
<!-- https://learn.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol -->
|
||||
<entrypoint name="mainCRTStartup"/>
|
||||
<entrypoint name="WinMain"/>
|
||||
<entrypoint name="WinMainCRTStartup"/>
|
||||
<entrypoint name="DllMain"/>
|
||||
<entrypoint name="_DllMainCRTStartup"/>
|
||||
<!-- TODO: these are UNICODE-specific -->
|
||||
<entrypoint name="wmain"/>
|
||||
<entrypoint name="wmainCRTStartup"/>
|
||||
<entrypoint name="wWinMain"/>
|
||||
<entrypoint name="wWinMainCRTStartup"/>
|
||||
<!-- TODO: this is actually a define -->
|
||||
<entrypoint name="_tmain"/>
|
||||
</def>
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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<std::string, unsigned int, std::string>;
|
||||
|
@ -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<std::string, Location> decls;
|
||||
std::set<std::string> calls;
|
||||
|
@ -453,11 +450,7 @@ void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger,
|
|||
for (std::map<std::string, Location>::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)) {
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -1764,7 +1764,7 @@ void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<s
|
|||
return;
|
||||
}
|
||||
if (mSettings.checks.isEnabled(Checks::unusedFunction))
|
||||
CheckUnusedFunctions::analyseWholeProgram(this, buildDir);
|
||||
CheckUnusedFunctions::analyseWholeProgram(mSettings, this, buildDir);
|
||||
std::list<Check::FileInfo*> fileInfoList;
|
||||
CTU::FileInfo ctuFileInfo;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -476,6 +477,10 @@ public:
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool isentrypoint(const std::string &func) const {
|
||||
return func == "main" || mEntrypoints.find(func) != mEntrypoints.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> defines; // to provide some library defines
|
||||
|
||||
struct SmartPointer {
|
||||
|
@ -643,6 +648,7 @@ private:
|
|||
std::map<std::string, Platform> mPlatforms; // platform dependent typedefs
|
||||
std::map<std::pair<std::string,std::string>, TypeCheck> mTypeChecks;
|
||||
std::unordered_map<std::string, NonOverlappingData> mNonOverlappingData;
|
||||
std::unordered_set<std::string> mEntrypoints;
|
||||
|
||||
const ArgumentChecks * getarg(const Token *ftok, int argnr) const;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue