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)
This commit is contained in:
Kimmo Varis 2011-01-31 15:25:51 +02:00
parent 8298c07d60
commit 6401271ceb
9 changed files with 480 additions and 78 deletions

View File

@ -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) {

View File

@ -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"

View File

@ -92,6 +92,14 @@ public:
return _exitAfterPrint;
}
/**
* Return a list of paths user wants to ignore.
*/
std::vector<std::string> GetIgnoredPaths() const
{
return _ignoredPaths;
}
protected:
/**
@ -111,6 +119,7 @@ private:
bool _showErrorMessages;
bool _exitAfterPrint;
std::vector<std::string> _pathnames;
std::vector<std::string> _ignoredPaths;
};
/// @}

View File

@ -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<std::string>::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<std::string>::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<std::string>::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;
}
}

45
cli/pathmatch.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "pathmatch.h"
PathMatch::PathMatch(const std::vector<std::string> &masks)
: _masks(masks)
{
}
bool PathMatch::Match(const std::string &path)
{
std::vector<std::string>::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);
}

63
cli/pathmatch.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef PATHMATCH_H
#define PATHMATCH_H
#include <string>
#include <vector>
/// @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<std::string> &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<std::string> _masks;
};
/// @}
#endif // PATHMATCH_H

View File

@ -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
}

View File

@ -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;

158
test/testpathmatch.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<std::string> masks;
PathMatch match(masks);
ASSERT(!match.Match(""));
}
void emptymaskpath1()
{
std::vector<std::string> masks;
PathMatch match(masks);
ASSERT(!match.Match("src/"));
}
void emptymaskpath2()
{
std::vector<std::string> masks;
PathMatch match(masks);
ASSERT(!match.Match("../src/"));
}
void emptymaskpath3()
{
std::vector<std::string> masks;
PathMatch match(masks);
ASSERT(!match.Match("/home/user/code/src/"));
}
void onemaskemptypath()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(!match.Match(""));
}
void onemasksamepath()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(match.Match("src/"));
}
void onemasksamepathwithfile()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(match.Match("src/file.txt"));
}
void onemasklongerpath1()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(match.Match("/tmp/src/"));
}
void onemasklongerpath2()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(match.Match("src/module/"));
}
void onemasklongerpath3()
{
std::vector<std::string> masks;
masks.push_back("src/");
PathMatch match(masks);
ASSERT(match.Match("project/src/module/"));
}
void twomasklongerpath1()
{
std::vector<std::string> masks;
masks.push_back("src/");
masks.push_back("module/");
PathMatch match(masks);
ASSERT(!match.Match("project/"));
}
void twomasklongerpath2()
{
std::vector<std::string> masks;
masks.push_back("src/");
masks.push_back("module/");
PathMatch match(masks);
ASSERT(match.Match("project/src/"));
}
void twomasklongerpath3()
{
std::vector<std::string> masks;
masks.push_back("src/");
masks.push_back("module/");
PathMatch match(masks);
ASSERT(match.Match("project/module/"));
}
void twomasklongerpath4()
{
std::vector<std::string> masks;
masks.push_back("src/");
masks.push_back("module/");
PathMatch match(masks);
ASSERT(match.Match("project/src/module/"));
}
};
REGISTER_TEST(TestPathMatch)