Skip to content

Fix #14021 (Better path matching) #7645

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

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
42f3717
Rework PathMatch
glankk Jul 6, 2025
9521baf
Fix gui testcase
glankk Jul 6, 2025
525bc6f
Run dmake
glankk Jul 6, 2025
dd76d65
Update test cases
glankk Jul 6, 2025
7f0b04e
Fix empty regex
glankk Jul 6, 2025
8c31bc2
Fix clang-tidy issues
glankk Jul 6, 2025
1b270da
Some pathmatch and matchglob refactoring, more tests
glankk Jul 8, 2025
9743d82
Run dmake
glankk Jul 8, 2025
2dcceca
Make basepath a class member
glankk Jul 8, 2025
740b363
Remove matchglobs
glankk Jul 8, 2025
a9c01ef
Fix clang-tidy issue and windows test
glankk Jul 8, 2025
6200cb8
Remove obsolete mIgnorePaths check
glankk Jul 9, 2025
5b21e25
Allow directories to be specified without trailing path separator
glankk Jul 9, 2025
6547d33
Fix test_project_pathmatch_other_cwd
glankk Jul 9, 2025
bf664fd
Hand-written matching function
glankk Jul 9, 2025
c5fb6db
Use explicit basepath
glankk Jul 10, 2025
2671b60
Fix clang-tidy issue
glankk Jul 10, 2025
8e50d1d
Suppression match shortcut
glankk Jul 10, 2025
89c0e71
Suppression stuff
glankk Jul 10, 2025
3327d87
Cleanups, comments, more tests
glankk Jul 11, 2025
8d37891
More comments
glankk Jul 11, 2025
a3ee6d0
Fix shadowing
glankk Jul 11, 2025
3f815c8
Run dmake
glankk Jul 11, 2025
b383b36
Fix typo
glankk Jul 11, 2025
0713512
Fix naming
glankk Jul 11, 2025
625d613
Update comment
glankk Jul 11, 2025
0c7d924
Update rules for relative patterns
glankk Jul 12, 2025
648c23a
Cleanup, fixes, more tests
glankk Jul 15, 2025
9347930
Run format
glankk Jul 15, 2025
2f87913
Revert CmdlineParser test cases
glankk Jul 15, 2025
2c31122
Fix for false positive on index range
glankk Jul 15, 2025
5426dd2
Update docs
glankk Jul 15, 2025
ae373f5
Update release notes
glankk Jul 15, 2025
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 Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ $(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/addoninfo.h lib/anal
$(libcppdir)/fwdanalysis.o: lib/fwdanalysis.cpp lib/addoninfo.h lib/astutils.h lib/checkers.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/fwdanalysis.cpp

$(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson/picojson.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/checkers.h lib/config.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson/picojson.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/checkers.h lib/config.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp

$(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueptr.h lib/vfvalue.h
Expand All @@ -606,7 +606,7 @@ $(libcppdir)/path.o: lib/path.cpp externals/simplecpp/simplecpp.h lib/config.h l
$(libcppdir)/pathanalysis.o: lib/pathanalysis.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathanalysis.cpp

$(libcppdir)/pathmatch.o: lib/pathmatch.cpp lib/config.h lib/path.h lib/pathmatch.h lib/standards.h lib/utils.h
$(libcppdir)/pathmatch.o: lib/pathmatch.cpp lib/config.h lib/path.h lib/pathmatch.h lib/standards.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathmatch.cpp

$(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/mathlib.h lib/path.h lib/platform.h lib/standards.h lib/xml.h
Expand All @@ -630,7 +630,7 @@ $(libcppdir)/standards.o: lib/standards.cpp externals/simplecpp/simplecpp.h lib/
$(libcppdir)/summaries.o: lib/summaries.cpp lib/addoninfo.h lib/analyzerinfo.h lib/checkers.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp

$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/platform.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp

$(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/addoninfo.h lib/checkers.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
Expand Down Expand Up @@ -816,7 +816,7 @@ test/testother.o: test/testother.cpp lib/addoninfo.h lib/check.h lib/checkers.h
test/testpath.o: test/testpath.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpath.cpp

test/testpathmatch.o: test/testpathmatch.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h
test/testpathmatch.o: test/testpathmatch.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpathmatch.cpp

test/testplatform.o: test/testplatform.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/utils.h lib/xml.h test/fixture.h
Expand Down
41 changes: 9 additions & 32 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
std::list<FileSettings> fileSettings;
if (!mSettings.fileFilters.empty()) {
// filter only for the selected filenames from all project files
PathMatch filtermatcher(mSettings.fileFilters, Path::getCurrentPath());
std::copy_if(fileSettingsRef.cbegin(), fileSettingsRef.cend(), std::back_inserter(fileSettings), [&](const FileSettings &fs) {
return matchglobs(mSettings.fileFilters, fs.filename());
return filtermatcher.match(fs.filename());
});
if (fileSettings.empty()) {
mLogger.printError("could not find any files matching the filter.");
Expand Down Expand Up @@ -242,16 +243,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])

if (!pathnamesRef.empty()) {
std::list<FileWithDetails> filesResolved;
// TODO: this needs to be inlined into PathMatch as it depends on the underlying filesystem
#if defined(_WIN32)
// For Windows we want case-insensitive path matching
const bool caseSensitive = false;
#else
const bool caseSensitive = true;
#endif
// Execute recursiveAddFiles() to each given file parameter
// TODO: verbose log which files were ignored?
const PathMatch matcher(ignored, caseSensitive);
const PathMatch matcher(ignored, Path::getCurrentPath());
for (const std::string &pathname : pathnamesRef) {
const std::string err = FileLister::recursiveAddFiles(filesResolved, Path::toNativeSeparators(pathname), mSettings.library.markupExtensions(), matcher, mSettings.debugignore);
if (!err.empty()) {
Expand Down Expand Up @@ -1622,19 +1616,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
for (auto& path : mIgnoredPaths)
{
path = Path::removeQuotationMarks(std::move(path));
path = Path::simplifyPath(std::move(path));

bool isdir = false;
if (!Path::exists(path, &isdir) && mSettings.debugignore) {
// FIXME: this is misleading because we match from the end of the path so it does not require to exist
//std::cout << "path to ignore does not exist: " << path << std::endl;
}
// TODO: this only works when it exists
if (isdir) {
// If directory name doesn't end with / or \, add it
if (!endsWith(path, '/'))
path += '/';
}
path = Path::fromNativeSeparators(std::move(path));
}

if (!project.guiProject.pathNames.empty())
Expand Down Expand Up @@ -1792,10 +1774,9 @@ void CmdLineParser::printHelp() const
" this is not needed.\n"
" --include=<file>\n"
" Force inclusion of a file before the checked file.\n"
" -i <dir or file> Give a source file or source file directory to exclude\n"
" from the check. This applies only to source files so\n"
" header files included by source files are not matched.\n"
" Directory name is matched to all parts of the path.\n"
" -i <str> Exclude source files or directories matching str from\n"
" the check. This applies only to source files so header\n"
" files included by source files are not matched.\n"
" --inconclusive Allow that Cppcheck reports even though the analysis is\n"
" inconclusive.\n"
" There are false positives with this option. Each result\n"
Expand Down Expand Up @@ -2160,13 +2141,9 @@ bool CmdLineParser::loadCppcheckCfg()
std::list<FileWithDetails> CmdLineParser::filterFiles(const std::vector<std::string>& fileFilters,
const std::list<FileWithDetails>& filesResolved) {
std::list<FileWithDetails> files;
#ifdef _WIN32
constexpr bool caseInsensitive = true;
#else
constexpr bool caseInsensitive = false;
#endif
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
std::copy_if(filesResolved.cbegin(), filesResolved.cend(), std::inserter(files, files.end()), [&](const FileWithDetails& entry) {
return matchglobs(fileFilters, entry.path(), caseInsensitive) || matchglobs(fileFilters, entry.spath(), caseInsensitive);
return filtermatcher.match(entry.path());
});
return files;
}
8 changes: 2 additions & 6 deletions cli/filelister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,7 @@ static std::string addFiles2(std::list<FileWithDetails>&files, const std::string
} else {
// Directory
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(fname + '/')) {
if (!ignored.match(fname)) {
std::list<FileWithDetails> filesSorted;

std::string err = addFiles2(filesSorted, fname, extra, recursive, ignored);
Expand Down Expand Up @@ -243,9 +241,7 @@ static std::string addFiles2(std::list<FileWithDetails> &files,
#endif
if (path_is_directory) {
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)) {
std::string err = addFiles2(files, new_path, extra, recursive, ignored, debug);
if (!err.empty()) {
return err;
Expand Down
6 changes: 1 addition & 5 deletions gui/filelist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,7 @@ static std::vector<std::string> toStdStringList(const QStringList &stringList)

QStringList FileList::applyExcludeList() const
{
#ifdef _WIN32
const PathMatch pathMatch(toStdStringList(mExcludedPaths), true);
#else
const PathMatch pathMatch(toStdStringList(mExcludedPaths), false);
#endif
const PathMatch pathMatch(toStdStringList(mExcludedPaths), QDir::currentPath().toStdString());

QStringList paths;
for (const QFileInfo& item : mFileList) {
Expand Down
52 changes: 25 additions & 27 deletions lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "importproject.h"

#include "path.h"
#include "pathmatch.h"
#include "settings.h"
#include "standards.h"
#include "suppressions.h"
Expand All @@ -42,30 +43,11 @@

#include "json.h"

// TODO: align the exclusion logic with PathMatch
// TODO: PathMatch lacks glob support
void ImportProject::ignorePaths(const std::vector<std::string> &ipaths, bool debug)
{
PathMatch matcher(ipaths, Path::getCurrentPath());
for (auto it = fileSettings.cbegin(); it != fileSettings.cend();) {
bool ignore = false;
for (std::string i : ipaths) {
if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
ignore = true;
break;
}
if (isValidGlobPattern(i) && matchglob(i, it->filename())) {
ignore = true;
break;
}
if (!Path::isAbsolute(i)) {
i = mPath + i;
if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
ignore = true;
break;
}
}
}
if (ignore) {
if (matcher.match(it->filename())) {
if (debug)
std::cout << "ignored path: " << it->filename() << std::endl;
it = fileSettings.erase(it);
Expand Down Expand Up @@ -858,8 +840,9 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
}

// Project files
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
for (const std::string &cfilename : compileList) {
if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename))
if (!fileFilters.empty() && !filtermatcher.match(cfilename))
continue;

for (const ProjectConfiguration &p : projectConfigurationList) {
Expand Down Expand Up @@ -937,6 +920,8 @@ ImportProject::SharedItemsProject ImportProject::importVcxitems(const std::strin
SharedItemsProject result;
result.pathToProjectFile = filename;

PathMatch filtermatcher(fileFilters, Path::getCurrentPath());

tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
if (error != tinyxml2::XML_SUCCESS) {
Expand All @@ -957,8 +942,8 @@ ImportProject::SharedItemsProject ImportProject::importVcxitems(const std::strin
std::string file(include);
findAndReplace(file, "$(MSBuildThisFileDirectory)", "./");

// Don't include file if it matches the filter
if (!fileFilters.empty() && !matchglobs(fileFilters, file))
// Skip file if it doesn't match the filter
if (!fileFilters.empty() && !filtermatcher.match(file))
continue;

result.sourceFiles.emplace_back(file);
Expand Down Expand Up @@ -1269,7 +1254,20 @@ static std::list<std::string> readXmlStringList(const tinyxml2::XMLElement *node
continue;
const char *attr = attribute ? child->Attribute(attribute) : child->GetText();
if (attr)
ret.push_back(joinRelativePath(path, attr));
ret.emplace_back(joinRelativePath(path, attr));
}
return ret;
}

static std::list<std::string> readXmlPathMatchList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[])
{
std::list<std::string> ret;
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
if (strcmp(child->Name(), name) != 0)
continue;
const char *attr = attribute ? child->Attribute(attribute) : child->GetText();
if (attr)
ret.emplace_back(PathMatch::joinRelativePattern(path, attr));
}
return ret;
}
Expand Down Expand Up @@ -1339,13 +1337,13 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings &setti
else if (strcmp(name, CppcheckXml::PathsElementName) == 0)
paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib);
else if (strcmp(name, CppcheckXml::ExcludeElementName) == 0)
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); // TODO: append instead of overwrite
guiProject.excludedPaths = readXmlPathMatchList(node, path, CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); // TODO: append instead of overwrite
else if (strcmp(name, CppcheckXml::FunctionContracts) == 0)
;
else if (strcmp(name, CppcheckXml::VariableContractsElementName) == 0)
;
else if (strcmp(name, CppcheckXml::IgnoreElementName) == 0)
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); // TODO: append instead of overwrite
guiProject.excludedPaths = readXmlPathMatchList(node, path, CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); // TODO: append instead of overwrite
else if (strcmp(name, CppcheckXml::LibrariesElementName) == 0)
guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); // TODO: append instead of overwrite
else if (strcmp(name, CppcheckXml::SuppressionsElementName) == 0) {
Expand Down
9 changes: 5 additions & 4 deletions lib/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,17 @@ std::string Path::getCurrentExecutablePath(const char* fallback)

bool Path::isAbsolute(const std::string& path)
{
const std::string& nativePath = toNativeSeparators(path);

#ifdef _WIN32
if (path.length() < 2)
return false;

if ((path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/'))
return true;

// On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not
return startsWith(nativePath, "\\\\") || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0);
return std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/');
#else
return !nativePath.empty() && nativePath[0] == '/';
return !path.empty() && path[0] == '/';
#endif
}

Expand Down
Loading