Skip to content

Commit 4d5a7f9

Browse files
authored
Fix #14021 (Better path matching) (danmar#7645)
1 parent 26cebf4 commit 4d5a7f9

20 files changed

+738
-230
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ $(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/addoninfo.h lib/anal
585585
$(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
586586
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/fwdanalysis.cpp
587587

588-
$(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
588+
$(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
589589
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp
590590

591591
$(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
@@ -606,7 +606,7 @@ $(libcppdir)/path.o: lib/path.cpp externals/simplecpp/simplecpp.h lib/config.h l
606606
$(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
607607
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathanalysis.cpp
608608

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

612612
$(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
@@ -630,7 +630,7 @@ $(libcppdir)/standards.o: lib/standards.cpp externals/simplecpp/simplecpp.h lib/
630630
$(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
631631
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp
632632

633-
$(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
633+
$(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
634634
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp
635635

636636
$(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
@@ -816,7 +816,7 @@ test/testother.o: test/testother.cpp lib/addoninfo.h lib/check.h lib/checkers.h
816816
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
817817
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpath.cpp
818818

819-
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
819+
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
820820
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpathmatch.cpp
821821

822822
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

cli/cmdlineparser.cpp

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
209209
std::list<FileSettings> fileSettings;
210210
if (!mSettings.fileFilters.empty()) {
211211
// filter only for the selected filenames from all project files
212+
PathMatch filtermatcher(mSettings.fileFilters, Path::getCurrentPath());
212213
std::copy_if(fileSettingsRef.cbegin(), fileSettingsRef.cend(), std::back_inserter(fileSettings), [&](const FileSettings &fs) {
213-
return matchglobs(mSettings.fileFilters, fs.filename());
214+
return filtermatcher.match(fs.filename());
214215
});
215216
if (fileSettings.empty()) {
216217
for (const std::string& f: mSettings.fileFilters)
@@ -243,16 +244,9 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
243244

244245
if (!pathnamesRef.empty()) {
245246
std::list<FileWithDetails> filesResolved;
246-
// TODO: this needs to be inlined into PathMatch as it depends on the underlying filesystem
247-
#if defined(_WIN32)
248-
// For Windows we want case-insensitive path matching
249-
const bool caseSensitive = false;
250-
#else
251-
const bool caseSensitive = true;
252-
#endif
253247
// Execute recursiveAddFiles() to each given file parameter
254248
// TODO: verbose log which files were ignored?
255-
const PathMatch matcher(ignored, caseSensitive);
249+
const PathMatch matcher(ignored, Path::getCurrentPath());
256250
for (const std::string &pathname : pathnamesRef) {
257251
const std::string err = FileLister::recursiveAddFiles(filesResolved, Path::toNativeSeparators(pathname), mSettings.library.markupExtensions(), matcher, mSettings.debugignore);
258252
if (!err.empty()) {
@@ -1624,19 +1618,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
16241618
for (auto& path : mIgnoredPaths)
16251619
{
16261620
path = Path::removeQuotationMarks(std::move(path));
1627-
path = Path::simplifyPath(std::move(path));
1628-
1629-
bool isdir = false;
1630-
if (!Path::exists(path, &isdir) && mSettings.debugignore) {
1631-
// FIXME: this is misleading because we match from the end of the path so it does not require to exist
1632-
//std::cout << "path to ignore does not exist: " << path << std::endl;
1633-
}
1634-
// TODO: this only works when it exists
1635-
if (isdir) {
1636-
// If directory name doesn't end with / or \, add it
1637-
if (!endsWith(path, '/'))
1638-
path += '/';
1639-
}
1621+
path = Path::fromNativeSeparators(std::move(path));
16401622
}
16411623

16421624
if (!project.guiProject.pathNames.empty())
@@ -1794,10 +1776,9 @@ void CmdLineParser::printHelp() const
17941776
" this is not needed.\n"
17951777
" --include=<file>\n"
17961778
" Force inclusion of a file before the checked file.\n"
1797-
" -i <dir or file> Give a source file or source file directory to exclude\n"
1798-
" from the check. This applies only to source files so\n"
1799-
" header files included by source files are not matched.\n"
1800-
" Directory name is matched to all parts of the path.\n"
1779+
" -i <str> Exclude source files or directories matching str from\n"
1780+
" the check. This applies only to source files so header\n"
1781+
" files included by source files are not matched.\n"
18011782
" --inconclusive Allow that Cppcheck reports even though the analysis is\n"
18021783
" inconclusive.\n"
18031784
" There are false positives with this option. Each result\n"
@@ -2162,13 +2143,9 @@ bool CmdLineParser::loadCppcheckCfg()
21622143
std::list<FileWithDetails> CmdLineParser::filterFiles(const std::vector<std::string>& fileFilters,
21632144
const std::list<FileWithDetails>& filesResolved) {
21642145
std::list<FileWithDetails> files;
2165-
#ifdef _WIN32
2166-
constexpr bool caseInsensitive = true;
2167-
#else
2168-
constexpr bool caseInsensitive = false;
2169-
#endif
2146+
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
21702147
std::copy_if(filesResolved.cbegin(), filesResolved.cend(), std::inserter(files, files.end()), [&](const FileWithDetails& entry) {
2171-
return matchglobs(fileFilters, entry.path(), caseInsensitive) || matchglobs(fileFilters, entry.spath(), caseInsensitive);
2148+
return filtermatcher.match(entry.path());
21722149
});
21732150
return files;
21742151
}

cli/filelister.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,7 @@ static std::string addFiles2(std::list<FileWithDetails>&files, const std::string
129129
} else {
130130
// Directory
131131
if (recursive) {
132-
// append a slash if it is a directory since that is what we are doing for mIgnoredPaths directory entries.
133-
// otherwise we would ignore all its contents individually instead as a whole.
134-
if (!ignored.match(fname + '/')) {
132+
if (!ignored.match(fname)) {
135133
std::list<FileWithDetails> filesSorted;
136134

137135
std::string err = addFiles2(filesSorted, fname, extra, recursive, ignored);
@@ -243,9 +241,7 @@ static std::string addFiles2(std::list<FileWithDetails> &files,
243241
#endif
244242
if (path_is_directory) {
245243
if (recursive) {
246-
// append a slash if it is a directory since that is what we are doing for mIgnoredPaths directory entries.
247-
// otherwise we would ignore all its contents individually instead as a whole.
248-
if (!ignored.match(new_path + '/')) {
244+
if (!ignored.match(new_path)) {
249245
std::string err = addFiles2(files, new_path, extra, recursive, ignored, debug);
250246
if (!err.empty()) {
251247
return err;

gui/filelist.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,7 @@ static std::vector<std::string> toStdStringList(const QStringList &stringList)
119119

120120
QStringList FileList::applyExcludeList() const
121121
{
122-
#ifdef _WIN32
123-
const PathMatch pathMatch(toStdStringList(mExcludedPaths), true);
124-
#else
125-
const PathMatch pathMatch(toStdStringList(mExcludedPaths), false);
126-
#endif
122+
const PathMatch pathMatch(toStdStringList(mExcludedPaths), QDir::currentPath().toStdString());
127123

128124
QStringList paths;
129125
for (const QFileInfo& item : mFileList) {

lib/importproject.cpp

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "importproject.h"
2020

2121
#include "path.h"
22+
#include "pathmatch.h"
2223
#include "settings.h"
2324
#include "standards.h"
2425
#include "suppressions.h"
@@ -42,30 +43,11 @@
4243

4344
#include "json.h"
4445

45-
// TODO: align the exclusion logic with PathMatch
46-
// TODO: PathMatch lacks glob support
4746
void ImportProject::ignorePaths(const std::vector<std::string> &ipaths, bool debug)
4847
{
48+
PathMatch matcher(ipaths, Path::getCurrentPath());
4949
for (auto it = fileSettings.cbegin(); it != fileSettings.cend();) {
50-
bool ignore = false;
51-
for (std::string i : ipaths) {
52-
if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
53-
ignore = true;
54-
break;
55-
}
56-
if (isValidGlobPattern(i) && matchglob(i, it->filename())) {
57-
ignore = true;
58-
break;
59-
}
60-
if (!Path::isAbsolute(i)) {
61-
i = mPath + i;
62-
if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
63-
ignore = true;
64-
break;
65-
}
66-
}
67-
}
68-
if (ignore) {
50+
if (matcher.match(it->filename())) {
6951
if (debug)
7052
std::cout << "ignored path: " << it->filename() << std::endl;
7153
it = fileSettings.erase(it);
@@ -858,8 +840,9 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
858840
}
859841

860842
// Project files
843+
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
861844
for (const std::string &cfilename : compileList) {
862-
if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename))
845+
if (!fileFilters.empty() && !filtermatcher.match(cfilename))
863846
continue;
864847

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

923+
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
924+
940925
tinyxml2::XMLDocument doc;
941926
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
942927
if (error != tinyxml2::XML_SUCCESS) {
@@ -957,8 +942,8 @@ ImportProject::SharedItemsProject ImportProject::importVcxitems(const std::strin
957942
std::string file(include);
958943
findAndReplace(file, "$(MSBuildThisFileDirectory)", "./");
959944

960-
// Don't include file if it matches the filter
961-
if (!fileFilters.empty() && !matchglobs(fileFilters, file))
945+
// Skip file if it doesn't match the filter
946+
if (!fileFilters.empty() && !filtermatcher.match(file))
962947
continue;
963948

964949
result.sourceFiles.emplace_back(file);
@@ -1269,7 +1254,20 @@ static std::list<std::string> readXmlStringList(const tinyxml2::XMLElement *node
12691254
continue;
12701255
const char *attr = attribute ? child->Attribute(attribute) : child->GetText();
12711256
if (attr)
1272-
ret.push_back(joinRelativePath(path, attr));
1257+
ret.emplace_back(joinRelativePath(path, attr));
1258+
}
1259+
return ret;
1260+
}
1261+
1262+
static std::list<std::string> readXmlPathMatchList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[])
1263+
{
1264+
std::list<std::string> ret;
1265+
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
1266+
if (strcmp(child->Name(), name) != 0)
1267+
continue;
1268+
const char *attr = attribute ? child->Attribute(attribute) : child->GetText();
1269+
if (attr)
1270+
ret.emplace_back(PathMatch::joinRelativePattern(path, attr));
12731271
}
12741272
return ret;
12751273
}
@@ -1339,13 +1337,13 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings &setti
13391337
else if (strcmp(name, CppcheckXml::PathsElementName) == 0)
13401338
paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib);
13411339
else if (strcmp(name, CppcheckXml::ExcludeElementName) == 0)
1342-
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); // TODO: append instead of overwrite
1340+
guiProject.excludedPaths = readXmlPathMatchList(node, path, CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); // TODO: append instead of overwrite
13431341
else if (strcmp(name, CppcheckXml::FunctionContracts) == 0)
13441342
;
13451343
else if (strcmp(name, CppcheckXml::VariableContractsElementName) == 0)
13461344
;
13471345
else if (strcmp(name, CppcheckXml::IgnoreElementName) == 0)
1348-
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); // TODO: append instead of overwrite
1346+
guiProject.excludedPaths = readXmlPathMatchList(node, path, CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); // TODO: append instead of overwrite
13491347
else if (strcmp(name, CppcheckXml::LibrariesElementName) == 0)
13501348
guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); // TODO: append instead of overwrite
13511349
else if (strcmp(name, CppcheckXml::SuppressionsElementName) == 0) {

lib/path.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,17 @@ std::string Path::getCurrentExecutablePath(const char* fallback)
175175

176176
bool Path::isAbsolute(const std::string& path)
177177
{
178-
const std::string& nativePath = toNativeSeparators(path);
179-
180178
#ifdef _WIN32
181179
if (path.length() < 2)
182180
return false;
183181

182+
if ((path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/'))
183+
return true;
184+
184185
// On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not
185-
return startsWith(nativePath, "\\\\") || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0);
186+
return std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/');
186187
#else
187-
return !nativePath.empty() && nativePath[0] == '/';
188+
return !path.empty() && path[0] == '/';
188189
#endif
189190
}
190191

0 commit comments

Comments
 (0)