added `entrypoint` to library configuration and got rid of hard-coded ones (#4691)

This commit is contained in:
Oliver Stöneberg 2023-01-18 20:52:33 +01:00 committed by GitHub
parent 3076f9def1
commit a8fd5cbaf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 43 deletions

View File

@ -625,6 +625,9 @@
</oneOrMore>
</element>
<element name="entrypoint">
<attribute name="name"><text/></attribute>
</element>
</choice>
</zeroOrMore>
</element>

View File

@ -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>

View File

@ -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>

View File

@ -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") {

View File

@ -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)) {

View File

@ -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:

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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"

View File

@ -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)