Skip to content

added glob support for PathMatch #7540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
8 changes: 4 additions & 4 deletions cli/filelister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ static std::string addFiles2(std::list<FileWithDetails>&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'
Expand Down Expand Up @@ -200,7 +200,7 @@ static std::string addFiles2(std::list<FileWithDetails> &files,
const PathMatch& ignored,
bool debug)
{
if (ignored.match(path))
if (ignored.match(path, true))
{
if (debug)
std::cout << "ignored path: " << path << std::endl;
Expand Down Expand Up @@ -245,7 +245,7 @@ static std::string addFiles2(std::list<FileWithDetails> &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;
Expand All @@ -259,7 +259,7 @@ static std::string addFiles2(std::list<FileWithDetails> &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;
Expand Down
9 changes: 7 additions & 2 deletions lib/pathmatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
#include <utility>

PathMatch::PathMatch(std::vector<std::string> paths, bool caseSensitive)
: mPaths(std::move(paths)), mCaseSensitive(caseSensitive)
: mPaths(std::move(paths))
, mCaseSensitive(caseSensitive)
{
for (std::string& p : mPaths)
{
Expand All @@ -37,7 +38,7 @@ PathMatch::PathMatch(std::vector<std::string> 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;
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/pathmatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
4 changes: 4 additions & 0 deletions lib/suppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::mutex> lg(mSuppressionsSync);
Expand Down
4 changes: 1 addition & 3 deletions lib/suppressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions lib/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
22 changes: 12 additions & 10 deletions test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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*')

Expand All @@ -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*\\')

Expand Down Expand Up @@ -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')

Expand All @@ -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/')

Expand All @@ -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)

Expand Down
20 changes: 19 additions & 1 deletion test/testfilelister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<FileWithDetails> files;
std::vector<std::string> 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 {
Expand Down
12 changes: 12 additions & 0 deletions test/testpathmatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class TestPathMatch : public TestFixture {
TEST_CASE(filemaskpath3);
TEST_CASE(filemaskpath4);
TEST_CASE(mixedallmatch);
TEST_CASE(glob);
}

// Test empty PathMatch
Expand Down Expand Up @@ -224,6 +225,17 @@ class TestPathMatch : public TestFixture {
ASSERT(match.match("tests/"));
ASSERT(match.match("lib/file.c"));
}

void glob() const { // #????
std::vector<std::string> 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)
17 changes: 17 additions & 0 deletions test/testutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class TestUtils : public TestFixture {
TEST_CASE(splitString);
TEST_CASE(as_const);
TEST_CASE(memoize);
TEST_CASE(isWildcard);
}

void isValidGlobPattern() const {
Expand Down Expand Up @@ -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)