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
6 changes: 6 additions & 0 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
mSettings.checkLibrary = true;
}

else if (std::strcmp(argv[i], "--check-unused-templates") == 0)
mSettings.checkUnusedTemplates = true;

else if (std::strncmp(argv[i], "--check-version=", 16) == 0) {
if (!loadCppcheckCfg())
return Result::Fail;
Expand Down Expand Up @@ -1007,6 +1010,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
else if (std::strcmp(argv[i], "--no-check-headers") == 0)
mSettings.checkHeaders = false;

else if (std::strcmp(argv[i], "--no-check-unused-templates") == 0)
mSettings.checkUnusedTemplates = false;

// undocumented option for usage in Python tests to indicate that no build dir should be injected
else if (std::strcmp(argv[i], "--no-cppcheck-build-dir") == 0) {
mSettings.buildDir.clear();
Expand Down
2 changes: 1 addition & 1 deletion lib/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class CPPCHECKLIB WARN_UNUSED Settings {
std::set<std::string> checkUnknownFunctionReturn; // TODO: move to Library?

/** Check unused/uninstantiated templates */
bool checkUnusedTemplates = true; // TODO: CLI
bool checkUnusedTemplates = true;

/** Use Clang */
bool clang{};
Expand Down
151 changes: 148 additions & 3 deletions test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3166,7 +3166,6 @@ def test_dir_ignore(tmp_path):
assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines, cwd=str(tmp_path))



def test_check_headers(tmp_path):
test_file_h = tmp_path / 'test.h'
with open(test_file_h, 'wt') as f:
Expand Down Expand Up @@ -3199,7 +3198,6 @@ def test_check_headers(tmp_path):
assert stderr.splitlines() == [] # no error since the header is not checked



def test_unique_error(tmp_path): # #6366
test_file = tmp_path / 'test.c'
with open(test_file, 'wt') as f:
Expand All @@ -3224,4 +3222,151 @@ def test_unique_error(tmp_path): # #6366
assert stderr.splitlines() == [
"{}:4:13: error: Array 'm[9]' accessed at index 9, which is out of bounds. [arrayIndexOutOfBounds]".format(test_file),
"{}:4:21: error: Array 'm[9]' accessed at index 9, which is out of bounds. [arrayIndexOutOfBounds]".format(test_file)
]
]


def test_check_unused_templates_class(tmp_path):
test_file_h = tmp_path / 'test.h'
with open(test_file_h, 'wt') as f:
f.write(
"""template<class T>
class HdrCl1
{
HdrCl1()
{
(void)(*((int*)0));
}
};

template<typename T>
class HdrCl2
{
HdrCl2()
{
(void)(*((int*)0));
}
};

template<class T>
struct HdrSt1
{
HdrSt1()
{
(void)(*((int*)0));
}
};

template<typename T>
struct HdrSt2
{
HdrSt2()
{
(void)(*((int*)0));
}
};
""")

test_file = tmp_path / 'test.cpp'
with open(test_file, 'wt') as f:
f.write(
"""#include "test.h"

template<class T>
class Cl1
{
CL1()
{
(void)(*((int*)0));
}
};

template<typename T>
class Cl2
{
Cl2()
{
(void)(*((int*)0));
}
};

template<class T>
struct St1
{
St1()
{
(void)(*((int*)0));
}
};

template<typename T>
struct St2
{
St2()
{
(void)(*((int*)0));
}
};

void f() {}
""")

args = [
'-q',
'--template=simple',
'--no-check-unused-templates',
str(test_file)
]
exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0, stdout
assert stdout.splitlines() == []
assert stderr.splitlines() == [] # no error since the unused templates are not being checked


@pytest.mark.xfail(strict=True) # TODO: only the first unused templated function is not being checked
def test_check_unused_templates_func(tmp_path): # #13714
test_file_h = tmp_path / 'test.h'
with open(test_file_h, 'wt') as f:
f.write(
"""template<class T>
void f_t_hdr_1()
{
(void)(*((int*)0));
}

template<typename T>
void f_t_hdr_2()
{
(void)(*((int*)0));
}
""")

test_file = tmp_path / 'test.cpp'
with open(test_file, 'wt') as f:
f.write(
"""#include "test.h"

template<class T>
void f_t_1()
{
(void)(*((int*)0));
}

template<typename T>
void f_t_2()
{
(void)(*((int*)0));
}

void f() {}
""")

args = [
'-q',
'--template=simple',
'--no-check-unused-templates',
str(test_file)
]
exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0, stdout
assert stdout.splitlines() == []
assert stderr.splitlines() == [] # no error since the unused templates are not being checked
24 changes: 24 additions & 0 deletions test/testcmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ class TestCmdlineParser : public TestFixture {
TEST_CASE(noCheckHeaders);
TEST_CASE(noCheckHeaders2);
TEST_CASE(filesdir);
TEST_CASE(checkUnusedTemplates);
TEST_CASE(noCheckUnusedTemplates);
TEST_CASE(noCheckUnusedTemplates);

TEST_CASE(ignorepaths1);
TEST_CASE(ignorepaths2);
Expand Down Expand Up @@ -3024,6 +3027,27 @@ class TestCmdlineParser : public TestFixture {
#endif
}

void checkUnusedTemplates() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--check-unused-templates", "file.cpp"};
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv));
ASSERT_EQUALS(true, settings->checkUnusedTemplates);
}

void noCheckUnusedTemplates() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--no-check-unused-templates", "file.cpp"};
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv));
ASSERT_EQUALS(false, settings->checkUnusedTemplates);
}

void noCheckUnusedTemplates2() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--check-unused-templates", "--no-check-unused-templates", "file.cpp"};
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv));
ASSERT_EQUALS(false, settings->checkUnusedTemplates);
}

void ignorepaths1() {
REDIRECT;
const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"};
Expand Down