Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 33 additions & 19 deletions lib/checkunusedfunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const Setting
usage.usedOtherFile = true;
}

if (!usage.lineNumber)
if (!usage.lineNumber) {
usage.lineNumber = func->token->linenr();
usage.column = func->token->column();
}
usage.isC = func->token->isC();
usage.isStatic = func->isStatic();

Expand Down Expand Up @@ -341,13 +343,14 @@ static bool isOperatorFunction(const std::string & funcName)

static void staticFunctionError(ErrorLogger& errorLogger,
const std::string &filename,
unsigned int fileIndex,
unsigned int lineNumber,
nonneg int fileIndex,
nonneg int lineNumber,
nonneg int column,
const std::string &funcname)
{
std::list<ErrorMessage::FileLocation> locationList;
if (!filename.empty()) {
locationList.emplace_back(filename, lineNumber, 0);
locationList.emplace_back(filename, lineNumber, column);
locationList.back().fileIndex = fileIndex;
}

Expand All @@ -366,7 +369,8 @@ bool CheckUnusedFunctions::check(const Settings& settings, ErrorLogger& errorLog
{
logChecker("CheckUnusedFunctions::check"); // unusedFunction

using ErrorParams = std::tuple<std::string, unsigned int, unsigned int, std::string>;
// filename, fileindex, line, column
using ErrorParams = std::tuple<std::string, nonneg int, nonneg int, nonneg int, std::string>;
std::vector<ErrorParams> errors; // ensure well-defined order
std::vector<ErrorParams> staticFunctionErrors;

Expand All @@ -382,33 +386,33 @@ bool CheckUnusedFunctions::check(const Settings& settings, ErrorLogger& errorLog
std::string filename;
if (func.filename != "+")
filename = func.filename;
errors.emplace_back(filename, func.fileIndex, func.lineNumber, it->first);
errors.emplace_back(filename, func.fileIndex, func.lineNumber, func.column, it->first);
} else if (func.isC && !func.isStatic && !func.usedOtherFile) {
std::string filename;
if (func.filename != "+")
filename = func.filename;
staticFunctionErrors.emplace_back(filename, func.fileIndex, func.lineNumber, it->first);
staticFunctionErrors.emplace_back(filename, func.fileIndex, func.lineNumber, func.column, it->first);
}
}

std::sort(errors.begin(), errors.end());
for (const auto& e : errors)
unusedFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e));
unusedFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e), std::get<4>(e));

std::sort(staticFunctionErrors.begin(), staticFunctionErrors.end());
for (const auto& e : staticFunctionErrors)
staticFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e));
staticFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e), std::get<4>(e));

return !errors.empty();
}

void CheckUnusedFunctions::unusedFunctionError(ErrorLogger& errorLogger,
const std::string &filename, unsigned int fileIndex, unsigned int lineNumber,
const std::string &filename, nonneg int fileIndex, nonneg int lineNumber, nonneg int column,
const std::string &funcname)
{
std::list<ErrorMessage::FileLocation> locationList;
if (!filename.empty()) {
locationList.emplace_back(filename, lineNumber, 0);
locationList.emplace_back(filename, lineNumber, column);
locationList.back().fileIndex = fileIndex;
}

Expand All @@ -417,7 +421,10 @@ void CheckUnusedFunctions::unusedFunctionError(ErrorLogger& errorLogger,
}

CheckUnusedFunctions::FunctionDecl::FunctionDecl(const Function *f)
: functionName(f->name()), fileIndex(f->token->fileIndex()), lineNumber(f->token->linenr())
: functionName(f->name())
, fileIndex(f->token->fileIndex())
, lineNumber(f->token->linenr())
, column(f->token->column())
{}

std::string CheckUnusedFunctions::analyzerInfo(const Tokenizer &tokenizer) const
Expand All @@ -427,7 +434,9 @@ std::string CheckUnusedFunctions::analyzerInfo(const Tokenizer &tokenizer) const
ret << " <functiondecl"
<< " file=\"" << ErrorLogger::toxml(tokenizer.list.getFiles()[functionDecl.fileIndex]) << '\"'
<< " functionName=\"" << ErrorLogger::toxml(functionDecl.functionName) << '\"'
<< " lineNumber=\"" << functionDecl.lineNumber << "\"/>\n";
<< " lineNumber=\"" << functionDecl.lineNumber << '\"'
<< " column=\"" << functionDecl.column << '\"'
<< "/>\n";
}
for (const std::string &fc : mFunctionCalls) {
ret << " <functioncall functionName=\"" << ErrorLogger::toxml(fc) << "\"/>\n";
Expand All @@ -437,13 +446,15 @@ std::string CheckUnusedFunctions::analyzerInfo(const Tokenizer &tokenizer) const

namespace {
struct Location {
Location() : lineNumber(0) {}
Location(std::string f, const int l) : fileName(std::move(f)), lineNumber(l) {}
Location() : lineNumber(0), column(0) {}
Location(std::string f, nonneg int l, nonneg int c) : fileName(std::move(f)), lineNumber(l), column(c) {}
std::string fileName;
int lineNumber;
nonneg int lineNumber;
nonneg int column;
};
}

// TODO: bail out on unexpected data
void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLogger &errorLogger, const std::string &buildDir)
{
std::map<std::string, Location> decls;
Expand Down Expand Up @@ -490,8 +501,9 @@ void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLo
const char* lineNumber = e2->Attribute("lineNumber");
if (lineNumber) {
const char* file = e2->Attribute("file");
const char* column = default_if_null(e2->Attribute("column"), "0");
// cppcheck-suppress templateInstantiation - TODO: fix this - see #11631
decls[functionName] = Location(file ? file : sourcefile, strToInt<int>(lineNumber));
decls[functionName] = Location(file ? file : sourcefile, strToInt<int>(lineNumber), strToInt<int>(column));
}
}
}
Expand All @@ -506,7 +518,7 @@ void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLo

