Skip to content

Commit 3d43800

Browse files
committed
Add internal check that searches Token::Match() patterns for missing % end charaters
Example: "%type" or "foo %var bar"
1 parent b67cb07 commit 3d43800

3 files changed

Lines changed: 121 additions & 1 deletion

File tree

lib/checkinternal.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,56 @@ void CheckInternal::checkTokenSimpleMatchPatterns()
116116
}
117117
}
118118

119+
void CheckInternal::checkMissingPercentCharacter()
120+
{
121+
set<string> magics;
122+
magics.insert("%any%");
123+
magics.insert("%var%");
124+
magics.insert("%type%");
125+
magics.insert("%num%");
126+
magics.insert("%bool%");
127+
magics.insert("%str%");
128+
magics.insert("%varid%");
129+
magics.insert("%or%");
130+
magics.insert("%oror%");
131+
132+
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
133+
if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
134+
continue;
135+
136+
const std::string funcname = tok->strAt(2);
137+
138+
// Get pattern string
139+
const Token *pattern_tok = tok->tokAt(4)->nextArgument();
140+
if (!pattern_tok || !Token::Match(pattern_tok, "%str%"))
141+
continue;
142+
143+
const string pattern = pattern_tok->strValue();
144+
145+
set<string>::const_iterator magic, magics_end = magics.end();
146+
for (magic = magics.begin(); magic != magics_end; ++magic) {
147+
const string broken_magic = (*magic).substr(0, (*magic).size()-1);
148+
149+
string::size_type pos = 0;
150+
while((pos = pattern.find(broken_magic, pos)) != string::npos) {
151+
// Check if it's the full pattern
152+
if (pattern.find(*magic, pos) != pos) {
153+
// Known whitelist of substrings
154+
if ((broken_magic == "%var" && pattern.find("%varid%", pos) == pos) ||
155+
(broken_magic == "%or" && pattern.find("%oror%", pos) == pos)) {
156+
++pos;
157+
continue;
158+
}
159+
160+
missingPercentCharacterError(tok, pattern, funcname);
161+
}
162+
163+
++pos;
164+
}
165+
}
166+
}
167+
}
168+
119169
void CheckInternal::simplePatternError(const Token* tok, const string& pattern, const std::string &funcname)
120170
{
121171
reportError(tok, Severity::warning, "simplePatternError",
@@ -129,3 +179,10 @@ void CheckInternal::complexPatternError(const Token* tok, const string& pattern,
129179
"Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\""
130180
);
131181
}
182+
183+
void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname)
184+
{
185+
reportError(tok, Severity::error, "missingPercentCharacter",
186+
"Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\""
187+
);
188+
}

lib/checkinternal.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class CheckInternal : public Check {
5151

5252
checkInternal.checkTokenMatchPatterns();
5353
checkInternal.checkTokenSimpleMatchPatterns();
54+
checkInternal.checkMissingPercentCharacter();
5455
}
5556

5657
/** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */
@@ -59,14 +60,19 @@ class CheckInternal : public Check {
5960
/** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */
6061
void checkTokenSimpleMatchPatterns();
6162

63+
/** @brief %Check for missing % end character in Token::Match pattern */
64+
void checkMissingPercentCharacter();
65+
6266
private:
6367
void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname);
6468
void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname);
69+
void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname);
6570

6671
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) {
6772
CheckInternal c(0, settings, errorLogger);
6873
c.simplePatternError(0, "class {", "Match");
6974
c.complexPatternError(0, "%type% ( )", "Match");
75+
c.missingPercentCharacterError(0, "%num", "Match");
7076
}
7177

7278
std::string myName() const {
@@ -76,7 +82,8 @@ class CheckInternal : public Check {
7682
std::string classInfo() const {
7783
return "Check for wrong or unsuitable internal API usage:\n"
7884
"* Found simple pattern inside Token::Match() call: \"class {\"\n"
79-
"* Found complex pattern inside Token::simpleMatch() call: \"%type\"\n";
85+
"* Found complex pattern inside Token::simpleMatch() call: \"%type%\"\n"
86+
"* Missing percent end character in Token::Match pattern: \"%num\"\n";
8087
}
8188
};
8289
/// @}

test/testinternal.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class TestInternal : public TestFixture {
3636
TEST_CASE(complexPatternInTokenSimpleMatch)
3737
TEST_CASE(simplePatternSquareBrackets)
3838
TEST_CASE(simplePatternAlternatives)
39+
TEST_CASE(missingPercentCharacter);
3940
}
4041

4142
void check(const std::string &code) {
@@ -171,6 +172,61 @@ class TestInternal : public TestFixture {
171172
"}");
172173
ASSERT_EQUALS("", errout.str());
173174
}
175+
176+
void missingPercentCharacter() {
177+
check("void f() {\n"
178+
" const Token *tok;\n"
179+
" Token::Match(tok, \"%type%\");\n"
180+
"}");
181+
ASSERT_EQUALS("", errout.str());
182+
183+
check("void f() {\n"
184+
" const Token *tok;\n"
185+
" Token::Match(tok, \"foo %type% bar\");\n"
186+
"}");
187+
ASSERT_EQUALS("", errout.str());
188+
189+
// Missing % at the end of string
190+
check("void f() {\n"
191+
" const Token *tok;\n"
192+
" Token::Match(tok, \"%type\");\n"
193+
"}");
194+
ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"%type\"\n", errout.str());
195+
196+
// Missing % in the middle of a pattern
197+
check("void f() {\n"
198+
" const Token *tok;\n"
199+
" Token::Match(tok, \"foo %type bar\");\n"
200+
"}");
201+
ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo %type bar\"\n", errout.str());
202+
203+
// Bei quiet on single %
204+
check("void f() {\n"
205+
" const Token *tok;\n"
206+
" Token::Match(tok, \"foo % %type% bar\");\n"
207+
"}");
208+
ASSERT_EQUALS("", errout.str());
209+
210+
check("void f() {\n"
211+
" const Token *tok;\n"
212+
" Token::Match(tok, \"foo % %type % bar\");\n"
213+
"}");
214+
ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n", errout.str());
215+
216+
// Find missing % also in 'alternatives' pattern
217+
check("void f() {\n"
218+
" const Token *tok;\n"
219+
" Token::Match(tok, \"foo|%type|bar\");\n"
220+
"}");
221+
ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n", errout.str());
222+
223+
// Make sure we don't take %or% for a broken %oror%
224+
check("void f() {\n"
225+
" const Token *tok;\n"
226+
" Token::Match(tok, \"foo|%oror%|bar\");\n"
227+
"}");
228+
ASSERT_EQUALS("", errout.str());
229+
}
174230
};
175231

176232
REGISTER_TEST(TestInternal)

0 commit comments

Comments
 (0)