diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 3db41617f63..da504dd9548 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -741,6 +741,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a path = Path::fromNativeSeparators(std::move(path)); path = Path::simplifyPath(std::move(path)); + // TODO: this only works when it exists if (Path::isDirectory(path)) { // If directory name doesn't end with / or \, add it if (!endsWith(path, '/')) diff --git a/lib/pathmatch.cpp b/lib/pathmatch.cpp index a72a57fe32b..3122b3c80fc 100644 --- a/lib/pathmatch.cpp +++ b/lib/pathmatch.cpp @@ -24,13 +24,17 @@ #include #include -PathMatch::PathMatch(std::vector excludedPaths, bool caseSensitive) - : mExcludedPaths(std::move(excludedPaths)), mCaseSensitive(caseSensitive) +PathMatch::PathMatch(std::vector paths, bool caseSensitive) + : mPaths(std::move(paths)), mCaseSensitive(caseSensitive) { - if (!mCaseSensitive) - for (std::string& excludedPath : mExcludedPaths) - strTolower(excludedPath); - mWorkingDirectory.push_back(Path::getCurrentPath()); + for (std::string& p : mPaths) + { + p = Path::fromNativeSeparators(p); + if (!mCaseSensitive) + strTolower(p); + } + // TODO: also make lowercase? + mWorkingDirectory.push_back(Path::fromNativeSeparators(Path::getCurrentPath())); } bool PathMatch::match(const std::string &path) const @@ -38,41 +42,42 @@ bool PathMatch::match(const std::string &path) const if (path.empty()) return false; - // TODO: align the exclusion logic with ImportProject::ignorePaths() - for (std::vector::const_iterator i = mExcludedPaths.cbegin(); i != mExcludedPaths.cend(); ++i) { - const std::string excludedPath((!Path::isAbsolute(path) && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); + std::string findpath = Path::fromNativeSeparators(path); + if (!mCaseSensitive) + strTolower(findpath); - std::string findpath = Path::fromNativeSeparators(path); - if (!mCaseSensitive) - strTolower(findpath); + const bool is_absolute = Path::isAbsolute(path); + + // TODO: align the match logic with ImportProject::ignorePaths() + for (std::vector::const_iterator i = mPaths.cbegin(); i != mPaths.cend(); ++i) { + const std::string pathToMatch((!is_absolute && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); // Filtering directory name - if (endsWith(excludedPath,'/')) { + if (endsWith(pathToMatch,'/')) { if (!endsWith(findpath,'/')) findpath = removeFilename(findpath); - if (excludedPath.length() > findpath.length()) + if (pathToMatch.length() > findpath.length()) continue; // Match relative paths starting with mask // -isrc matches src/foo.cpp - if (findpath.compare(0, excludedPath.size(), excludedPath) == 0) + if (findpath.compare(0, pathToMatch.size(), pathToMatch) == 0) return true; // Match only full directory name in middle or end of the path // -isrc matches myproject/src/ but does not match // myproject/srcfiles/ or myproject/mysrc/ - if (findpath.find("/" + excludedPath) != std::string::npos) + if (findpath.find("/" + pathToMatch) != std::string::npos) return true; } // Filtering filename else { - if (excludedPath.length() > findpath.length()) + if (pathToMatch.length() > findpath.length()) continue; // Check if path ends with mask // -ifoo.cpp matches (./)foo.c, src/foo.cpp and proj/src/foo.cpp // -isrc/file.cpp matches src/foo.cpp and proj/src/foo.cpp - if (findpath.compare(findpath.size() - excludedPath.size(), findpath.size(), excludedPath) == 0) + if (findpath.compare(findpath.size() - pathToMatch.size(), findpath.size(), pathToMatch) == 0) return true; - } } return false; diff --git a/lib/pathmatch.h b/lib/pathmatch.h index 7c8df1edc83..8060d6b04e7 100644 --- a/lib/pathmatch.h +++ b/lib/pathmatch.h @@ -35,11 +35,11 @@ class CPPCHECKLIB PathMatch { /** * The constructor. - * @param excludedPaths List of masks. + * @param paths List of masks. * @param caseSensitive Match the case of the characters when * matching paths? */ - explicit PathMatch(std::vector excludedPaths, bool caseSensitive = true); + explicit PathMatch(std::vector paths, bool caseSensitive = true); /** * @brief Match path against list of masks. @@ -58,7 +58,7 @@ class CPPCHECKLIB PathMatch { static std::string removeFilename(const std::string &path); private: - std::vector mExcludedPaths; + std::vector mPaths; bool mCaseSensitive; std::vector mWorkingDirectory; }; diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 7c1057852d5..58939b2b667 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -1762,3 +1762,283 @@ def test_checkers_report(tmpdir): assert exitcode == 0, stdout assert 'Active checkers:' in stderr assert '--checkers-report' not in stderr + + +def test_ignore(tmpdir): + os.mkdir(os.path.join(tmpdir, 'src')) + test_file = os.path.join(tmpdir, 'src', 'test.cpp') + with open(test_file, 'wt'): + pass + + lines_exp = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + args = [ + '-itest.cpp', + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + # make sure it also matches when specified after the file + args = [ + test_file, + '-itest.cpp' + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + args = [ + '-isrc/test.cpp', + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + args = [ + '-isrc\\test.cpp', + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + args = [ + '-isrc/', + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + args = [ + '-isrc\\', + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + args = [ + '-i{}'.format(test_file), + test_file + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + +def __write_gui_project(tmpdir, test_file, ignore): + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + + +""".format(test_file, ignore)) + + return project_file + + +def test_ignore_project(tmpdir): + os.mkdir(os.path.join(tmpdir, 'src')) + test_file = os.path.join(tmpdir, 'src', 'test.cpp') + with open(test_file, 'wt'): + pass + + lines_exp = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + project_file = __write_gui_project(tmpdir, test_file, 'test.cpp') + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + # make sure -i works when specified before project + project_file = __write_gui_project(tmpdir, test_file, 'test2.cpp') + args = [ + '-itest.cpp', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + # make sure -i works when specified after project + project_file = __write_gui_project(tmpdir, test_file, 'test2.cpp') + args = [ + '--project={}'.format(project_file), + '-itest.cpp' + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_gui_project(tmpdir, test_file, 'src/test.cpp') + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_gui_project(tmpdir, test_file, 'src\\test.cpp') + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_gui_project(tmpdir, test_file, 'src/') + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_gui_project(tmpdir, test_file, 'src\\') + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_gui_project(tmpdir, test_file, test_file) + args = [ + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + +def __write_compdb(tmpdir, test_file): + compile_commands = os.path.join(tmpdir, 'compile_commands.json') + j = [ + { + 'directory': os.path.dirname(test_file), + 'file': test_file, + 'command': 'gcc -c {}'.format(test_file) + } + ] + with open(compile_commands, 'wt') as f: + f.write(json.dumps(j)) + return compile_commands + + +# TODO: -i appears to be ignored +@pytest.mark.xfail(strict=True) +def test_ignore_project_2(tmpdir): + os.mkdir(os.path.join(tmpdir, 'src')) + test_file = os.path.join(tmpdir, 'src', 'test.cpp') + with open(test_file, 'wt'): + pass + + lines_exp = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-itest.cpp', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + # make sure it also matches when specified after project + project_file = __write_compdb(tmpdir, test_file) + args = [ + '--project={}'.format(project_file), + '-itest.cpp' + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-isrc/test.cpp', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-isrc\\test.cpp', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-isrc/', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-isrc\\', + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp + + project_file = __write_compdb(tmpdir, test_file) + args = [ + '-i{}'.format(test_file), + '--project={}'.format(project_file) + ] + + exitcode, stdout, _ = cppcheck(args, cwd=tmpdir) + assert exitcode == 1, stdout + assert stdout.splitlines() == lines_exp diff --git a/test/testpathmatch.cpp b/test/testpathmatch.cpp index 41c602d788c..25cfbe5595b 100644 --- a/test/testpathmatch.cpp +++ b/test/testpathmatch.cpp @@ -41,6 +41,7 @@ class TestPathMatch : public TestFixture { TEST_CASE(emptymaskpath3); TEST_CASE(onemaskemptypath); TEST_CASE(onemasksamepath); + TEST_CASE(onemasksamepathdifferentslash); TEST_CASE(onemasksamepathdifferentcase); TEST_CASE(onemasksamepathwithfile); TEST_CASE(onemaskshorterpath); @@ -80,6 +81,7 @@ class TestPathMatch : public TestFixture { void emptymaskpath3() const { ASSERT(!emptyMatcher.match("/home/user/code/src/")); + ASSERT(!emptyMatcher.match("d:/home/user/code/src/")); } // Test PathMatch containing "src/" @@ -91,6 +93,11 @@ class TestPathMatch : public TestFixture { ASSERT(srcMatcher.match("src/")); } + void onemasksamepathdifferentslash() const { + const PathMatch srcMatcher2{std::vector(1, "src\\")}; + ASSERT(srcMatcher2.match("src/")); + } + void onemasksamepathdifferentcase() const { std::vector masks(1, "sRc/"); PathMatch match(std::move(masks), false); @@ -128,6 +135,7 @@ class TestPathMatch : public TestFixture { void onemasklongerpath1() const { ASSERT(srcMatcher.match("/tmp/src/")); + ASSERT(srcMatcher.match("d:/tmp/src/")); } void onemasklongerpath2() const {