Merge branch 'suppress-glob' of https://github.com/ghewgill/cppcheck into ghewgill-suppress-glob
This commit is contained in:
commit
d8da1ac390
153
lib/settings.cpp
153
lib/settings.cpp
|
@ -25,6 +25,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cctype> // std::isdigit, std::isalnum, etc
|
#include <cctype> // std::isdigit, std::isalnum, etc
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
Settings::Settings()
|
Settings::Settings()
|
||||||
{
|
{
|
||||||
|
@ -116,6 +117,137 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
|
||||||
|
{
|
||||||
|
const char *p = pattern.c_str();
|
||||||
|
const char *n = name.c_str();
|
||||||
|
std::stack<std::pair<const char *, const char *> > backtrack;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
bool matching = true;
|
||||||
|
while (*p != '\0' && matching)
|
||||||
|
{
|
||||||
|
switch (*p)
|
||||||
|
{
|
||||||
|
case '*':
|
||||||
|
// Step forward until we match the next character after *
|
||||||
|
while (*n != '\0' && *n != p[1])
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (*n != '\0')
|
||||||
|
{
|
||||||
|
// If this isn't the last possibility, save it for later
|
||||||
|
backtrack.push(std::make_pair(p, n));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
// Any character matches unless we're at the end of the name
|
||||||
|
if (*n != '\0')
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matching = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Non-wildcard characters match literally
|
||||||
|
if (*n == *p)
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matching = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't failed matching and we've reached the end of the name, then success
|
||||||
|
if (matching && *n == '\0')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no other paths to tray, then fail
|
||||||
|
if (backtrack.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore pointers from backtrack stack
|
||||||
|
p = backtrack.top().first;
|
||||||
|
n = backtrack.top().second;
|
||||||
|
backtrack.pop();
|
||||||
|
|
||||||
|
// Advance name pointer by one because the current position didn't work
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Settings::Suppressions::FileMatcher::addFile(const std::string &name, unsigned int line)
|
||||||
|
{
|
||||||
|
if (name.find_first_of("*?") != std::string::npos)
|
||||||
|
{
|
||||||
|
for (std::string::const_iterator i = name.begin(); i != name.end(); ++i)
|
||||||
|
{
|
||||||
|
if (*i == '*')
|
||||||
|
{
|
||||||
|
std::string::const_iterator j = i + 1;
|
||||||
|
if (j != name.end() && (*j == '*' || *j == '?'))
|
||||||
|
{
|
||||||
|
return "Failed to add suppression. Syntax error in glob.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_globs[name].insert(line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_files[name].insert(line);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line)
|
||||||
|
{
|
||||||
|
// Check are all errors of this type filtered out
|
||||||
|
if (_files.find("") != _files.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::set<unsigned int> lineset;
|
||||||
|
|
||||||
|
std::map<std::string, std::set<unsigned int> >::const_iterator f = _files.find(file);
|
||||||
|
if (f != _files.end())
|
||||||
|
{
|
||||||
|
lineset.insert(f->second.begin(), f->second.end());
|
||||||
|
}
|
||||||
|
for (std::map<std::string, std::set<unsigned int> >::iterator g = _globs.begin(); g != _globs.end(); ++g)
|
||||||
|
{
|
||||||
|
if (match(g->first, file))
|
||||||
|
{
|
||||||
|
lineset.insert(g->second.begin(), g->second.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineset.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check should all errors in this file be filtered out
|
||||||
|
if (lineset.find(0U) != lineset.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (lineset.find(line) == lineset.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line)
|
std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line)
|
||||||
{
|
{
|
||||||
// Check that errorId is valid..
|
// Check that errorId is valid..
|
||||||
|
@ -135,10 +267,7 @@ std::string Settings::Suppressions::addSuppression(const std::string &errorId, c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_suppressions[errorId][file].push_back(line);
|
return _suppressions[errorId].addFile(file, line);
|
||||||
_suppressions[errorId][file].sort();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
|
bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
|
||||||
|
@ -146,21 +275,7 @@ bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std:
|
||||||
if (_suppressions.find(errorId) == _suppressions.end())
|
if (_suppressions.find(errorId) == _suppressions.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check are all errors of this type filtered out
|
return _suppressions[errorId].isSuppressed(file, line);
|
||||||
if (_suppressions[errorId].find("") != _suppressions[errorId].end())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (_suppressions[errorId].find(file) == _suppressions[errorId].end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check should all errors in this file be filtered out
|
|
||||||
if (std::find(_suppressions[errorId][file].begin(), _suppressions[errorId][file].end(), 0) != _suppressions[errorId][file].end())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (std::find(_suppressions[errorId][file].begin(), _suppressions[errorId][file].end(), line) == _suppressions[errorId][file].end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Settings::addEnabled(const std::string &str)
|
std::string Settings::addEnabled(const std::string &str)
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
/// @addtogroup Core
|
/// @addtogroup Core
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -135,8 +136,42 @@ public:
|
||||||
class Suppressions
|
class Suppressions
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
class FileMatcher
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/** @brief List of filenames suppressed. */
|
||||||
|
std::map<std::string, std::set<unsigned int> > _files;
|
||||||
|
/** @brief List of globs suppressed. */
|
||||||
|
std::map<std::string, std::set<unsigned int> > _globs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Match a name against a glob pattern.
|
||||||
|
* @param pattern The glob pattern to match.
|
||||||
|
* @param name The filename to match against the glob pattern.
|
||||||
|
* @return match success
|
||||||
|
*/
|
||||||
|
static bool match(const std::string &pattern, const std::string &name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Add a file or glob (and line number).
|
||||||
|
* @param name File name or glob pattern
|
||||||
|
* @param line Line number
|
||||||
|
* @return error message. empty upon success
|
||||||
|
*/
|
||||||
|
std::string addFile(const std::string &name, unsigned int line);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the file name matches a previously added file or glob pattern.
|
||||||
|
* @param name File name to check
|
||||||
|
* @param line Line number
|
||||||
|
* @return true if this filename/line matches
|
||||||
|
*/
|
||||||
|
bool isSuppressed(const std::string &file, unsigned int line);
|
||||||
|
};
|
||||||
|
|
||||||
/** @brief List of error which the user doesn't want to see. */
|
/** @brief List of error which the user doesn't want to see. */
|
||||||
std::map<std::string, std::map<std::string, std::list<unsigned int> > > _suppressions;
|
std::map<std::string, FileMatcher> _suppressions;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Don't show errors listed in the file.
|
* @brief Don't show errors listed in the file.
|
||||||
|
|
|
@ -307,7 +307,8 @@ Directory name is matched to all parts of the path.</para>
|
||||||
<term><option>--suppressions-list=<file></option></term>
|
<term><option>--suppressions-list=<file></option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>Suppress warnings listed in the file. Filename and line are optional. The format of the single line in file is: [error id]:[filename]:[line].
|
<para>Suppress warnings listed in the file. Filename and line are optional. The format of the single line in file is: [error id]:[filename]:[line].
|
||||||
You can use --template or --xml to see the error id.</para>
|
You can use --template or --xml to see the error id.
|
||||||
|
The filename may contain the wildcard characters * or ?.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|
|
@ -385,6 +385,10 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
|
||||||
line flag. Copy and paste the <literal>id</literal> string from the XML
|
line flag. Copy and paste the <literal>id</literal> string from the XML
|
||||||
output.</para>
|
output.</para>
|
||||||
|
|
||||||
|
<para>The <literal>filename</literal> may include the wildcard characters
|
||||||
|
* or ?, which match any sequence of characters or any single character
|
||||||
|
respectively.</para>
|
||||||
|
|
||||||
<para>Here is an example:</para>
|
<para>Here is an example:</para>
|
||||||
|
|
||||||
<programlisting>memleak:file1.cpp
|
<programlisting>memleak:file1.cpp
|
||||||
|
|
|
@ -36,6 +36,7 @@ private:
|
||||||
TEST_CASE(suppressionsBadId1);
|
TEST_CASE(suppressionsBadId1);
|
||||||
TEST_CASE(suppressionsDosFormat); // Ticket #1836
|
TEST_CASE(suppressionsDosFormat); // Ticket #1836
|
||||||
TEST_CASE(suppressionsFileNameWithColon); // Ticket #1919 - filename includes colon
|
TEST_CASE(suppressionsFileNameWithColon); // Ticket #1919 - filename includes colon
|
||||||
|
TEST_CASE(suppressionsGlob);
|
||||||
}
|
}
|
||||||
|
|
||||||
void suppressionsBadId1()
|
void suppressionsBadId1()
|
||||||
|
@ -63,6 +64,41 @@ private:
|
||||||
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 10));
|
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 10));
|
||||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 12));
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void suppressionsGlob()
|
||||||
|
{
|
||||||
|
// Check for syntax errors in glob
|
||||||
|
{
|
||||||
|
Settings::Suppressions suppressions;
|
||||||
|
std::istringstream s("errorid:**.cpp\n");
|
||||||
|
ASSERT_EQUALS("Failed to add suppression. Syntax error in glob.", suppressions.parseFile(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that globbing works
|
||||||
|
{
|
||||||
|
Settings::Suppressions suppressions;
|
||||||
|
std::istringstream s("errorid:x*.cpp\nerrorid:y?.cpp\nerrorid:test.c*");
|
||||||
|
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp.cpp", 1));
|
||||||
|
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "ya.cpp", 1));
|
||||||
|
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "y.cpp", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.c", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.cpp", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that both a filename match and a glob match apply
|
||||||
|
{
|
||||||
|
Settings::Suppressions suppressions;
|
||||||
|
std::istringstream s("errorid:x*.cpp\nerrorid:xyz.cpp:1\nerrorid:a*.cpp:1\nerrorid:abc.cpp:2");
|
||||||
|
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 2));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 1));
|
||||||
|
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestSettings)
|
REGISTER_TEST(TestSettings)
|
||||||
|
|
Loading…
Reference in New Issue