Cppcheck
checkinternal.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2023 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef CHECK_INTERNAL
20 
21 #include "checkinternal.h"
22 
23 #include "astutils.h"
24 #include "symboldatabase.h"
25 #include "token.h"
26 #include "tokenize.h"
27 
28 #include <cstring>
29 #include <set>
30 #include <vector>
31 
32 // Register this check class (by creating a static instance of it).
33 // Disabled in release builds
34 namespace {
35  CheckInternal instance;
36 }
37 
39 {
40  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
41  for (const Scope *scope : symbolDatabase->functionScopes) {
42  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
43  if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
44  continue;
45 
46  const std::string& funcname = tok->strAt(2);
47 
48  // Get pattern string
49  const Token *patternTok = tok->tokAt(4)->nextArgument();
50  if (!patternTok || patternTok->tokType() != Token::eString)
51  continue;
52 
53  const std::string pattern = patternTok->strValue();
54  if (pattern.empty()) {
55  simplePatternError(tok, pattern, funcname);
56  continue;
57  }
58 
59  if (pattern.find("||") != std::string::npos || pattern.find(" | ") != std::string::npos || pattern[0] == '|' || (pattern[pattern.length() - 1] == '|' && pattern[pattern.length() - 2] == ' '))
60  orInComplexPattern(tok, pattern, funcname);
61 
62  // Check for signs of complex patterns
63  if (pattern.find_first_of("[|") != std::string::npos)
64  continue;
65  if (pattern.find("!!") != std::string::npos)
66  continue;
67 
68  bool complex = false;
69  size_t index = pattern.find('%');
70  while (index != std::string::npos) {
71  if (pattern.length() <= index + 2) {
72  complex = true;
73  break;
74  }
75  if (pattern[index + 1] == 'o' && pattern[index + 2] == 'r') // %or% or %oror%
76  index = pattern.find('%', index + 1);
77  else {
78  complex = true;
79  break;
80  }
81  index = pattern.find('%', index + 1);
82  }
83  if (!complex)
84  simplePatternError(tok, pattern, funcname);
85  }
86  }
87 }
88 
90 {
91  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
92  if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) {
93  // in code like
94  // if (tok->previous() && Token::match(tok->previous(), "bla")) {}
95  // the first tok->previous() check is redundant
96  const Token *astOp1 = tok->astOperand1();
97  const Token *astOp2 = getArguments(tok->tokAt(3))[0];
98  if (Token::simpleMatch(astOp1, "&&")) {
99  astOp1 = astOp1->astOperand2();
100  }
101  if (astOp1->expressionString() == astOp2->expressionString()) {
103  }
104  // if (!tok || !Token::match(tok, "foo"))
105  } else if (Token::Match(tok, "%oror% ! Token :: simpleMatch|Match|findsimplematch|findmatch (")) {
106  const Token *negTok = tok->next()->astParent()->astOperand1();
107  if (Token::simpleMatch(negTok, "||")) {
108  negTok = negTok->astOperand2();
109  }
110  // the first tok condition is negated
111  if (Token::simpleMatch(negTok, "!")) {
112  const Token *astOp1 = negTok->astOperand1();
113  const Token *astOp2 = getArguments(tok->tokAt(4))[0];
114 
115  if (astOp1->expressionString() == astOp2->expressionString()) {
117  }
118  }
119  }
120  }
121 }
122 
123 
125 {
126  reportError(tok, Severity::style, "redundantTokCheck",
127  "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null.");
128 }
129 
131 {
132  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
133  for (const Scope* scope : symbolDatabase->functionScopes) {
134  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
135  if (!Token::simpleMatch(tok, "Token :: simpleMatch (") && !Token::simpleMatch(tok, "Token :: findsimplematch ("))
136  continue;
137 
138  const std::string& funcname = tok->strAt(2);
139 
140  // Get pattern string
141  const Token *patternTok = tok->tokAt(4)->nextArgument();
142  if (!patternTok || patternTok->tokType() != Token::eString)
143  continue;
144 
145  const std::string pattern = patternTok->strValue();
146  if (pattern.empty()) {
147  complexPatternError(tok, pattern, funcname);
148  continue;
149  }
150 
151  // Check for [xyz] usage - but exclude standalone square brackets
152  unsigned int char_count = 0;
153  for (const char c : pattern) {
154  if (c == ' ') {
155  char_count = 0;
156  } else if (c == ']') {
157  if (char_count > 0) {
158  complexPatternError(tok, pattern, funcname);
159  continue;
160  }
161  } else {
162  ++char_count;
163  }
164  }
165 
166  // Check | usage: Count characters before the symbol
167  char_count = 0;
168  for (const char c : pattern) {
169  if (c == ' ') {
170  char_count = 0;
171  } else if (c == '|') {
172  if (char_count > 0) {
173  complexPatternError(tok, pattern, funcname);
174  continue;
175  }
176  } else {
177  ++char_count;
178  }
179  }
180 
181  // Check for real errors
182  if (pattern.length() > 1) {
183  for (size_t j = 0; j < pattern.length() - 1; j++) {
184  if (pattern[j] == '%' && pattern[j + 1] != ' ')
185  complexPatternError(tok, pattern, funcname);
186  else if (pattern[j] == '!' && pattern[j + 1] == '!')
187  complexPatternError(tok, pattern, funcname);
188  }
189  }
190  }
191  }
192 }
193 
194 namespace {
195  const std::set<std::string> knownPatterns = {
196  "%any%"
197  , "%assign%"
198  , "%bool%"
199  , "%char%"
200  , "%comp%"
201  , "%num%"
202  , "%op%"
203  , "%cop%"
204  , "%or%"
205  , "%oror%"
206  , "%str%"
207  , "%type%"
208  , "%name%"
209  , "%var%"
210  , "%varid%"
211  };
212 }
213 
215 {
216  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
217  for (const Scope* scope : symbolDatabase->functionScopes) {
218  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
219  if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
220  continue;
221 
222  const std::string& funcname = tok->strAt(2);
223 
224  // Get pattern string
225  const Token *patternTok = tok->tokAt(4)->nextArgument();
226  if (!patternTok || patternTok->tokType() != Token::eString)
227  continue;
228 
229  const std::string pattern = patternTok->strValue();
230 
231  std::set<std::string>::const_iterator knownPattern, knownPatternsEnd = knownPatterns.cend();
232  for (knownPattern = knownPatterns.cbegin(); knownPattern != knownPatternsEnd; ++knownPattern) {
233  const std::string brokenPattern = knownPattern->substr(0, knownPattern->size() - 1);
234 
235  std::string::size_type pos = 0;
236  while ((pos = pattern.find(brokenPattern, pos)) != std::string::npos) {
237  // Check if it's the full pattern
238  if (pattern.find(*knownPattern, pos) != pos) {
239  // Known whitelist of substrings
240  if ((brokenPattern == "%var" && pattern.find("%varid%", pos) == pos) ||
241  (brokenPattern == "%or" && pattern.find("%oror%", pos) == pos)) {
242  ++pos;
243  continue;
244  }
245 
246  missingPercentCharacterError(tok, pattern, funcname);
247  }
248 
249  ++pos;
250  }
251  }
252  }
253  }
254 }
255 
257 {
258  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
259  for (const Scope* scope : symbolDatabase->functionScopes) {
260  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
261  if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
262  continue;
263 
264  // Get pattern string
265  const Token *patternTok = tok->tokAt(4)->nextArgument();
266  if (!patternTok || patternTok->tokType() != Token::eString)
267  continue;
268 
269  const std::string pattern = patternTok->strValue();
270  bool inBrackets = false;
271 
272  for (std::string::size_type j = 0; j < pattern.length() - 1; j++) {
273  if (pattern[j] == '[' && (j == 0 || pattern[j - 1] == ' '))
274  inBrackets = true;
275  else if (pattern[j] == ']')
276  inBrackets = false;
277  else if (pattern[j] == '%' && pattern[j + 1] != ' ' && pattern[j + 1] != '|' && !inBrackets) {
278  const std::string::size_type end = pattern.find('%', j + 1);
279  if (end != std::string::npos) {
280  const std::string s = pattern.substr(j, end - j + 1);
281  if (knownPatterns.find(s) == knownPatterns.end())
282  unknownPatternError(tok, s);
283  }
284  }
285  }
286  }
287  }
288 }
289 
291 {
292  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
293  for (const Scope* scope : symbolDatabase->functionScopes) {
294  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
295  if (tok->str() != ".")
296  continue;
297  tok = tok->next();
298 
299  if (Token::Match(tok, "previous ( ) . next|tokAt|strAt|linkAt (") || Token::Match(tok, "next ( ) . previous|tokAt|strAt|linkAt (") ||
300  (Token::simpleMatch(tok, "tokAt (") && Token::Match(tok->linkAt(1), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) {
301  const std::string& func1 = tok->str();
302  const std::string& func2 = tok->linkAt(1)->strAt(2);
303 
304  if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(1)->strAt(4) != ")")
305  continue;
306 
307  redundantNextPreviousError(tok, func1, func2);
308  } else if (Token::Match(tok, "next|previous ( ) . next|previous ( ) . next|previous|linkAt|strAt|link|str (")) {
309  const std::string& func1 = tok->str();
310  const std::string& func2 = tok->strAt(8);
311 
312  if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(10) != ")")
313  continue;
314 
315  redundantNextPreviousError(tok, func1, func2);
316  }
317  }
318  }
319 }
320 
322 {
323  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
324  for (const Scope* scope : symbolDatabase->functionScopes) {
325  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
326  if (!Token::Match(tok, "Token :: simpleMatch|findsimplematch|Match|findmatch ("))
327  continue;
328 
329  const std::string& funcname = tok->strAt(2);
330 
331  // Get pattern string
332  const Token *patternTok = tok->tokAt(4)->nextArgument();
333  if (!patternTok || patternTok->tokType() != Token::eString)
334  continue;
335 
336  const std::string pattern = patternTok->strValue();
337  if (!pattern.empty() && (pattern[0] == ' ' || *pattern.crbegin() == ' '))
338  extraWhitespaceError(tok, pattern, funcname);
339 
340  // two whitespaces or more
341  if (pattern.find(" ") != std::string::npos)
342  extraWhitespaceError(tok, pattern, funcname);
343  }
344  }
345 }
346 
347 void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
348 {
349  reportError(tok, Severity::error, "multiComparePatternError",
350  "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\""
351  );
352 }
353 
354 void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
355 {
356  reportError(tok, Severity::warning, "simplePatternError",
357  "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\""
358  );
359 }
360 
361 void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
362 {
363  reportError(tok, Severity::error, "complexPatternError",
364  "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\""
365  );
366 }
367 
368 void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname)
369 {
370  reportError(tok, Severity::error, "missingPercentCharacter",
371  "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\""
372  );
373 }
374 
375 void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern)
376 {
377  reportError(tok, Severity::error, "unknownPattern",
378  "Unknown pattern used: \"" + pattern + "\"");
379 }
380 
381 void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2)
382 {
383  reportError(tok, Severity::style, "redundantNextPrevious",
384  "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified.");
385 }
386 
387 void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname)
388 {
389  reportError(tok, Severity::error, "orInComplexPattern",
390  "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".");
391 }
392 
393 void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname)
394 {
395  reportError(tok, Severity::warning, "extraWhitespaceError",
396  "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\""
397  );
398 }
399 
400 #endif // #ifdef CHECK_INTERNAL
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:3078
Check Internal cppcheck API usage
Definition: checkinternal.h:41
void checkRedundantNextPrevious()
Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt()
void checkRedundantTokCheckError(const Token *tok)
void redundantNextPreviousError(const Token *tok, const std::string &func1, const std::string &func2)
void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname)
void checkUnknownPattern()
Check for unknown (invalid) complex patterns like "%typ%"
void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname)
void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname)
void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname)
void checkTokenSimpleMatchPatterns()
Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch
void checkTokenMatchPatterns()
Check if a simple pattern is used inside Token::Match or Token::findmatch
void checkMissingPercentCharacter()
Check for missing % end character in Token::Match pattern
void unknownPatternError(const Token *tok, const std::string &pattern)
void checkExtraWhitespace()
Check if there is whitespace at the beginning or at the end of a pattern
void checkRedundantTokCheck()
Check if there is a redundant check for none-nullness of parameter before Match functions,...
void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname)
void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname)
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Tokenizer *const mTokenizer
Definition: check.h:133
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
std::string expressionString() const
Definition: token.cpp:1681
const Token * tokAt(int index) const
Definition: token.cpp:427
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1502
const Token * linkAt(int index) const
Definition: token.cpp:447
@ eString
Definition: token.h:162
std::string strValue() const
This can be called only for tokens that are strings, else the assert() is called.
Definition: token.cpp:257
Token * next()
Definition: token.h:830
const Token * nextArgument() const
Definition: token.cpp:903
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
const Token * tokens() const
Definition: tokenize.h:592
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
static const std::string emptyString
Definition: config.h:127
@ warning
Warning.
@ style
Style warning.
@ error
Programming error.