Skip to content

Commit a102ff3

Browse files
Fix #11485 FN unusedFunction / Fix #7739 FP unusedFunction (#4707)
1 parent a79dff1 commit a102ff3

4 files changed

Lines changed: 60 additions & 19 deletions

File tree

lib/checkunusedfunctions.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ CheckUnusedFunctions CheckUnusedFunctions::instance;
5555

5656
static const struct CWE CWE561(561U); // Dead Code
5757

58+
static std::string stripTemplateParameters(const std::string& funcName) {
59+
std::string name = funcName;
60+
const auto pos = name.find('<');
61+
if (pos > 0 && pos != std::string::npos)
62+
name.erase(pos - 1);
63+
return name;
64+
}
5865

5966
//---------------------------------------------------------------------------
6067
// FUNCTION USAGE - Check for unused functions etc
@@ -78,13 +85,9 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
7885
if (func->isExtern())
7986
continue;
8087

81-
// Don't care about templates
82-
if (tokenizer.isCPP() && func->templateDef != nullptr)
83-
continue;
84-
8588
mFunctionDecl.emplace_back(func);
8689

87-
FunctionUsage &usage = mFunctions[func->name()];
90+
FunctionUsage &usage = mFunctions[stripTemplateParameters(func->name())];
8891

8992
if (!usage.lineNumber)
9093
usage.lineNumber = func->token->linenr();
@@ -223,11 +226,11 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
223226
while (Token::Match(funcname, "%name% :: %name%"))
224227
funcname = funcname->tokAt(2);
225228

226-
if (!Token::Match(funcname, "%name% [(),;]:}>]"))
229+
if (!Token::Match(funcname, "%name% [(),;]:}>]") || funcname->varId())
227230
continue;
228231
}
229232

230-
if (!funcname)
233+
if (!funcname || funcname->isKeyword() || funcname->isStandardType())
231234
continue;
232235

233236
// funcname ( => Assert that the end parentheses isn't followed by {
@@ -240,15 +243,16 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
240243
}
241244

242245
if (funcname) {
243-
FunctionUsage &func = mFunctions[funcname->str()];
246+
const auto baseName = stripTemplateParameters(funcname->str());
247+
FunctionUsage &func = mFunctions[baseName];
244248
const std::string& called_from_file = tokenizer.list.getSourceFilePath();
245249

246250
if (func.filename.empty() || func.filename == "+" || func.filename != called_from_file)
247251
func.usedOtherFile = true;
248252
else
249253
func.usedSameFile = true;
250254

251-
mFunctionCalls.insert(funcname->str());
255+
mFunctionCalls.insert(baseName);
252256
}
253257
}
254258
}

lib/forwardanalyzer.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,6 @@ struct ForwardTraversal {
255255
return traverseRecursive(tok, f, false);
256256
}
257257

258-
template<class T, class F>
259-
T* findRange(T* start, const Token* end, F pred) {
260-
for (T* tok = start; tok && tok != end; tok = tok->next()) {
261-
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
262-
if (pred(action))
263-
return tok;
264-
}
265-
return nullptr;
266-
}
267-
268258
Analyzer::Action analyzeRecursive(const Token* start) {
269259
Analyzer::Action result = Analyzer::Action::None;
270260
auto f = [&](const Token* tok) {

lib/token.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Token::~Token()
6868
* e.g. for the sequence of tokens A B C D E, C.until(E) would yield the Range C D
6969
* note t can be nullptr to iterate all the way to the end.
7070
*/
71+
// cppcheck-suppress unusedFunction // only used in testtokenrange.cpp
7172
ConstTokenRange Token::until(const Token* t) const
7273
{
7374
return ConstTokenRange(this, t);

test/testunusedfunctions.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class TestUnusedFunctions : public TestFixture {
5050
TEST_CASE(template5);
5151
TEST_CASE(template6); // #10475 crash
5252
TEST_CASE(template7); // #9766 crash
53+
TEST_CASE(template8);
54+
TEST_CASE(template9);
5355
TEST_CASE(throwIsNotAFunction);
5456
TEST_CASE(unusedError);
5557
TEST_CASE(unusedMain);
@@ -282,6 +284,50 @@ class TestUnusedFunctions : public TestFixture {
282284
ASSERT_EQUALS("[test.cpp:1]: (style) The function 'f' is never used.\n", errout.str());
283285
}
284286

287+
void template8() { // #11485
288+
check("struct S {\n"
289+
" template<typename T>\n"
290+
" void tf(const T&) { }\n"
291+
"};\n");
292+
ASSERT_EQUALS("[test.cpp:3]: (style) The function 'tf' is never used.\n", errout.str());
293+
294+
check("struct S {\n"
295+
" template<typename T>\n"
296+
" void tf(const T&) { }\n"
297+
"};\n"
298+
"int main() {\n"
299+
" C c;\n"
300+
" c.tf(1.5);\n"
301+
"}\n");
302+
ASSERT_EQUALS("", errout.str());
303+
304+
check("struct S {\n"
305+
" template<typename T>\n"
306+
" void tf(const T&) { }\n"
307+
"};\n"
308+
"int main() {\n"
309+
" C c;\n"
310+
" c.tf<int>(1);\n"
311+
"}\n");
312+
ASSERT_EQUALS("", errout.str());
313+
}
314+
315+
void template9() { // #7739
316+
check("template<class T>\n"
317+
"void f(T const& t) {}\n"
318+
"template<class T>\n"
319+
"void g(T const& t) {\n"
320+
" f(t);\n"
321+
"}\n"
322+
"template<>\n"
323+
"void f<double>(double const& d) {}\n"
324+
"int main() {\n"
325+
" g(2);\n"
326+
" g(3.14);\n"
327+
"}\n");
328+
ASSERT_EQUALS("", errout.str());
329+
}
330+
285331
void throwIsNotAFunction() {
286332
check("struct A {void f() const throw () {}}; int main() {A a; a.f();}");
287333
ASSERT_EQUALS("", errout.str());

0 commit comments

Comments
 (0)