Skip to content

Commit 8b29139

Browse files
authored
Detect C functions that can be declared with static linkage (danmar#7127)
Currently limited to C as I assume the anonymous namespace should be favored for C++.
1 parent 27a4500 commit 8b29139

File tree

9 files changed

+55
-14
lines changed

9 files changed

+55
-14
lines changed

lib/checkunusedfunctions.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const Setting
107107

108108
if (!usage.lineNumber)
109109
usage.lineNumber = func->token->linenr();
110+
usage.isC = func->token->isC();
111+
usage.isStatic = func->isStatic();
110112

111113
// TODO: why always overwrite this but not the filename and line?
112114
usage.fileIndex = func->token->fileIndex();
@@ -337,6 +339,23 @@ static bool isOperatorFunction(const std::string & funcName)
337339
return std::find(additionalOperators.cbegin(), additionalOperators.cend(), funcName.substr(operatorPrefix.length())) != additionalOperators.cend();
338340
}
339341

342+
static void staticFunctionError(ErrorLogger& errorLogger,
343+
const std::string &filename,
344+
unsigned int fileIndex,
345+
unsigned int lineNumber,
346+
const std::string &funcname)
347+
{
348+
std::list<ErrorMessage::FileLocation> locationList;
349+
if (!filename.empty()) {
350+
locationList.emplace_back(filename, lineNumber, 0);
351+
locationList.back().fileIndex = fileIndex;
352+
}
353+
354+
const ErrorMessage errmsg(std::move(locationList), emptyString, Severity::style, "$symbol:" + funcname + "\nThe function '$symbol' should have static linkage since it is not used outside of its translation unit.", "staticFunction", Certainty::normal);
355+
errorLogger.reportErr(errmsg);
356+
}
357+
358+
340359
#define logChecker(id) \
341360
do { \
342361
const ErrorMessage errmsg({}, nullptr, Severity::internal, "logChecker", (id), CWE(0U), Certainty::normal); \
@@ -349,6 +368,7 @@ bool CheckUnusedFunctions::check(const Settings& settings, ErrorLogger& errorLog
349368

350369
using ErrorParams = std::tuple<std::string, unsigned int, unsigned int, std::string>;
351370
std::vector<ErrorParams> errors; // ensure well-defined order
371+
std::vector<ErrorParams> staticFunctionErrors;
352372

353373
for (auto it = mFunctions.cbegin(); it != mFunctions.cend(); ++it) {
354374
const FunctionUsage &func = it->second;
@@ -363,19 +383,22 @@ bool CheckUnusedFunctions::check(const Settings& settings, ErrorLogger& errorLog
363383
if (func.filename != "+")
364384
filename = func.filename;
365385
errors.emplace_back(filename, func.fileIndex, func.lineNumber, it->first);
366-
} else if (!func.usedOtherFile) {
367-
/** @todo add error message "function is only used in <file> it can be static" */
368-
/*
369-
std::ostringstream errmsg;
370-
errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage.";
371-
mErrorLogger->reportErr( errmsg.str() );
372-
errors = true;
373-
*/
386+
} else if (func.isC && !func.isStatic && !func.usedOtherFile) {
387+
std::string filename;
388+
if (func.filename != "+")
389+
filename = func.filename;
390+
staticFunctionErrors.emplace_back(filename, func.fileIndex, func.lineNumber, it->first);
374391
}
375392
}
393+
376394
std::sort(errors.begin(), errors.end());
377395
for (const auto& e : errors)
378396
unusedFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e));
397+
398+
std::sort(staticFunctionErrors.begin(), staticFunctionErrors.end());
399+
for (const auto& e : staticFunctionErrors)
400+
staticFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e));
401+
379402
return !errors.empty();
380403
}
381404

lib/checkunusedfunctions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class CPPCHECKLIB CheckUnusedFunctions {
7676
unsigned int fileIndex{};
7777
bool usedSameFile{};
7878
bool usedOtherFile{};
79+
bool isC{};
80+
bool isStatic{};
7981
};
8082

8183
std::unordered_map<std::string, FunctionUsage> mFunctions;

samples/AssignmentAddressToInteger/bad.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
int foo(int *p)
1+
static int foo(int *p)
22
{
33
int a = p;
44
return a + 4;

samples/AssignmentAddressToInteger/good.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
int* foo(int *p)
1+
static int* foo(int *p)
22
{
33
return p + 4;
44
}

samples/autoVariables/bad.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
void foo(int **a)
1+
static void foo(int **a)
22
{
33
int b = 1;
44
*a = &b;

samples/autoVariables/good.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
void foo(int **a)
1+
static void foo(int **a)
22
{
33
int b = 1;
44
**a = b;

samples/incorrectLogicOperator/bad.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
void foo(int x) {
2+
static void foo(int x) {
33
if (x >= 0 || x <= 10) {}
44
}
55

samples/incorrectLogicOperator/good.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
void foo(int x) {
2+
static void foo(int x) {
33
if (x >= 0 && x <= 10) {}
44
}
55

test/testunusedfunctions.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class TestUnusedFunctions : public TestFixture {
8888
TEST_CASE(attributeCleanup);
8989
TEST_CASE(attributeUnused);
9090
TEST_CASE(attributeMaybeUnused);
91+
TEST_CASE(staticFunction);
9192
}
9293

9394
#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
@@ -820,6 +821,21 @@ class TestUnusedFunctions : public TestFixture {
820821
check("[[maybe_unused]] void f() {}\n");
821822
ASSERT_EQUALS("", errout_str());
822823
}
824+
825+
void staticFunction()
826+
{
827+
check("void f(void) {}\n"
828+
"int main() {\n"
829+
" f();\n"
830+
"}\n");
831+
ASSERT_EQUALS("", errout_str());
832+
833+
check("void f(void) {}\n"
834+
"int main() {\n"
835+
" f();\n"
836+
"}\n", Platform::Type::Native, nullptr, false);
837+
ASSERT_EQUALS("[test.c:1]: (style) The function 'f' should have static linkage since it is not used outside of its translation unit.\n", errout_str());
838+
}
823839
};
824840

825841
REGISTER_TEST(TestUnusedFunctions)

0 commit comments

Comments
 (0)