if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) {
const Location &loc = decl->second;
unusedFunctionError(errorLogger, loc.fileName, /*fileIndex*/ 0, loc.lineNumber, functionName);
unusedFunctionError(errorLogger, loc.fileName, /*fileIndex*/ 0, loc.lineNumber, loc.column, functionName);
}
}
}
Expand All @@ -516,8 +528,10 @@ void CheckUnusedFunctions::updateFunctionData(const CheckUnusedFunctions& check)
for (const auto& entry : check.mFunctions)
{
FunctionUsage &usage = mFunctions[entry.first];
if (!usage.lineNumber)
if (!usage.lineNumber) {
usage.lineNumber = entry.second.lineNumber;
usage.column = entry.second.column;
}
// TODO: why always overwrite this but not the filename and line?
usage.fileIndex = entry.second.fileIndex;
if (usage.filename.empty())
Expand Down
12 changes: 7 additions & 5 deletions lib/checkunusedfunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class CPPCHECKLIB CheckUnusedFunctions {
static void analyseWholeProgram(const Settings &settings, ErrorLogger& errorLogger, const std::string &buildDir);

static void getErrorMessages(ErrorLogger &errorLogger) {
unusedFunctionError(errorLogger, "", 0, 0, "funcName");
unusedFunctionError(errorLogger, "", 0, 0, 0, "funcName");
}

// Return true if an error is reported.
Expand All @@ -67,13 +67,14 @@ class CPPCHECKLIB CheckUnusedFunctions {

private:
static void unusedFunctionError(ErrorLogger& errorLogger,
const std::string &filename, unsigned int fileIndex, unsigned int lineNumber,
const std::string &filename, nonneg int fileIndex, nonneg int lineNumber, nonneg int column,
const std::string &funcname);

struct CPPCHECKLIB FunctionUsage {
std::string filename;
unsigned int lineNumber{};
unsigned int fileIndex{};
nonneg int lineNumber{};
nonneg int column{};
nonneg int fileIndex{};
bool usedSameFile{};
bool usedOtherFile{};
bool isC{};
Expand All @@ -87,7 +88,8 @@ class CPPCHECKLIB CheckUnusedFunctions {
explicit FunctionDecl(const Function *f);
std::string functionName;
nonneg int fileIndex;
unsigned int lineNumber;
nonneg int lineNumber;
nonneg int column;
};
std::list<FunctionDecl> mFunctionDecl;
std::set<std::string> mFunctionCalls;
Expand Down
2 changes: 1 addition & 1 deletion test/cli/inline-suppress_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def __test_compile_commands_unused_function(tmpdir, use_j):
proj_path_sep = os.path.join(__script_dir, 'proj-inline-suppress-unusedFunction') + os.path.sep
lines = stderr.splitlines()
assert lines == [
"{}B.cpp:6:0: style: The function 'unusedFunctionTest' is never used. [unusedFunction]".format(proj_path_sep)
"{}B.cpp:6:9: style: The function 'unusedFunctionTest' is never used. [unusedFunction]".format(proj_path_sep)
]
assert stdout == ''
assert ret == 1, stdout
Expand Down
2 changes: 1 addition & 1 deletion test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ class A {
args += extra_args

_, _, stderr = cppcheck(args)
assert stderr == "{}:4:0: style: The function 'f' is never used. [unusedFunction]\n".format(test_h_file)
assert stderr == "{}:4:26: style: The function 'f' is never used. [unusedFunction]\n".format(test_h_file)


def test_unused_function_include(tmpdir):
Expand Down
6 changes: 3 additions & 3 deletions test/cli/qml_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ def __test_unused_functions(extra_args):
lines.sort()
# there are unused functions. But fillSampleData is not unused because that is referenced from main.qml
assert lines == [
"{}samplemodel.cpp:15:0: style: The function 'data' is never used. [unusedFunction]".format(__project_dir_sep),
"{}samplemodel.cpp:38:0: style: The function 'roleNames' is never used. [unusedFunction]".format(__project_dir_sep),
"{}samplemodel.cpp:9:0: style: The function 'rowCount' is never used. [unusedFunction]".format(__project_dir_sep)
"{}samplemodel.cpp:15:23: style: The function 'data' is never used. [unusedFunction]".format(__project_dir_sep),
"{}samplemodel.cpp:38:37: style: The function 'roleNames' is never used. [unusedFunction]".format(__project_dir_sep),
"{}samplemodel.cpp:9:18: style: The function 'rowCount' is never used. [unusedFunction]".format(__project_dir_sep)
]
assert ret == 0, stdout

Expand Down
6 changes: 3 additions & 3 deletions test/cli/unused_function_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __test_unused_functions(extra_args):
ret, stdout, stderr = cppcheck(args)
assert stdout.splitlines() == []
assert stderr.splitlines() == [
"{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
"{}3.c:3:6: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
]
assert ret == 0, stdout

Expand Down Expand Up @@ -103,7 +103,7 @@ def __test_unused_functions_project(extra_args):
ret, stdout, stderr = cppcheck(args)
assert stdout.splitlines() == []
assert [
"{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
"{}3.c:3:6: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
] == stderr.splitlines()
assert ret == 0, stdout

Expand Down Expand Up @@ -163,7 +163,7 @@ def __test_unused_functions_compdb(tmpdir, extra_args):
ret, stdout, stderr = cppcheck(args)
assert stdout.splitlines() == []
assert stderr.splitlines() == [
"{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
"{}3.c:3:6: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep)
]
assert ret == 0, stdout

Expand Down
Loading
Loading