Skip to content

Commit 7a219b1

Browse files
committed
support wildcard characters * and ? in suppression list
1 parent a9f2879 commit 7a219b1

4 files changed

Lines changed: 125 additions & 10 deletions

File tree

lib/settings.cpp

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,81 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
116116
return "";
117117
}
118118

119+
bool Settings::Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
120+
{
121+
const char *p = pattern.c_str();
122+
const char *n = name.c_str();
123+
std::list<std::pair<const char *, const char *> > backtrack;
124+
125+
for (;;) {
126+
bool matching = true;
127+
while (*p != '\0' && matching) {
128+
switch (*p) {
129+
case '*':
130+
// Step forward until we match the next character after *
131+
while (*n != '\0' && *n != p[1]) {
132+
n++;
133+
}
134+
if (*n != '\0') {
135+
// If this isn't the last possibility, save it for later
136+
backtrack.push_back(std::make_pair(p, n));
137+
}
138+
break;
139+
case '?':
140+
// Any character matches unless we're at the end of the name
141+
if (*n != '\0') {
142+
n++;
143+
} else {
144+
matching = false;
145+
}
146+
break;
147+
default:
148+
// Non-wildcard characters match literally
149+
if (*n == *p) {
150+
n++;
151+
} else {
152+
matching = false;
153+
}
154+
break;
155+
}
156+
p++;
157+
}
158+
159+
// If we haven't failed matching and we've reached the end of the name, then success
160+
if (matching && *n == '\0') {
161+
return true;
162+
}
163+
164+
// If there are no other paths to tray, then fail
165+
if (backtrack.empty()) {
166+
return false;
167+
}
168+
169+
// Restore pointers from backtrack stack
170+
p = backtrack.back().first;
171+
n = backtrack.back().second;
172+
backtrack.pop_back();
173+
174+
// Advance name pointer by one because the current position didn't work
175+
n++;
176+
}
177+
}
178+
119179
std::string Settings::Suppressions::FileMatcher::addFile(const std::string &name, unsigned int line)
120180
{
121-
_files[name].insert(line);
181+
if (name.find_first_of("*?") != std::string::npos) {
182+
for (std::string::const_iterator i = name.begin(); i != name.end(); ++i) {
183+
if (*i == '*') {
184+
std::string::const_iterator j = i + 1;
185+
if (j != name.end() && (*j == '*' || *j == '?')) {
186+
return "Failed to add suppression. Syntax error in glob.";
187+
}
188+
}
189+
}
190+
_globs[name].insert(line);
191+
} else {
192+
_files[name].insert(line);
193+
}
122194
return "";
123195
}
124196

@@ -128,14 +200,26 @@ bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file,
128200
if (_files.find("") != _files.end())
129201
return true;
130202

131-
if (_files.find(file) == _files.end())
203+
std::set<unsigned int> lineset;
204+
205+
std::map<std::string, std::set<unsigned int> >::const_iterator f = _files.find(file);
206+
if (f != _files.end()) {
207+
lineset.insert(f->second.begin(), f->second.end());
208+
}
209+
for (std::map<std::string, std::set<unsigned int> >::iterator g = _globs.begin(); g != _globs.end(); ++g) {
210+
if (match(g->first, file)) {
211+
lineset.insert(g->second.begin(), g->second.end());
212+
}
213+
}
214+
215+
if (lineset.empty())
132216
return false;
133217

134218
// Check should all errors in this file be filtered out
135-
if (std::find(_files[file].begin(), _files[file].end(), 0U) != _files[file].end())
219+
if (lineset.find(0U) != lineset.end())
136220
return true;
137221

138-
if (std::find(_files[file].begin(), _files[file].end(), line) == _files[file].end())
222+
if (lineset.find(line) == lineset.end())
139223
return false;
140224

141225
return true;

man/cppcheck.1.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ Directory name is matched to all parts of the path.</para>
307307
<term><option>--suppressions-list=&lt;file&gt;</option></term>
308308
<listitem>
309309
<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].
310-
You can use --template or --xml to see the error id.</para>
310+
You can use --template or --xml to see the error id.
311+
The filename may contain the wildcard characters * or ?.</para>
311312
</listitem>
312313
</varlistentry>
313314
<varlistentry>

man/manual.docbook

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
385385
line flag. Copy and paste the <literal>id</literal> string from the XML
386386
output.</para>
387387

388+
<para>The <literal>filename</literal> may include the wildcard characters
389+
* or ?, which match any sequence of characters or any single character
390+
respectively.</para>
391+
388392
<para>Here is an example:</para>
389393

390394
<programlisting>memleak:file1.cpp

test/testsettings.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,37 @@ class TestSettings : public TestFixture
6767

6868
void suppressionsGlob()
6969
{
70-
Settings::Suppressions suppressions;
71-
std::istringstream s("error:x*.cpp\n");
72-
ASSERT_EQUALS("", suppressions.parseFile(s));
73-
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
74-
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1));
70+
// Check for syntax errors in glob
71+
{
72+
Settings::Suppressions suppressions;
73+
std::istringstream s("errorid:**.cpp\n");
74+
ASSERT_EQUALS("Failed to add suppression. Syntax error in glob.", suppressions.parseFile(s));
75+
}
76+
77+
// Check that globbing works
78+
{
79+
Settings::Suppressions suppressions;
80+
std::istringstream s("errorid:x*.cpp\nerrorid:y?.cpp\nerrorid:test.c*");
81+
ASSERT_EQUALS("", suppressions.parseFile(s));
82+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
83+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp.cpp", 1));
84+
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1));
85+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "ya.cpp", 1));
86+
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "y.cpp", 1));
87+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.c", 1));
88+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.cpp", 1));
89+
}
90+
91+
// Check that both a filename match and a glob match apply
92+
{
93+
Settings::Suppressions suppressions;
94+
std::istringstream s("errorid:x*.cpp\nerrorid:xyz.cpp:1\nerrorid:a*.cpp:1\nerrorid:abc.cpp:2");
95+
ASSERT_EQUALS("", suppressions.parseFile(s));
96+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
97+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 2));
98+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 1));
99+
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 2));
100+
}
75101
}
76102
};
77103

0 commit comments

Comments
 (0)