From 6401271ceb85ff93a697b2c09fb4765a560fb7a3 Mon Sep 17 00:00:00 2001 From: Kimmo Varis Date: Mon, 31 Jan 2011 15:25:51 +0200 Subject: [PATCH] Add CLI support for ignoring paths. Add support for giving list of ignored paths from CLI. This way user can define paths one doesn't want to check (like generated code). This first simple implementation only does exact matching, no support for wildcards etc. And matching is always agains dir names. If the filtered dir name is part of the checked filename then the file is ignored. Ticket #1690 (Ability to exclude files and directories from checks) --- cli/cli.pro | 3 + cli/cmdlineparser.cpp | 38 +++++++++ cli/cmdlineparser.h | 9 +++ cli/cppcheckexecutor.cpp | 31 ++++++-- cli/pathmatch.cpp | 45 +++++++++++ cli/pathmatch.h | 63 +++++++++++++++ test/test.pro | 150 ++++++++++++++++++----------------- test/testcmdlineparser.cpp | 61 ++++++++++++++ test/testpathmatch.cpp | 158 +++++++++++++++++++++++++++++++++++++ 9 files changed, 480 insertions(+), 78 deletions(-) create mode 100644 cli/pathmatch.cpp create mode 100644 cli/pathmatch.h create mode 100644 test/testpathmatch.cpp diff --git a/cli/cli.pro b/cli/cli.pro index 85702b9fb..e04e4e158 100644 --- a/cli/cli.pro +++ b/cli/cli.pro @@ -17,12 +17,15 @@ SOURCES += main.cpp \ filelister.cpp \ filelister_unix.cpp \ filelister_win32.cpp \ + pathmatch.cpp \ threadexecutor.cpp + HEADERS += cppcheckexecutor.h \ cmdlineparser.h \ filelister.h \ filelister_unix.h \ filelister_win32.h \ + pathmatch.h \ threadexecutor.h CONFIG(release, debug|release) { diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index c3eaa5764..340ca5ae0 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -327,6 +327,41 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) AddFilesToList(12 + argv[i], _pathnames); } + // Ignored paths + else if (strncmp(argv[i], "-i", 2) == 0) + { + std::string path; + + // "-i path/" + if (strcmp(argv[i], "-i") == 0) + { + ++i; + if (i >= argc) + { + PrintMessage("cppcheck: argument to '-i' is missing"); + return false; + } + path = argv[i]; + } + + // "-Ipath/" + else + { + path = 2 + argv[i]; + } + + if (!path.empty()) + { + path = Path::fromNativeSeparators(path); + + // If path doesn't end with / or \, add it + if (path[path.length()-1] != '/') + path += '/'; + + _ignoredPaths.push_back(path); + } + } + // Report progress else if (strcmp(argv[i], "--report-progress") == 0) { @@ -589,6 +624,9 @@ void CmdLineParser::PrintHelp() " -I [dir] Give include path. Give several -I parameters to give\n" " several paths. First given path is checked first. If\n" " paths are relative to source files, this is not needed\n" + " -i [dir] Give path to ignore. Give several -i parameters to ignore\n" + " several paths. If any part of the checked path matches the\n" + " given dir the path is ignored and not checked.\n" " --inline-suppr Enable inline suppressions. Use them by placing one or\n" " more comments, like: // cppcheck-suppress warningId\n" " on the lines before the warning to suppress.\n" diff --git a/cli/cmdlineparser.h b/cli/cmdlineparser.h index 7a60e24a6..bfc047cb9 100644 --- a/cli/cmdlineparser.h +++ b/cli/cmdlineparser.h @@ -92,6 +92,14 @@ public: return _exitAfterPrint; } + /** + * Return a list of paths user wants to ignore. + */ + std::vector GetIgnoredPaths() const + { + return _ignoredPaths; + } + protected: /** @@ -111,6 +119,7 @@ private: bool _showErrorMessages; bool _exitAfterPrint; std::vector _pathnames; + std::vector _ignoredPaths; }; /// @} diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index fdb886471..de1756d3f 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -25,6 +25,7 @@ #include "cmdlineparser.h" #include "filelister.h" #include "path.h" +#include "pathmatch.h" CppCheckExecutor::CppCheckExecutor() { @@ -87,19 +88,39 @@ bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* c std::vector::const_iterator iter; for (iter = pathnames.begin(); iter != pathnames.end(); ++iter) getFileLister()->recursiveAddFiles(filenames, Path::toNativeSeparators(iter->c_str())); - - for (iter = filenames.begin(); iter != filenames.end(); ++iter) - cppcheck->addFile(*iter); } - if (filenames.empty()) + if (!filenames.empty()) + { + PathMatch matcher(parser.GetIgnoredPaths()); + // Ignore files + std::vector::iterator iter = filenames.end(); + do + { + --iter; + if (matcher.Match(*iter)) + filenames.erase(iter); + } + while (iter != filenames.begin()); + } + else { std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl; return false; } + + if (!filenames.empty()) + { + std::vector::iterator iter; + for (iter = filenames.begin(); iter != filenames.end(); ++iter) + cppcheck->addFile(*iter); + + return true; + } else { - return true; + std::cout << "cppcheck: error: no files to check - all paths ignored." << std::endl; + return false; } } diff --git a/cli/pathmatch.cpp b/cli/pathmatch.cpp new file mode 100644 index 000000000..115b34d5a --- /dev/null +++ b/cli/pathmatch.cpp @@ -0,0 +1,45 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "pathmatch.h" + +PathMatch::PathMatch(const std::vector &masks) + : _masks(masks) +{ +} + +bool PathMatch::Match(const std::string &path) +{ + std::vector::const_iterator iterMask; + for (iterMask = _masks.begin(); iterMask != _masks.end(); ++iterMask) + { + std::string findpath(path); + if (findpath[findpath.length() - 1] != '/') + findpath = RemoveFilename(findpath); + + if (findpath.find(*iterMask) != std::string::npos) + return true; + } + return false; +} + +std::string PathMatch::RemoveFilename(const std::string &path) +{ + const size_t ind = path.find_last_of('/'); + return path.substr(0, ind + 1); +} diff --git a/cli/pathmatch.h b/cli/pathmatch.h new file mode 100644 index 000000000..5e1a0566c --- /dev/null +++ b/cli/pathmatch.h @@ -0,0 +1,63 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PATHMATCH_H +#define PATHMATCH_H + +#include +#include + +/// @addtogroup CLI +/// @{ + +/** + * @brief Simple path matching for ignoring paths in CLI. + */ +class PathMatch +{ +public: + + /** + * The constructor. + * @param masks List of masks. + */ + PathMatch(const std::vector &masks); + + /** + * @brief Match path against list of masks. + * @param path Path to match. + * @return true if any of the masks match the path, false otherwise. + */ + bool Match(const std::string &path); + +protected: + + /** + * @brief Remove filename part from the path. + * @param path Path to edit. + * @return path without filename part. + */ + std::string RemoveFilename(const std::string &path); + +private: + std::vector _masks; +}; + +/// @} + +#endif // PATHMATCH_H diff --git a/test/test.pro b/test/test.pro index 0196222ed..b786428cf 100644 --- a/test/test.pro +++ b/test/test.pro @@ -1,73 +1,77 @@ - -TEMPLATE = app -TARGET = test -DEPENDPATH += . -INCLUDEPATH += . ../cli ../lib -OBJECTS_DIR = temp -CONFIG += warn_on console -CONFIG -= qt app_bundle -win32 { - LIBS += -lshlwapi -} - -BASEPATH = ../externals/tinyxml/ -include(../externals/tinyxml/tinyxml.pri) -BASEPATH = ../lib/ -include(../lib/lib.pri) - -# cli/* -SOURCES += ../cli/cmdlineparser.cpp \ - ../cli/cppcheckexecutor.cpp \ - ../cli/filelister.cpp \ - ../cli/filelister_unix.cpp \ - ../cli/filelister_win32.cpp \ - ../cli/threadexecutor.cpp -HEADERS += ../cli/cmdlineparser.h \ - ../cli/cppcheckexecutor.h \ - ../cli/filelister.h \ - ../cli/filelister_unix.h \ - ../cli/filelister_win32.h \ - ../cli/threadexecutor.h - -# test/* -HEADERS += options.h redirect.h testsuite.h -SOURCES += options.cpp \ - testautovariables.cpp \ - testbufferoverrun.cpp \ - testcharvar.cpp \ - testclass.cpp \ - testcmdlineparser.cpp \ - testconstructors.cpp \ - testcppcheck.cpp \ - testdivision.cpp \ - testerrorlogger.cpp \ - testexceptionsafety.cpp \ - testincompletestatement.cpp \ - testmathlib.cpp \ - testmemleak.cpp \ - testnullpointer.cpp \ - testobsoletefunctions.cpp \ - testoptions.cpp \ - testother.cpp \ - testpath.cpp \ - testpostfixoperator.cpp \ - testpreprocessor.cpp \ - testrunner.cpp \ - testsettings.cpp \ - testsimplifytokens.cpp \ - teststl.cpp \ - testsuite.cpp \ - testthreadexecutor.cpp \ - testtoken.cpp \ - testtokenize.cpp \ - testuninitvar.cpp \ - testunusedfunctions.cpp \ - testunusedprivfunc.cpp \ - testunusedvar.cpp - -# Change Visual Studio compiler (CL) warning level to W4 -contains(QMAKE_CXX, cl) { - QMAKE_CXXFLAGS_WARN_ON -= -W3 - QMAKE_CXXFLAGS_WARN_ON += -W4 - DEFINES += _CRT_SECURE_NO_WARNINGS -} + +TEMPLATE = app +TARGET = test +DEPENDPATH += . +INCLUDEPATH += . ../cli ../lib +OBJECTS_DIR = temp +CONFIG += warn_on console +CONFIG -= qt app_bundle +win32 { + LIBS += -lshlwapi +} + +BASEPATH = ../externals/tinyxml/ +include(../externals/tinyxml/tinyxml.pri) +BASEPATH = ../lib/ +include(../lib/lib.pri) + +# cli/* +SOURCES += ../cli/cmdlineparser.cpp \ + ../cli/cppcheckexecutor.cpp \ + ../cli/filelister.cpp \ + ../cli/filelister_unix.cpp \ + ../cli/filelister_win32.cpp \ + ../cli/pathmatch.cpp \ + ../cli/threadexecutor.cpp \ + testpathmatch.cpp +HEADERS += ../cli/cmdlineparser.h \ + ../cli/cppcheckexecutor.h \ + ../cli/filelister.h \ + ../cli/filelister_unix.h \ + ../cli/filelister_win32.h \ + ../cli/pathmatch.h \ + ../cli/threadexecutor.h + +# test/* +HEADERS += options.h redirect.h testsuite.h +SOURCES += options.cpp \ + testautovariables.cpp \ + testbufferoverrun.cpp \ + testcharvar.cpp \ + testclass.cpp \ + testcmdlineparser.cpp \ + testconstructors.cpp \ + testcppcheck.cpp \ + testdivision.cpp \ + testerrorlogger.cpp \ + testexceptionsafety.cpp \ + testincompletestatement.cpp \ + testmathlib.cpp \ + testmemleak.cpp \ + testnullpointer.cpp \ + testobsoletefunctions.cpp \ + testoptions.cpp \ + testother.cpp \ + testpath.cpp \ + testpathmatch.cpp \ + testpostfixoperator.cpp \ + testpreprocessor.cpp \ + testrunner.cpp \ + testsettings.cpp \ + testsimplifytokens.cpp \ + teststl.cpp \ + testsuite.cpp \ + testthreadexecutor.cpp \ + testtoken.cpp \ + testtokenize.cpp \ + testuninitvar.cpp \ + testunusedfunctions.cpp \ + testunusedprivfunc.cpp \ + testunusedvar.cpp + +# Change Visual Studio compiler (CL) warning level to W4 +contains(QMAKE_CXX, cl) { + QMAKE_CXXFLAGS_WARN_ON -= -W3 + QMAKE_CXXFLAGS_WARN_ON += -W4 + DEFINES += _CRT_SECURE_NO_WARNINGS +} diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 009be82f0..f030ffa1f 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -87,6 +87,11 @@ private: TEST_CASE(errorlist1); TEST_CASE(errorlistverbose1) TEST_CASE(errorlistverbose2) + TEST_CASE(ignorepathsnopath) + TEST_CASE(ignorepaths1) + TEST_CASE(ignorepaths2) + TEST_CASE(ignorepaths3) + TEST_CASE(ignorepaths4) TEST_CASE(unknownParam); } @@ -647,6 +652,62 @@ private: ASSERT(settings._verbose); } + void ignorepathsnopath() + { + REDIRECT; + const char *argv[] = {"cppcheck", "-i"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(!parser.ParseFromArgs(2, argv)); + ASSERT_EQUALS(0, parser.GetIgnoredPaths().size()); + } + + void ignorepaths1() + { + REDIRECT; + const char *argv[] = {"cppcheck", "-isrc", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(3, argv)); + ASSERT_EQUALS(1, parser.GetIgnoredPaths().size()); + ASSERT_EQUALS("src/", parser.GetIgnoredPaths()[0]); + } + + void ignorepaths2() + { + REDIRECT; + const char *argv[] = {"cppcheck", "-i", "src", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(4, argv)); + ASSERT_EQUALS(1, parser.GetIgnoredPaths().size()); + ASSERT_EQUALS("src/", parser.GetIgnoredPaths()[0]); + } + + void ignorepaths3() + { + REDIRECT; + const char *argv[] = {"cppcheck", "-isrc", "-imodule", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(4, argv)); + ASSERT_EQUALS(2, parser.GetIgnoredPaths().size()); + ASSERT_EQUALS("src/", parser.GetIgnoredPaths()[0]); + ASSERT_EQUALS("module/", parser.GetIgnoredPaths()[1]); + } + + void ignorepaths4() + { + REDIRECT; + const char *argv[] = {"cppcheck", "-i", "src", "-i", "module", "file.cpp"}; + Settings settings; + CmdLineParser parser(&settings); + ASSERT(parser.ParseFromArgs(6, argv)); + ASSERT_EQUALS(2, parser.GetIgnoredPaths().size()); + ASSERT_EQUALS("src/", parser.GetIgnoredPaths()[0]); + ASSERT_EQUALS("module/", parser.GetIgnoredPaths()[1]); + } + void unknownParam() { REDIRECT; diff --git a/test/testpathmatch.cpp b/test/testpathmatch.cpp new file mode 100644 index 000000000..7b78dc0bc --- /dev/null +++ b/test/testpathmatch.cpp @@ -0,0 +1,158 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testsuite.h" +#include "pathmatch.h" + +class TestPathMatch : public TestFixture +{ +public: + TestPathMatch() : TestFixture("TestPathMatch") + { } + +private: + + void run() + { + TEST_CASE(emptymaskemptyfile); + TEST_CASE(emptymaskpath1); + TEST_CASE(emptymaskpath2); + TEST_CASE(emptymaskpath3); + TEST_CASE(onemaskemptypath); + TEST_CASE(onemasksamepath); + TEST_CASE(onemasksamepathwithfile); + TEST_CASE(onemasklongerpath1); + TEST_CASE(onemasklongerpath2); + TEST_CASE(onemasklongerpath3); + } + + void emptymaskemptyfile() + { + std::vector masks; + PathMatch match(masks); + ASSERT(!match.Match("")); + } + + void emptymaskpath1() + { + std::vector masks; + PathMatch match(masks); + ASSERT(!match.Match("src/")); + } + + void emptymaskpath2() + { + std::vector masks; + PathMatch match(masks); + ASSERT(!match.Match("../src/")); + } + + void emptymaskpath3() + { + std::vector masks; + PathMatch match(masks); + ASSERT(!match.Match("/home/user/code/src/")); + } + + void onemaskemptypath() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(!match.Match("")); + } + + void onemasksamepath() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(match.Match("src/")); + } + + void onemasksamepathwithfile() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(match.Match("src/file.txt")); + } + + void onemasklongerpath1() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(match.Match("/tmp/src/")); + } + + void onemasklongerpath2() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(match.Match("src/module/")); + } + + void onemasklongerpath3() + { + std::vector masks; + masks.push_back("src/"); + PathMatch match(masks); + ASSERT(match.Match("project/src/module/")); + } + + void twomasklongerpath1() + { + std::vector masks; + masks.push_back("src/"); + masks.push_back("module/"); + PathMatch match(masks); + ASSERT(!match.Match("project/")); + } + + void twomasklongerpath2() + { + std::vector masks; + masks.push_back("src/"); + masks.push_back("module/"); + PathMatch match(masks); + ASSERT(match.Match("project/src/")); + } + + void twomasklongerpath3() + { + std::vector masks; + masks.push_back("src/"); + masks.push_back("module/"); + PathMatch match(masks); + ASSERT(match.Match("project/module/")); + } + + void twomasklongerpath4() + { + std::vector masks; + masks.push_back("src/"); + masks.push_back("module/"); + PathMatch match(masks); + ASSERT(match.Match("project/src/module/")); + } + +}; + +REGISTER_TEST(TestPathMatch)