From eecc09c3e43e647254cf21ad985bef89f6e9afde Mon Sep 17 00:00:00 2001 From: firewave Date: Sat, 26 Apr 2025 09:32:51 +0200 Subject: [PATCH 1/2] glob --- cli/filelister.cpp | 8 ++++---- lib/pathmatch.cpp | 9 +++++++-- lib/pathmatch.h | 3 ++- lib/suppressions.cpp | 4 ++++ lib/suppressions.h | 4 +--- lib/utils.h | 4 ++++ test/cli/other_test.py | 22 ++++++++++++---------- test/testpathmatch.cpp | 12 ++++++++++++ test/testutils.cpp | 17 +++++++++++++++++ 9 files changed, 63 insertions(+), 20 deletions(-) diff --git a/cli/filelister.cpp b/cli/filelister.cpp index f2975d104ad..04a269d66f7 100644 --- a/cli/filelister.cpp +++ b/cli/filelister.cpp @@ -109,7 +109,7 @@ static std::string addFiles2(std::list&files, const std::string // File Standards::Language lang = Standards::Language::None; if ((!checkAllFilesInDir || Path::acceptFile(fname, extra, &lang))) { - if (!ignored.match(fname)) { + if (!ignored.match(fname, true)) { std::string nativename = Path::fromNativeSeparators(fname); // Limitation: file sizes are assumed to fit in a 'size_t' @@ -200,7 +200,7 @@ static std::string addFiles2(std::list &files, const PathMatch& ignored, bool debug) { - if (ignored.match(path)) + if (ignored.match(path, true)) { if (debug) std::cout << "ignored path: " << path << std::endl; @@ -245,7 +245,7 @@ static std::string addFiles2(std::list &files, if (recursive) { // append a slash if it is a directory since that is what we are doing for mIgnoredPaths directory entries. // otherwise we would ignore all its contents individually instead as a whole. - if (!ignored.match(new_path + '/')) { + if (!ignored.match(new_path + '/', true)) { std::string err = addFiles2(files, new_path, extra, recursive, ignored, debug); if (!err.empty()) { return err; @@ -259,7 +259,7 @@ static std::string addFiles2(std::list &files, } else { Standards::Language lang = Standards::Language::None; if (Path::acceptFile(new_path, extra, &lang)) { - if (!ignored.match(new_path)) + if (!ignored.match(new_path, true)) { if (stat(new_path.c_str(), &file_stat) == -1) { const int err = errno; diff --git a/lib/pathmatch.cpp b/lib/pathmatch.cpp index 638c2bce005..128b22e0373 100644 --- a/lib/pathmatch.cpp +++ b/lib/pathmatch.cpp @@ -25,7 +25,8 @@ #include PathMatch::PathMatch(std::vector paths, bool caseSensitive) - : mPaths(std::move(paths)), mCaseSensitive(caseSensitive) + : mPaths(std::move(paths)) + , mCaseSensitive(caseSensitive) { for (std::string& p : mPaths) { @@ -37,7 +38,7 @@ PathMatch::PathMatch(std::vector paths, bool caseSensitive) mWorkingDirectory.push_back(Path::fromNativeSeparators(Path::getCurrentPath())); } -bool PathMatch::match(const std::string &path) const +bool PathMatch::match(const std::string &path, bool glob) const { if (path.empty()) return false; @@ -55,6 +56,10 @@ bool PathMatch::match(const std::string &path) const // TODO: align the match logic with ImportProject::ignorePaths() for (auto i = mPaths.cbegin(); i != mPaths.cend(); ++i) { + if (glob && utils::isWildcard(*i) && isValidGlobPattern(*i) && matchglob(*i, findpath)) { + return true; + } + const std::string pathToMatch((!is_absolute && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); // Filtering directory name diff --git a/lib/pathmatch.h b/lib/pathmatch.h index f0ace4fbc94..c385e48dcd8 100644 --- a/lib/pathmatch.h +++ b/lib/pathmatch.h @@ -50,9 +50,10 @@ class CPPCHECKLIB PathMatch { * If you want to match a directory the given path needs to end with a path separator. * * @param path Path to match. + * @param glob Match glob patterns. * @return true if any of the masks match the path, false otherwise. */ - bool match(const std::string &path) const; + bool match(const std::string &path, bool glob = false) const; protected: diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 7f2351878c0..11e7c41344e 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -459,6 +459,10 @@ std::string SuppressionList::Suppression::getText() const return ret; } +bool SuppressionList::Suppression::isWildcard() const { + return utils::isWildcard(fileName); +} + bool SuppressionList::isSuppressed(const SuppressionList::ErrorMessage &errmsg, bool global) { std::lock_guard lg(mSuppressionsSync); diff --git a/lib/suppressions.h b/lib/suppressions.h index 5e4cccb3890..b93c71f1d65 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -130,9 +130,7 @@ class CPPCHECKLIB SuppressionList { std::string getText() const; - bool isWildcard() const { - return fileName.find_first_of("?*") != std::string::npos; - } + bool isWildcard() const; bool isLocal() const { return !fileName.empty() && !isWildcard(); diff --git a/lib/utils.h b/lib/utils.h index 82edc353160..6eec649efc2 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -427,6 +427,10 @@ namespace utils { return result; }; } + + static inline bool isWildcard(const std::string& expr) { + return expr.find_first_of("?*") != std::string::npos; + } } #endif diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 0d792b90b98..64d647ef983 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -2067,17 +2067,14 @@ def test_ignore_file_append(tmpdir): __test_ignore_file(tmpdir, 'test.cpp', append=True) -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_file_wildcard_back(tmpdir): __test_ignore_file(tmpdir, 'test.c*') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_file_wildcard_front(tmpdir): __test_ignore_file(tmpdir, '*test.cpp') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_file_placeholder(tmpdir): __test_ignore_file(tmpdir, 't?st.cpp') @@ -2090,12 +2087,10 @@ def test_ignore_file_relative_backslash(tmpdir): __test_ignore_file(tmpdir, 'src\\test.cpp') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_file_relative_wildcard(tmpdir): __test_ignore_file(tmpdir, 'src/test.c*') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_file_relative_wildcard_backslash(tmpdir): __test_ignore_file(tmpdir, 'src\\test.c*') @@ -2108,12 +2103,10 @@ def test_ignore_path_relative_backslash(tmpdir): __test_ignore_file(tmpdir, 'src\\') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_path_relative_wildcard(tmpdir): __test_ignore_file(tmpdir, 'src*/') -@pytest.mark.xfail(strict=True) # TODO: glob syntax is not supported? def test_ignore_path_relative_wildcard_backslash(tmpdir): __test_ignore_file(tmpdir, 'src*\\') @@ -2183,17 +2176,14 @@ def test_ignore_project_file_cli_append(tmpdir): __test_ignore_project(tmpdir, ign_proj='test2.cpp', ign_cli='test.cpp', append_cli=True) -@pytest.mark.xfail(strict=True) # TODO: ? def test_ignore_project_file_wildcard_back(tmpdir): __test_ignore_project(tmpdir, 'test.c*') -@pytest.mark.xfail(strict=True) # TODO: ? def test_ignore_project_file_wildcard_front(tmpdir): __test_ignore_project(tmpdir, '*test.cpp') -@pytest.mark.xfail(strict=True) # TODO: ? def test_ignore_project_file_placeholder(tmpdir): __test_ignore_project(tmpdir, 't?st.cpp') @@ -2206,6 +2196,14 @@ def test_ignore_project_file_relative_backslash(tmpdir): __test_ignore_project(tmpdir, 'src\\test.cpp') +def test_ignore_project_file_relative_wildcard(tmpdir): + __test_ignore_project(tmpdir, 'src/test.c*') + + +def test_ignore_project_file_relative_wildcard_backslash(tmpdir): + __test_ignore_project(tmpdir, 'src\\test.c*') + + def test_ignore_project_path_relative(tmpdir): __test_ignore_project(tmpdir, 'src/') @@ -2214,6 +2212,10 @@ def test_ignore_project_path_relative_backslash(tmpdir): __test_ignore_project(tmpdir, 'src\\') +def test_ignore_project_path_relative_wildcard_backslash(tmpdir): + __test_ignore_project(tmpdir, 'src*\\') + + def test_ignore_project_abspath(tmpdir): __test_ignore_project(tmpdir, '$path', inject_path_proj=True) diff --git a/test/testpathmatch.cpp b/test/testpathmatch.cpp index 45fbde54d58..1e81e6acfaf 100644 --- a/test/testpathmatch.cpp +++ b/test/testpathmatch.cpp @@ -67,6 +67,7 @@ class TestPathMatch : public TestFixture { TEST_CASE(filemaskpath3); TEST_CASE(filemaskpath4); TEST_CASE(mixedallmatch); + TEST_CASE(glob); } // Test empty PathMatch @@ -224,6 +225,17 @@ class TestPathMatch : public TestFixture { ASSERT(match.match("tests/")); ASSERT(match.match("lib/file.c")); } + + void glob() const { // #???? + std::vector mask = { "test?.cpp" }; + PathMatch match(std::move(mask)); + ASSERT(match.match("test1.cpp", true)); + ASSERT(match.match("src/test1.cpp", true)); + + ASSERT(!match.match("test1.c", true)); + ASSERT(!match.match("test.cpp", true)); + ASSERT(!match.match("test1.cpp/src", true)); + } }; REGISTER_TEST(TestPathMatch) diff --git a/test/testutils.cpp b/test/testutils.cpp index d1150b7b0d2..278c8d7a1f5 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -44,6 +44,7 @@ class TestUtils : public TestFixture { TEST_CASE(splitString); TEST_CASE(as_const); TEST_CASE(memoize); + TEST_CASE(isWildcard); } void isValidGlobPattern() const { @@ -535,6 +536,22 @@ class TestUtils : public TestFixture { ASSERT_EQUALS(1, callF()); ASSERT_EQUALS(1, count); } + + void isWildcard() const { + ASSERT_EQUALS(true, utils::isWildcard("*")); + ASSERT_EQUALS(true, utils::isWildcard("?")); + ASSERT_EQUALS(true, utils::isWildcard("*test")); + ASSERT_EQUALS(true, utils::isWildcard("test*")); + ASSERT_EQUALS(true, utils::isWildcard("t*t")); + ASSERT_EQUALS(true, utils::isWildcard("t?st")); + ASSERT_EQUALS(true, utils::isWildcard("?est")); + ASSERT_EQUALS(true, utils::isWildcard("tes?")); + ASSERT_EQUALS(true, utils::isWildcard("?es?")); + + ASSERT_EQUALS(false, utils::isWildcard("")); + ASSERT_EQUALS(false, utils::isWildcard(" ")); + ASSERT_EQUALS(false, utils::isWildcard("test")); + } }; REGISTER_TEST(TestUtils) From ac72d6915c7bd1383cf1d15f03e07752c977f5c2 Mon Sep 17 00:00:00 2001 From: firewave Date: Tue, 29 Oct 2024 23:47:35 +0100 Subject: [PATCH 2/2] TestFileLister: improved testing [skip ci] --- test/testfilelister.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/testfilelister.cpp b/test/testfilelister.cpp index 048c2e4c17a..75590b77d38 100644 --- a/test/testfilelister.cpp +++ b/test/testfilelister.cpp @@ -106,6 +106,8 @@ class TestFileLister : public TestFixture { // Make sure headers are not added.. ASSERT(find_file(dirprefix + "lib/tokenize.h") == files.end()); + + ASSERT_EQUALS_ENUM(Standards::Language::CPP, find_file(dirprefix + "cli/main.cpp")->lang()); } void recursiveAddFilesEmptyPath() const { @@ -134,7 +136,23 @@ class TestFileLister : public TestFixture { std::string err = FileLister::recursiveAddFiles(files, basedir + "lib/token.cpp", {}, matcher); ASSERT_EQUALS("", err); ASSERT_EQUALS(1, files.size()); - ASSERT_EQUALS(basedir + "lib/token.cpp", files.begin()->path()); + auto it = files.begin(); + ASSERT_EQUALS(basedir + "lib/token.cpp", it->path()); + ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang()); // explicitly specified files are not accepted + } + + void header() const { + const std::string basedir = findBaseDir(); + + std::list files; + std::vector ignored; + PathMatch matcher(ignored); + std::string err = FileLister::recursiveAddFiles(files, basedir + "lib/token.h", matcher); + ASSERT_EQUALS("", err); + ASSERT_EQUALS(1, files.size()); + auto it = files.begin(); + ASSERT_EQUALS(basedir + "lib/token.h", it->path()); + ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang()); // explicitly specified files are not accepted } void excludeDir() const {