From be8012e9e11c989e4a965489748316e4cc574fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 6 Sep 2025 14:51:27 +0200 Subject: [PATCH 1/4] Fix #14116 (cmdline: Improve IDE integration with --project-settings option) --- cli/cmdlineparser.cpp | 16 +++++++++++++--- test/testcmdlineparser.cpp | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 48592a3e5c0..275ef0bf053 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -382,6 +382,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a bool def = false; bool maxconfigs = false; bool debug = false; + bool inputAsFilter = false; // set by: --file-filter=+ ImportProject::Type projectType = ImportProject::Type::NONE; ImportProject project; @@ -768,6 +769,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a mLogger.printError("Failed: --file-filter=-"); return Result::Fail; } + } else if (std::strcmp(filter, "+") == 0) { + inputAsFilter = true; } else { mSettings.fileFilters.emplace_back(filter); } @@ -1582,6 +1585,11 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a //mLogger.printMessage("whole program analysis requires --cppcheck-build-dir to be active with -j."); } + if (inputAsFilter) { + mSettings.fileFilters.insert(mSettings.fileFilters.end(), mPathNames.cbegin(), mPathNames.cend()); + mPathNames.clear(); + } + if (!mPathNames.empty() && projectType != ImportProject::Type::NONE) { mLogger.printError("--project cannot be used in conjunction with source files."); return Result::Fail; @@ -1777,10 +1785,12 @@ void CmdLineParser::printHelp() const " --exitcode-suppressions=\n" " Used when certain messages should be displayed but\n" " should not cause a non-zero exitcode.\n" - " --file-filter= Analyze only those files matching the given filter str\n" - " Can be used multiple times\n" + " --file-filter= Analyze only those files matching the given filter str.\n" + " Can be used multiple times. When str is '-', the file\n" + " filter will be read from standard input. When str is '+',\n" + " all path arguments are treated as file filters.\n" " Example: --file-filter=*bar.cpp analyzes only files\n" - " that end with bar.cpp.\n" + " that end with bar.cpp.\n" " --file-list= Specify the files to check in a text file. Add one\n" " filename per line. When file is '-,' the file list will\n" " be read from standard input.\n" diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 65bf6906841..3527ff6cc76 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -208,6 +208,7 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(fileFilterFileWithDetailsSimplifiedPath); TEST_CASE(fileFilterFileWithDetailsCase); TEST_CASE(fileFilterStdin); + TEST_CASE(fileFilterPlus); TEST_CASE(fileFilterNoMatch); TEST_CASE(fileList); TEST_CASE(fileListNoFile); @@ -1233,6 +1234,19 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("file2.cpp", settings->fileFilters[1]); } + void fileFilterPlus() { + REDIRECT; + ScopedFile file("project.cppcheck", + "\n" + "\n" + "\n" + "\n" + ""); + const char * const argv[] = {"cppcheck", "--project=project.cppcheck", "--file-filter=+", "src/file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS("src/file.cpp", settings->fileFilters.front()); + } + void fileFilterNoMatch() { REDIRECT; RedirectInput input("notexist1.c\nnotexist2.cpp\n"); From 2161714eade0dcc3f7eb72f6a72cf9c008b4ace5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 6 Sep 2025 18:44:38 +0200 Subject: [PATCH 2/4] tweak help text --- cli/cmdlineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 275ef0bf053..2343652fa61 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1788,7 +1788,7 @@ void CmdLineParser::printHelp() const " --file-filter= Analyze only those files matching the given filter str.\n" " Can be used multiple times. When str is '-', the file\n" " filter will be read from standard input. When str is '+',\n" - " all path arguments are treated as file filters.\n" + " given files on CLI will be treated as file filters.\n" " Example: --file-filter=*bar.cpp analyzes only files\n" " that end with bar.cpp.\n" " --file-list= Specify the files to check in a text file. Add one\n" From 388a65e5e12310981d21a23e9592f40e56d49f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 6 Sep 2025 18:55:59 +0200 Subject: [PATCH 3/4] test --- test/cli/more-projects_test.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/cli/more-projects_test.py b/test/cli/more-projects_test.py index 71959e7f4c8..f50dc8b188c 100644 --- a/test/cli/more-projects_test.py +++ b/test/cli/more-projects_test.py @@ -295,7 +295,13 @@ def test_clang_tidy(tmpdir): assert stderr == '' -def test_project_file_filter(tmpdir): +@pytest.mark.parametrize("file_filter", [ + ['--file-filter=test.cpp'], + ['--file-filter=*.cpp'], + ['--file-filter=+', 'test.cpp'], + ['--file-filter=+', '*.cpp'], +]) +def test_project_file_filter(tmpdir, file_filter): test_file = os.path.join(tmpdir, 'test.cpp') with open(test_file, 'wt') as f: pass @@ -310,7 +316,7 @@ def test_project_file_filter(tmpdir): """.format(test_file)) - args = ['--file-filter=*.cpp', '--project={}'.format(project_file)] + args = file_filter + ['--project={}'.format(project_file)] out_lines = [ 'Checking {} ...'.format(test_file) ] From 3c29453bbaf6190394ac433584f5252fe98cca6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 6 Sep 2025 19:00:02 +0200 Subject: [PATCH 4/4] more tests --- test/cli/more-projects_test.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/cli/more-projects_test.py b/test/cli/more-projects_test.py index f50dc8b188c..f3ed273c4b9 100644 --- a/test/cli/more-projects_test.py +++ b/test/cli/more-projects_test.py @@ -324,7 +324,11 @@ def test_project_file_filter(tmpdir, file_filter): assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) -def test_project_file_filter_2(tmpdir): +@pytest.mark.parametrize("file_filter", [ + ['--file-filter=*.cpp'], + ['--file-filter=+', '*.cpp'], +]) +def test_project_file_filter_cpp(tmpdir, file_filter): test_file_1 = os.path.join(tmpdir, 'test.cpp') with open(test_file_1, 'wt') as f: pass @@ -343,7 +347,7 @@ def test_project_file_filter_2(tmpdir): """.format(test_file_1, test_file_2)) - args = ['--file-filter=*.cpp', '--project={}'.format(project_file)] + args = file_filter + ['--project={}'.format(project_file)] out_lines = [ 'Checking {} ...'.format(test_file_1) ] @@ -351,7 +355,11 @@ def test_project_file_filter_2(tmpdir): assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) -def test_project_file_filter_3(tmpdir): +@pytest.mark.parametrize("file_filter", [ + ['--file-filter=*.c'], + ['--file-filter=+', '*.c'], +]) +def test_project_file_filter_c(tmpdir, file_filter): test_file_1 = os.path.join(tmpdir, 'test.cpp') with open(test_file_1, 'wt') as f: pass @@ -370,7 +378,7 @@ def test_project_file_filter_3(tmpdir): """.format(test_file_1, test_file_2)) - args = ['--file-filter=*.c', '--project={}'.format(project_file)] + args = file_filter + ['--project={}'.format(project_file)] out_lines = [ 'Checking {} ...'.format(test_file_2) ]