/src/cppcheck/oss-fuzz/build/checkother.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include "matchcompiler.h" |
2 | | #include <string> |
3 | | #include <cstring> |
4 | | #include "errorlogger.h" |
5 | | #include "token.h" |
6 | | // pattern: %var%|EOF %comp%|= |
7 | 21.5k | static inline bool match1(const Token* tok) { |
8 | 21.5k | if (!tok || !((tok->varId() != 0) || (tok->str() == MatchCompiler::makeConstString("EOF")))) |
9 | 17.5k | return false; |
10 | 4.05k | tok = tok->next(); |
11 | 4.05k | if (!tok || !(tok->isComparisonOp() || ((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("=")))) |
12 | 3.16k | return false; |
13 | 889 | return true; |
14 | 4.05k | } |
15 | | // pattern: %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc ( |
16 | 889 | static inline bool match2(const Token* tok) { |
17 | 889 | if (!tok || !(tok->varId() != 0)) |
18 | 0 | return false; |
19 | 889 | tok = tok->next(); |
20 | 889 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
21 | 629 | return false; |
22 | 260 | tok = tok->next(); |
23 | 260 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("fclose")) || (tok->str() == MatchCompiler::makeConstString("fflush")) || (tok->str() == MatchCompiler::makeConstString("fputc")) || (tok->str() == MatchCompiler::makeConstString("fputs")) || (tok->str() == MatchCompiler::makeConstString("fscanf")) || (tok->str() == MatchCompiler::makeConstString("getchar")) || (tok->str() == MatchCompiler::makeConstString("getc")) || (tok->str() == MatchCompiler::makeConstString("fgetc")) || (tok->str() == MatchCompiler::makeConstString("putchar")) || (tok->str() == MatchCompiler::makeConstString("putc")) || (tok->str() == MatchCompiler::makeConstString("puts")) || (tok->str() == MatchCompiler::makeConstString("scanf")) || (tok->str() == MatchCompiler::makeConstString("sscanf")) || (tok->str() == MatchCompiler::makeConstString("ungetc")))) |
24 | 260 | return false; |
25 | 0 | tok = tok->next(); |
26 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
27 | 0 | return false; |
28 | 0 | return true; |
29 | 0 | } |
30 | | // pattern: EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc ( |
31 | 889 | static inline bool match3(const Token* tok) { |
32 | 889 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("EOF"))) |
33 | 889 | return false; |
34 | 0 | tok = tok->next(); |
35 | 0 | if (!tok || !tok->isComparisonOp()) |
36 | 0 | return false; |
37 | 0 | tok = tok->next(); |
38 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
39 | 0 | return false; |
40 | 0 | tok = tok->next(); |
41 | 0 | if (!tok || !(tok->varId() != 0)) |
42 | 0 | return false; |
43 | 0 | tok = tok->next(); |
44 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
45 | 0 | return false; |
46 | 0 | tok = tok->next(); |
47 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("fclose")) || (tok->str() == MatchCompiler::makeConstString("fflush")) || (tok->str() == MatchCompiler::makeConstString("fputc")) || (tok->str() == MatchCompiler::makeConstString("fputs")) || (tok->str() == MatchCompiler::makeConstString("fscanf")) || (tok->str() == MatchCompiler::makeConstString("getchar")) || (tok->str() == MatchCompiler::makeConstString("getc")) || (tok->str() == MatchCompiler::makeConstString("fgetc")) || (tok->str() == MatchCompiler::makeConstString("putchar")) || (tok->str() == MatchCompiler::makeConstString("putc")) || (tok->str() == MatchCompiler::makeConstString("puts")) || (tok->str() == MatchCompiler::makeConstString("scanf")) || (tok->str() == MatchCompiler::makeConstString("sscanf")) || (tok->str() == MatchCompiler::makeConstString("ungetc")))) |
48 | 0 | return false; |
49 | 0 | tok = tok->next(); |
50 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
51 | 0 | return false; |
52 | 0 | return true; |
53 | 0 | } |
54 | | // pattern: EOF %comp% ( %var% = std :: cin . get ( |
55 | 889 | static inline bool match4(const Token* tok) { |
56 | 889 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("EOF"))) |
57 | 889 | return false; |
58 | 0 | tok = tok->next(); |
59 | 0 | if (!tok || !tok->isComparisonOp()) |
60 | 0 | return false; |
61 | 0 | tok = tok->next(); |
62 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
63 | 0 | return false; |
64 | 0 | tok = tok->next(); |
65 | 0 | if (!tok || !(tok->varId() != 0)) |
66 | 0 | return false; |
67 | 0 | tok = tok->next(); |
68 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
69 | 0 | return false; |
70 | 0 | tok = tok->next(); |
71 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("std"))) |
72 | 0 | return false; |
73 | 0 | tok = tok->next(); |
74 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("::"))) |
75 | 0 | return false; |
76 | 0 | tok = tok->next(); |
77 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("cin"))) |
78 | 0 | return false; |
79 | 0 | tok = tok->next(); |
80 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
81 | 0 | return false; |
82 | 0 | tok = tok->next(); |
83 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("get"))) |
84 | 0 | return false; |
85 | 0 | tok = tok->next(); |
86 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
87 | 0 | return false; |
88 | 0 | return true; |
89 | 0 | } |
90 | | // pattern: EOF %comp% ( %var% = cin . get ( |
91 | 889 | static inline bool match5(const Token* tok) { |
92 | 889 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("EOF"))) |
93 | 889 | return false; |
94 | 0 | tok = tok->next(); |
95 | 0 | if (!tok || !tok->isComparisonOp()) |
96 | 0 | return false; |
97 | 0 | tok = tok->next(); |
98 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
99 | 0 | return false; |
100 | 0 | tok = tok->next(); |
101 | 0 | if (!tok || !(tok->varId() != 0)) |
102 | 0 | return false; |
103 | 0 | tok = tok->next(); |
104 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
105 | 0 | return false; |
106 | 0 | tok = tok->next(); |
107 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("cin"))) |
108 | 0 | return false; |
109 | 0 | tok = tok->next(); |
110 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
111 | 0 | return false; |
112 | 0 | tok = tok->next(); |
113 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("get"))) |
114 | 0 | return false; |
115 | 0 | tok = tok->next(); |
116 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
117 | 0 | return false; |
118 | 0 | return true; |
119 | 0 | } |
120 | | // pattern: %var% = std :: cin . get ( |
121 | 889 | static inline bool match6(const Token* tok) { |
122 | 889 | if (!tok || !(tok->varId() != 0)) |
123 | 0 | return false; |
124 | 889 | tok = tok->next(); |
125 | 889 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
126 | 629 | return false; |
127 | 260 | tok = tok->next(); |
128 | 260 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("std"))) |
129 | 260 | return false; |
130 | 0 | tok = tok->next(); |
131 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("::"))) |
132 | 0 | return false; |
133 | 0 | tok = tok->next(); |
134 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("cin"))) |
135 | 0 | return false; |
136 | 0 | tok = tok->next(); |
137 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
138 | 0 | return false; |
139 | 0 | tok = tok->next(); |
140 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("get"))) |
141 | 0 | return false; |
142 | 0 | tok = tok->next(); |
143 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
144 | 0 | return false; |
145 | 0 | return true; |
146 | 0 | } |
147 | | // pattern: %var% = cin . get ( |
148 | 889 | static inline bool match7(const Token* tok) { |
149 | 889 | if (!tok || !(tok->varId() != 0)) |
150 | 0 | return false; |
151 | 889 | tok = tok->next(); |
152 | 889 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
153 | 629 | return false; |
154 | 260 | tok = tok->next(); |
155 | 260 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("cin"))) |
156 | 260 | return false; |
157 | 0 | tok = tok->next(); |
158 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
159 | 0 | return false; |
160 | 0 | tok = tok->next(); |
161 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("get"))) |
162 | 0 | return false; |
163 | 0 | tok = tok->next(); |
164 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
165 | 0 | return false; |
166 | 0 | return true; |
167 | 0 | } |
168 | | // pattern: %var% %comp% EOF |
169 | 889 | static inline bool match8(const Token* tok) { |
170 | 889 | if (!tok || !(tok->varId() != 0)) |
171 | 0 | return false; |
172 | 889 | tok = tok->next(); |
173 | 889 | if (!tok || !tok->isComparisonOp()) |
174 | 260 | return false; |
175 | 629 | tok = tok->next(); |
176 | 629 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("EOF"))) |
177 | 629 | return false; |
178 | 0 | return true; |
179 | 629 | } |
180 | | // pattern: EOF %comp% %var% |
181 | 889 | static inline bool match9(const Token* tok) { |
182 | 889 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("EOF"))) |
183 | 889 | return false; |
184 | 0 | tok = tok->next(); |
185 | 0 | if (!tok || !tok->isComparisonOp()) |
186 | 0 | return false; |
187 | 0 | tok = tok->next(); |
188 | 0 | if (!tok || !(tok->varId() != 0)) |
189 | 0 | return false; |
190 | 0 | return true; |
191 | 0 | } |
192 | | // pattern: %or%|&|%|*|/ |
193 | 0 | static inline bool match10(const Token* tok) { |
194 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp && tok->str() == MatchCompiler::makeConstString("|") ) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("%")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("/")))) |
195 | 0 | return false; |
196 | 0 | return true; |
197 | 0 | } |
198 | | // pattern: %char% |
199 | 0 | static inline bool match11(const Token* tok) { |
200 | 0 | if (!tok || !(tok->tokType() == Token::eChar)) |
201 | 0 | return false; |
202 | 0 | return true; |
203 | 0 | } |
204 | | // pattern: * %name% |
205 | 6.52k | static inline bool match12(const Token* tok) { |
206 | 6.52k | if (!tok || !((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*"))) |
207 | 6.41k | return false; |
208 | 115 | tok = tok->next(); |
209 | 115 | if (!tok || !tok->isName()) |
210 | 25 | return false; |
211 | 90 | return true; |
212 | 115 | } |
213 | | // pattern: [{};] |
214 | 18 | static inline bool match13(const Token* tok) { |
215 | 18 | if (!tok || tok->str().size() != 1U || !strchr("{};", tok->str()[0])) |
216 | 18 | return false; |
217 | 0 | return true; |
218 | 18 | } |
219 | | // pattern: ++|-- [;,] |
220 | 0 | static inline bool match14(const Token* tok) { |
221 | 0 | if (!tok || !(((tok->tokType() == Token::eIncDecOp) && tok->str() == MatchCompiler::makeConstString("++")) || ((tok->tokType() == Token::eIncDecOp) && tok->str() == MatchCompiler::makeConstString("--")))) |
222 | 0 | return false; |
223 | 0 | tok = tok->next(); |
224 | 0 | if (!tok || tok->str().size() != 1U || !strchr(";,", tok->str()[0])) |
225 | 0 | return false; |
226 | 0 | return true; |
227 | 0 | } |
228 | | // pattern: { ; } { |
229 | 931 | static inline bool match15(const Token* tok) { |
230 | 931 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
231 | 0 | return false; |
232 | 931 | tok = tok->next(); |
233 | 931 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
234 | 931 | return false; |
235 | 0 | tok = tok->next(); |
236 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
237 | 0 | return false; |
238 | 0 | tok = tok->next(); |
239 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
240 | 0 | return false; |
241 | 0 | return true; |
242 | 0 | } |
243 | | // pattern: const|volatile|class|struct|union|%type%|:: |
244 | 1.22k | static inline bool match16(const Token* tok) { |
245 | 1.22k | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("volatile")) || (tok->str() == MatchCompiler::makeConstString("class")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("struct")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("union")) || (tok->isName() && tok->varId() == 0U) || (tok->str() == MatchCompiler::makeConstString("::")))) |
246 | 990 | return false; |
247 | 237 | return true; |
248 | 1.22k | } |
249 | | // pattern: < |
250 | 237 | static inline bool match17(const Token* tok) { |
251 | 237 | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<"))) |
252 | 207 | return false; |
253 | 30 | return true; |
254 | 237 | } |
255 | | // pattern: *|const|& |
256 | 251 | static inline bool match18(const Token* tok) { |
257 | 251 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")))) |
258 | 237 | return false; |
259 | 14 | return true; |
260 | 251 | } |
261 | | // pattern: ) (| %name%|%num%|%bool%|%char%|%str%|& |
262 | 14 | static inline bool match19(const Token* tok) { |
263 | 14 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
264 | 14 | return false; |
265 | 0 | tok = tok->next(); |
266 | 0 | if (tok && (((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")))) |
267 | 0 | tok = tok->next(); |
268 | 0 | if (!tok || !(tok->isName() || tok->isNumber() || tok->isBoolean() || (tok->tokType() == Token::eChar) || (tok->tokType() == Token::eString) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")))) |
269 | 0 | return false; |
270 | 0 | return true; |
271 | 0 | } |
272 | | // pattern: %type% |
273 | 0 | static inline bool match20(const Token* tok) { |
274 | 0 | if (!tok || !(tok->isName() && tok->varId() == 0U)) |
275 | 0 | return false; |
276 | 0 | return true; |
277 | 0 | } |
278 | | // pattern: const|volatile|class|struct|union |
279 | 0 | static inline bool match21(const Token* tok) { |
280 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("volatile")) || (tok->str() == MatchCompiler::makeConstString("class")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("struct")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("union")))) |
281 | 0 | return false; |
282 | 0 | return true; |
283 | 0 | } |
284 | | // pattern: ( const|volatile| const|volatile| %type% %type%| const| * ) |
285 | 21.5k | static inline bool match22(const Token* tok) { |
286 | 21.5k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
287 | 20.5k | return false; |
288 | 990 | tok = tok->next(); |
289 | 990 | if (tok && (((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("volatile")))) |
290 | 0 | tok = tok->next(); |
291 | 990 | if (tok && (((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("volatile")))) |
292 | 0 | tok = tok->next(); |
293 | 990 | if (!tok || !(tok->isName() && tok->varId() == 0U)) |
294 | 753 | return false; |
295 | 237 | tok = tok->next(); |
296 | 237 | if (tok && ((tok->isName() && tok->varId() == 0U))) |
297 | 0 | tok = tok->next(); |
298 | 237 | if (tok && (((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")))) |
299 | 0 | tok = tok->next(); |
300 | 237 | if (!tok || !((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*"))) |
301 | 233 | return false; |
302 | 4 | tok = tok->next(); |
303 | 4 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
304 | 4 | return false; |
305 | 0 | return true; |
306 | 4 | } |
307 | | // pattern: reinterpret_cast < |
308 | 21.5k | static inline bool match23(const Token* tok) { |
309 | 21.5k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("reinterpret_cast"))) |
310 | 21.5k | return false; |
311 | 0 | tok = tok->next(); |
312 | 0 | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<"))) |
313 | 0 | return false; |
314 | 0 | return true; |
315 | 0 | } |
316 | | // pattern: ] ( |
317 | 21.5k | static inline bool match24(const Token* tok) { |
318 | 21.5k | if (!tok || !((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("]"))) |
319 | 21.5k | return false; |
320 | 0 | tok = tok->next(); |
321 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
322 | 0 | return false; |
323 | 0 | return true; |
324 | 0 | } |
325 | | // pattern: try { |
326 | 21.5k | static inline bool match25(const Token* tok) { |
327 | 21.5k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("try"))) |
328 | 21.5k | return false; |
329 | 0 | tok = tok->next(); |
330 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
331 | 0 | return false; |
332 | 0 | return true; |
333 | 0 | } |
334 | | // pattern: ; %var% = |
335 | 1.99k | static inline bool match26(const Token* tok) { |
336 | 1.99k | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
337 | 688 | return false; |
338 | 1.31k | tok = tok->next(); |
339 | 1.31k | if (!tok || !(tok->varId() != 0)) |
340 | 1.29k | return false; |
341 | 14 | tok = tok->next(); |
342 | 14 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
343 | 0 | return false; |
344 | 14 | return true; |
345 | 14 | } |
346 | | // pattern: { 0 } |
347 | 0 | static inline bool match27(const Token* tok) { |
348 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
349 | 0 | return false; |
350 | 0 | tok = tok->next(); |
351 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("0"))) |
352 | 0 | return false; |
353 | 0 | tok = tok->next(); |
354 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
355 | 0 | return false; |
356 | 0 | return true; |
357 | 0 | } |
358 | | // pattern: %str%|%num%|%name% |
359 | 0 | static inline bool match28(const Token* tok) { |
360 | 0 | if (!tok || !((tok->tokType() == Token::eString) || tok->isNumber() || tok->isName())) |
361 | 0 | return false; |
362 | 0 | return true; |
363 | 0 | } |
364 | | // pattern: :: %name% |
365 | 0 | static inline bool match29(const Token* tok) { |
366 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("::"))) |
367 | 0 | return false; |
368 | 0 | tok = tok->next(); |
369 | 0 | if (!tok || !tok->isName()) |
370 | 0 | return false; |
371 | 0 | return true; |
372 | 0 | } |
373 | | // pattern: . |
374 | 7.42k | static inline bool match30(const Token* tok) { |
375 | 7.42k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
376 | 7.42k | return false; |
377 | 0 | return true; |
378 | 7.42k | } |
379 | | // pattern: = |
380 | 2.16k | static inline bool match31(const Token* tok) { |
381 | 2.16k | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
382 | 2.11k | return false; |
383 | 53 | return true; |
384 | 2.16k | } |
385 | | // pattern: %name% ( |
386 | 53.3k | static inline bool match32(const Token* tok) { |
387 | 53.3k | if (!tok || !tok->isName()) |
388 | 30.4k | return false; |
389 | 22.9k | tok = tok->next(); |
390 | 22.9k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
391 | 20.8k | return false; |
392 | 2.09k | return true; |
393 | 22.9k | } |
394 | | // pattern: break|continue|return|exit|goto|throw |
395 | 0 | static inline bool match33(const Token* tok) { |
396 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("break")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("continue")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return")) || (tok->str() == MatchCompiler::makeConstString("exit")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("goto")) || (tok->str() == MatchCompiler::makeConstString("throw")))) |
397 | 0 | return false; |
398 | 0 | return true; |
399 | 0 | } |
400 | | // pattern: )|else { |
401 | 0 | static inline bool match34(const Token* tok) { |
402 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("else")))) |
403 | 0 | return false; |
404 | 0 | tok = tok->next(); |
405 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
406 | 0 | return false; |
407 | 0 | return true; |
408 | 0 | } |
409 | | // pattern: ;|{|}|: %var% = %any% ; |
410 | 0 | static inline bool match35(const Token* tok) { |
411 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString(";")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":")))) |
412 | 0 | return false; |
413 | 0 | tok = tok->next(); |
414 | 0 | if (!tok || !(tok->varId() != 0)) |
415 | 0 | return false; |
416 | 0 | tok = tok->next(); |
417 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
418 | 0 | return false; |
419 | 0 | tok = tok->next(); |
420 | 0 | if (!tok || false) |
421 | 0 | return false; |
422 | 0 | tok = tok->next(); |
423 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
424 | 0 | return false; |
425 | 0 | return true; |
426 | 0 | } |
427 | | // pattern: ;|{|}|: %var% %assign% %num% ; |
428 | 0 | static inline bool match36(const Token* tok) { |
429 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString(";")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":")))) |
430 | 0 | return false; |
431 | 0 | tok = tok->next(); |
432 | 0 | if (!tok || !(tok->varId() != 0)) |
433 | 0 | return false; |
434 | 0 | tok = tok->next(); |
435 | 0 | if (!tok || !tok->isAssignmentOp()) |
436 | 0 | return false; |
437 | 0 | tok = tok->next(); |
438 | 0 | if (!tok || !tok->isNumber()) |
439 | 0 | return false; |
440 | 0 | tok = tok->next(); |
441 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
442 | 0 | return false; |
443 | 0 | return true; |
444 | 0 | } |
445 | | // pattern: %num% |
446 | 0 | static inline bool match37(const Token* tok) { |
447 | 0 | if (!tok || !tok->isNumber()) |
448 | 0 | return false; |
449 | 0 | return true; |
450 | 0 | } |
451 | | // pattern: ;|{|}|: %var% = %name% %or%|& %num% ; |
452 | 0 | static inline bool match38(const Token* tok) { |
453 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString(";")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":")))) |
454 | 0 | return false; |
455 | 0 | tok = tok->next(); |
456 | 0 | if (!tok || !(tok->varId() != 0)) |
457 | 0 | return false; |
458 | 0 | tok = tok->next(); |
459 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
460 | 0 | return false; |
461 | 0 | tok = tok->next(); |
462 | 0 | if (!tok || !tok->isName()) |
463 | 0 | return false; |
464 | 0 | tok = tok->next(); |
465 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp && tok->str() == MatchCompiler::makeConstString("|") ) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")))) |
466 | 0 | return false; |
467 | 0 | tok = tok->next(); |
468 | 0 | if (!tok || !tok->isNumber()) |
469 | 0 | return false; |
470 | 0 | tok = tok->next(); |
471 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
472 | 0 | return false; |
473 | 0 | return true; |
474 | 0 | } |
475 | | // pattern: [;}{] |
476 | 0 | static inline bool match39(const Token* tok) { |
477 | 0 | if (!tok || tok->str().size() != 1U || !strchr(";}{", tok->str()[0])) |
478 | 0 | return false; |
479 | 0 | return true; |
480 | 0 | } |
481 | | // pattern: &&|%oror% |
482 | 3.18k | static inline bool match40(const Token* tok) { |
483 | 3.18k | if (!tok || !(((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || (tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")))) |
484 | 3.18k | return false; |
485 | 0 | return true; |
486 | 3.18k | } |
487 | | // pattern: ; |
488 | 240 | template<class T> static inline T * findmatch41(T * start_tok) { |
489 | 2.37k | for (; start_tok; start_tok = start_tok->next()) { |
490 | | |
491 | 2.37k | T * tok = start_tok; |
492 | 2.37k | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
493 | 2.13k | continue; |
494 | 240 | return start_tok; |
495 | 2.37k | } |
496 | 0 | return nullptr; |
497 | 240 | } |
498 | | // pattern: (|[|< |
499 | 3.69k | static inline bool match42(const Token* tok) { |
500 | 3.69k | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")) || ((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("[")) || ((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<")))) |
501 | 2.96k | return false; |
502 | 727 | return true; |
503 | 3.69k | } |
504 | | // pattern: break|continue ; |
505 | 12.8k | static inline bool match43(const Token* tok) { |
506 | 12.8k | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("break")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("continue")))) |
507 | 12.8k | return false; |
508 | 0 | tok = tok->next(); |
509 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
510 | 0 | return false; |
511 | 0 | return true; |
512 | 0 | } |
513 | | // pattern: [;{}:] return|throw |
514 | 12.8k | static inline bool match44(const Token* tok) { |
515 | 12.8k | if (!tok || tok->str().size() != 1U || !strchr(";{}:", tok->str()[0])) |
516 | 7.86k | return false; |
517 | 4.96k | tok = tok->next(); |
518 | 4.96k | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return")) || (tok->str() == MatchCompiler::makeConstString("throw")))) |
519 | 3.77k | return false; |
520 | 1.19k | return true; |
521 | 4.96k | } |
522 | | // pattern: ? |
523 | 4.37k | static inline bool match45(const Token* tok) { |
524 | 4.37k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?"))) |
525 | 4.37k | return false; |
526 | 0 | return true; |
527 | 4.37k | } |
528 | | // pattern: goto %any% ; |
529 | 11.6k | static inline bool match46(const Token* tok) { |
530 | 11.6k | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("goto"))) |
531 | 11.6k | return false; |
532 | 0 | tok = tok->next(); |
533 | 0 | if (!tok || false) |
534 | 0 | return false; |
535 | 0 | tok = tok->next(); |
536 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
537 | 0 | return false; |
538 | 0 | return true; |
539 | 0 | } |
540 | | // pattern: ?|: |
541 | 0 | static inline bool match47(const Token* tok) { |
542 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":")))) |
543 | 0 | return false; |
544 | 0 | return true; |
545 | 0 | } |
546 | | // pattern: return |
547 | 0 | static inline bool match48(const Token* tok) { |
548 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return"))) |
549 | 0 | return false; |
550 | 0 | return true; |
551 | 0 | } |
552 | | // pattern: } |
553 | 13.5k | static inline bool match49(const Token* tok) { |
554 | 13.5k | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
555 | 12.3k | return false; |
556 | 1.19k | return true; |
557 | 13.5k | } |
558 | | // pattern: { |
559 | 0 | static inline bool match50(const Token* tok) { |
560 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
561 | 0 | return false; |
562 | 0 | return true; |
563 | 0 | } |
564 | | // pattern: ; |
565 | 50.0k | static inline bool match51(const Token* tok) { |
566 | 50.0k | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
567 | 46.8k | return false; |
568 | 3.18k | return true; |
569 | 50.0k | } |
570 | | // pattern: continue|goto|throw|return |
571 | 1.19k | static inline bool match52(const Token* tok) { |
572 | 1.19k | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("continue")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("goto")) || (tok->str() == MatchCompiler::makeConstString("throw")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return")))) |
573 | 1.19k | return false; |
574 | 0 | return true; |
575 | 1.19k | } |
576 | | // pattern: [}:] |
577 | 0 | template<class T> static inline T * findmatch53(T * start_tok) { |
578 | 0 | for (; start_tok; start_tok = start_tok->next()) { |
579 | |
|
580 | 0 | T * tok = start_tok; |
581 | 0 | if (!tok || tok->str().size() != 1U || !strchr("}:", tok->str()[0])) |
582 | 0 | continue; |
583 | 0 | return start_tok; |
584 | 0 | } |
585 | 0 | return nullptr; |
586 | 0 | } |
587 | | // pattern: return|}|case|default |
588 | 1.19k | static inline bool match54(const Token* tok) { |
589 | 1.19k | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("case")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("default")))) |
590 | 0 | return false; |
591 | 1.19k | return true; |
592 | 1.19k | } |
593 | | // pattern: while|do|for |
594 | 0 | static inline bool match55(const Token* tok) { |
595 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("while")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("do")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("for")))) |
596 | 0 | return false; |
597 | 0 | return true; |
598 | 0 | } |
599 | | // pattern: { |
600 | 0 | template<class T> static inline T * findmatch56(T * start_tok) { |
601 | 0 | for (; start_tok; start_tok = start_tok->next()) { |
602 | |
|
603 | 0 | T * tok = start_tok; |
604 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
605 | 0 | continue; |
606 | 0 | return start_tok; |
607 | 0 | } |
608 | 0 | return nullptr; |
609 | 0 | } |
610 | | // pattern: [;{}] %any% : |
611 | 0 | static inline bool match57(const Token* tok) { |
612 | 0 | if (!tok || tok->str().size() != 1U || !strchr(";{}", tok->str()[0])) |
613 | 0 | return false; |
614 | 0 | tok = tok->next(); |
615 | 0 | if (!tok || false) |
616 | 0 | return false; |
617 | 0 | tok = tok->next(); |
618 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":"))) |
619 | 0 | return false; |
620 | 0 | return true; |
621 | 0 | } |
622 | | // pattern: ( void ) %name% ; |
623 | 0 | static inline bool match58(const Token* tok) { |
624 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
625 | 0 | return false; |
626 | 0 | tok = tok->next(); |
627 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword || tok->tokType() == Token::eType) && tok->str() == MatchCompiler::makeConstString("void"))) |
628 | 0 | return false; |
629 | 0 | tok = tok->next(); |
630 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
631 | 0 | return false; |
632 | 0 | tok = tok->next(); |
633 | 0 | if (!tok || !tok->isName()) |
634 | 0 | return false; |
635 | 0 | tok = tok->next(); |
636 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
637 | 0 | return false; |
638 | 0 | return true; |
639 | 0 | } |
640 | | // pattern: & %var% = %var% . |
641 | 0 | static inline bool match59(const Token* tok) { |
642 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
643 | 0 | return false; |
644 | 0 | tok = tok->next(); |
645 | 0 | if (!tok || !(tok->varId() != 0)) |
646 | 0 | return false; |
647 | 0 | tok = tok->next(); |
648 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
649 | 0 | return false; |
650 | 0 | tok = tok->next(); |
651 | 0 | if (!tok || !(tok->varId() != 0)) |
652 | 0 | return false; |
653 | 0 | tok = tok->next(); |
654 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("."))) |
655 | 0 | return false; |
656 | 0 | return true; |
657 | 0 | } |
658 | | // pattern: %var% : |
659 | 0 | static inline bool match60(const Token* tok) { |
660 | 0 | if (!tok || !(tok->varId() != 0)) |
661 | 0 | return false; |
662 | 0 | tok = tok->next(); |
663 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":"))) |
664 | 0 | return false; |
665 | 0 | return true; |
666 | 0 | } |
667 | | // pattern: for ( |
668 | 9.52k | static inline bool match61(const Token* tok) { |
669 | 9.52k | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("for"))) |
670 | 9.52k | return false; |
671 | 0 | tok = tok->next(); |
672 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
673 | 0 | return false; |
674 | 0 | return true; |
675 | 0 | } |
676 | | // pattern: [;{}] |
677 | 0 | static inline bool match62(const Token* tok) { |
678 | 0 | if (!tok || tok->str().size() != 1U || !strchr(";{}", tok->str()[0])) |
679 | 0 | return false; |
680 | 0 | return true; |
681 | 0 | } |
682 | | // pattern: ; %varid% = |
683 | 0 | static inline bool match63(const Token* tok, const int varid) { |
684 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
685 | 0 | return false; |
686 | 0 | tok = tok->next(); |
687 | 0 | if (varid==0U) |
688 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
689 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
690 | 0 | return false; |
691 | 0 | tok = tok->next(); |
692 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
693 | 0 | return false; |
694 | 0 | return true; |
695 | 0 | } |
696 | | // pattern: {|( |
697 | 0 | static inline bool match64(const Token* tok) { |
698 | 0 | if (!tok || !(((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")))) |
699 | 0 | return false; |
700 | 0 | return true; |
701 | 0 | } |
702 | | // pattern: , |
703 | 7.68k | static inline bool match65(const Token* tok) { |
704 | 7.68k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(","))) |
705 | 7.67k | return false; |
706 | 4 | return true; |
707 | 7.68k | } |
708 | | // pattern: [(=] |
709 | 0 | static inline bool match66(const Token* tok) { |
710 | 0 | if (!tok || tok->str().size() != 1U || !strchr("(=", tok->str()[0])) |
711 | 0 | return false; |
712 | 0 | return true; |
713 | 0 | } |
714 | | // pattern: ( |
715 | 2.24k | static inline bool match67(const Token* tok) { |
716 | 2.24k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
717 | 2.24k | return false; |
718 | 0 | return true; |
719 | 2.24k | } |
720 | | // pattern: else { if ( |
721 | 0 | static inline bool match68(const Token* tok) { |
722 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("else"))) |
723 | 0 | return false; |
724 | 0 | tok = tok->next(); |
725 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
726 | 0 | return false; |
727 | 0 | tok = tok->next(); |
728 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if"))) |
729 | 0 | return false; |
730 | 0 | tok = tok->next(); |
731 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
732 | 0 | return false; |
733 | 0 | return true; |
734 | 0 | } |
735 | | // pattern: ) { |
736 | 0 | static inline bool match69(const Token* tok) { |
737 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
738 | 0 | return false; |
739 | 0 | tok = tok->next(); |
740 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
741 | 0 | return false; |
742 | 0 | return true; |
743 | 0 | } |
744 | | // pattern: %varid% = |
745 | 0 | static inline bool match70(const Token* tok, const int varid) { |
746 | 0 | if (varid==0U) |
747 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
748 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
749 | 0 | return false; |
750 | 0 | tok = tok->next(); |
751 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
752 | 0 | return false; |
753 | 0 | return true; |
754 | 0 | } |
755 | | // pattern: %varid% !!= |
756 | 0 | static inline bool match71(const Token* tok, const int varid) { |
757 | 0 | if (varid==0U) |
758 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
759 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
760 | 0 | return false; |
761 | 0 | tok = tok->next(); |
762 | 0 | if (tok && tok->str() == MatchCompiler::makeConstString("=")) |
763 | 0 | return false; |
764 | 0 | return true; |
765 | 0 | } |
766 | | // pattern: & %varid% |
767 | 0 | static inline bool match72(const Token* tok, const int varid) { |
768 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
769 | 0 | return false; |
770 | 0 | tok = tok->next(); |
771 | 0 | if (varid==0U) |
772 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
773 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
774 | 0 | return false; |
775 | 0 | return true; |
776 | 0 | } |
777 | | // pattern: %varid% |
778 | 0 | template<class T> static inline T * findmatch73(T * start_tok, const Token * end, int varid) { |
779 | 0 | for (; start_tok && start_tok != end; start_tok = start_tok->next()) { |
780 | |
|
781 | 0 | T * tok = start_tok; |
782 | 0 | if (varid==0U) |
783 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
784 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
785 | 0 | continue; |
786 | 0 | return start_tok; |
787 | 0 | } |
788 | 0 | return nullptr; |
789 | 0 | } |
790 | | // pattern: * %varid% |
791 | 0 | static inline bool match74(const Token* tok, const int varid) { |
792 | 0 | if (!tok || !((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*"))) |
793 | 0 | return false; |
794 | 0 | tok = tok->next(); |
795 | 0 | if (varid==0U) |
796 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
797 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
798 | 0 | return false; |
799 | 0 | return true; |
800 | 0 | } |
801 | | // pattern: = %varid% |
802 | 0 | static inline bool match75(const Token* tok, const int varid) { |
803 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
804 | 0 | return false; |
805 | 0 | tok = tok->next(); |
806 | 0 | if (varid==0U) |
807 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
808 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
809 | 0 | return false; |
810 | 0 | return true; |
811 | 0 | } |
812 | | // pattern: [([{<] |
813 | 0 | static inline bool match76(const Token* tok) { |
814 | 0 | if (!tok || tok->str().size() != 1U || !strchr("([{<", tok->str()[0])) |
815 | 0 | return false; |
816 | 0 | return true; |
817 | 0 | } |
818 | | // pattern: [ |
819 | 14.2k | static inline bool match77(const Token* tok) { |
820 | 14.2k | if (!tok || !((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("["))) |
821 | 14.2k | return false; |
822 | 0 | return true; |
823 | 14.2k | } |
824 | | // pattern: %var% ( |
825 | 0 | static inline bool match78(const Token* tok) { |
826 | 0 | if (!tok || !(tok->varId() != 0)) |
827 | 0 | return false; |
828 | 0 | tok = tok->next(); |
829 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
830 | 0 | return false; |
831 | 0 | return true; |
832 | 0 | } |
833 | | // pattern: & |
834 | 0 | static inline bool match79(const Token* tok) { |
835 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
836 | 0 | return false; |
837 | 0 | return true; |
838 | 0 | } |
839 | | // pattern: & %var% = %varid% |
840 | 0 | static inline bool match80(const Token* tok, const int varid) { |
841 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
842 | 0 | return false; |
843 | 0 | tok = tok->next(); |
844 | 0 | if (!tok || !(tok->varId() != 0)) |
845 | 0 | return false; |
846 | 0 | tok = tok->next(); |
847 | 0 | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
848 | 0 | return false; |
849 | 0 | tok = tok->next(); |
850 | 0 | if (varid==0U) |
851 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
852 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
853 | 0 | return false; |
854 | 0 | return true; |
855 | 0 | } |
856 | | // pattern: %varid% |
857 | 0 | static inline bool match81(const Token* tok, const int varid) { |
858 | 0 | if (varid==0U) |
859 | 0 | throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); |
860 | 0 | if (!tok || !(tok->isName() && tok->varId() == varid)) |
861 | 0 | return false; |
862 | 0 | return true; |
863 | 0 | } |
864 | | // pattern: %name% ) ( |
865 | 0 | static inline bool match82(const Token* tok) { |
866 | 0 | if (!tok || !tok->isName()) |
867 | 0 | return false; |
868 | 0 | tok = tok->next(); |
869 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
870 | 0 | return false; |
871 | 0 | tok = tok->next(); |
872 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
873 | 0 | return false; |
874 | 0 | return true; |
875 | 0 | } |
876 | | // pattern: %op% |
877 | 0 | static inline bool match83(const Token* tok) { |
878 | 0 | if (!tok || !tok->isOp()) |
879 | 0 | return false; |
880 | 0 | return true; |
881 | 0 | } |
882 | | // pattern: %cop% |
883 | 0 | static inline bool match84(const Token* tok) { |
884 | 0 | if (!tok || !tok->isConstOp()) |
885 | 0 | return false; |
886 | 0 | return true; |
887 | 0 | } |
888 | | // pattern: %assign% |
889 | 0 | static inline bool match85(const Token* tok) { |
890 | 0 | if (!tok || !tok->isAssignmentOp()) |
891 | 0 | return false; |
892 | 0 | return true; |
893 | 0 | } |
894 | | // pattern: %oror%|%comp%|&&|?|!|-|<< |
895 | 0 | static inline bool match86(const Token* tok) { |
896 | 0 | if (!tok || !((tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || tok->isComparisonOp() || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("<<")))) |
897 | 0 | return false; |
898 | 0 | return true; |
899 | 0 | } |
900 | | // pattern: if|while |
901 | 0 | static inline bool match87(const Token* tok) { |
902 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("while")))) |
903 | 0 | return false; |
904 | 0 | return true; |
905 | 0 | } |
906 | | // pattern: * |
907 | 0 | static inline bool match88(const Token* tok) { |
908 | 0 | if (!tok || !((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*"))) |
909 | 0 | return false; |
910 | 0 | return true; |
911 | 0 | } |
912 | | // pattern: %var% [ |
913 | 32.1k | static inline bool match89(const Token* tok) { |
914 | 32.1k | if (!tok || !(tok->varId() != 0)) |
915 | 25.3k | return false; |
916 | 6.81k | tok = tok->next(); |
917 | 6.81k | if (!tok || !((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("["))) |
918 | 6.81k | return false; |
919 | 0 | return true; |
920 | 6.81k | } |
921 | | // pattern: [&|^] |
922 | 22.6k | static inline bool match90(const Token* tok) { |
923 | 22.6k | if (!tok || tok->str().size() != 1U || !strchr("&|^", tok->str()[0])) |
924 | 22.4k | return false; |
925 | 261 | return true; |
926 | 22.6k | } |
927 | | // pattern: :: |
928 | 0 | static inline bool match91(const Token* tok) { |
929 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("::"))) |
930 | 0 | return false; |
931 | 0 | return true; |
932 | 0 | } |
933 | | // pattern: %name% !!( |
934 | 0 | static inline bool match92(const Token* tok) { |
935 | 0 | if (!tok || !tok->isName()) |
936 | 0 | return false; |
937 | 0 | tok = tok->next(); |
938 | 0 | if (tok && tok->str() == MatchCompiler::makeConstString("(")) |
939 | 0 | return false; |
940 | 0 | return true; |
941 | 0 | } |
942 | | // pattern: %bool%|%num%|%str%|%char%|nullptr|NULL |
943 | 3.18k | static inline bool match93(const Token* tok) { |
944 | 3.18k | if (!tok || !(tok->isBoolean() || tok->isNumber() || (tok->tokType() == Token::eString) || (tok->tokType() == Token::eChar) || (tok->str() == MatchCompiler::makeConstString("nullptr")) || (tok->str() == MatchCompiler::makeConstString("NULL")))) |
945 | 3.18k | return false; |
946 | 0 | return true; |
947 | 3.18k | } |
948 | | // pattern: *|&|&& |
949 | 3.18k | static inline bool match94(const Token* tok) { |
950 | 3.18k | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")))) |
951 | 3.18k | return false; |
952 | 0 | return true; |
953 | 3.18k | } |
954 | | // pattern: ::|.|const|volatile|restrict |
955 | 0 | static inline bool match95(const Token* tok) { |
956 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("::")) || (tok->str() == MatchCompiler::makeConstString(".")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("const")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("volatile")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("restrict")))) |
957 | 0 | return false; |
958 | 0 | return true; |
959 | 0 | } |
960 | | // pattern: <<|>> |
961 | 3.18k | static inline bool match96(const Token* tok) { |
962 | 3.18k | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("<<")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString(">>")))) |
963 | 3.18k | return false; |
964 | 0 | return true; |
965 | 3.18k | } |
966 | | // pattern: delete |
967 | 3.18k | static inline bool match97(const Token* tok) { |
968 | 3.18k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("delete"))) |
969 | 3.18k | return false; |
970 | 0 | return true; |
971 | 3.18k | } |
972 | | // pattern: !|~|%cop% |
973 | 3.18k | static inline bool match98(const Token* tok) { |
974 | 3.18k | if (!tok || !(((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("~")) || tok->isConstOp())) |
975 | 3.18k | return false; |
976 | 0 | return true; |
977 | 3.18k | } |
978 | | // pattern: sizeof ( |
979 | 3.18k | static inline bool match99(const Token* tok) { |
980 | 3.18k | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("sizeof"))) |
981 | 3.18k | return false; |
982 | 0 | tok = tok->next(); |
983 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
984 | 0 | return false; |
985 | 0 | return true; |
986 | 0 | } |
987 | | // pattern: dynamic_cast |
988 | 0 | static inline bool match100(const Token* tok) { |
989 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("dynamic_cast"))) |
990 | 0 | return false; |
991 | 0 | return true; |
992 | 0 | } |
993 | | // pattern: & > |
994 | 0 | static inline bool match101(const Token* tok) { |
995 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
996 | 0 | return false; |
997 | 0 | tok = tok->next(); |
998 | 0 | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">"))) |
999 | 0 | return false; |
1000 | 0 | return true; |
1001 | 0 | } |
1002 | | // pattern: .|[|(|* |
1003 | 0 | static inline bool match102(const Token* tok) { |
1004 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString(".")) || ((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("[")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")))) |
1005 | 0 | return false; |
1006 | 0 | return true; |
1007 | 0 | } |
1008 | | // pattern: : |
1009 | 0 | static inline bool match103(const Token* tok) { |
1010 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":"))) |
1011 | 0 | return false; |
1012 | 0 | return true; |
1013 | 0 | } |
1014 | | // pattern: ( void |
1015 | 0 | static inline bool match104(const Token* tok) { |
1016 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1017 | 0 | return false; |
1018 | 0 | tok = tok->next(); |
1019 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword || tok->tokType() == Token::eType) && tok->str() == MatchCompiler::makeConstString("void"))) |
1020 | 0 | return false; |
1021 | 0 | return true; |
1022 | 0 | } |
1023 | | // pattern: < void *| > |
1024 | 0 | static inline bool match105(const Token* tok) { |
1025 | 0 | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<"))) |
1026 | 0 | return false; |
1027 | 0 | tok = tok->next(); |
1028 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword || tok->tokType() == Token::eType) && tok->str() == MatchCompiler::makeConstString("void"))) |
1029 | 0 | return false; |
1030 | 0 | tok = tok->next(); |
1031 | 0 | if (tok && (((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")))) |
1032 | 0 | tok = tok->next(); |
1033 | 0 | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">"))) |
1034 | 0 | return false; |
1035 | 0 | return true; |
1036 | 0 | } |
1037 | | // pattern: ) |
1038 | 0 | static inline bool match106(const Token* tok) { |
1039 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1040 | 0 | return false; |
1041 | 0 | return true; |
1042 | 0 | } |
1043 | | // pattern: delete|throw|return |
1044 | 0 | static inline bool match107(const Token* tok) { |
1045 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("delete")) || (tok->str() == MatchCompiler::makeConstString("throw")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return")))) |
1046 | 0 | return false; |
1047 | 0 | return true; |
1048 | 0 | } |
1049 | | // pattern: for|if ( |
1050 | 0 | static inline bool match108(const Token* tok) { |
1051 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("for")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if")))) |
1052 | 0 | return false; |
1053 | 0 | tok = tok->next(); |
1054 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1055 | 0 | return false; |
1056 | 0 | return true; |
1057 | 0 | } |
1058 | | // pattern: %oror%|&& |
1059 | 12.7k | static inline bool match109(const Token* tok) { |
1060 | 12.7k | if (!tok || !((tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")))) |
1061 | 12.7k | return false; |
1062 | 0 | return true; |
1063 | 12.7k | } |
1064 | | // pattern: ;|}|{ %any% ; |
1065 | 9.52k | static inline bool match110(const Token* tok) { |
1066 | 9.52k | if (!tok || !((tok->str() == MatchCompiler::makeConstString(";")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")))) |
1067 | 7.20k | return false; |
1068 | 2.31k | tok = tok->next(); |
1069 | 2.31k | if (!tok || false) |
1070 | 0 | return false; |
1071 | 2.31k | tok = tok->next(); |
1072 | 2.31k | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1073 | 2.31k | return false; |
1074 | 6 | return true; |
1075 | 2.31k | } |
1076 | | // pattern: ; } ) |
1077 | 3.18k | static inline bool match111(const Token* tok) { |
1078 | 3.18k | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1079 | 6 | return false; |
1080 | 3.18k | tok = tok->next(); |
1081 | 3.18k | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
1082 | 1.83k | return false; |
1083 | 1.35k | tok = tok->next(); |
1084 | 1.35k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1085 | 1.35k | return false; |
1086 | 0 | return true; |
1087 | 1.35k | } |
1088 | | // pattern: == |
1089 | 0 | static inline bool match112(const Token* tok) { |
1090 | 0 | if (!tok || !((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("=="))) |
1091 | 0 | return false; |
1092 | 0 | return true; |
1093 | 0 | } |
1094 | | // pattern: ,|!|~|%cop% |
1095 | 0 | static inline bool match113(const Token* tok) { |
1096 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(",")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")) || ((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("~")) || tok->isConstOp())) |
1097 | 0 | return false; |
1098 | 0 | return true; |
1099 | 0 | } |
1100 | | // pattern: %var% |
1101 | 2.24k | static inline bool match114(const Token* tok) { |
1102 | 2.24k | if (!tok || !(tok->varId() != 0)) |
1103 | 2.00k | return false; |
1104 | 240 | return true; |
1105 | 2.24k | } |
1106 | | // pattern: [+-] |
1107 | 69 | static inline bool match115(const Token* tok) { |
1108 | 69 | if (!tok || tok->str().size() != 1U || !strchr("+-", tok->str()[0])) |
1109 | 65 | return false; |
1110 | 4 | return true; |
1111 | 69 | } |
1112 | | // pattern: 0.0 |
1113 | 4 | static inline bool match116(const Token* tok) { |
1114 | 4 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("0.0"))) |
1115 | 4 | return false; |
1116 | 0 | return true; |
1117 | 4 | } |
1118 | | // pattern: [;{}] %name% |
1119 | 22.6k | static inline bool match117(const Token* tok) { |
1120 | 22.6k | if (!tok || tok->str().size() != 1U || !strchr(";{}", tok->str()[0])) |
1121 | 16.5k | return false; |
1122 | 6.15k | tok = tok->next(); |
1123 | 6.15k | if (!tok || !tok->isName()) |
1124 | 2.03k | return false; |
1125 | 4.12k | return true; |
1126 | 6.15k | } |
1127 | | // pattern: %name% :: |
1128 | 2.00k | static inline bool match118(const Token* tok) { |
1129 | 2.00k | if (!tok || !tok->isName()) |
1130 | 0 | return false; |
1131 | 2.00k | tok = tok->next(); |
1132 | 2.00k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("::"))) |
1133 | 2.00k | return false; |
1134 | 0 | return true; |
1135 | 2.00k | } |
1136 | | // pattern: %name% < |
1137 | 2.00k | static inline bool match119(const Token* tok) { |
1138 | 2.00k | if (!tok || !tok->isName()) |
1139 | 0 | return false; |
1140 | 2.00k | tok = tok->next(); |
1141 | 2.00k | if (!tok || !((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<"))) |
1142 | 2.00k | return false; |
1143 | 0 | return true; |
1144 | 2.00k | } |
1145 | | // pattern: %name%|> (|{ |
1146 | 2.00k | static inline bool match120(const Token* tok) { |
1147 | 2.00k | if (!tok || !(tok->isName() || ((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">")))) |
1148 | 0 | return false; |
1149 | 2.00k | tok = tok->next(); |
1150 | 2.00k | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")))) |
1151 | 2.00k | return false; |
1152 | 0 | return true; |
1153 | 2.00k | } |
1154 | | // pattern: )|} ; |
1155 | 0 | static inline bool match121(const Token* tok) { |
1156 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")))) |
1157 | 0 | return false; |
1158 | 0 | tok = tok->next(); |
1159 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1160 | 0 | return false; |
1161 | 0 | return true; |
1162 | 0 | } |
1163 | | // pattern: class|struct|union |
1164 | 0 | static inline bool match122(const Token* tok) { |
1165 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("class")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("struct")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("union")))) |
1166 | 0 | return false; |
1167 | 0 | return true; |
1168 | 0 | } |
1169 | | // pattern: ; } |
1170 | 212 | static inline bool match123(const Token* tok) { |
1171 | 212 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1172 | 98 | return false; |
1173 | 114 | tok = tok->next(); |
1174 | 114 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
1175 | 59 | return false; |
1176 | 55 | return true; |
1177 | 114 | } |
1178 | | // pattern: } else { |
1179 | 501 | static inline bool match124(const Token* tok) { |
1180 | 501 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
1181 | 0 | return false; |
1182 | 501 | tok = tok->next(); |
1183 | 501 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("else"))) |
1184 | 222 | return false; |
1185 | 279 | tok = tok->next(); |
1186 | 279 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
1187 | 0 | return false; |
1188 | 279 | return true; |
1189 | 279 | } |
1190 | | // pattern: %var% = new |
1191 | 21.5k | static inline bool match125(const Token* tok) { |
1192 | 21.5k | if (!tok || !(tok->varId() != 0)) |
1193 | 17.5k | return false; |
1194 | 4.05k | tok = tok->next(); |
1195 | 4.05k | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
1196 | 3.79k | return false; |
1197 | 260 | tok = tok->next(); |
1198 | 260 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("new"))) |
1199 | 260 | return false; |
1200 | 0 | return true; |
1201 | 260 | } |
1202 | | // pattern: %var% = %name% ( |
1203 | 21.5k | static inline bool match126(const Token* tok) { |
1204 | 21.5k | if (!tok || !(tok->varId() != 0)) |
1205 | 17.5k | return false; |
1206 | 4.05k | tok = tok->next(); |
1207 | 4.05k | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
1208 | 3.79k | return false; |
1209 | 260 | tok = tok->next(); |
1210 | 260 | if (!tok || !tok->isName()) |
1211 | 43 | return false; |
1212 | 217 | tok = tok->next(); |
1213 | 217 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1214 | 217 | return false; |
1215 | 0 | return true; |
1216 | 217 | } |
1217 | | // pattern: %var% = %name% +|- |
1218 | 21.5k | static inline bool match127(const Token* tok) { |
1219 | 21.5k | if (!tok || !(tok->varId() != 0)) |
1220 | 17.5k | return false; |
1221 | 4.05k | tok = tok->next(); |
1222 | 4.05k | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
1223 | 3.79k | return false; |
1224 | 260 | tok = tok->next(); |
1225 | 260 | if (!tok || !tok->isName()) |
1226 | 43 | return false; |
1227 | 217 | tok = tok->next(); |
1228 | 217 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1229 | 206 | return false; |
1230 | 11 | return true; |
1231 | 217 | } |
1232 | | // pattern: %var% = |
1233 | 21.5k | static inline bool match128(const Token* tok) { |
1234 | 21.5k | if (!tok || !(tok->varId() != 0)) |
1235 | 17.5k | return false; |
1236 | 4.05k | tok = tok->next(); |
1237 | 4.05k | if (!tok || !((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("="))) |
1238 | 3.79k | return false; |
1239 | 260 | return true; |
1240 | 4.05k | } |
1241 | | // pattern: %name% ( %any% +|- |
1242 | 21.3k | static inline bool match129(const Token* tok) { |
1243 | 21.3k | if (!tok || !tok->isName()) |
1244 | 12.5k | return false; |
1245 | 8.74k | tok = tok->next(); |
1246 | 8.74k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1247 | 8.02k | return false; |
1248 | 722 | tok = tok->next(); |
1249 | 722 | if (!tok || false) |
1250 | 0 | return false; |
1251 | 722 | tok = tok->next(); |
1252 | 722 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1253 | 707 | return false; |
1254 | 15 | return true; |
1255 | 722 | } |
1256 | | // pattern: delete [ ] ( %any% +|- |
1257 | 21.3k | static inline bool match130(const Token* tok) { |
1258 | 21.3k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("delete"))) |
1259 | 21.3k | return false; |
1260 | 0 | tok = tok->next(); |
1261 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("["))) |
1262 | 0 | return false; |
1263 | 0 | tok = tok->next(); |
1264 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("]"))) |
1265 | 0 | return false; |
1266 | 0 | tok = tok->next(); |
1267 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1268 | 0 | return false; |
1269 | 0 | tok = tok->next(); |
1270 | 0 | if (!tok || false) |
1271 | 0 | return false; |
1272 | 0 | tok = tok->next(); |
1273 | 0 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1274 | 0 | return false; |
1275 | 0 | return true; |
1276 | 0 | } |
1277 | | // pattern: delete %any% +|- %any% |
1278 | 21.3k | static inline bool match131(const Token* tok) { |
1279 | 21.3k | if (!tok || !(tok->str() == MatchCompiler::makeConstString("delete"))) |
1280 | 21.3k | return false; |
1281 | 0 | tok = tok->next(); |
1282 | 0 | if (!tok || false) |
1283 | 0 | return false; |
1284 | 0 | tok = tok->next(); |
1285 | 0 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1286 | 0 | return false; |
1287 | 0 | tok = tok->next(); |
1288 | 0 | if (!tok || false) |
1289 | 0 | return false; |
1290 | 0 | return true; |
1291 | 0 | } |
1292 | | // pattern: %var% |
1293 | 2.24k | template<class T> static inline T * findmatch132(T * start_tok, const Token * end) { |
1294 | 5.39k | for (; start_tok && start_tok != end; start_tok = start_tok->next()) { |
1295 | | |
1296 | 4.67k | T * tok = start_tok; |
1297 | 4.67k | if (!tok || !(tok->varId() != 0)) |
1298 | 3.14k | continue; |
1299 | 1.52k | return start_tok; |
1300 | 4.67k | } |
1301 | 722 | return nullptr; |
1302 | 2.24k | } |
1303 | | // pattern: static_assert |
1304 | 82 | static inline bool match133(const Token* tok) { |
1305 | 82 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("static_assert"))) |
1306 | 82 | return false; |
1307 | 0 | return true; |
1308 | 82 | } |
1309 | | // pattern: _Static_assert |
1310 | 0 | static inline bool match134(const Token* tok) { |
1311 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("_Static_assert"))) |
1312 | 0 | return false; |
1313 | 0 | return true; |
1314 | 0 | } |
1315 | | // pattern: ; %type% %var% ; |
1316 | 240 | static inline bool match135(const Token* tok) { |
1317 | 240 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1318 | 0 | return false; |
1319 | 240 | tok = tok->next(); |
1320 | 240 | if (!tok || !(tok->isName() && tok->varId() == 0U)) |
1321 | 135 | return false; |
1322 | 105 | tok = tok->next(); |
1323 | 105 | if (!tok || !(tok->varId() != 0)) |
1324 | 102 | return false; |
1325 | 3 | tok = tok->next(); |
1326 | 3 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1327 | 1 | return false; |
1328 | 2 | return true; |
1329 | 3 | } |
1330 | | // pattern: %var% %assign% |
1331 | 240 | static inline bool match136(const Token* tok) { |
1332 | 240 | if (!tok || !(tok->varId() != 0)) |
1333 | 240 | return false; |
1334 | 0 | tok = tok->next(); |
1335 | 0 | if (!tok || !tok->isAssignmentOp()) |
1336 | 0 | return false; |
1337 | 0 | return true; |
1338 | 0 | } |
1339 | | // pattern: ;|{|} %var% |
1340 | 0 | static inline bool match137(const Token* tok) { |
1341 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString(";")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")))) |
1342 | 0 | return false; |
1343 | 0 | tok = tok->next(); |
1344 | 0 | if (!tok || !(tok->varId() != 0)) |
1345 | 0 | return false; |
1346 | 0 | return true; |
1347 | 0 | } |
1348 | | // pattern: %assign%|%comp% |
1349 | 0 | static inline bool match138(const Token* tok) { |
1350 | 0 | if (!tok || !(tok->isAssignmentOp() || tok->isComparisonOp())) |
1351 | 0 | return false; |
1352 | 0 | return true; |
1353 | 0 | } |
1354 | | // pattern: +|*|<<|>>|+=|*=|<<=|>>= |
1355 | 4.68k | static inline bool match139(const Token* tok) { |
1356 | 4.68k | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("*")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("<<")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString(">>")) || ((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("+=")) || ((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("*=")) || (tok->str() == MatchCompiler::makeConstString("<<=")) || (tok->str() == MatchCompiler::makeConstString(">>=")))) |
1357 | 4.48k | return false; |
1358 | 200 | return true; |
1359 | 4.68k | } |
1360 | | // pattern: ==|!=|- |
1361 | 4.48k | static inline bool match140(const Token* tok) { |
1362 | 4.48k | if (!tok || !(((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("==")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("!=")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1363 | 4.09k | return false; |
1364 | 397 | return true; |
1365 | 4.48k | } |
1366 | | // pattern: %comp%|%oror%|&& |
1367 | 9 | static inline bool match141(const Token* tok) { |
1368 | 9 | if (!tok || !(tok->isComparisonOp() || (tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")))) |
1369 | 9 | return false; |
1370 | 0 | return true; |
1371 | 9 | } |
1372 | | // pattern: =|-|-=|/|/= |
1373 | 1 | static inline bool match142(const Token* tok) { |
1374 | 1 | if (!tok || !(((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("=")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")) || ((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("-=")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("/")) || ((tok->tokType() == Token::eAssignmentOp) && tok->str() == MatchCompiler::makeConstString("/=")))) |
1375 | 1 | return false; |
1376 | 0 | return true; |
1377 | 1 | } |
1378 | | // pattern: [-/%] |
1379 | 4.00k | static inline bool match143(const Token* tok) { |
1380 | 4.00k | if (!tok || tok->str().size() != 1U || !strchr("-/%", tok->str()[0])) |
1381 | 3.83k | return false; |
1382 | 174 | return true; |
1383 | 4.00k | } |
1384 | | // pattern: %oror%|%comp%|&&|?|! |
1385 | 0 | static inline bool match144(const Token* tok) { |
1386 | 0 | if (!tok || !((tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || tok->isComparisonOp() || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")))) |
1387 | 0 | return false; |
1388 | 0 | return true; |
1389 | 0 | } |
1390 | | // pattern: ==|>=|<= |
1391 | 0 | static inline bool match145(const Token* tok) { |
1392 | 0 | if (!tok || !(((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("==")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">=")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<=")))) |
1393 | 0 | return false; |
1394 | 0 | return true; |
1395 | 0 | } |
1396 | | // pattern: !=|>|< |
1397 | 0 | static inline bool match146(const Token* tok) { |
1398 | 0 | if (!tok || !(((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("!=")) || ((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">")) || ((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<")))) |
1399 | 0 | return false; |
1400 | 0 | return true; |
1401 | 0 | } |
1402 | | // pattern: %num%|NULL|nullptr |
1403 | 0 | static inline bool match147(const Token* tok) { |
1404 | 0 | if (!tok || !(tok->isNumber() || (tok->str() == MatchCompiler::makeConstString("NULL")) || (tok->str() == MatchCompiler::makeConstString("nullptr")))) |
1405 | 0 | return false; |
1406 | 0 | return true; |
1407 | 0 | } |
1408 | | // pattern: isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% ) |
1409 | 9.00k | static inline bool match148(const Token* tok) { |
1410 | 9.00k | if (!tok || !((tok->str() == MatchCompiler::makeConstString("isgreater")) || (tok->str() == MatchCompiler::makeConstString("isless")) || (tok->str() == MatchCompiler::makeConstString("islessgreater")) || (tok->str() == MatchCompiler::makeConstString("isgreaterequal")) || (tok->str() == MatchCompiler::makeConstString("islessequal")))) |
1411 | 9.00k | return false; |
1412 | 0 | tok = tok->next(); |
1413 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1414 | 0 | return false; |
1415 | 0 | tok = tok->next(); |
1416 | 0 | if (!tok || !(tok->varId() != 0)) |
1417 | 0 | return false; |
1418 | 0 | tok = tok->next(); |
1419 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(","))) |
1420 | 0 | return false; |
1421 | 0 | tok = tok->next(); |
1422 | 0 | if (!tok || !(tok->varId() != 0)) |
1423 | 0 | return false; |
1424 | 0 | tok = tok->next(); |
1425 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1426 | 0 | return false; |
1427 | 0 | return true; |
1428 | 0 | } |
1429 | | // pattern: <|<= |
1430 | 934 | static inline bool match149(const Token* tok) { |
1431 | 934 | if (!tok || !(((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<=")))) |
1432 | 615 | return false; |
1433 | 319 | return true; |
1434 | 934 | } |
1435 | | // pattern: >|>= |
1436 | 930 | static inline bool match150(const Token* tok) { |
1437 | 930 | if (!tok || !(((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">=")))) |
1438 | 620 | return false; |
1439 | 310 | return true; |
1440 | 930 | } |
1441 | | // pattern: >= |
1442 | 934 | static inline bool match151(const Token* tok) { |
1443 | 934 | if (!tok || !((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">="))) |
1444 | 874 | return false; |
1445 | 60 | return true; |
1446 | 934 | } |
1447 | | // pattern: <= |
1448 | 933 | static inline bool match152(const Token* tok) { |
1449 | 933 | if (!tok || !((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<="))) |
1450 | 874 | return false; |
1451 | 59 | return true; |
1452 | 933 | } |
1453 | | // pattern: (|{ |
1454 | 0 | static inline bool match153(const Token* tok) { |
1455 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")))) |
1456 | 0 | return false; |
1457 | 0 | return true; |
1458 | 0 | } |
1459 | | // pattern: ) )|}| ; |
1460 | 0 | static inline bool match154(const Token* tok) { |
1461 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1462 | 0 | return false; |
1463 | 0 | tok = tok->next(); |
1464 | 0 | if (tok && (((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")))) |
1465 | 0 | tok = tok->next(); |
1466 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1467 | 0 | return false; |
1468 | 0 | return true; |
1469 | 0 | } |
1470 | | // pattern: return %var% ; |
1471 | 0 | static inline bool match155(const Token* tok) { |
1472 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return"))) |
1473 | 0 | return false; |
1474 | 0 | tok = tok->next(); |
1475 | 0 | if (!tok || !(tok->varId() != 0)) |
1476 | 0 | return false; |
1477 | 0 | tok = tok->next(); |
1478 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1479 | 0 | return false; |
1480 | 0 | return true; |
1481 | 0 | } |
1482 | | // pattern: <<|>>|<<=|>>= |
1483 | 8.26k | static inline bool match156(const Token* tok) { |
1484 | 8.26k | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("<<")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString(">>")) || (tok->str() == MatchCompiler::makeConstString("<<=")) || (tok->str() == MatchCompiler::makeConstString(">>=")))) |
1485 | 8.26k | return false; |
1486 | 1 | return true; |
1487 | 8.26k | } |
1488 | | // pattern: memset|memcpy|memmove ( |
1489 | 21.5k | static inline bool match157(const Token* tok) { |
1490 | 21.5k | if (!tok || !((tok->str() == MatchCompiler::makeConstString("memset")) || (tok->str() == MatchCompiler::makeConstString("memcpy")) || (tok->str() == MatchCompiler::makeConstString("memmove")))) |
1491 | 21.5k | return false; |
1492 | 0 | tok = tok->next(); |
1493 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1494 | 0 | return false; |
1495 | 0 | return true; |
1496 | 0 | } |
1497 | | // pattern: %name% ::|. |
1498 | 0 | static inline bool match158(const Token* tok) { |
1499 | 0 | if (!tok || !tok->isName()) |
1500 | 0 | return false; |
1501 | 0 | tok = tok->next(); |
1502 | 0 | if (!tok || !((tok->str() == MatchCompiler::makeConstString("::")) || (tok->str() == MatchCompiler::makeConstString(".")))) |
1503 | 0 | return false; |
1504 | 0 | return true; |
1505 | 0 | } |
1506 | | // pattern: %var% , |
1507 | 0 | static inline bool match159(const Token* tok) { |
1508 | 0 | if (!tok || !(tok->varId() != 0)) |
1509 | 0 | return false; |
1510 | 0 | tok = tok->next(); |
1511 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(","))) |
1512 | 0 | return false; |
1513 | 0 | return true; |
1514 | 0 | } |
1515 | | // pattern: [(,] NULL [,)] |
1516 | 22.6k | static inline bool match160(const Token* tok) { |
1517 | 22.6k | if (!tok || tok->str().size() != 1U || !strchr("(,", tok->str()[0])) |
1518 | 21.6k | return false; |
1519 | 996 | tok = tok->next(); |
1520 | 996 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("NULL"))) |
1521 | 996 | return false; |
1522 | 0 | tok = tok->next(); |
1523 | 0 | if (!tok || tok->str().size() != 1U || !strchr(",)", tok->str()[0])) |
1524 | 0 | return false; |
1525 | 0 | return true; |
1526 | 0 | } |
1527 | | // pattern: ... |
1528 | 0 | static inline bool match161(const Token* tok) { |
1529 | 0 | if (!tok || !((tok->tokType() == Token::eEllipsis) && tok->str() == MatchCompiler::makeConstString("..."))) |
1530 | 0 | return false; |
1531 | 0 | return true; |
1532 | 0 | } |
1533 | | // pattern: InterlockedDecrement ( & %name% ) ; if ( %name%|!|0 |
1534 | 0 | static inline bool match162(const Token* tok) { |
1535 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("InterlockedDecrement"))) |
1536 | 0 | return false; |
1537 | 0 | tok = tok->next(); |
1538 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1539 | 0 | return false; |
1540 | 0 | tok = tok->next(); |
1541 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
1542 | 0 | return false; |
1543 | 0 | tok = tok->next(); |
1544 | 0 | if (!tok || !tok->isName()) |
1545 | 0 | return false; |
1546 | 0 | tok = tok->next(); |
1547 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1548 | 0 | return false; |
1549 | 0 | tok = tok->next(); |
1550 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
1551 | 0 | return false; |
1552 | 0 | tok = tok->next(); |
1553 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if"))) |
1554 | 0 | return false; |
1555 | 0 | tok = tok->next(); |
1556 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1557 | 0 | return false; |
1558 | 0 | tok = tok->next(); |
1559 | 0 | if (!tok || !(tok->isName() || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")) || (tok->str() == MatchCompiler::makeConstString("0")))) |
1560 | 0 | return false; |
1561 | 0 | return true; |
1562 | 0 | } |
1563 | | // pattern: 0 %comp% %name% ) |
1564 | 0 | static inline bool match163(const Token* tok) { |
1565 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("0"))) |
1566 | 0 | return false; |
1567 | 0 | tok = tok->next(); |
1568 | 0 | if (!tok || !tok->isComparisonOp()) |
1569 | 0 | return false; |
1570 | 0 | tok = tok->next(); |
1571 | 0 | if (!tok || !tok->isName()) |
1572 | 0 | return false; |
1573 | 0 | tok = tok->next(); |
1574 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1575 | 0 | return false; |
1576 | 0 | return true; |
1577 | 0 | } |
1578 | | // pattern: ! %name% ) |
1579 | 0 | static inline bool match164(const Token* tok) { |
1580 | 0 | if (!tok || !((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!"))) |
1581 | 0 | return false; |
1582 | 0 | tok = tok->next(); |
1583 | 0 | if (!tok || !tok->isName()) |
1584 | 0 | return false; |
1585 | 0 | tok = tok->next(); |
1586 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1587 | 0 | return false; |
1588 | 0 | return true; |
1589 | 0 | } |
1590 | | // pattern: %name% ) |
1591 | 0 | static inline bool match165(const Token* tok) { |
1592 | 0 | if (!tok || !tok->isName()) |
1593 | 0 | return false; |
1594 | 0 | tok = tok->next(); |
1595 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1596 | 0 | return false; |
1597 | 0 | return true; |
1598 | 0 | } |
1599 | | // pattern: %name% %comp% 0 ) |
1600 | 0 | static inline bool match166(const Token* tok) { |
1601 | 0 | if (!tok || !tok->isName()) |
1602 | 0 | return false; |
1603 | 0 | tok = tok->next(); |
1604 | 0 | if (!tok || !tok->isComparisonOp()) |
1605 | 0 | return false; |
1606 | 0 | tok = tok->next(); |
1607 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("0"))) |
1608 | 0 | return false; |
1609 | 0 | tok = tok->next(); |
1610 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")"))) |
1611 | 0 | return false; |
1612 | 0 | return true; |
1613 | 0 | } |
1614 | | // pattern: if ( ::| InterlockedDecrement ( & %name% |
1615 | 0 | static inline bool match167(const Token* tok) { |
1616 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if"))) |
1617 | 0 | return false; |
1618 | 0 | tok = tok->next(); |
1619 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1620 | 0 | return false; |
1621 | 0 | tok = tok->next(); |
1622 | 0 | if (tok && ((tok->str() == MatchCompiler::makeConstString("::")))) |
1623 | 0 | tok = tok->next(); |
1624 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("InterlockedDecrement"))) |
1625 | 0 | return false; |
1626 | 0 | tok = tok->next(); |
1627 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1628 | 0 | return false; |
1629 | 0 | tok = tok->next(); |
1630 | 0 | if (!tok || !((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&"))) |
1631 | 0 | return false; |
1632 | 0 | tok = tok->next(); |
1633 | 0 | if (!tok || !tok->isName()) |
1634 | 0 | return false; |
1635 | 0 | return true; |
1636 | 0 | } |
1637 | | // pattern: } return %name% |
1638 | 0 | static inline bool match168(const Token* tok) { |
1639 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
1640 | 0 | return false; |
1641 | 0 | tok = tok->next(); |
1642 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return"))) |
1643 | 0 | return false; |
1644 | 0 | tok = tok->next(); |
1645 | 0 | if (!tok || !tok->isName()) |
1646 | 0 | return false; |
1647 | 0 | return true; |
1648 | 0 | } |
1649 | | // pattern: } else { return %name% |
1650 | 0 | static inline bool match169(const Token* tok) { |
1651 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}"))) |
1652 | 0 | return false; |
1653 | 0 | tok = tok->next(); |
1654 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("else"))) |
1655 | 0 | return false; |
1656 | 0 | tok = tok->next(); |
1657 | 0 | if (!tok || !((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{"))) |
1658 | 0 | return false; |
1659 | 0 | tok = tok->next(); |
1660 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return"))) |
1661 | 0 | return false; |
1662 | 0 | tok = tok->next(); |
1663 | 0 | if (!tok || !tok->isName()) |
1664 | 0 | return false; |
1665 | 0 | return true; |
1666 | 0 | } |
1667 | | // pattern: {|}|; %name% : |
1668 | 22.6k | static inline bool match170(const Token* tok) { |
1669 | 22.6k | if (!tok || !(((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("}")) || (tok->str() == MatchCompiler::makeConstString(";")))) |
1670 | 16.5k | return false; |
1671 | 6.15k | tok = tok->next(); |
1672 | 6.15k | if (!tok || !tok->isName()) |
1673 | 2.03k | return false; |
1674 | 4.12k | tok = tok->next(); |
1675 | 4.12k | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":"))) |
1676 | 4.12k | return false; |
1677 | 0 | return true; |
1678 | 4.12k | } |
1679 | | // pattern: sizeof |
1680 | 0 | static inline bool match171(const Token* tok) { |
1681 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("sizeof"))) |
1682 | 0 | return false; |
1683 | 0 | return true; |
1684 | 0 | } |
1685 | | // pattern: %oror%|&&|?|:|; |
1686 | 1.64k | static inline bool match172(const Token* tok) { |
1687 | 1.64k | if (!tok || !((tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(":")) || (tok->str() == MatchCompiler::makeConstString(";")))) |
1688 | 1.64k | return false; |
1689 | 0 | return true; |
1690 | 1.64k | } |
1691 | | // pattern: ,|)|; |
1692 | 0 | static inline bool match173(const Token* tok) { |
1693 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(",")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(")")) || (tok->str() == MatchCompiler::makeConstString(";")))) |
1694 | 0 | return false; |
1695 | 0 | return true; |
1696 | 0 | } |
1697 | | // pattern: 0 |
1698 | 0 | static inline bool match174(const Token* tok) { |
1699 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("0"))) |
1700 | 0 | return false; |
1701 | 0 | return true; |
1702 | 0 | } |
1703 | | // pattern: && |
1704 | 0 | static inline bool match175(const Token* tok) { |
1705 | 0 | if (!tok || !((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&"))) |
1706 | 0 | return false; |
1707 | 0 | return true; |
1708 | 0 | } |
1709 | | // pattern: false |
1710 | 0 | static inline bool match176(const Token* tok) { |
1711 | 0 | if (!tok || !((tok->tokType() == Token::eBoolean) && tok->str() == MatchCompiler::makeConstString("false"))) |
1712 | 0 | return false; |
1713 | 0 | return true; |
1714 | 0 | } |
1715 | | // pattern: || |
1716 | 0 | static inline bool match177(const Token* tok) { |
1717 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString("||"))) |
1718 | 0 | return false; |
1719 | 0 | return true; |
1720 | 0 | } |
1721 | | // pattern: true |
1722 | 0 | static inline bool match178(const Token* tok) { |
1723 | 0 | if (!tok || !((tok->tokType() == Token::eBoolean) && tok->str() == MatchCompiler::makeConstString("true"))) |
1724 | 0 | return false; |
1725 | 0 | return true; |
1726 | 0 | } |
1727 | | // pattern: ++|--|%assign% |
1728 | 1.22k | static inline bool match179(const Token* tok) { |
1729 | 1.22k | if (!tok || !(((tok->tokType() == Token::eIncDecOp) && tok->str() == MatchCompiler::makeConstString("++")) || ((tok->tokType() == Token::eIncDecOp) && tok->str() == MatchCompiler::makeConstString("--")) || tok->isAssignmentOp())) |
1730 | 1.12k | return false; |
1731 | 100 | return true; |
1732 | 1.22k | } |
1733 | | // pattern: (|{|, |
1734 | 1.12k | static inline bool match180(const Token* tok) { |
1735 | 1.12k | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("(")) || ((tok->tokType() == Token::eBracket) && tok->str() == MatchCompiler::makeConstString("{")) || ((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString(",")))) |
1736 | 1.02k | return false; |
1737 | 107 | return true; |
1738 | 1.12k | } |
1739 | | // pattern: if|while|switch|sizeof |
1740 | 107 | static inline bool match181(const Token* tok) { |
1741 | 107 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("while")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("switch")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("sizeof")))) |
1742 | 0 | return false; |
1743 | 107 | return true; |
1744 | 107 | } |
1745 | | // pattern: %var%|.|[ |
1746 | 0 | static inline bool match182(const Token* tok) { |
1747 | 0 | if (!tok || !((tok->varId() != 0) || (tok->str() == MatchCompiler::makeConstString(".")) || ((tok->tokType() == Token::eExtendedOp || tok->tokType() == Token::eLambda) && tok->str() == MatchCompiler::makeConstString("[")))) |
1748 | 0 | return false; |
1749 | 0 | return true; |
1750 | 0 | } |
1751 | | // pattern: ?|!|&&|%oror%|%comp% |
1752 | 0 | static inline bool match183(const Token* tok) { |
1753 | 0 | if (!tok || !(((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("?")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!")) || ((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || (tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")) || tok->isComparisonOp())) |
1754 | 0 | return false; |
1755 | 0 | return true; |
1756 | 0 | } |
1757 | | // pattern: if|while|switch|sizeof ( |
1758 | 0 | static inline bool match184(const Token* tok) { |
1759 | 0 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("while")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("switch")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("sizeof")))) |
1760 | 0 | return false; |
1761 | 0 | tok = tok->next(); |
1762 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
1763 | 0 | return false; |
1764 | 0 | return true; |
1765 | 0 | } |
1766 | | // pattern: <|>|<=|>=|- |
1767 | 22.6k | static inline bool match185(const Token* tok) { |
1768 | 22.6k | if (!tok || !(((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<")) || ((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<=")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">=")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1769 | 21.9k | return false; |
1770 | 720 | return true; |
1771 | 22.6k | } |
1772 | | // pattern: - |
1773 | 0 | static inline bool match186(const Token* tok) { |
1774 | 0 | if (!tok || !((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-"))) |
1775 | 0 | return false; |
1776 | 0 | return true; |
1777 | 0 | } |
1778 | | // pattern: +|- |
1779 | 0 | static inline bool match187(const Token* tok) { |
1780 | 0 | if (!tok || !(((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("+")) || ((tok->tokType() == Token::eArithmeticalOp) && tok->str() == MatchCompiler::makeConstString("-")))) |
1781 | 0 | return false; |
1782 | 0 | return true; |
1783 | 0 | } |
1784 | | /* |
1785 | | * Cppcheck - A tool for static C/C++ code analysis |
1786 | | * Copyright (C) 2007-2024 Cppcheck team. |
1787 | | * |
1788 | | * This program is free software: you can redistribute it and/or modify |
1789 | | * it under the terms of the GNU General Public License as published by |
1790 | | * the Free Software Foundation, either version 3 of the License, or |
1791 | | * (at your option) any later version. |
1792 | | * |
1793 | | * This program is distributed in the hope that it will be useful, |
1794 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1795 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1796 | | * GNU General Public License for more details. |
1797 | | * |
1798 | | * You should have received a copy of the GNU General Public License |
1799 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1800 | | */ |
1801 | | |
1802 | | |
1803 | | //--------------------------------------------------------------------------- |
1804 | | #include "checkother.h" |
1805 | | |
1806 | | #include "astutils.h" |
1807 | | #include "fwdanalysis.h" |
1808 | | #include "library.h" |
1809 | | #include "mathlib.h" |
1810 | | #include "platform.h" |
1811 | | #include "settings.h" |
1812 | | #include "standards.h" |
1813 | | #include "symboldatabase.h" |
1814 | | #include "token.h" |
1815 | | #include "tokenize.h" |
1816 | | #include "tokenlist.h" |
1817 | | #include "utils.h" |
1818 | | #include "valueflow.h" |
1819 | | #include "vfvalue.h" |
1820 | | |
1821 | | #include <algorithm> // find_if() |
1822 | | #include <cctype> |
1823 | | #include <cstddef> |
1824 | | #include <cstdint> |
1825 | | #include <list> |
1826 | | #include <map> |
1827 | | #include <set> |
1828 | | #include <sstream> |
1829 | | #include <utility> |
1830 | | |
1831 | | //--------------------------------------------------------------------------- |
1832 | | |
1833 | | // Register this check class (by creating a static instance of it) |
1834 | | namespace { |
1835 | | CheckOther instance; |
1836 | | } |
1837 | | |
1838 | | static const CWE CWE128(128U); // Wrap-around Error |
1839 | | static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size |
1840 | | static const CWE CWE197(197U); // Numeric Truncation Error |
1841 | | static const CWE CWE362(362U); // Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') |
1842 | | static const CWE CWE369(369U); // Divide By Zero |
1843 | | static const CWE CWE398(398U); // Indicator of Poor Code Quality |
1844 | | static const CWE CWE475(475U); // Undefined Behavior for Input to API |
1845 | | static const CWE CWE561(561U); // Dead Code |
1846 | | static const CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') |
1847 | | static const CWE CWE570(570U); // Expression is Always False |
1848 | | static const CWE CWE571(571U); // Expression is Always True |
1849 | | static const CWE CWE672(672U); // Operation on a Resource after Expiration or Release |
1850 | | static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments |
1851 | | static const CWE CWE683(683U); // Function Call With Incorrect Order of Arguments |
1852 | | static const CWE CWE704(704U); // Incorrect Type Conversion or Cast |
1853 | | static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior |
1854 | | static const CWE CWE768(768U); // Incorrect Short Circuit Evaluation |
1855 | | static const CWE CWE783(783U); // Operator Precedence Logic Error |
1856 | | |
1857 | | //---------------------------------------------------------------------------------- |
1858 | | // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. |
1859 | | // If this return value is stored in a character variable and then compared |
1860 | | // to EOF, which is an integer, the comparison maybe be false. |
1861 | | // |
1862 | | // Reference: |
1863 | | // - Ticket #160 |
1864 | | // - http://www.cplusplus.com/reference/cstdio/fgetc/ |
1865 | | // - http://www.cplusplus.com/reference/cstdio/getc/ |
1866 | | // - http://www.cplusplus.com/reference/cstdio/getchar/ |
1867 | | // - http://www.cplusplus.com/reference/cstdio/ungetc/ ... |
1868 | | //---------------------------------------------------------------------------------- |
1869 | | void CheckOther::checkCastIntToCharAndBack() |
1870 | 763 | { |
1871 | 763 | if (!mSettings->severity.isEnabled(Severity::warning)) |
1872 | 0 | return; |
1873 | | |
1874 | 763 | logChecker("CheckOther::checkCastIntToCharAndBack"); // warning |
1875 | | |
1876 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
1877 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
1878 | 1.10k | std::map<int, std::string> vars; |
1879 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { |
1880 | | // Quick check to see if any of the matches below have any chances |
1881 | 21.5k | if (!match1(tok)) |
1882 | 20.6k | continue; |
1883 | 889 | if (match2(tok)) { |
1884 | 0 | const Variable *var = tok->variable(); |
1885 | 0 | if (var && var->typeEndToken()->str() == MatchCompiler::makeConstString("char") && !var->typeEndToken()->isSigned()) { |
1886 | 0 | vars[tok->varId()] = tok->strAt(2); |
1887 | 0 | } |
1888 | 889 | } else if (match3(tok)) { |
1889 | 0 | tok = tok->tokAt(3); |
1890 | 0 | const Variable *var = tok->variable(); |
1891 | 0 | if (var && var->typeEndToken()->str() == MatchCompiler::makeConstString("char") && !var->typeEndToken()->isSigned()) { |
1892 | 0 | checkCastIntToCharAndBackError(tok, tok->strAt(2)); |
1893 | 0 | } |
1894 | 889 | } else if (tok->isCpp() && (match4(tok) || match5(tok))) { |
1895 | 0 | tok = tok->tokAt(3); |
1896 | 0 | const Variable *var = tok->variable(); |
1897 | 0 | if (var && var->typeEndToken()->str() == MatchCompiler::makeConstString("char") && !var->typeEndToken()->isSigned()) { |
1898 | 0 | checkCastIntToCharAndBackError(tok, "cin.get"); |
1899 | 0 | } |
1900 | 889 | } else if (tok->isCpp() && (match6(tok) || match7(tok))) { |
1901 | 0 | const Variable *var = tok->variable(); |
1902 | 0 | if (var && var->typeEndToken()->str() == MatchCompiler::makeConstString("char") && !var->typeEndToken()->isSigned()) { |
1903 | 0 | vars[tok->varId()] = "cin.get"; |
1904 | 0 | } |
1905 | 889 | } else if (match8(tok)) { |
1906 | 0 | if (vars.find(tok->varId()) != vars.end()) { |
1907 | 0 | checkCastIntToCharAndBackError(tok, vars[tok->varId()]); |
1908 | 0 | } |
1909 | 889 | } else if (match9(tok)) { |
1910 | 0 | tok = tok->tokAt(2); |
1911 | 0 | if (vars.find(tok->varId()) != vars.end()) { |
1912 | 0 | checkCastIntToCharAndBackError(tok, vars[tok->varId()]); |
1913 | 0 | } |
1914 | 0 | } |
1915 | 889 | } |
1916 | 1.10k | } |
1917 | 763 | } |
1918 | | |
1919 | | void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) |
1920 | 0 | { |
1921 | 0 | reportError( |
1922 | 0 | tok, |
1923 | 0 | Severity::warning, |
1924 | 0 | "checkCastIntToCharAndBack", |
1925 | 0 | "$symbol:" + strFunctionName + "\n" |
1926 | 0 | "Storing $symbol() return value in char variable and then comparing with EOF.\n" |
1927 | 0 | "When saving $symbol() return value in char variable there is loss of precision. " |
1928 | 0 | " When $symbol() returns EOF this value is truncated. Comparing the char " |
1929 | 0 | "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" " |
1930 | 0 | "loops forever on some compilers/platforms and on other compilers/platforms it will stop " |
1931 | 0 | "when the file contains a matching character.", CWE197, Certainty::normal |
1932 | 0 | ); |
1933 | 0 | } |
1934 | | |
1935 | | |
1936 | | //--------------------------------------------------------------------------- |
1937 | | // Clarify calculation precedence for ternary operators. |
1938 | | //--------------------------------------------------------------------------- |
1939 | | void CheckOther::clarifyCalculation() |
1940 | 763 | { |
1941 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("clarifyCalculation")) |
1942 | 0 | return; |
1943 | | |
1944 | 763 | logChecker("CheckOther::clarifyCalculation"); // style |
1945 | | |
1946 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
1947 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
1948 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
1949 | | // ? operator where lhs is arithmetical expression |
1950 | 21.5k | if (tok->str() != MatchCompiler::makeConstString("?") || !tok->astOperand1() || !tok->astOperand1()->isCalculation()) |
1951 | 21.5k | continue; |
1952 | 0 | if (!tok->astOperand1()->isArithmeticalOp() && tok->astOperand1()->tokType() != Token::eBitOp) |
1953 | 0 | continue; |
1954 | | |
1955 | | // non-pointer calculation in lhs and pointer in rhs => no clarification is needed |
1956 | 0 | if (tok->astOperand1()->isBinaryOp() && match10(tok->astOperand1()) && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer > 0) |
1957 | 0 | continue; |
1958 | | |
1959 | | // bit operation in lhs and char literals in rhs => probably no mistake |
1960 | 0 | if (tok->astOperand1()->tokType() == Token::eBitOp && match11(tok->astOperand2()->astOperand1()) && match11(tok->astOperand2()->astOperand2())) |
1961 | 0 | continue; |
1962 | | |
1963 | | // 2nd operand in lhs has known integer value => probably no mistake |
1964 | 0 | if (tok->astOperand1()->isBinaryOp() && tok->astOperand1()->astOperand2()->hasKnownIntValue()) { |
1965 | 0 | const Token *op = tok->astOperand1()->astOperand2(); |
1966 | 0 | if (op->isNumber()) |
1967 | 0 | continue; |
1968 | 0 | if (op->valueType() && op->valueType()->isEnum()) |
1969 | 0 | continue; |
1970 | 0 | } |
1971 | | |
1972 | | // Is code clarified by parentheses already? |
1973 | 0 | const Token *tok2 = tok->astOperand1(); |
1974 | 0 | for (; tok2; tok2 = tok2->next()) { |
1975 | 0 | if (tok2->str() == MatchCompiler::makeConstString("(")) |
1976 | 0 | tok2 = tok2->link(); |
1977 | 0 | else if (tok2->str() == MatchCompiler::makeConstString(")")) |
1978 | 0 | break; |
1979 | 0 | else if (tok2->str() == MatchCompiler::makeConstString("?")) { |
1980 | 0 | clarifyCalculationError(tok, tok->astOperand1()->str()); |
1981 | 0 | break; |
1982 | 0 | } |
1983 | 0 | } |
1984 | 0 | } |
1985 | 1.10k | } |
1986 | 763 | } |
1987 | | |
1988 | | void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) |
1989 | 0 | { |
1990 | | // suspicious calculation |
1991 | 0 | const std::string calc("'a" + op + "b?c:d'"); |
1992 | | |
1993 | | // recommended calculation #1 |
1994 | 0 | const std::string s1("'(a" + op + "b)?c:d'"); |
1995 | | |
1996 | | // recommended calculation #2 |
1997 | 0 | const std::string s2("'a" + op + "(b?c:d)'"); |
1998 | |
|
1999 | 0 | reportError(tok, |
2000 | 0 | Severity::style, |
2001 | 0 | "clarifyCalculation", |
2002 | 0 | "Clarify calculation precedence for '" + op + "' and '?'.\n" |
2003 | 0 | "Suspicious calculation. Please use parentheses to clarify the code. " |
2004 | 0 | "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'.", CWE783, Certainty::normal); |
2005 | 0 | } |
2006 | | |
2007 | | //--------------------------------------------------------------------------- |
2008 | | // Clarify (meaningless) statements like *foo++; with parentheses. |
2009 | | //--------------------------------------------------------------------------- |
2010 | | void CheckOther::clarifyStatement() |
2011 | 763 | { |
2012 | 763 | if (!mSettings->severity.isEnabled(Severity::warning)) |
2013 | 0 | return; |
2014 | | |
2015 | 763 | logChecker("CheckOther::clarifyStatement"); // warning |
2016 | | |
2017 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2018 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
2019 | 23.7k | for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { |
2020 | 22.6k | if (tok->astOperand1() && match12(tok)) { |
2021 | 90 | const Token *tok2 = tok->previous(); |
2022 | | |
2023 | 90 | while (tok2 && tok2->str() == MatchCompiler::makeConstString("*")) |
2024 | 0 | tok2 = tok2->previous(); |
2025 | | |
2026 | 90 | if (tok2 && !tok2->astParent() && match13(tok2)) { |
2027 | 0 | tok2 = tok->astOperand1(); |
2028 | 0 | if (match14(tok2)) |
2029 | 0 | clarifyStatementError(tok2); |
2030 | 0 | } |
2031 | 90 | } |
2032 | 22.6k | } |
2033 | 1.10k | } |
2034 | 763 | } |
2035 | | |
2036 | | void CheckOther::clarifyStatementError(const Token *tok) |
2037 | 0 | { |
2038 | 0 | reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n" |
2039 | 0 | "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " |
2040 | 0 | "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?", CWE783, Certainty::normal); |
2041 | 0 | } |
2042 | | |
2043 | | //--------------------------------------------------------------------------- |
2044 | | // Check for suspicious occurrences of 'if(); {}'. |
2045 | | //--------------------------------------------------------------------------- |
2046 | | void CheckOther::checkSuspiciousSemicolon() |
2047 | 763 | { |
2048 | 763 | if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) |
2049 | 0 | return; |
2050 | | |
2051 | 763 | const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); |
2052 | | |
2053 | 763 | logChecker("CheckOther::checkSuspiciousSemicolon"); // warning,inconclusive |
2054 | | |
2055 | | // Look for "if(); {}", "for(); {}" or "while(); {}" |
2056 | 2.79k | for (const Scope &scope : symbolDatabase->scopeList) { |
2057 | 2.79k | if (scope.type == Scope::eIf || scope.type == Scope::eElse || scope.type == Scope::eWhile || scope.type == Scope::eFor) { |
2058 | | // Ensure the semicolon is at the same line number as the if/for/while statement |
2059 | | // and the {..} block follows it without an extra empty line. |
2060 | 931 | if (match15(scope.bodyStart) && |
2061 | 931 | scope.bodyStart->previous()->linenr() == scope.bodyStart->tokAt(2)->linenr() && |
2062 | 931 | scope.bodyStart->linenr()+1 >= scope.bodyStart->tokAt(3)->linenr() && |
2063 | 931 | !scope.bodyStart->tokAt(3)->isExpandedMacro()) { |
2064 | 0 | suspiciousSemicolonError(scope.classDef); |
2065 | 0 | } |
2066 | 931 | } |
2067 | 2.79k | } |
2068 | 763 | } |
2069 | | |
2070 | | void CheckOther::suspiciousSemicolonError(const Token* tok) |
2071 | 0 | { |
2072 | 0 | reportError(tok, Severity::warning, "suspiciousSemicolon", |
2073 | 0 | "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal); |
2074 | 0 | } |
2075 | | |
2076 | | |
2077 | | //--------------------------------------------------------------------------- |
2078 | | // For C++ code, warn if C-style casts are used on pointer types |
2079 | | //--------------------------------------------------------------------------- |
2080 | | void CheckOther::warningOldStylePointerCast() |
2081 | 763 | { |
2082 | | // Only valid on C++ code |
2083 | 763 | if (!mTokenizer->isCPP()) |
2084 | 0 | return; |
2085 | | |
2086 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("cstyleCast")) |
2087 | 0 | return; |
2088 | | |
2089 | 763 | logChecker("CheckOther::warningOldStylePointerCast"); // style,c++ |
2090 | | |
2091 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2092 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
2093 | 1.10k | const Token* tok; |
2094 | 1.10k | if (scope->function && scope->function->isConstructor()) |
2095 | 0 | tok = scope->classDef; |
2096 | 1.10k | else |
2097 | 1.10k | tok = scope->bodyStart; |
2098 | 23.7k | for (; tok && tok != scope->bodyEnd; tok = tok->next()) { |
2099 | | // Old style pointer casting.. |
2100 | 22.6k | if (tok->str() != MatchCompiler::makeConstString("(")) |
2101 | 21.6k | continue; |
2102 | 990 | const Token* castTok = tok->next(); |
2103 | 1.22k | while (match16(castTok)) { |
2104 | 237 | castTok = castTok->next(); |
2105 | 237 | if (match17(castTok) && castTok->link()) |
2106 | 0 | castTok = castTok->link()->next(); |
2107 | 237 | } |
2108 | 990 | if (castTok == tok->next()) |
2109 | 753 | continue; |
2110 | 237 | bool isPtr = false, isRef = false; |
2111 | 251 | while (match18(castTok)) { |
2112 | 14 | if (castTok->str() == MatchCompiler::makeConstString("*")) |
2113 | 4 | isPtr = true; |
2114 | 10 | else if (castTok->str() == MatchCompiler::makeConstString("&")) |
2115 | 10 | isRef = true; |
2116 | 14 | castTok = castTok->next(); |
2117 | 14 | } |
2118 | 237 | if ((!isPtr && !isRef) || !match19(castTok)) |
2119 | 237 | continue; |
2120 | | |
2121 | 0 | if (match20(tok->previous())) |
2122 | 0 | continue; |
2123 | | |
2124 | | // skip first "const" in "const Type* const" |
2125 | 0 | while (match21(tok->next())) |
2126 | 0 | tok = tok->next(); |
2127 | 0 | const Token* typeTok = tok->next(); |
2128 | | // skip second "const" in "const Type* const" |
2129 | 0 | if (tok->strAt(3) == MatchCompiler::makeConstString("const")) |
2130 | 0 | tok = tok->next(); |
2131 | |
|
2132 | 0 | const Token *p = tok->tokAt(4); |
2133 | 0 | if (p->hasKnownIntValue() && p->values().front().intvalue==0) // Casting nullpointers is safe |
2134 | 0 | continue; |
2135 | | |
2136 | 0 | if (typeTok->tokType() == Token::eType || typeTok->tokType() == Token::eName) |
2137 | 0 | cstyleCastError(tok, isPtr); |
2138 | 0 | } |
2139 | 1.10k | } |
2140 | 763 | } |
2141 | | |
2142 | | void CheckOther::cstyleCastError(const Token *tok, bool isPtr) |
2143 | 0 | { |
2144 | 0 | const std::string type = isPtr ? "pointer" : "reference"; |
2145 | 0 | reportError(tok, Severity::style, "cstyleCast", |
2146 | 0 | "C-style " + type + " casting\n" |
2147 | 0 | "C-style " + type + " casting detected. C++ offers four different kinds of casts as replacements: " |
2148 | 0 | "static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to " |
2149 | 0 | "any of those automatically, thus it is considered safer if the programmer explicitly states " |
2150 | 0 | "which kind of cast is expected.", CWE398, Certainty::normal); |
2151 | 0 | } |
2152 | | |
2153 | | void CheckOther::suspiciousFloatingPointCast() |
2154 | 763 | { |
2155 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("suspiciousFloatingPointCast")) |
2156 | 0 | return; |
2157 | | |
2158 | 763 | logChecker("CheckOther::suspiciousFloatingPointCast"); // style |
2159 | | |
2160 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2161 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
2162 | 1.10k | const Token* tok = scope->bodyStart; |
2163 | 1.10k | if (scope->function && scope->function->isConstructor()) |
2164 | 0 | tok = scope->classDef; |
2165 | 23.7k | for (; tok && tok != scope->bodyEnd; tok = tok->next()) { |
2166 | | |
2167 | 22.6k | if (!tok->isCast()) |
2168 | 22.6k | continue; |
2169 | | |
2170 | 1 | const ValueType* vt = tok->valueType(); |
2171 | 1 | if (!vt || vt->pointer || vt->reference != Reference::None || (vt->type != ValueType::FLOAT && vt->type != ValueType::DOUBLE)) |
2172 | 1 | continue; |
2173 | | |
2174 | 0 | using VTT = std::vector<ValueType::Type>; |
2175 | 0 | const VTT sourceTypes = vt->type == ValueType::FLOAT ? VTT{ ValueType::DOUBLE, ValueType::LONGDOUBLE } : VTT{ ValueType::LONGDOUBLE }; |
2176 | |
|
2177 | 0 | const Token* source = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); |
2178 | 0 | if (!source || !source->valueType() || std::find(sourceTypes.begin(), sourceTypes.end(), source->valueType()->type) == sourceTypes.end()) |
2179 | 0 | continue; |
2180 | | |
2181 | 0 | const Token* parent = tok->astParent(); |
2182 | 0 | if (!parent) |
2183 | 0 | continue; |
2184 | | |
2185 | 0 | const ValueType* parentVt = parent->valueType(); |
2186 | 0 | if (!parentVt || parent->str() == MatchCompiler::makeConstString("(")) { |
2187 | 0 | int argn{}; |
2188 | 0 | if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { |
2189 | 0 | if (ftok->function()) { |
2190 | 0 | if (const Variable* argVar = ftok->function()->getArgumentVar(argn)) |
2191 | 0 | parentVt = argVar->valueType(); |
2192 | 0 | } |
2193 | 0 | } |
2194 | 0 | } |
2195 | 0 | if (!parentVt || std::find(sourceTypes.begin(), sourceTypes.end(), parentVt->type) == sourceTypes.end()) |
2196 | 0 | continue; |
2197 | | |
2198 | 0 | suspiciousFloatingPointCastError(tok); |
2199 | 0 | } |
2200 | 1.10k | } |
2201 | 763 | } |
2202 | | |
2203 | | void CheckOther::suspiciousFloatingPointCastError(const Token* tok) |
2204 | 0 | { |
2205 | 0 | reportError(tok, Severity::style, "suspiciousFloatingPointCast", |
2206 | 0 | "Floating-point cast causes loss of precision.\n" |
2207 | 0 | "If this cast is not intentional, remove it to avoid loss of precision", CWE398, Certainty::normal); |
2208 | 0 | } |
2209 | | |
2210 | | //--------------------------------------------------------------------------- |
2211 | | // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation |
2212 | | //--------------------------------------------------------------------------- |
2213 | | |
2214 | | void CheckOther::invalidPointerCast() |
2215 | 763 | { |
2216 | 763 | if (!mSettings->severity.isEnabled(Severity::portability)) |
2217 | 0 | return; |
2218 | | |
2219 | 763 | logChecker("CheckOther::invalidPointerCast"); // portability |
2220 | | |
2221 | 763 | const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
2222 | 763 | const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); |
2223 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
2224 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
2225 | 21.5k | const Token* toTok = nullptr; |
2226 | 21.5k | const Token* fromTok = nullptr; |
2227 | | // Find cast |
2228 | 21.5k | if (match22(tok)) { |
2229 | 0 | toTok = tok; |
2230 | 0 | fromTok = tok->astOperand1(); |
2231 | 21.5k | } else if (match23(tok) && tok->linkAt(1)) { |
2232 | 0 | toTok = tok->linkAt(1)->next(); |
2233 | 0 | fromTok = toTok->astOperand2(); |
2234 | 0 | } |
2235 | 21.5k | if (!fromTok) |
2236 | 21.5k | continue; |
2237 | | |
2238 | 0 | const ValueType* fromType = fromTok->valueType(); |
2239 | 0 | const ValueType* toType = toTok->valueType(); |
2240 | 0 | if (!fromType || !toType || !fromType->pointer || !toType->pointer) |
2241 | 0 | continue; |
2242 | | |
2243 | 0 | if (fromType->type != toType->type && fromType->type >= ValueType::Type::BOOL && toType->type >= ValueType::Type::BOOL && (toType->type != ValueType::Type::CHAR || printInconclusive)) { |
2244 | 0 | if (toType->isIntegral() && fromType->isIntegral()) |
2245 | 0 | continue; |
2246 | | |
2247 | 0 | invalidPointerCastError(tok, fromType->str(), toType->str(), toType->type == ValueType::Type::CHAR, toType->isIntegral()); |
2248 | 0 | } |
2249 | 0 | } |
2250 | 1.10k | } |
2251 | 763 | } |
2252 | | |
2253 | | |
2254 | | void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) |
2255 | 0 | { |
2256 | 0 | if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation |
2257 | 0 | reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive ? Certainty::inconclusive : Certainty::normal); |
2258 | 0 | } else |
2259 | 0 | reportError(tok, Severity::portability, "invalidPointerCast", "Casting between " + from + " and " + to + " which have an incompatible binary data representation.", CWE704, Certainty::normal); |
2260 | 0 | } |
2261 | | |
2262 | | |
2263 | | //--------------------------------------------------------------------------- |
2264 | | // Detect redundant assignments: x = 0; x = 4; |
2265 | | //--------------------------------------------------------------------------- |
2266 | | |
2267 | | void CheckOther::checkRedundantAssignment() |
2268 | 763 | { |
2269 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && |
2270 | 763 | !mSettings->isPremiumEnabled("redundantAssignment") && |
2271 | 763 | !mSettings->isPremiumEnabled("redundantAssignInSwitch")) |
2272 | 0 | return; |
2273 | | |
2274 | 763 | logChecker("CheckOther::checkRedundantAssignment"); // style |
2275 | | |
2276 | 763 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
2277 | 1.10k | for (const Scope *scope : symbolDatabase->functionScopes) { |
2278 | 1.10k | if (!scope->bodyStart) |
2279 | 0 | continue; |
2280 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
2281 | 21.5k | if (match24(tok)) |
2282 | | // todo: handle lambdas |
2283 | 0 | break; |
2284 | 21.5k | if (match25(tok)) |
2285 | | // todo: check try blocks |
2286 | 0 | tok = tok->linkAt(1); |
2287 | 21.5k | if ((tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) && tok->astOperand1()) { |
2288 | 2.87k | if (tok->astParent()) |
2289 | 881 | continue; |
2290 | | |
2291 | | // Do not warn about redundant initialization when rhs is trivial |
2292 | | // TODO : do not simplify the variable declarations |
2293 | 1.99k | bool isInitialization = false; |
2294 | 1.99k | if (match26(tok->tokAt(-2)) && tok->tokAt(-2)->isSplittedVarDeclEq()) { |
2295 | 0 | isInitialization = true; |
2296 | 0 | bool trivial = true; |
2297 | 0 | visitAstNodes(tok->astOperand2(), |
2298 | 0 | [&](const Token *rhs) { |
2299 | 0 | if (match27(rhs)) |
2300 | 0 | return ChildrenToVisit::none; |
2301 | 0 | if (match28(rhs) && !rhs->varId()) |
2302 | 0 | return ChildrenToVisit::none; |
2303 | 0 | if (match29(rhs) && rhs->hasKnownIntValue()) |
2304 | 0 | return ChildrenToVisit::none; |
2305 | 0 | if (rhs->isCast()) |
2306 | 0 | return ChildrenToVisit::op2; |
2307 | 0 | trivial = false; |
2308 | 0 | return ChildrenToVisit::done; |
2309 | 0 | }); |
2310 | 0 | if (trivial) |
2311 | 0 | continue; |
2312 | 0 | } |
2313 | | |
2314 | 1.99k | const Token* rhs = tok->astOperand2(); |
2315 | | // Do not warn about assignment with 0 / NULL |
2316 | 1.99k | if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) |
2317 | 3 | continue; |
2318 | | |
2319 | 1.99k | if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) |
2320 | | // todo: check references |
2321 | 0 | continue; |
2322 | | |
2323 | 1.99k | if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) |
2324 | | // todo: check static variables |
2325 | 0 | continue; |
2326 | | |
2327 | 1.99k | bool inconclusive = false; |
2328 | 1.99k | if (tok->isCpp() && tok->astOperand1()->valueType()) { |
2329 | | // If there is a custom assignment operator => this is inconclusive |
2330 | 14 | if (tok->astOperand1()->valueType()->typeScope) { |
2331 | 0 | const std::string op = "operator" + tok->str(); |
2332 | 0 | const std::list<Function>& fList = tok->astOperand1()->valueType()->typeScope->functionList; |
2333 | 0 | inconclusive = std::any_of(fList.cbegin(), fList.cend(), [&](const Function& f) { |
2334 | 0 | return f.name() == op; |
2335 | 0 | }); |
2336 | 0 | } |
2337 | | // assigning a smart pointer has side effects |
2338 | 14 | if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER) |
2339 | 0 | break; |
2340 | 14 | } |
2341 | 1.99k | if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) |
2342 | 0 | continue; |
2343 | | |
2344 | 1.99k | FwdAnalysis fwdAnalysis(*mSettings); |
2345 | 1.99k | if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) |
2346 | 8 | continue; |
2347 | | |
2348 | | // Is there a redundant assignment? |
2349 | 1.98k | const Token *start; |
2350 | 1.98k | if (tok->isAssignmentOp()) |
2351 | 1.98k | start = tok->next(); |
2352 | 0 | else |
2353 | 0 | start = tok->findExpressionStartEndTokens().second->next(); |
2354 | | |
2355 | 1.98k | const Token * tokenToCheck = tok->astOperand1(); |
2356 | | |
2357 | | // Check if we are working with union |
2358 | 1.98k | for (const Token* tempToken = tokenToCheck; match30(tempToken);) { |
2359 | 0 | tempToken = tempToken->astOperand1(); |
2360 | 0 | if (tempToken && tempToken->variable() && tempToken->variable()->type() && tempToken->variable()->type()->isUnionType()) |
2361 | 0 | tokenToCheck = tempToken; |
2362 | 0 | } |
2363 | | |
2364 | 1.98k | if (start->hasKnownSymbolicValue(tokenToCheck) && match31(start->astParent()) && !diag(tok)) { |
2365 | 0 | const ValueFlow::Value* val = start->getKnownValue(ValueFlow::Value::ValueType::SYMBOLIC); |
2366 | 0 | if (val->intvalue == 0) // no offset |
2367 | 0 | redundantAssignmentSameValueError(tokenToCheck, val, tok->astOperand1()->expressionString()); |
2368 | 0 | } |
2369 | | |
2370 | | // Get next assignment.. |
2371 | 1.98k | const Token *nextAssign = fwdAnalysis.reassign(tokenToCheck, start, scope->bodyEnd); |
2372 | | // extra check for union |
2373 | 1.98k | if (nextAssign && tokenToCheck != tok->astOperand1()) |
2374 | 0 | nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); |
2375 | | |
2376 | 1.98k | if (!nextAssign) |
2377 | 1.98k | continue; |
2378 | | |
2379 | | // there is redundant assignment. Is there a case between the assignments? |
2380 | 0 | bool hasCase = false; |
2381 | 0 | for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { |
2382 | 0 | if (tok2->str() == MatchCompiler::makeConstString("break") || tok2->str() == MatchCompiler::makeConstString("return")) |
2383 | 0 | break; |
2384 | 0 | if (tok2->str() == MatchCompiler::makeConstString("case")) { |
2385 | 0 | hasCase = true; |
2386 | 0 | break; |
2387 | 0 | } |
2388 | 0 | } |
2389 | | |
2390 | | // warn |
2391 | 0 | if (hasCase) |
2392 | 0 | redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); |
2393 | 0 | else if (isInitialization) |
2394 | 0 | redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); |
2395 | 0 | else { |
2396 | 0 | diag(nextAssign); |
2397 | 0 | redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); |
2398 | 0 | } |
2399 | 0 | } |
2400 | 21.5k | } |
2401 | 1.10k | } |
2402 | 763 | } |
2403 | | |
2404 | | void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) |
2405 | 0 | { |
2406 | 0 | const std::list<const Token *> callstack = { tok1, tok2 }; |
2407 | 0 | reportError(callstack, Severity::performance, "redundantCopy", |
2408 | 0 | "$symbol:" + var + "\n" |
2409 | 0 | "Buffer '$symbol' is being written before its old content has been used.", CWE563, Certainty::normal); |
2410 | 0 | } |
2411 | | |
2412 | | void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) |
2413 | 0 | { |
2414 | 0 | const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") }; |
2415 | 0 | if (inconclusive) |
2416 | 0 | reportError(errorPath, Severity::style, "redundantAssignment", |
2417 | 0 | "$symbol:" + var + "\n" |
2418 | 0 | "Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" |
2419 | 0 | "Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, Certainty::inconclusive); |
2420 | 0 | else |
2421 | 0 | reportError(errorPath, Severity::style, "redundantAssignment", |
2422 | 0 | "$symbol:" + var + "\n" |
2423 | 0 | "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, Certainty::normal); |
2424 | 0 | } |
2425 | | |
2426 | | void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) |
2427 | 0 | { |
2428 | 0 | const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") }; |
2429 | 0 | reportError(errorPath, Severity::style, "redundantInitialization", |
2430 | 0 | "$symbol:" + var + "\nRedundant initialization for '$symbol'. The initialized value is overwritten before it is read.", |
2431 | 0 | CWE563, |
2432 | 0 | inconclusive ? Certainty::inconclusive : Certainty::normal); |
2433 | 0 | } |
2434 | | |
2435 | | void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) |
2436 | 0 | { |
2437 | 0 | const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") }; |
2438 | 0 | reportError(errorPath, Severity::style, "redundantAssignInSwitch", |
2439 | 0 | "$symbol:" + var + "\n" |
2440 | 0 | "Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, Certainty::normal); |
2441 | 0 | } |
2442 | | |
2443 | | void CheckOther::redundantAssignmentSameValueError(const Token *tok, const ValueFlow::Value* val, const std::string &var) |
2444 | 0 | { |
2445 | 0 | auto errorPath = val->errorPath; |
2446 | 0 | errorPath.emplace_back(tok, ""); |
2447 | 0 | reportError(errorPath, Severity::style, "redundantAssignment", |
2448 | 0 | "$symbol:" + var + "\n" |
2449 | 0 | "Variable '$symbol' is assigned an expression that holds the same value.", CWE563, Certainty::normal); |
2450 | 0 | } |
2451 | | |
2452 | | |
2453 | | //--------------------------------------------------------------------------- |
2454 | | // switch (x) |
2455 | | // { |
2456 | | // case 2: |
2457 | | // y = a; // <- this assignment is redundant |
2458 | | // case 3: |
2459 | | // y = b; // <- case 2 falls through and sets y twice |
2460 | | // } |
2461 | | //--------------------------------------------------------------------------- |
2462 | | static inline bool isFunctionOrBreakPattern(const Token *tok) |
2463 | 0 | { |
2464 | 0 | return match32(tok) || match33(tok); |
2465 | 0 | } |
2466 | | |
2467 | | void CheckOther::redundantBitwiseOperationInSwitchError() |
2468 | 763 | { |
2469 | 763 | if (!mSettings->severity.isEnabled(Severity::warning)) |
2470 | 0 | return; |
2471 | | |
2472 | 763 | logChecker("CheckOther::redundantBitwiseOperationInSwitch"); // warning |
2473 | | |
2474 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2475 | | |
2476 | | // Find the beginning of a switch. E.g.: |
2477 | | // switch (var) { ... |
2478 | 2.79k | for (const Scope &switchScope : symbolDatabase->scopeList) { |
2479 | 2.79k | if (switchScope.type != Scope::eSwitch || !switchScope.bodyStart) |
2480 | 2.79k | continue; |
2481 | | |
2482 | | // Check the contents of the switch statement |
2483 | 0 | std::map<int, const Token*> varsWithBitsSet; |
2484 | 0 | std::map<int, std::string> bitOperations; |
2485 | |
|
2486 | 0 | for (const Token *tok2 = switchScope.bodyStart->next(); tok2 != switchScope.bodyEnd; tok2 = tok2->next()) { |
2487 | 0 | if (tok2->str() == MatchCompiler::makeConstString("{")) { |
2488 | | // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: |
2489 | | // case 3: b = 1; |
2490 | | // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional |
2491 | 0 | if (match34(tok2->previous()) && tok2->link()) { |
2492 | 0 | const Token* endOfConditional = tok2->link(); |
2493 | 0 | for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { |
2494 | 0 | if (tok3->varId() != 0) { |
2495 | 0 | varsWithBitsSet.erase(tok3->varId()); |
2496 | 0 | bitOperations.erase(tok3->varId()); |
2497 | 0 | } else if (isFunctionOrBreakPattern(tok3)) { |
2498 | 0 | varsWithBitsSet.clear(); |
2499 | 0 | bitOperations.clear(); |
2500 | 0 | } |
2501 | 0 | } |
2502 | 0 | tok2 = endOfConditional; |
2503 | 0 | } |
2504 | 0 | } |
2505 | | |
2506 | | // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: |
2507 | | // case 3: b = 1; // <== redundant |
2508 | | // case 4: b = 2; |
2509 | |
|
2510 | 0 | if (match35(tok2->previous())) { |
2511 | 0 | varsWithBitsSet.erase(tok2->varId()); |
2512 | 0 | bitOperations.erase(tok2->varId()); |
2513 | 0 | } |
2514 | | |
2515 | | // Bitwise operation. Report an error if it's performed twice before a break. E.g.: |
2516 | | // case 3: b |= 1; // <== redundant |
2517 | | // case 4: b |= 1; |
2518 | 0 | else if (match36(tok2->previous()) && |
2519 | 0 | (tok2->strAt(1) == MatchCompiler::makeConstString("|=") || tok2->strAt(1) == MatchCompiler::makeConstString("&=")) && |
2520 | 0 | match37(tok2->next()->astOperand2())) { |
2521 | 0 | const std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2); |
2522 | 0 | const auto i2 = utils::as_const(varsWithBitsSet).find(tok2->varId()); |
2523 | | |
2524 | | // This variable has not had a bit operation performed on it yet, so just make a note of it |
2525 | 0 | if (i2 == varsWithBitsSet.end()) { |
2526 | 0 | varsWithBitsSet[tok2->varId()] = tok2; |
2527 | 0 | bitOperations[tok2->varId()] = bitOp; |
2528 | 0 | } |
2529 | | |
2530 | | // The same bit operation has been performed on the same variable twice, so report an error |
2531 | 0 | else if (bitOperations[tok2->varId()] == bitOp) |
2532 | 0 | redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); |
2533 | | |
2534 | | // A different bit operation was performed on the variable, so clear it |
2535 | 0 | else { |
2536 | 0 | varsWithBitsSet.erase(tok2->varId()); |
2537 | 0 | bitOperations.erase(tok2->varId()); |
2538 | 0 | } |
2539 | 0 | } |
2540 | | |
2541 | | // Bitwise operation. Report an error if it's performed twice before a break. E.g.: |
2542 | | // case 3: b = b | 1; // <== redundant |
2543 | | // case 4: b = b | 1; |
2544 | 0 | else if (match38(tok2->previous()) && |
2545 | 0 | tok2->varId() == tok2->tokAt(2)->varId()) { |
2546 | 0 | const std::string bitOp = tok2->strAt(3) + tok2->strAt(4); |
2547 | 0 | const auto i2 = utils::as_const(varsWithBitsSet).find(tok2->varId()); |
2548 | | |
2549 | | // This variable has not had a bit operation performed on it yet, so just make a note of it |
2550 | 0 | if (i2 == varsWithBitsSet.end()) { |
2551 | 0 | varsWithBitsSet[tok2->varId()] = tok2; |
2552 | 0 | bitOperations[tok2->varId()] = bitOp; |
2553 | 0 | } |
2554 | | |
2555 | | // The same bit operation has been performed on the same variable twice, so report an error |
2556 | 0 | else if (bitOperations[tok2->varId()] == bitOp) |
2557 | 0 | redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); |
2558 | | |
2559 | | // A different bit operation was performed on the variable, so clear it |
2560 | 0 | else { |
2561 | 0 | varsWithBitsSet.erase(tok2->varId()); |
2562 | 0 | bitOperations.erase(tok2->varId()); |
2563 | 0 | } |
2564 | 0 | } |
2565 | | |
2566 | | // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: |
2567 | | // case 3: b = 1; |
2568 | | // case 4: b++; |
2569 | 0 | else if (tok2->varId() != 0 && tok2->strAt(1) != MatchCompiler::makeConstString("|") && tok2->strAt(1) != MatchCompiler::makeConstString("&")) { |
2570 | 0 | varsWithBitsSet.erase(tok2->varId()); |
2571 | 0 | bitOperations.erase(tok2->varId()); |
2572 | 0 | } |
2573 | | |
2574 | | // Reset our record of assignments if there is a break or function call. E.g.: |
2575 | | // case 3: b = 1; break; |
2576 | 0 | if (isFunctionOrBreakPattern(tok2)) { |
2577 | 0 | varsWithBitsSet.clear(); |
2578 | 0 | bitOperations.clear(); |
2579 | 0 | } |
2580 | 0 | } |
2581 | 0 | } |
2582 | 763 | } |
2583 | | |
2584 | | void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) |
2585 | 0 | { |
2586 | 0 | reportError(tok, Severity::style, |
2587 | 0 | "redundantBitwiseOperationInSwitch", |
2588 | 0 | "$symbol:" + varname + "\n" |
2589 | 0 | "Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?"); |
2590 | 0 | } |
2591 | | |
2592 | | |
2593 | | //--------------------------------------------------------------------------- |
2594 | | // Check for statements like case A||B: in switch() |
2595 | | //--------------------------------------------------------------------------- |
2596 | | void CheckOther::checkSuspiciousCaseInSwitch() |
2597 | 763 | { |
2598 | 763 | if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) |
2599 | 0 | return; |
2600 | | |
2601 | 763 | logChecker("CheckOther::checkSuspiciousCaseInSwitch"); // warning,inconclusive |
2602 | | |
2603 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2604 | | |
2605 | 2.79k | for (const Scope & scope : symbolDatabase->scopeList) { |
2606 | 2.79k | if (scope.type != Scope::eSwitch) |
2607 | 2.79k | continue; |
2608 | | |
2609 | 0 | for (const Token* tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { |
2610 | 0 | if (tok->str() == MatchCompiler::makeConstString("case")) { |
2611 | 0 | const Token* finding = nullptr; |
2612 | 0 | for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { |
2613 | 0 | if (tok2->str() == MatchCompiler::makeConstString(":")) |
2614 | 0 | break; |
2615 | 0 | if (match39(tok2)) |
2616 | 0 | break; |
2617 | | |
2618 | 0 | if (tok2->str() == MatchCompiler::makeConstString("?")) |
2619 | 0 | finding = nullptr; |
2620 | 0 | else if (match40(tok2)) |
2621 | 0 | finding = tok2; |
2622 | 0 | } |
2623 | 0 | if (finding) |
2624 | 0 | suspiciousCaseInSwitchError(finding, finding->str()); |
2625 | 0 | } |
2626 | 0 | } |
2627 | 0 | } |
2628 | 763 | } |
2629 | | |
2630 | | void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) |
2631 | 0 | { |
2632 | 0 | reportError(tok, Severity::warning, "suspiciousCase", |
2633 | 0 | "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" |
2634 | 0 | "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, Certainty::inconclusive); |
2635 | 0 | } |
2636 | | |
2637 | | static bool isNestedInSwitch(const Scope* scope) |
2638 | 0 | { |
2639 | 0 | while (scope) { |
2640 | 0 | if (scope->type == Scope::ScopeType::eSwitch) |
2641 | 0 | return true; |
2642 | 0 | if (scope->type == Scope::ScopeType::eUnconditional) { |
2643 | 0 | scope = scope->nestedIn; |
2644 | 0 | continue; |
2645 | 0 | } |
2646 | 0 | break; |
2647 | 0 | } |
2648 | 0 | return false; |
2649 | 0 | } |
2650 | | |
2651 | | static bool isVardeclInSwitch(const Token* tok) |
2652 | 0 | { |
2653 | 0 | if (!tok) |
2654 | 0 | return false; |
2655 | 0 | if (!isNestedInSwitch(tok->scope())) |
2656 | 0 | return false; |
2657 | 0 | if (const Token* end = findmatch41(tok) ) { |
2658 | 0 | for (const Token* tok2 = tok; tok2 != end; tok2 = tok2->next()) { |
2659 | 0 | if (tok2->isKeyword() && tok2->str() == MatchCompiler::makeConstString("case")) |
2660 | 0 | return false; |
2661 | 0 | if (tok2->variable() && tok2->variable()->nameToken() == tok2) { |
2662 | 0 | end = tok2->scope()->bodyEnd; |
2663 | 0 | for (const Token* tok3 = tok2; tok3 != end; tok3 = tok3->next()) { |
2664 | 0 | if (tok3->isKeyword()) |
2665 | 0 | return tok3->str() == MatchCompiler::makeConstString("case"); |
2666 | 0 | } |
2667 | 0 | return false; |
2668 | 0 | } |
2669 | 0 | } |
2670 | 0 | } |
2671 | 0 | return false; |
2672 | 0 | } |
2673 | | |
2674 | | //--------------------------------------------------------------------------- |
2675 | | // Find consecutive return, break, continue, goto or throw statements. e.g.: |
2676 | | // break; break; |
2677 | | // Detect dead code, that follows such a statement. e.g.: |
2678 | | // return(0); foo(); |
2679 | | //--------------------------------------------------------------------------- |
2680 | | void CheckOther::checkUnreachableCode() |
2681 | 763 | { |
2682 | | // misra-c-2012-2.1 |
2683 | | // misra-c-2023-2.1 |
2684 | | // misra-cpp-2008-0-1-1 |
2685 | | // autosar |
2686 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateBreak") && !mSettings->isPremiumEnabled("unreachableCode")) |
2687 | 0 | return; |
2688 | | |
2689 | 763 | logChecker("CheckOther::checkUnreachableCode"); // style |
2690 | | |
2691 | 763 | const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
2692 | 763 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
2693 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
2694 | 1.10k | if (scope->hasInlineOrLambdaFunction(nullptr, /*onlyInline*/ true)) |
2695 | 0 | continue; |
2696 | 14.6k | for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { |
2697 | 13.5k | const Token* secondBreak = nullptr; |
2698 | 13.5k | const Token* labelName = nullptr; |
2699 | 13.5k | if (tok->link() && match42(tok)) |
2700 | 727 | tok = tok->link(); |
2701 | 12.8k | else if (match43(tok)) |
2702 | 0 | secondBreak = tok->tokAt(2); |
2703 | 12.8k | else if (match44(tok) && tok->next()->isKeyword()) { |
2704 | 1.19k | if (match45(tok->astParent())) |
2705 | 0 | continue; |
2706 | 1.19k | tok = tok->next(); // tok should point to return or throw |
2707 | 2.95k | for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { |
2708 | 2.95k | if (tok2->str() == MatchCompiler::makeConstString("(") || tok2->str() == MatchCompiler::makeConstString("{")) |
2709 | 83 | tok2 = tok2->link(); |
2710 | 2.95k | if (tok2->str() == MatchCompiler::makeConstString(";")) { |
2711 | 1.19k | secondBreak = tok2->next(); |
2712 | 1.19k | break; |
2713 | 1.19k | } |
2714 | 2.95k | } |
2715 | 11.6k | } else if (match46(tok)) { |
2716 | 0 | secondBreak = tok->tokAt(3); |
2717 | 0 | labelName = tok->next(); |
2718 | 11.6k | } else if (match32(tok) && mSettings->library.isnoreturn(tok) && !match47(tok->next()->astParent())) { |
2719 | 0 | if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != MatchCompiler::makeConstString("{")) |
2720 | 0 | secondBreak = tok->linkAt(1)->tokAt(2); |
2721 | 0 | if (match48(secondBreak)) { |
2722 | | // clarification for tools that function returns |
2723 | 0 | continue; |
2724 | 0 | } |
2725 | 0 | } |
2726 | 13.5k | while (match49(secondBreak) && secondBreak->scope()->type == Scope::ScopeType::eUnconditional) |
2727 | 0 | secondBreak = secondBreak->next(); |
2728 | 13.5k | if (secondBreak && secondBreak->scope()->nestedIn && secondBreak->scope()->nestedIn->type == Scope::ScopeType::eSwitch && |
2729 | 13.5k | tok->str() == MatchCompiler::makeConstString("break")) { |
2730 | 0 | while (match50(secondBreak) && secondBreak->scope()->type == Scope::ScopeType::eUnconditional) |
2731 | 0 | secondBreak = secondBreak->next(); |
2732 | 0 | } |
2733 | 13.5k | while (match51(secondBreak)) |
2734 | 0 | secondBreak = secondBreak->next(); |
2735 | | |
2736 | | // Statements follow directly, no line between them. (#3383) |
2737 | | // TODO: Try to find a better way to avoid false positives due to preprocessor configurations. |
2738 | 13.5k | const bool inconclusive = secondBreak && (secondBreak->linenr() - 1 > secondBreak->previous()->linenr()); |
2739 | | |
2740 | 13.5k | if (secondBreak && (printInconclusive || !inconclusive)) { |
2741 | 1.19k | if (match52(secondBreak) && secondBreak->isKeyword()) { |
2742 | 0 | duplicateBreakError(secondBreak, inconclusive); |
2743 | 0 | tok = findmatch53(secondBreak) ; |
2744 | 1.19k | } else if (secondBreak->str() == MatchCompiler::makeConstString("break")) { // break inside switch as second break statement should not issue a warning |
2745 | 0 | if (tok->str() == MatchCompiler::makeConstString("break")) // If the previous was a break, too: Issue warning |
2746 | 0 | duplicateBreakError(secondBreak, inconclusive); |
2747 | 0 | else { |
2748 | 0 | if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch |
2749 | 0 | duplicateBreakError(secondBreak, inconclusive); |
2750 | 0 | } |
2751 | 0 | tok = findmatch53(secondBreak) ; |
2752 | 1.19k | } else if (!match54(secondBreak) && secondBreak->strAt(1) != MatchCompiler::makeConstString(":")) { // TODO: No bailout for unconditional scopes |
2753 | | // If the goto label is followed by a loop construct in which the label is defined it's quite likely |
2754 | | // that the goto jump was intended to skip some code on the first loop iteration. |
2755 | 0 | bool labelInFollowingLoop = false; |
2756 | 0 | if (labelName && match55(secondBreak)) { |
2757 | 0 | const Token *scope2 = findmatch56(secondBreak) ; |
2758 | 0 | if (scope2) { |
2759 | 0 | for (const Token *tokIter = scope2; tokIter != scope2->link() && tokIter; tokIter = tokIter->next()) { |
2760 | 0 | if (match57(tokIter) && labelName->str() == tokIter->strAt(1)) { |
2761 | 0 | labelInFollowingLoop = true; |
2762 | 0 | break; |
2763 | 0 | } |
2764 | 0 | } |
2765 | 0 | } |
2766 | 0 | } |
2767 | | |
2768 | | // hide FP for statements that just hide compiler warnings about unused function arguments |
2769 | 0 | bool silencedCompilerWarningOnly = false; |
2770 | 0 | const Token *silencedWarning = secondBreak; |
2771 | 0 | for (;;) { |
2772 | 0 | if (match58(silencedWarning)) { |
2773 | 0 | silencedWarning = silencedWarning->tokAt(5); |
2774 | 0 | continue; |
2775 | 0 | } |
2776 | 0 | if (silencedWarning && silencedWarning == scope->bodyEnd) |
2777 | 0 | silencedCompilerWarningOnly = true; |
2778 | 0 | break; |
2779 | 0 | } |
2780 | 0 | if (silencedWarning) |
2781 | 0 | secondBreak = silencedWarning; |
2782 | |
|
2783 | 0 | if (!labelInFollowingLoop && !silencedCompilerWarningOnly && !isVardeclInSwitch(secondBreak)) |
2784 | 0 | unreachableCodeError(secondBreak, tok, inconclusive); |
2785 | 0 | tok = findmatch53(secondBreak) ; |
2786 | 1.19k | } else if (secondBreak->scope() && secondBreak->scope()->isLoopScope() && secondBreak->str() == MatchCompiler::makeConstString("}") && tok->str() == MatchCompiler::makeConstString("continue")) { |
2787 | 0 | redundantContinueError(tok); |
2788 | 0 | tok = secondBreak; |
2789 | 0 | } else |
2790 | 1.19k | tok = secondBreak; |
2791 | | |
2792 | 1.19k | if (!tok) |
2793 | 0 | break; |
2794 | 1.19k | tok = tok->previous(); // Will be advanced again by for loop |
2795 | 1.19k | } |
2796 | 13.5k | } |
2797 | 1.10k | } |
2798 | 763 | } |
2799 | | |
2800 | | void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) |
2801 | 0 | { |
2802 | 0 | reportError(tok, Severity::style, "duplicateBreak", |
2803 | 0 | "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" |
2804 | 0 | "Consecutive return, break, continue, goto or throw statements are unnecessary. " |
2805 | 0 | "The second statement can never be executed, and so should be removed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); |
2806 | 0 | } |
2807 | | |
2808 | | void CheckOther::unreachableCodeError(const Token *tok, const Token* noreturn, bool inconclusive) |
2809 | 0 | { |
2810 | 0 | std::string msg = "Statements following "; |
2811 | 0 | if (noreturn && (noreturn->function() || mSettings->library.isnoreturn(noreturn))) |
2812 | 0 | msg += "noreturn function '" + noreturn->str() + "()'"; |
2813 | 0 | else if (noreturn && noreturn->isKeyword()) |
2814 | 0 | msg += "'" + noreturn->str() + "'"; |
2815 | 0 | else |
2816 | 0 | msg += "return, break, continue, goto or throw"; |
2817 | 0 | msg += " will never be executed."; |
2818 | 0 | reportError(tok, Severity::style, "unreachableCode", |
2819 | 0 | msg, CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); |
2820 | 0 | } |
2821 | | |
2822 | | void CheckOther::redundantContinueError(const Token *tok) |
2823 | 0 | { |
2824 | 0 | reportError(tok, Severity::style, "redundantContinue", |
2825 | 0 | "'continue' is redundant since it is the last statement in a loop.", CWE561, Certainty::normal); |
2826 | 0 | } |
2827 | | |
2828 | 0 | static bool isSimpleExpr(const Token* tok, const Variable* var, const Settings& settings) { |
2829 | 0 | if (!tok) |
2830 | 0 | return false; |
2831 | 0 | if (tok->isNumber() || tok->tokType() == Token::eString || tok->tokType() == Token::eChar || tok->isBoolean()) |
2832 | 0 | return true; |
2833 | 0 | bool needsCheck = tok->varId() > 0; |
2834 | 0 | if (!needsCheck) { |
2835 | 0 | if (tok->isArithmeticalOp()) |
2836 | 0 | return isSimpleExpr(tok->astOperand1(), var, settings) && (!tok->astOperand2() || isSimpleExpr(tok->astOperand2(), var, settings)); |
2837 | 0 | const Token* ftok = tok->previous(); |
2838 | 0 | if (match32(ftok) && |
2839 | 0 | ((ftok->function() && ftok->function()->isConst()) || settings.library.isFunctionConst(ftok->str(), /*pure*/ true))) |
2840 | 0 | needsCheck = true; |
2841 | 0 | else if (tok->str() == MatchCompiler::makeConstString("[")) { |
2842 | 0 | needsCheck = tok->astOperand1() && tok->astOperand1()->varId() > 0; |
2843 | 0 | tok = tok->astOperand1(); |
2844 | 0 | } |
2845 | 0 | else if (isLeafDot(tok->astOperand2())) { |
2846 | 0 | needsCheck = tok->astOperand2()->varId() > 0; |
2847 | 0 | tok = tok->astOperand2(); |
2848 | 0 | } |
2849 | 0 | } |
2850 | 0 | return (needsCheck && !findExpressionChanged(tok, tok->astParent(), var->scope()->bodyEnd, settings)); |
2851 | 0 | } |
2852 | | |
2853 | | //--------------------------------------------------------------------------- |
2854 | | // Check scope of variables.. |
2855 | | //--------------------------------------------------------------------------- |
2856 | | void CheckOther::checkVariableScope() |
2857 | 763 | { |
2858 | 763 | if (mSettings->clang) |
2859 | 0 | return; |
2860 | | |
2861 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("variableScope")) |
2862 | 0 | return; |
2863 | | |
2864 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
2865 | | |
2866 | | // In C it is common practice to declare local variables at the |
2867 | | // start of functions. |
2868 | 763 | if (mSettings->daca && mTokenizer->isC()) |
2869 | 0 | return; |
2870 | | |
2871 | 763 | logChecker("CheckOther::checkVariableScope"); // style,notclang |
2872 | | |
2873 | 4.57k | for (const Variable* var : symbolDatabase->variableList()) { |
2874 | 4.57k | if (!var || !var->isLocal() || var->isConst()) |
2875 | 4.57k | continue; |
2876 | | |
2877 | 0 | if (var->nameToken()->isExpandedMacro()) |
2878 | 0 | continue; |
2879 | | |
2880 | 0 | const bool isPtrOrRef = var->isPointer() || var->isReference(); |
2881 | 0 | const bool isSimpleType = var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType() || (var->typeStartToken()->isC() && var->type() && var->type()->isStructType()); |
2882 | 0 | if (!isPtrOrRef && !isSimpleType && !astIsContainer(var->nameToken())) |
2883 | 0 | continue; |
2884 | | |
2885 | 0 | if (mTokenizer->hasIfdef(var->nameToken(), var->scope()->bodyEnd)) |
2886 | 0 | continue; |
2887 | | |
2888 | | // reference of range for loop variable.. |
2889 | 0 | if (match59(var->nameToken()->previous())) { |
2890 | 0 | const Token *otherVarToken = var->nameToken()->tokAt(2); |
2891 | 0 | const Variable *otherVar = otherVarToken->variable(); |
2892 | 0 | if (otherVar && match60(otherVar->nameToken()) && |
2893 | 0 | otherVar->nameToken()->next()->astParent() && |
2894 | 0 | match61(otherVar->nameToken()->next()->astParent()->previous())) |
2895 | 0 | continue; |
2896 | 0 | } |
2897 | | |
2898 | 0 | bool forHead = false; // Don't check variables declared in header of a for loop |
2899 | 0 | for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { |
2900 | 0 | if (tok->str() == MatchCompiler::makeConstString("(")) { |
2901 | 0 | forHead = true; |
2902 | 0 | break; |
2903 | 0 | } |
2904 | 0 | if (match62(tok)) |
2905 | 0 | break; |
2906 | 0 | } |
2907 | 0 | if (forHead) |
2908 | 0 | continue; |
2909 | | |
2910 | 0 | const Token* tok = var->nameToken()->next(); |
2911 | 0 | bool isConstructor = false; |
2912 | 0 | if (match63(tok, var->declarationId())) { // bailout for assignment |
2913 | 0 | tok = tok->tokAt(2)->astOperand2(); |
2914 | 0 | if (!isSimpleExpr(tok, var, *mSettings)) |
2915 | 0 | continue; |
2916 | 0 | } |
2917 | 0 | else if (match64(tok)) { // bailout for constructor |
2918 | 0 | isConstructor = true; |
2919 | 0 | const Token* argTok = tok->astOperand2(); |
2920 | 0 | bool bail = false; |
2921 | 0 | while (argTok) { |
2922 | 0 | if (match65(argTok)) { |
2923 | 0 | if (!isSimpleExpr(argTok->astOperand2(), var, *mSettings)) { |
2924 | 0 | bail = true; |
2925 | 0 | break; |
2926 | 0 | } |
2927 | 0 | } else if (argTok->str() != MatchCompiler::makeConstString(".") && !isSimpleExpr(argTok, var, *mSettings)) { |
2928 | 0 | bail = true; |
2929 | 0 | break; |
2930 | 0 | } |
2931 | 0 | argTok = argTok->astOperand1(); |
2932 | 0 | } |
2933 | 0 | if (bail) |
2934 | 0 | continue; |
2935 | 0 | } |
2936 | | // bailout if initialized with function call that has possible side effects |
2937 | 0 | if (!isConstructor && match66(tok) && match67(tok->astOperand2())) |
2938 | 0 | continue; |
2939 | 0 | bool reduce = true; |
2940 | 0 | bool used = false; // Don't warn about unused variables |
2941 | 0 | for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { |
2942 | 0 | if (tok->str() == MatchCompiler::makeConstString("{") && tok->scope() != tok->previous()->scope() && !tok->isExpandedMacro() && !isWithinScope(tok, var, Scope::ScopeType::eLambda)) { |
2943 | 0 | if (used) { |
2944 | 0 | bool used2 = false; |
2945 | 0 | if (!checkInnerScope(tok, var, used2) || used2) { |
2946 | 0 | reduce = false; |
2947 | 0 | break; |
2948 | 0 | } |
2949 | 0 | } else if (!checkInnerScope(tok, var, used)) { |
2950 | 0 | reduce = false; |
2951 | 0 | break; |
2952 | 0 | } |
2953 | | |
2954 | 0 | tok = tok->link(); |
2955 | | |
2956 | | // parse else if blocks.. |
2957 | 0 | } else if (match68(tok) && match69(tok->linkAt(3))) { |
2958 | 0 | tok = tok->next(); |
2959 | 0 | } else if (tok->varId() == var->declarationId() || tok->str() == MatchCompiler::makeConstString("goto")) { |
2960 | 0 | reduce = false; |
2961 | 0 | break; |
2962 | 0 | } |
2963 | 0 | } |
2964 | |
|
2965 | 0 | if (reduce && used) |
2966 | 0 | variableScopeError(var->nameToken(), var->name()); |
2967 | 0 | } |
2968 | 763 | } |
2969 | | |
2970 | | // used to check if an argument to a function might depend on another argument |
2971 | | static bool mayDependOn(const ValueType *other, const ValueType *original) |
2972 | 0 | { |
2973 | 0 | if (!other || !original) |
2974 | 0 | return false; |
2975 | | |
2976 | | // other must be pointer |
2977 | 0 | if (!other->pointer) |
2978 | 0 | return false; |
2979 | | |
2980 | | // must be same underlying type |
2981 | 0 | if (other->type != original->type) |
2982 | 0 | return false; |
2983 | | |
2984 | 0 | const int otherPtr = other->pointer + (other->reference == Reference::LValue ? 1 : 0); |
2985 | 0 | const int originalPtr = original->pointer; |
2986 | |
|
2987 | 0 | if (otherPtr == originalPtr) { |
2988 | | // if other is not const than original may be copied to other |
2989 | 0 | return !other->isConst(otherPtr); |
2990 | 0 | } |
2991 | | |
2992 | | // other may be reassigned to original |
2993 | 0 | return otherPtr > originalPtr; |
2994 | 0 | } |
2995 | | |
2996 | | bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) const |
2997 | 0 | { |
2998 | 0 | const Scope* scope = tok->next()->scope(); |
2999 | 0 | bool loopVariable = scope->isLoopScope(); |
3000 | 0 | bool noContinue = true; |
3001 | 0 | const Token* forHeadEnd = nullptr; |
3002 | 0 | const Token* end = tok->link(); |
3003 | 0 | if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == MatchCompiler::makeConstString(")") || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH |
3004 | 0 | loopVariable = true; |
3005 | |
|
3006 | 0 | if (scope->type == Scope::eDo) { |
3007 | 0 | end = end->linkAt(2); |
3008 | 0 | } else if (loopVariable && tok->strAt(-1) == MatchCompiler::makeConstString(")")) { |
3009 | 0 | tok = tok->linkAt(-1); // Jump to opening ( of for/while statement |
3010 | 0 | } else if (scope->type == Scope::eSwitch) { |
3011 | 0 | for (const Scope* innerScope : scope->nestedList) { |
3012 | 0 | if (used) { |
3013 | 0 | bool used2 = false; |
3014 | 0 | if (!checkInnerScope(innerScope->bodyStart, var, used2) || used2) { |
3015 | 0 | return false; |
3016 | 0 | } |
3017 | 0 | } else if (!checkInnerScope(innerScope->bodyStart, var, used)) { |
3018 | 0 | return false; |
3019 | 0 | } |
3020 | 0 | } |
3021 | 0 | } |
3022 | | |
3023 | 0 | bool bFirstAssignment=false; |
3024 | 0 | for (; tok && tok != end; tok = tok->next()) { |
3025 | 0 | if (tok->str() == MatchCompiler::makeConstString("goto")) |
3026 | 0 | return false; |
3027 | 0 | if (tok->str() == MatchCompiler::makeConstString("continue")) |
3028 | 0 | noContinue = false; |
3029 | |
|
3030 | 0 | if (match61(tok)) |
3031 | 0 | forHeadEnd = tok->linkAt(1); |
3032 | 0 | if (tok == forHeadEnd) |
3033 | 0 | forHeadEnd = nullptr; |
3034 | |
|
3035 | 0 | if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && match70(tok, var->declarationId())) { // Assigned in outer scope. |
3036 | 0 | loopVariable = false; |
3037 | 0 | std::pair<const Token*, const Token*> range = tok->next()->findExpressionStartEndTokens(); |
3038 | 0 | if (range.first) |
3039 | 0 | range.first = range.first->next(); |
3040 | 0 | const Token* exprTok = findExpression(var->nameToken()->exprId(), range.first, range.second, [&](const Token* tok2) { |
3041 | 0 | return tok2->varId() == var->declarationId(); |
3042 | 0 | }); |
3043 | 0 | if (exprTok) { |
3044 | 0 | tok = exprTok; |
3045 | 0 | loopVariable = true; |
3046 | 0 | } |
3047 | 0 | } |
3048 | |
|
3049 | 0 | if (loopVariable && match71(tok, var->declarationId())) // Variable used in loop |
3050 | 0 | return false; |
3051 | | |
3052 | 0 | if (match72(tok, var->declarationId())) // Taking address of variable |
3053 | 0 | return false; |
3054 | | |
3055 | 0 | if (match70(tok, var->declarationId())) { |
3056 | 0 | if (!bFirstAssignment && var->isInit() && findmatch73(tok->tokAt(2), findmatch41(tok->tokAt(3)) , var->declarationId()) ) |
3057 | 0 | return false; |
3058 | 0 | bFirstAssignment = true; |
3059 | 0 | } |
3060 | | |
3061 | 0 | if (!bFirstAssignment && match74(tok, var->declarationId())) // dereferencing means access to previous content |
3062 | 0 | return false; |
3063 | | |
3064 | 0 | if (match75(tok, var->declarationId()) && (var->isArray() || var->isPointer() || (var->valueType() && var->valueType()->container))) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope |
3065 | 0 | return false; |
3066 | | |
3067 | 0 | if (tok->varId() == var->declarationId()) { |
3068 | 0 | used = true; |
3069 | 0 | if (scope == tok->scope()) { |
3070 | 0 | if (scope->type == Scope::eSwitch) |
3071 | 0 | return false; // Used in outer switch scope - unsafe or impossible to reduce scope |
3072 | | |
3073 | 0 | if (scope->bodyStart && scope->bodyStart->isSimplifiedScope()) |
3074 | 0 | return false; // simplified if/for/switch init statement |
3075 | 0 | } |
3076 | 0 | if (var->isArrayOrPointer()) { |
3077 | 0 | int argn{}; |
3078 | 0 | if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { // var passed to function? |
3079 | 0 | if (ftok->next()->astParent()) { // return value used? |
3080 | 0 | if (ftok->function() && Function::returnsPointer(ftok->function())) |
3081 | 0 | return false; |
3082 | 0 | const std::string ret = mSettings->library.returnValueType(ftok); // assume that var is returned |
3083 | 0 | if (!ret.empty() && ret.back() == '*') |
3084 | 0 | return false; |
3085 | 0 | } |
3086 | 0 | if (ftok->function()) { |
3087 | 0 | const std::list<Variable> &argvars = ftok->function()->argumentList; |
3088 | 0 | if (const Variable* argvar = ftok->function()->getArgumentVar(argn)) { |
3089 | 0 | if (!std::all_of(argvars.cbegin(), argvars.cend(), [&](const Variable& other) { |
3090 | 0 | return &other == argvar || !mayDependOn(other.valueType(), argvar->valueType()); |
3091 | 0 | })) |
3092 | 0 | return false; |
3093 | 0 | } |
3094 | 0 | } |
3095 | 0 | } |
3096 | 0 | } |
3097 | 0 | const auto yield = astContainerYield(tok); |
3098 | 0 | if (yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT) |
3099 | 0 | return false; |
3100 | 0 | } |
3101 | 0 | } |
3102 | | |
3103 | 0 | return true; |
3104 | 0 | } |
3105 | | |
3106 | | void CheckOther::variableScopeError(const Token *tok, const std::string &varname) |
3107 | 0 | { |
3108 | 0 | reportError(tok, |
3109 | 0 | Severity::style, |
3110 | 0 | "variableScope", |
3111 | 0 | "$symbol:" + varname + "\n" |
3112 | 0 | "The scope of the variable '$symbol' can be reduced.\n" |
3113 | 0 | "The scope of the variable '$symbol' can be reduced. Warning: Be careful " |
3114 | 0 | "when fixing this message, especially when there are inner loops. Here is an " |
3115 | 0 | "example where cppcheck will write that the scope for 'i' can be reduced:\n" |
3116 | 0 | "void f(int x)\n" |
3117 | 0 | "{\n" |
3118 | 0 | " int i = 0;\n" |
3119 | 0 | " if (x) {\n" |
3120 | 0 | " // it's safe to move 'int i = 0;' here\n" |
3121 | 0 | " for (int n = 0; n < 10; ++n) {\n" |
3122 | 0 | " // it is possible but not safe to move 'int i = 0;' here\n" |
3123 | 0 | " do_something(&i);\n" |
3124 | 0 | " }\n" |
3125 | 0 | " }\n" |
3126 | 0 | "}\n" |
3127 | 0 | "When you see this message it is always safe to reduce the variable scope 1 level.", CWE398, Certainty::normal); |
3128 | 0 | } |
3129 | | |
3130 | | //--------------------------------------------------------------------------- |
3131 | | // Comma in return statement: return a+1, b++;. (experimental) |
3132 | | //--------------------------------------------------------------------------- |
3133 | | void CheckOther::checkCommaSeparatedReturn() |
3134 | 763 | { |
3135 | | // This is experimental for now. See #5076 |
3136 | 763 | if ((true) || !mSettings->severity.isEnabled(Severity::style)) // NOLINT(readability-simplify-boolean-expr) |
3137 | 763 | return; |
3138 | | |
3139 | | // logChecker |
3140 | | |
3141 | 0 | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
3142 | 0 | if (tok->str() == MatchCompiler::makeConstString("return")) { |
3143 | 0 | tok = tok->next(); |
3144 | 0 | while (tok && tok->str() != MatchCompiler::makeConstString(";")) { |
3145 | 0 | if (tok->link() && match76(tok)) |
3146 | 0 | tok = tok->link(); |
3147 | |
|
3148 | 0 | if (!tok->isExpandedMacro() && tok->str() == MatchCompiler::makeConstString(",") && tok->linenr() != tok->next()->linenr()) |
3149 | 0 | commaSeparatedReturnError(tok); |
3150 | |
|
3151 | 0 | tok = tok->next(); |
3152 | 0 | } |
3153 | | // bailout: missing semicolon (invalid code / bad tokenizer) |
3154 | 0 | if (!tok) |
3155 | 0 | break; |
3156 | 0 | } |
3157 | 0 | } |
3158 | 0 | } |
3159 | | |
3160 | | void CheckOther::commaSeparatedReturnError(const Token *tok) |
3161 | 0 | { |
3162 | 0 | reportError(tok, |
3163 | 0 | Severity::style, |
3164 | 0 | "commaSeparatedReturn", |
3165 | 0 | "Comma is used in return statement. The comma can easily be misread as a ';'.\n" |
3166 | 0 | "Comma is used in return statement. When comma is used in a return statement it can " |
3167 | 0 | "easily be misread as a semicolon. For example in the code below the value " |
3168 | 0 | "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is " |
3169 | 0 | "returned:\n" |
3170 | 0 | " if (x)\n" |
3171 | 0 | " return a + 1,\n" |
3172 | 0 | " b++;\n" |
3173 | 0 | "However it can be useful to use comma in macros. Cppcheck does not warn when such a " |
3174 | 0 | "macro is then used in a return statement, it is less likely such code is misunderstood.", CWE398, Certainty::normal); |
3175 | 0 | } |
3176 | | |
3177 | | static bool isLargeContainer(const Variable* var, const Settings* settings) |
3178 | 0 | { |
3179 | 0 | const ValueType* vt = var->valueType(); |
3180 | 0 | if (vt->container->size_templateArgNo < 0) |
3181 | 0 | return true; |
3182 | 0 | const std::size_t maxByValueSize = 2 * settings->platform.sizeof_pointer; |
3183 | 0 | if (var->dimensions().empty()) { |
3184 | 0 | if (vt->container->startPattern == MatchCompiler::makeConstString("std :: bitset <")) { |
3185 | 0 | if (vt->containerTypeToken->hasKnownIntValue()) |
3186 | 0 | return vt->containerTypeToken->getKnownIntValue() / 8 > maxByValueSize; |
3187 | 0 | } |
3188 | 0 | return false; |
3189 | 0 | } |
3190 | 0 | const ValueType vtElem = ValueType::parseDecl(vt->containerTypeToken, *settings); |
3191 | 0 | const auto elemSize = std::max<std::size_t>(ValueFlow::getSizeOf(vtElem, *settings), 1); |
3192 | 0 | const auto arraySize = var->dimension(0) * elemSize; |
3193 | 0 | return arraySize > maxByValueSize; |
3194 | 0 | } |
3195 | | |
3196 | | void CheckOther::checkPassByReference() |
3197 | 763 | { |
3198 | 763 | if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC()) |
3199 | 0 | return; |
3200 | | |
3201 | 763 | logChecker("CheckOther::checkPassByReference"); // performance,c++ |
3202 | | |
3203 | 763 | const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); |
3204 | | |
3205 | 4.57k | for (const Variable* var : symbolDatabase->variableList()) { |
3206 | 4.57k | if (!var || !var->isClass() || var->isPointer() || (var->isArray() && !var->isStlType()) || var->isReference() || var->isEnumType()) |
3207 | 4.57k | continue; |
3208 | | |
3209 | 0 | const bool isRangeBasedFor = astIsRangeBasedForDecl(var->nameToken()); |
3210 | 0 | if (!var->isArgument() && !isRangeBasedFor) |
3211 | 0 | continue; |
3212 | | |
3213 | 0 | if (!isRangeBasedFor && var->scope() && var->scope()->function->arg->link()->strAt(-1) == MatchCompiler::makeConstString("...")) |
3214 | 0 | continue; // references could not be used as va_start parameters (#5824) |
3215 | | |
3216 | 0 | const Token * const varDeclEndToken = var->declEndToken(); |
3217 | 0 | if ((varDeclEndToken && varDeclEndToken->isExternC()) || |
3218 | 0 | (var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC())) |
3219 | 0 | continue; // references cannot be used in functions in extern "C" blocks |
3220 | | |
3221 | 0 | bool inconclusive = false; |
3222 | |
|
3223 | 0 | const bool isContainer = var->valueType() && var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container && !var->valueType()->container->view; |
3224 | 0 | if (isContainer && !isLargeContainer(var, mSettings)) |
3225 | 0 | continue; |
3226 | 0 | if (!isContainer) { |
3227 | 0 | if (var->type() && !var->type()->isEnumType()) { // Check if type is a struct or class. |
3228 | | // Ensure that it is a large object. |
3229 | 0 | if (!var->type()->classScope) |
3230 | 0 | inconclusive = true; |
3231 | 0 | else if (!var->valueType() || ValueFlow::getSizeOf(*var->valueType(), *mSettings) <= 2 * mSettings->platform.sizeof_pointer) |
3232 | 0 | continue; |
3233 | 0 | } |
3234 | 0 | else |
3235 | 0 | continue; |
3236 | 0 | } |
3237 | | |
3238 | 0 | if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) |
3239 | 0 | continue; |
3240 | | |
3241 | 0 | if (var->isArray() && (!var->isStlType() || match77(var->nameToken()->next()))) |
3242 | 0 | continue; |
3243 | | |
3244 | 0 | const bool isConst = var->isConst(); |
3245 | 0 | if (isConst) { |
3246 | 0 | passedByValueError(var, inconclusive, isRangeBasedFor); |
3247 | 0 | continue; |
3248 | 0 | } |
3249 | | |
3250 | | // Check if variable could be const |
3251 | 0 | if (!isRangeBasedFor && (!var->scope() || var->scope()->function->isImplicitlyVirtual())) |
3252 | 0 | continue; |
3253 | | |
3254 | 0 | if (!isVariableChanged(var, *mSettings)) { |
3255 | 0 | passedByValueError(var, inconclusive, isRangeBasedFor); |
3256 | 0 | } |
3257 | 0 | } |
3258 | 763 | } |
3259 | | |
3260 | | void CheckOther::passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor) |
3261 | 0 | { |
3262 | 0 | std::string id = isRangeBasedFor ? "iterateByValue" : "passedByValue"; |
3263 | 0 | const std::string action = isRangeBasedFor ? "declared as": "passed by"; |
3264 | 0 | const std::string type = isRangeBasedFor ? "Range variable" : "Function parameter"; |
3265 | 0 | std::string msg = "$symbol:" + (var ? var->name() : "") + "\n" + |
3266 | 0 | type + " '$symbol' should be " + action + " const reference."; |
3267 | 0 | ErrorPath errorPath; |
3268 | 0 | if (var && var->scope() && var->scope()->function && var->scope()->function->functionPointerUsage) { |
3269 | 0 | id += "Callback"; |
3270 | 0 | errorPath.emplace_front(var->scope()->function->functionPointerUsage, "Function pointer used here."); |
3271 | 0 | msg += " However it seems that '" + var->scope()->function->name() + "' is a callback function."; |
3272 | 0 | } |
3273 | 0 | if (var) |
3274 | 0 | errorPath.emplace_back(var->nameToken(), msg); |
3275 | 0 | if (isRangeBasedFor) |
3276 | 0 | msg += "\nVariable '$symbol' is used to iterate by value. It could be declared as a const reference which is usually faster and recommended in C++."; |
3277 | 0 | else |
3278 | 0 | msg += "\nParameter '$symbol' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++."; |
3279 | 0 | reportError(errorPath, Severity::performance, id.c_str(), msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); |
3280 | 0 | } |
3281 | | |
3282 | | static bool isVariableMutableInInitializer(const Token* start, const Token * end, nonneg int varid) |
3283 | 0 | { |
3284 | 0 | if (!start) |
3285 | 0 | return false; |
3286 | 0 | if (!end) |
3287 | 0 | return false; |
3288 | 0 | for (const Token *tok = start; tok != end; tok = tok->next()) { |
3289 | 0 | if (tok->varId() != varid) |
3290 | 0 | continue; |
3291 | 0 | if (tok->astParent()) { |
3292 | 0 | const Token * memberTok = tok->astParent()->previous(); |
3293 | 0 | if (match78(memberTok) && memberTok->variable()) { |
3294 | 0 | const Variable * memberVar = memberTok->variable(); |
3295 | 0 | if (memberVar->isClass()) |
3296 | | //TODO: check if the called constructor could live with a const variable |
3297 | | // pending that, assume the worst (that it can't) |
3298 | 0 | return true; |
3299 | 0 | if (!memberVar->isReference()) |
3300 | 0 | continue; |
3301 | 0 | if (memberVar->isConst()) |
3302 | 0 | continue; |
3303 | 0 | } |
3304 | 0 | } |
3305 | 0 | return true; |
3306 | 0 | } |
3307 | 0 | return false; |
3308 | 0 | } |
3309 | | |
3310 | | void CheckOther::checkConstVariable() |
3311 | 763 | { |
3312 | 763 | if ((!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) && !mSettings->isPremiumEnabled("constVariable")) |
3313 | 0 | return; |
3314 | | |
3315 | 763 | logChecker("CheckOther::checkConstVariable"); // style,c++ |
3316 | | |
3317 | 763 | const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); |
3318 | | |
3319 | 4.57k | for (const Variable *var : symbolDatabase->variableList()) { |
3320 | 4.57k | if (!var) |
3321 | 763 | continue; |
3322 | 3.81k | if (!var->isReference()) |
3323 | 3.81k | continue; |
3324 | 0 | if (var->isRValueReference()) |
3325 | 0 | continue; |
3326 | 0 | if (var->isPointer()) |
3327 | 0 | continue; |
3328 | 0 | if (var->isConst()) |
3329 | 0 | continue; |
3330 | 0 | const Scope* scope = var->scope(); |
3331 | 0 | if (!scope) |
3332 | 0 | continue; |
3333 | 0 | const Function* function = scope->function; |
3334 | 0 | if (!function && !scope->isLocal()) |
3335 | 0 | continue; |
3336 | 0 | if (function && var->isArgument()) { |
3337 | 0 | if (function->isImplicitlyVirtual() || function->templateDef) |
3338 | 0 | continue; |
3339 | 0 | if (function->isConstructor() && isVariableMutableInInitializer(function->constructorMemberInitialization(), scope->bodyStart, var->declarationId())) |
3340 | 0 | continue; |
3341 | 0 | } |
3342 | 0 | if (var->isGlobal()) |
3343 | 0 | continue; |
3344 | 0 | if (var->isStatic()) |
3345 | 0 | continue; |
3346 | 0 | if (var->isArray() && !var->isStlType()) |
3347 | 0 | continue; |
3348 | 0 | if (var->isEnumType()) |
3349 | 0 | continue; |
3350 | 0 | if (var->isVolatile()) |
3351 | 0 | continue; |
3352 | 0 | if (var->isMaybeUnused()) |
3353 | 0 | continue; |
3354 | 0 | if (var->nameToken()->isExpandedMacro()) |
3355 | 0 | continue; |
3356 | 0 | if (isStructuredBindingVariable(var)) // TODO: check all bound variables |
3357 | 0 | continue; |
3358 | 0 | if (isVariableChanged(var, *mSettings)) |
3359 | 0 | continue; |
3360 | 0 | const bool hasFunction = function != nullptr; |
3361 | 0 | if (!hasFunction) { |
3362 | 0 | const Scope* functionScope = scope; |
3363 | 0 | do { |
3364 | 0 | functionScope = functionScope->nestedIn; |
3365 | 0 | } while (functionScope && !(function = functionScope->function)); |
3366 | 0 | } |
3367 | 0 | if (function && (Function::returnsReference(function) || Function::returnsPointer(function)) && !Function::returnsConst(function)) { |
3368 | 0 | std::vector<const Token*> returns = Function::findReturns(function); |
3369 | 0 | if (std::any_of(returns.cbegin(), returns.cend(), [&](const Token* retTok) { |
3370 | 0 | if (retTok->varId() == var->declarationId()) |
3371 | 0 | return true; |
3372 | 0 | while (retTok && retTok->isCast()) |
3373 | 0 | retTok = retTok->astOperand2(); |
3374 | 0 | while (match30(retTok)) |
3375 | 0 | retTok = retTok->astOperand2(); |
3376 | 0 | if (match79(retTok)) |
3377 | 0 | retTok = retTok->astOperand1(); |
3378 | 0 | return ValueFlow::hasLifetimeToken(getParentLifetime(retTok), var->nameToken(), *mSettings); |
3379 | 0 | })) |
3380 | 0 | continue; |
3381 | 0 | } |
3382 | | // Skip if another non-const variable is initialized with this variable |
3383 | 0 | { |
3384 | | //Is it the right side of an initialization of a non-const reference |
3385 | 0 | bool usedInAssignment = false; |
3386 | 0 | for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { |
3387 | 0 | if (match80(tok, var->declarationId())) { |
3388 | 0 | const Variable* refvar = tok->next()->variable(); |
3389 | 0 | if (refvar && !refvar->isConst() && refvar->nameToken() == tok->next()) { |
3390 | 0 | usedInAssignment = true; |
3391 | 0 | break; |
3392 | 0 | } |
3393 | 0 | } |
3394 | 0 | if (tok->isUnaryOp("&") && match72(tok, var->declarationId())) { |
3395 | 0 | const Token* opTok = tok->astParent(); |
3396 | 0 | int argn = -1; |
3397 | 0 | if (opTok && (opTok->isUnaryOp("!") || opTok->isComparisonOp())) |
3398 | 0 | continue; |
3399 | 0 | if (opTok && (opTok->isAssignmentOp() || opTok->isCalculation())) { |
3400 | 0 | if (opTok->isCalculation()) { |
3401 | 0 | if (opTok->astOperand1() != tok) |
3402 | 0 | opTok = opTok->astOperand1(); |
3403 | 0 | else |
3404 | 0 | opTok = opTok->astOperand2(); |
3405 | 0 | } |
3406 | 0 | if (opTok && opTok->valueType() && var->valueType() && opTok->valueType()->isConst(var->valueType()->pointer)) |
3407 | 0 | continue; |
3408 | 0 | } else if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { |
3409 | 0 | bool inconclusive{}; |
3410 | 0 | if (var->valueType() && !isVariableChangedByFunctionCall(ftok, var->valueType()->pointer, var->declarationId(), *mSettings, &inconclusive) && !inconclusive) |
3411 | 0 | continue; |
3412 | 0 | } |
3413 | 0 | usedInAssignment = true; |
3414 | 0 | break; |
3415 | 0 | } |
3416 | 0 | if (astIsRangeBasedForDecl(tok) && match81(tok->astParent()->astOperand2(), var->declarationId())) { |
3417 | 0 | const Variable* refvar = tok->astParent()->astOperand1()->variable(); |
3418 | 0 | if (refvar && refvar->isReference() && !refvar->isConst()) { |
3419 | 0 | usedInAssignment = true; |
3420 | 0 | break; |
3421 | 0 | } |
3422 | 0 | } |
3423 | 0 | } |
3424 | 0 | if (usedInAssignment) |
3425 | 0 | continue; |
3426 | 0 | } |
3427 | | |
3428 | 0 | constVariableError(var, hasFunction ? function : nullptr); |
3429 | 0 | } |
3430 | 763 | } |
3431 | | |
3432 | | static const Token* getVariableChangedStart(const Variable* p) |
3433 | 0 | { |
3434 | 0 | if (p->isArgument()) |
3435 | 0 | return p->scope()->bodyStart; |
3436 | 0 | const Token* start = p->nameToken()->next(); |
3437 | 0 | if (start->isSplittedVarDeclEq()) |
3438 | 0 | start = start->tokAt(3); |
3439 | 0 | return start; |
3440 | 0 | } |
3441 | | |
3442 | | static bool isConstPointerVariable(const Variable* p, const Settings& settings) |
3443 | 0 | { |
3444 | 0 | const int indirect = p->isArray() ? p->dimensions().size() : 1; |
3445 | 0 | const Token* start = getVariableChangedStart(p); |
3446 | 0 | while (const Token* tok = |
3447 | 0 | findVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, settings)) { |
3448 | 0 | if (p->isReference()) |
3449 | 0 | return false; |
3450 | | // Assigning a pointer through another pointer may still be const |
3451 | 0 | if (!match31(tok->astParent())) |
3452 | 0 | return false; |
3453 | 0 | if (!astIsLHS(tok)) |
3454 | 0 | return false; |
3455 | 0 | start = tok->next(); |
3456 | 0 | } |
3457 | 0 | return true; |
3458 | 0 | } |
3459 | | |
3460 | | namespace { |
3461 | | struct CompareVariables { |
3462 | 0 | bool operator()(const Variable* a, const Variable* b) const { |
3463 | 0 | const int fileA = a->nameToken()->fileIndex(); |
3464 | 0 | const int fileB = b->nameToken()->fileIndex(); |
3465 | 0 | if (fileA != fileB) |
3466 | 0 | return fileA < fileB; |
3467 | 0 | const int lineA = a->nameToken()->linenr(); |
3468 | 0 | const int lineB = b->nameToken()->linenr(); |
3469 | 0 | if (lineA != lineB) |
3470 | 0 | return lineA < lineB; |
3471 | 0 | const int columnA = a->nameToken()->column(); |
3472 | 0 | const int columnB = b->nameToken()->column(); |
3473 | 0 | return columnA < columnB; |
3474 | 0 | } |
3475 | | }; |
3476 | | } |
3477 | | |
3478 | | void CheckOther::checkConstPointer() |
3479 | 763 | { |
3480 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && |
3481 | 763 | !mSettings->isPremiumEnabled("constParameter") && |
3482 | 763 | !mSettings->isPremiumEnabled("constParameterPointer") && |
3483 | 763 | !mSettings->isPremiumEnabled("constParameterReference") && |
3484 | 763 | !mSettings->isPremiumEnabled("constVariablePointer")) |
3485 | 0 | return; |
3486 | | |
3487 | 763 | logChecker("CheckOther::checkConstPointer"); // style |
3488 | | |
3489 | 763 | std::set<const Variable*, CompareVariables> pointers, nonConstPointers; |
3490 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
3491 | 54.8k | const Variable* const var = tok->variable(); |
3492 | 54.8k | if (!var) |
3493 | 43.1k | continue; |
3494 | 11.6k | if (!var->isLocal() && !var->isArgument()) |
3495 | 11.6k | continue; |
3496 | 0 | const Token* const nameTok = var->nameToken(); |
3497 | 0 | if (tok == nameTok) { |
3498 | | // declarations of (static) pointers are (not) split up, array declarations are never split up |
3499 | 0 | if (var->isLocal() && (!var->isStatic() || match77(nameTok->next())) && |
3500 | 0 | !astIsRangeBasedForDecl(nameTok)) |
3501 | 0 | continue; |
3502 | 0 | } |
3503 | | // Skip function pointers |
3504 | 0 | if (match82(nameTok)) |
3505 | 0 | continue; |
3506 | 0 | const ValueType* const vt = tok->valueType(); |
3507 | 0 | if (!vt) |
3508 | 0 | continue; |
3509 | 0 | if ((vt->pointer != 1 && !(vt->pointer == 2 && var->isArray())) || (vt->constness & 1)) |
3510 | 0 | continue; |
3511 | 0 | if (var->typeStartToken()->isTemplateArg()) |
3512 | 0 | continue; |
3513 | 0 | if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), var) != nonConstPointers.cend()) |
3514 | 0 | continue; |
3515 | 0 | pointers.emplace(var); |
3516 | 0 | const Token* parent = tok->astParent(); |
3517 | 0 | enum Deref : std::uint8_t { NONE, DEREF, MEMBER } deref = NONE; |
3518 | 0 | bool hasIncDecPlus = false; |
3519 | 0 | if (parent && (parent->isUnaryOp("*") || (((hasIncDecPlus = parent->isIncDecOp()) || (hasIncDecPlus = (parent->str() == MatchCompiler::makeConstString("+")))) && |
3520 | 0 | parent->astParent() && parent->astParent()->isUnaryOp("*")))) |
3521 | 0 | deref = DEREF; |
3522 | 0 | else if (match77(parent) && parent->astOperand1() == tok && tok != nameTok) |
3523 | 0 | deref = DEREF; |
3524 | 0 | else if (match83(parent) && match30(parent->astParent())) |
3525 | 0 | deref = MEMBER; |
3526 | 0 | else if (match30(parent)) |
3527 | 0 | deref = MEMBER; |
3528 | 0 | else if (astIsRangeBasedForDecl(tok)) |
3529 | 0 | continue; |
3530 | 0 | if (deref != NONE) { |
3531 | 0 | const Token* gparent = parent->astParent(); |
3532 | 0 | while (match77(gparent) && parent != gparent->astOperand2() && parent->str() == gparent->str()) |
3533 | 0 | gparent = gparent->astParent(); |
3534 | 0 | if (deref == MEMBER) { |
3535 | 0 | if (!gparent) |
3536 | 0 | continue; |
3537 | 0 | if (parent->astOperand2()) { |
3538 | 0 | if (parent->astOperand2()->function() && parent->astOperand2()->function()->isConst()) |
3539 | 0 | continue; |
3540 | 0 | if (mSettings->library.isFunctionConst(parent->astOperand2())) |
3541 | 0 | continue; |
3542 | 0 | } |
3543 | 0 | } |
3544 | 0 | if (hasIncDecPlus) { |
3545 | 0 | parent = gparent; |
3546 | 0 | gparent = gparent ? gparent->astParent() : nullptr; |
3547 | 0 | } |
3548 | 0 | if (match84(gparent) && !gparent->isUnaryOp("&") && !gparent->isUnaryOp("*")) |
3549 | 0 | continue; |
3550 | 0 | int argn = -1; |
3551 | 0 | if (match48(gparent)) { |
3552 | 0 | const Function* function = gparent->scope()->function; |
3553 | 0 | if (function && (!Function::returnsReference(function) || Function::returnsConst(function))) |
3554 | 0 | continue; |
3555 | 0 | } |
3556 | 0 | else if (match85(gparent) && parent == gparent->astOperand2()) { |
3557 | 0 | bool takingRef = false, nonConstPtrAssignment = false; |
3558 | 0 | const Token *lhs = gparent->astOperand1(); |
3559 | 0 | if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs && !lhs->variable()->isConst()) |
3560 | 0 | takingRef = true; |
3561 | 0 | if (lhs && lhs->valueType() && lhs->valueType()->pointer && (lhs->valueType()->constness & 1) == 0 && |
3562 | 0 | parent->valueType() && parent->valueType()->pointer) |
3563 | 0 | nonConstPtrAssignment = true; |
3564 | 0 | if (!takingRef && !nonConstPtrAssignment) |
3565 | 0 | continue; |
3566 | 0 | } else if (match77(gparent) && gparent->astOperand2() == parent) |
3567 | 0 | continue; |
3568 | 0 | else if (gparent && gparent->isCast() && gparent->valueType() && |
3569 | 0 | ((gparent->valueType()->pointer == 0 && gparent->valueType()->reference == Reference::None) || |
3570 | 0 | (var->valueType() && gparent->valueType()->isConst(var->valueType()->pointer)))) |
3571 | 0 | continue; |
3572 | 0 | else if (const Token* ftok = getTokenArgumentFunction(parent, argn)) { |
3573 | 0 | bool inconclusive{}; |
3574 | 0 | if (!isVariableChangedByFunctionCall(ftok->next(), vt->pointer, var->declarationId(), *mSettings, &inconclusive) && !inconclusive) |
3575 | 0 | continue; |
3576 | 0 | } |
3577 | 0 | } else { |
3578 | 0 | int argn = -1; |
3579 | 0 | if (match86(parent)) |
3580 | 0 | continue; |
3581 | 0 | if (hasIncDecPlus && !parent->astParent()) |
3582 | 0 | continue; |
3583 | 0 | if (match67(parent) && match87(parent->astOperand1())) |
3584 | 0 | continue; |
3585 | 0 | if (match31(parent) && parent->astOperand1() == tok) |
3586 | 0 | continue; |
3587 | 0 | if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { |
3588 | 0 | if (ftok->function()) { |
3589 | 0 | const bool isCastArg = parent->isCast() && !ftok->function()->getOverloadedFunctions().empty(); // assume that cast changes the called function |
3590 | 0 | if (!isCastArg) { |
3591 | 0 | const Variable* argVar = ftok->function()->getArgumentVar(argn); |
3592 | 0 | if (argVar && argVar->valueType() && argVar->valueType()->isConst(vt->pointer)) { |
3593 | 0 | bool inconclusive{}; |
3594 | 0 | if (!isVariableChangedByFunctionCall(ftok, vt->pointer, var->declarationId(), *mSettings, &inconclusive) && !inconclusive) |
3595 | 0 | continue; |
3596 | 0 | } |
3597 | 0 | } |
3598 | 0 | } else { |
3599 | 0 | const auto dir = mSettings->library.getArgDirection(ftok, argn + 1); |
3600 | 0 | if (dir == Library::ArgumentChecks::Direction::DIR_IN) |
3601 | 0 | continue; |
3602 | 0 | } |
3603 | 0 | } |
3604 | 0 | else if (match67(parent)) { |
3605 | 0 | if (parent->isCast() && parent->valueType() && var->valueType() && parent->valueType()->isConst(var->valueType()->pointer)) |
3606 | 0 | continue; |
3607 | 0 | } |
3608 | 0 | } |
3609 | 0 | if (tok != nameTok) |
3610 | 0 | nonConstPointers.emplace(var); |
3611 | 0 | } |
3612 | 763 | for (const Variable *p: pointers) { |
3613 | 0 | if (p->isArgument()) { |
3614 | 0 | if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(true) || p->scope()->function->hasVirtualSpecifier()) |
3615 | 0 | continue; |
3616 | 0 | if (p->isMaybeUnused()) |
3617 | 0 | continue; |
3618 | 0 | } |
3619 | 0 | if (const Function* func = Scope::nestedInFunction(p->scope())) |
3620 | 0 | if (func->templateDef) |
3621 | 0 | continue; |
3622 | 0 | if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), p) == nonConstPointers.cend()) { |
3623 | | // const Token *start = getVariableChangedStart(p); |
3624 | | // const int indirect = p->isArray() ? p->dimensions().size() : 1; |
3625 | | // if (isVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, *mSettings)) |
3626 | | // continue; |
3627 | 0 | if (!isConstPointerVariable(p, *mSettings)) |
3628 | 0 | continue; |
3629 | 0 | if (p->typeStartToken() && p->typeStartToken()->isSimplifiedTypedef() && !(match88(p->typeEndToken()) && !p->typeEndToken()->isSimplifiedTypedef())) |
3630 | 0 | continue; |
3631 | 0 | constVariableError(p, p->isArgument() ? p->scope()->function : nullptr); |
3632 | 0 | } |
3633 | 0 | } |
3634 | 763 | } |
3635 | | |
3636 | | void CheckOther::constVariableError(const Variable *var, const Function *function) |
3637 | 0 | { |
3638 | 0 | if (!var) { |
3639 | 0 | reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const"); |
3640 | 0 | reportError(nullptr, Severity::style, "constVariable", "Variable 'x' can be declared with const"); |
3641 | 0 | reportError(nullptr, Severity::style, "constParameterReference", "Parameter 'x' can be declared with const"); |
3642 | 0 | reportError(nullptr, Severity::style, "constVariableReference", "Variable 'x' can be declared with const"); |
3643 | 0 | reportError(nullptr, Severity::style, "constParameterPointer", "Parameter 'x' can be declared with const"); |
3644 | 0 | reportError(nullptr, Severity::style, "constVariablePointer", "Variable 'x' can be declared with const"); |
3645 | 0 | reportError(nullptr, Severity::style, "constParameterCallback", "Parameter 'x' can be declared with const, however it seems that 'f' is a callback function."); |
3646 | 0 | return; |
3647 | 0 | } |
3648 | | |
3649 | 0 | const std::string vartype(var->isArgument() ? "Parameter" : "Variable"); |
3650 | 0 | const std::string& varname(var->name()); |
3651 | 0 | const std::string ptrRefArray = var->isArray() ? "const array" : (var->isPointer() ? "pointer to const" : "reference to const"); |
3652 | |
|
3653 | 0 | ErrorPath errorPath; |
3654 | 0 | std::string id = "const" + vartype; |
3655 | 0 | std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray; |
3656 | 0 | errorPath.emplace_back(var->nameToken(), message); |
3657 | 0 | if (var->isArgument() && function && function->functionPointerUsage) { |
3658 | 0 | errorPath.emplace_front(function->functionPointerUsage, "You might need to cast the function pointer here"); |
3659 | 0 | id += "Callback"; |
3660 | 0 | message += ". However it seems that '" + function->name() + "' is a callback function, if '$symbol' is declared with const you might also need to cast function pointer(s)."; |
3661 | 0 | } else if (var->isReference()) { |
3662 | 0 | id += "Reference"; |
3663 | 0 | } else if (var->isPointer() && !var->isArray()) { |
3664 | 0 | id += "Pointer"; |
3665 | 0 | } |
3666 | |
|
3667 | 0 | reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); |
3668 | 0 | } |
3669 | | |
3670 | | //--------------------------------------------------------------------------- |
3671 | | // Check usage of char variables.. |
3672 | | //--------------------------------------------------------------------------- |
3673 | | |
3674 | | void CheckOther::checkCharVariable() |
3675 | 763 | { |
3676 | 763 | const bool warning = mSettings->severity.isEnabled(Severity::warning); |
3677 | 763 | const bool portability = mSettings->severity.isEnabled(Severity::portability); |
3678 | 763 | if (!warning && !portability) |
3679 | 0 | return; |
3680 | | |
3681 | 763 | logChecker("CheckOther::checkCharVariable"); // warning,portability |
3682 | | |
3683 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
3684 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
3685 | 23.7k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
3686 | 22.6k | if (match89(tok)) { |
3687 | 0 | if (!tok->variable()) |
3688 | 0 | continue; |
3689 | 0 | if (!tok->variable()->isArray() && !tok->variable()->isPointer()) |
3690 | 0 | continue; |
3691 | 0 | const Token *index = tok->next()->astOperand2(); |
3692 | 0 | if (warning && tok->variable()->isArray() && astIsSignedChar(index) && index->getValueGE(0x80, *mSettings)) |
3693 | 0 | signedCharArrayIndexError(tok); |
3694 | 0 | if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, *mSettings)) |
3695 | 0 | unknownSignCharArrayIndexError(tok); |
3696 | 22.6k | } else if (warning && match90(tok) && tok->isBinaryOp()) { |
3697 | 251 | bool warn = false; |
3698 | 251 | if (astIsSignedChar(tok->astOperand1())) { |
3699 | 0 | const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, *mSettings); |
3700 | 0 | const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false); |
3701 | 0 | if (!v1) |
3702 | 0 | v1 = tok->astOperand1()->getValueGE(0x80, *mSettings); |
3703 | 0 | if (v1 && !(tok->str() == MatchCompiler::makeConstString("&") && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) |
3704 | 0 | warn = true; |
3705 | 251 | } else if (astIsSignedChar(tok->astOperand2())) { |
3706 | 0 | const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, *mSettings); |
3707 | 0 | const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false); |
3708 | 0 | if (!v1) |
3709 | 0 | v1 = tok->astOperand2()->getValueGE(0x80, *mSettings); |
3710 | 0 | if (v1 && !(tok->str() == MatchCompiler::makeConstString("&") && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) |
3711 | 0 | warn = true; |
3712 | 0 | } |
3713 | | |
3714 | | // is the result stored in a short|int|long? |
3715 | 251 | if (warn && match31(tok->astParent())) { |
3716 | 0 | const Token *lhs = tok->astParent()->astOperand1(); |
3717 | 0 | if (lhs && lhs->valueType() && lhs->valueType()->type >= ValueType::Type::SHORT) |
3718 | 0 | charBitOpError(tok); // This is an error.. |
3719 | 0 | } |
3720 | 251 | } |
3721 | 22.6k | } |
3722 | 1.10k | } |
3723 | 763 | } |
3724 | | |
3725 | | void CheckOther::signedCharArrayIndexError(const Token *tok) |
3726 | 0 | { |
3727 | 0 | reportError(tok, |
3728 | 0 | Severity::warning, |
3729 | 0 | "signedCharArrayIndex", |
3730 | 0 | "Signed 'char' type used as array index.\n" |
3731 | 0 | "Signed 'char' type used as array index. If the value " |
3732 | 0 | "can be greater than 127 there will be a buffer underflow " |
3733 | 0 | "because of sign extension.", CWE128, Certainty::normal); |
3734 | 0 | } |
3735 | | |
3736 | | void CheckOther::unknownSignCharArrayIndexError(const Token *tok) |
3737 | 0 | { |
3738 | 0 | reportError(tok, |
3739 | 0 | Severity::portability, |
3740 | 0 | "unknownSignCharArrayIndex", |
3741 | 0 | "'char' type used as array index.\n" |
3742 | 0 | "'char' type used as array index. Values greater than 127 will be " |
3743 | 0 | "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, Certainty::normal); |
3744 | 0 | } |
3745 | | |
3746 | | void CheckOther::charBitOpError(const Token *tok) |
3747 | 0 | { |
3748 | 0 | reportError(tok, |
3749 | 0 | Severity::warning, |
3750 | 0 | "charBitOp", |
3751 | 0 | "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" |
3752 | 0 | "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" |
3753 | 0 | " char c = 0x80;\n" |
3754 | 0 | " int i = 0 | c;\n" |
3755 | 0 | " if (i & 0x8000)\n" |
3756 | 0 | " printf(\"not expected\");\n" |
3757 | 0 | "The \"not expected\" will be printed on the screen.", CWE398, Certainty::normal); |
3758 | 0 | } |
3759 | | |
3760 | | //--------------------------------------------------------------------------- |
3761 | | // Incomplete statement.. |
3762 | | //--------------------------------------------------------------------------- |
3763 | | |
3764 | | static bool isType(const Token * tok, bool unknown) |
3765 | 0 | { |
3766 | 0 | if (tok && (tok->isStandardType() || (!tok->isKeyword() && match20(tok)) || tok->str() == MatchCompiler::makeConstString("auto"))) |
3767 | 0 | return true; |
3768 | 0 | if (tok && tok->varId()) |
3769 | 0 | return false; |
3770 | 0 | if (match91(tok)) |
3771 | 0 | return isType(tok->astOperand2(), unknown); |
3772 | 0 | if (match17(tok) && tok->link()) |
3773 | 0 | return true; |
3774 | 0 | if (unknown && match92(tok)) |
3775 | 0 | return true; |
3776 | 0 | return false; |
3777 | 0 | } |
3778 | | |
3779 | | static bool isVarDeclOp(const Token* tok) |
3780 | 0 | { |
3781 | 0 | if (!tok) |
3782 | 0 | return false; |
3783 | 0 | const Token * vartok = tok->astOperand2(); |
3784 | 0 | if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok) |
3785 | 0 | return true; |
3786 | 0 | const Token * typetok = tok->astOperand1(); |
3787 | 0 | return isType(typetok, vartok && vartok->varId() != 0); |
3788 | 0 | } |
3789 | | |
3790 | | static bool isBracketAccess(const Token* tok) |
3791 | 3.18k | { |
3792 | 3.18k | if (!match77(tok) || !tok->astOperand1()) |
3793 | 3.18k | return false; |
3794 | 0 | tok = tok->astOperand1(); |
3795 | 0 | if (tok->str() == MatchCompiler::makeConstString(".")) |
3796 | 0 | tok = tok->astOperand2(); |
3797 | 0 | while (match77(tok)) |
3798 | 0 | tok = tok->astOperand1(); |
3799 | 0 | if (!tok || !tok->variable()) |
3800 | 0 | return false; |
3801 | 0 | return tok->variable()->nameToken() != tok; |
3802 | 0 | } |
3803 | | |
3804 | 3.18k | static bool isConstant(const Token* tok) { |
3805 | 3.18k | return tok && (tok->isEnumerator() || match93(tok)); |
3806 | 3.18k | } |
3807 | | |
3808 | | static bool isConstStatement(const Token *tok, bool isNestedBracket = false) |
3809 | 3.18k | { |
3810 | 3.18k | if (!tok) |
3811 | 0 | return false; |
3812 | 3.18k | if (tok->isExpandedMacro()) |
3813 | 0 | return false; |
3814 | 3.18k | if (tok->varId() != 0) |
3815 | 0 | return true; |
3816 | 3.18k | if (isConstant(tok)) |
3817 | 0 | return true; |
3818 | 3.18k | if (match94(tok) && |
3819 | 3.18k | (match95(tok->previous()) || isVarDeclOp(tok))) |
3820 | 0 | return false; |
3821 | 3.18k | if (match96(tok) && !astIsIntegral(tok, false)) |
3822 | 0 | return false; |
3823 | 3.18k | const Token* tok2 = tok; |
3824 | 6.37k | while (tok2) { |
3825 | 3.18k | if (match97(tok2->astOperand1())) |
3826 | 0 | return false; |
3827 | 3.18k | tok2 = tok2->astParent(); |
3828 | 3.18k | } |
3829 | 3.18k | if (match40(tok)) |
3830 | 0 | return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()); |
3831 | 3.18k | if (match98(tok) && (tok->astOperand1() || tok->astOperand2())) |
3832 | 0 | return true; |
3833 | 3.18k | if (match99(tok->previous())) |
3834 | 0 | return true; |
3835 | 3.18k | if (isCPPCast(tok)) { |
3836 | 0 | if (match100(tok->astOperand1()) && match101(tok->astOperand1()->linkAt(1)->previous())) |
3837 | 0 | return false; |
3838 | 0 | return isWithoutSideEffects(tok) && isConstStatement(tok->astOperand2()); |
3839 | 0 | } |
3840 | 3.18k | if (tok->isCast() && tok->next() && tok->next()->isStandardType()) |
3841 | 0 | return isWithoutSideEffects(tok->astOperand1()) && isConstStatement(tok->astOperand1()); |
3842 | 3.18k | if (match30(tok)) |
3843 | 0 | return isConstStatement(tok->astOperand2()); |
3844 | 3.18k | if (match65(tok)) { |
3845 | 0 | if (tok->astParent()) // warn about const statement on rhs at the top level |
3846 | 0 | return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()); |
3847 | | |
3848 | 0 | const Token* lml = previousBeforeAstLeftmostLeaf(tok); // don't warn about matrix/vector assignment (e.g. Eigen) |
3849 | 0 | if (lml) |
3850 | 0 | lml = lml->next(); |
3851 | 0 | const Token* stream = lml; |
3852 | 0 | while (stream && match102(stream->astParent())) |
3853 | 0 | stream = stream->astParent(); |
3854 | 0 | return (!stream || !isLikelyStream(stream)) && isConstStatement(tok->astOperand2()); |
3855 | 0 | } |
3856 | 3.18k | if (match45(tok) && match103(tok->astOperand2())) // ternary operator |
3857 | 0 | return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()->astOperand1()) && isConstStatement(tok->astOperand2()->astOperand2()); |
3858 | 3.18k | if (isBracketAccess(tok) && isWithoutSideEffects(tok->astOperand1(), /*checkArrayAccess*/ true, /*checkReference*/ false)) { |
3859 | 0 | const bool isChained = succeeds(tok->astParent(), tok); |
3860 | 0 | if (match77(tok->astParent())) { |
3861 | 0 | if (isChained) |
3862 | 0 | return isConstStatement(tok->astOperand2()) && isConstStatement(tok->astParent()); |
3863 | 0 | return isNestedBracket && isConstStatement(tok->astOperand2()); |
3864 | 0 | } |
3865 | 0 | return isConstStatement(tok->astOperand2(), /*isNestedBracket*/ !isChained); |
3866 | 0 | } |
3867 | 3.18k | if (!tok->astParent() && findLambdaEndToken(tok)) |
3868 | 0 | return true; |
3869 | 3.18k | return false; |
3870 | 3.18k | } |
3871 | | |
3872 | | static bool isVoidStmt(const Token *tok) |
3873 | 0 | { |
3874 | 0 | if (match104(tok)) |
3875 | 0 | return true; |
3876 | 0 | if (isCPPCast(tok) && tok->astOperand1() && match105(tok->astOperand1()->next())) |
3877 | 0 | return true; |
3878 | 0 | const Token *tok2 = tok; |
3879 | 0 | while (tok2->astOperand1()) |
3880 | 0 | tok2 = tok2->astOperand1(); |
3881 | 0 | if (match106(tok2->previous()) && match104(tok2->linkAt(-1))) |
3882 | 0 | return true; |
3883 | 0 | if (match104(tok2)) |
3884 | 0 | return true; |
3885 | 0 | return match107(tok2->previous()); |
3886 | 0 | } |
3887 | | |
3888 | | static bool isConstTop(const Token *tok) |
3889 | 23.7k | { |
3890 | 23.7k | if (!tok) |
3891 | 0 | return false; |
3892 | 23.7k | if (!tok->astParent()) |
3893 | 12.7k | return true; |
3894 | 11.0k | if (match51(tok->astParent()) && |
3895 | 11.0k | match108(tok->astTop()->previous()) && match51(tok->astTop()->astOperand2())) { |
3896 | 0 | if (match51(tok->astParent()->astParent())) |
3897 | 0 | return tok->astParent()->astOperand2() == tok; |
3898 | 0 | return tok->astParent()->astOperand1() == tok; |
3899 | 0 | } |
3900 | 11.0k | if (match77(tok)) { |
3901 | 0 | const Token* bracTok = tok; |
3902 | 0 | while (match77(bracTok->astParent())) |
3903 | 0 | bracTok = bracTok->astParent(); |
3904 | 0 | if (!bracTok->astParent()) |
3905 | 0 | return true; |
3906 | 0 | } |
3907 | 11.0k | if (tok->str() == MatchCompiler::makeConstString(",") && tok->astParent()->isAssignmentOp()) |
3908 | 0 | return true; |
3909 | 11.0k | return false; |
3910 | 11.0k | } |
3911 | | |
3912 | | void CheckOther::checkIncompleteStatement() |
3913 | 763 | { |
3914 | 763 | if (!mSettings->severity.isEnabled(Severity::warning) && |
3915 | 763 | !mSettings->isPremiumEnabled("constStatement")) |
3916 | 0 | return; |
3917 | | |
3918 | 763 | logChecker("CheckOther::checkIncompleteStatement"); // warning |
3919 | | |
3920 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
3921 | 54.8k | const Scope *scope = tok->scope(); |
3922 | 54.8k | if (scope && !scope->isExecutable()) |
3923 | 31.1k | continue; |
3924 | 23.7k | if (!isConstTop(tok)) |
3925 | 11.0k | continue; |
3926 | 12.7k | if (tok->str() == MatchCompiler::makeConstString(",") && match61(tok->astTop()->previous())) |
3927 | 0 | continue; |
3928 | | |
3929 | | // Do not warn for statement when both lhs and rhs has side effects: |
3930 | | // dostuff() || x=213; |
3931 | 12.7k | if (match109(tok)) { |
3932 | 0 | bool warn = false; |
3933 | 0 | visitAstNodes(tok, [&warn](const Token *child) { |
3934 | 0 | if (match109(child)) |
3935 | 0 | return ChildrenToVisit::op1_and_op2; |
3936 | 0 | if (child->isAssignmentOp()) |
3937 | 0 | return ChildrenToVisit::none; |
3938 | 0 | if (child->tokType() == Token::Type::eIncDecOp) |
3939 | 0 | return ChildrenToVisit::none; |
3940 | 0 | if (match32(child->previous())) |
3941 | 0 | return ChildrenToVisit::none; |
3942 | 0 | warn = true; |
3943 | 0 | return ChildrenToVisit::done; |
3944 | 0 | }); |
3945 | 0 | if (!warn) |
3946 | 0 | continue; |
3947 | 0 | } |
3948 | | |
3949 | 12.7k | const Token *rtok = nextAfterAstRightmostLeaf(tok); |
3950 | 12.7k | if (!match51(tok->astParent()) && !match51(rtok) && |
3951 | 12.7k | !match110(tok->previous()) && |
3952 | 12.7k | !(tok->isCpp() && tok->isCast() && !tok->astParent()) && |
3953 | 12.7k | !match61(tok->tokAt(-2)) && |
3954 | 12.7k | !match89(tok->tokAt(-1)) && |
3955 | 12.7k | !(tok->str() == MatchCompiler::makeConstString(",") && tok->astParent() && tok->astParent()->isAssignmentOp())) |
3956 | 9.52k | continue; |
3957 | | // Skip statement expressions |
3958 | 3.18k | if (match111(rtok)) |
3959 | 0 | continue; |
3960 | 3.18k | if (!isConstStatement(tok)) |
3961 | 3.18k | continue; |
3962 | 0 | if (isVoidStmt(tok)) |
3963 | 0 | continue; |
3964 | 0 | if (tok->isCpp() && tok->str() == MatchCompiler::makeConstString("&") && !(tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isIntegral())) |
3965 | | // Possible archive |
3966 | 0 | continue; |
3967 | 0 | const bool inconclusive = tok->isConstOp() && !mSettings->isPremiumEnabled("constStatement"); |
3968 | 0 | if (mSettings->certainty.isEnabled(Certainty::inconclusive) || !inconclusive) |
3969 | 0 | constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive); |
3970 | 0 | } |
3971 | 763 | } |
3972 | | |
3973 | | void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive) |
3974 | 0 | { |
3975 | 0 | const Token *valueTok = tok; |
3976 | 0 | while (valueTok && valueTok->isCast()) |
3977 | 0 | valueTok = valueTok->astOperand2() ? valueTok->astOperand2() : valueTok->astOperand1(); |
3978 | |
|
3979 | 0 | std::string msg; |
3980 | 0 | if (match112(tok)) |
3981 | 0 | msg = "Found suspicious equality comparison. Did you intend to assign a value instead?"; |
3982 | 0 | else if (match113(tok)) |
3983 | 0 | msg = "Found suspicious operator '" + tok->str() + "', result is not used."; |
3984 | 0 | else if (match114(tok)) |
3985 | 0 | msg = "Unused variable value '" + tok->str() + "'"; |
3986 | 0 | else if (isConstant(valueTok)) { |
3987 | 0 | std::string typeStr("string"); |
3988 | 0 | if (valueTok->isNumber()) |
3989 | 0 | typeStr = "numeric"; |
3990 | 0 | else if (valueTok->isBoolean()) |
3991 | 0 | typeStr = "bool"; |
3992 | 0 | else if (valueTok->tokType() == Token::eChar) |
3993 | 0 | typeStr = "character"; |
3994 | 0 | else if (isNullOperand(valueTok)) |
3995 | 0 | typeStr = "NULL"; |
3996 | 0 | else if (valueTok->isEnumerator()) |
3997 | 0 | typeStr = "enumerator"; |
3998 | 0 | msg = "Redundant code: Found a statement that begins with " + typeStr + " constant."; |
3999 | 0 | } |
4000 | 0 | else if (!tok) |
4001 | 0 | msg = "Redundant code: Found a statement that begins with " + type + " constant."; |
4002 | 0 | else if (tok->isCast() && tok->tokType() == Token::Type::eExtendedOp) { |
4003 | 0 | msg = "Redundant code: Found unused cast "; |
4004 | 0 | msg += valueTok ? "of expression '" + valueTok->expressionString() + "'." : "expression."; |
4005 | 0 | } |
4006 | 0 | else if (tok->str() == MatchCompiler::makeConstString("?") && tok->tokType() == Token::Type::eExtendedOp) |
4007 | 0 | msg = "Redundant code: Found unused result of ternary operator."; |
4008 | 0 | else if (tok->str() == MatchCompiler::makeConstString(".") && tok->tokType() == Token::Type::eOther) |
4009 | 0 | msg = "Redundant code: Found unused member access."; |
4010 | 0 | else if (tok->str() == MatchCompiler::makeConstString("[") && tok->tokType() == Token::Type::eExtendedOp) |
4011 | 0 | msg = "Redundant code: Found unused array access."; |
4012 | 0 | else if (tok->str() == MatchCompiler::makeConstString("[") && !tok->astParent()) |
4013 | 0 | msg = "Redundant code: Found unused lambda."; |
4014 | 0 | else if (mSettings->debugwarnings) { |
4015 | 0 | reportError(tok, Severity::debug, "debug", "constStatementError not handled."); |
4016 | 0 | return; |
4017 | 0 | } |
4018 | 0 | reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); |
4019 | 0 | } |
4020 | | |
4021 | | //--------------------------------------------------------------------------- |
4022 | | // Detect division by zero. |
4023 | | //--------------------------------------------------------------------------- |
4024 | | void CheckOther::checkZeroDivision() |
4025 | 763 | { |
4026 | 763 | logChecker("CheckOther::checkZeroDivision"); |
4027 | | |
4028 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
4029 | 54.8k | if (!tok->astOperand2() || !tok->astOperand1()) |
4030 | 46.5k | continue; |
4031 | 8.33k | if (tok->str() != MatchCompiler::makeConstString("%") && tok->str() != MatchCompiler::makeConstString("/") && tok->str() != MatchCompiler::makeConstString("%=") && tok->str() != MatchCompiler::makeConstString("/=")) |
4032 | 8.17k | continue; |
4033 | 160 | if (!tok->valueType() || !tok->valueType()->isIntegral()) |
4034 | 72 | continue; |
4035 | 88 | if (tok->scope() && tok->scope()->type == Scope::eEnum) // don't warn for compile-time error |
4036 | 0 | continue; |
4037 | | |
4038 | | // Value flow.. |
4039 | 88 | const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); |
4040 | 88 | if (value && mSettings->isEnabled(value, false)) |
4041 | 0 | zerodivError(tok, value); |
4042 | 88 | } |
4043 | 763 | } |
4044 | | |
4045 | | void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) |
4046 | 0 | { |
4047 | 0 | if (!tok && !value) { |
4048 | 0 | reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, Certainty::normal); |
4049 | 0 | reportError(tok, Severity::warning, "zerodivcond", ValueFlow::eitherTheConditionIsRedundant(nullptr) + " or there is division by zero.", CWE369, Certainty::normal); |
4050 | 0 | return; |
4051 | 0 | } |
4052 | | |
4053 | 0 | const ErrorPath errorPath = getErrorPath(tok, value, "Division by zero"); |
4054 | |
|
4055 | 0 | std::ostringstream errmsg; |
4056 | 0 | if (value->condition) { |
4057 | 0 | const int line = tok ? tok->linenr() : 0; |
4058 | 0 | errmsg << ValueFlow::eitherTheConditionIsRedundant(value->condition) |
4059 | 0 | << " or there is division by zero at line " << line << "."; |
4060 | 0 | } else |
4061 | 0 | errmsg << "Division by zero."; |
4062 | |
|
4063 | 0 | reportError(errorPath, |
4064 | 0 | value->errorSeverity() ? Severity::error : Severity::warning, |
4065 | 0 | value->condition ? "zerodivcond" : "zerodiv", |
4066 | 0 | errmsg.str(), CWE369, value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); |
4067 | 0 | } |
4068 | | |
4069 | | //--------------------------------------------------------------------------- |
4070 | | // Check for NaN (not-a-number) in an arithmetic expression, e.g. |
4071 | | // double d = 1.0 / 0.0 + 100.0; |
4072 | | //--------------------------------------------------------------------------- |
4073 | | |
4074 | | void CheckOther::checkNanInArithmeticExpression() |
4075 | 763 | { |
4076 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("nanInArithmeticExpression")) |
4077 | 0 | return; |
4078 | 763 | logChecker("CheckOther::checkNanInArithmeticExpression"); // style |
4079 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
4080 | 54.8k | if (tok->str() != MatchCompiler::makeConstString("/")) |
4081 | 54.8k | continue; |
4082 | 69 | if (!match115(tok->astParent())) |
4083 | 65 | continue; |
4084 | 4 | if (match116(tok->astOperand2())) |
4085 | 0 | nanInArithmeticExpressionError(tok); |
4086 | 4 | } |
4087 | 763 | } |
4088 | | |
4089 | | void CheckOther::nanInArithmeticExpressionError(const Token *tok) |
4090 | 0 | { |
4091 | 0 | reportError(tok, Severity::style, "nanInArithmeticExpression", |
4092 | 0 | "Using NaN/Inf in a computation.\n" |
4093 | 0 | "Using NaN/Inf in a computation. " |
4094 | 0 | "Although nothing bad really happens, it is suspicious.", CWE369, Certainty::normal); |
4095 | 0 | } |
4096 | | |
4097 | | //--------------------------------------------------------------------------- |
4098 | | // Creating instance of classes which are destroyed immediately |
4099 | | //--------------------------------------------------------------------------- |
4100 | | void CheckOther::checkMisusedScopedObject() |
4101 | 763 | { |
4102 | | // Skip this check for .c files |
4103 | 763 | if (mTokenizer->isC()) |
4104 | 0 | return; |
4105 | | |
4106 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedScopedObject")) |
4107 | 0 | return; |
4108 | | |
4109 | 763 | logChecker("CheckOther::checkMisusedScopedObject"); // style,c++ |
4110 | | |
4111 | 22.6k | auto getConstructorTok = [](const Token* tok, std::string& typeStr) -> const Token* { |
4112 | 22.6k | if (!match117(tok) || tok->next()->isKeyword()) |
4113 | 20.6k | return nullptr; |
4114 | 2.00k | tok = tok->next(); |
4115 | 2.00k | typeStr.clear(); |
4116 | 2.00k | while (match118(tok)) { |
4117 | 0 | typeStr += tok->str(); |
4118 | 0 | typeStr += "::"; |
4119 | 0 | tok = tok->tokAt(2); |
4120 | 0 | } |
4121 | 2.00k | typeStr += tok->str(); |
4122 | 2.00k | const Token* endTok = tok; |
4123 | 2.00k | if (match119(endTok)) |
4124 | 0 | endTok = endTok->linkAt(1); |
4125 | 2.00k | if (match120(endTok) && match121(endTok->linkAt(1)) && |
4126 | 2.00k | !match51(endTok->next()->astParent())) { // for loop condition |
4127 | 0 | return tok; |
4128 | 0 | } |
4129 | 2.00k | return nullptr; |
4130 | 2.00k | }; |
4131 | | |
4132 | 763 | auto isLibraryConstructor = [&](const Token* tok, const std::string& typeStr) -> bool { |
4133 | 0 | const Library::TypeCheck typeCheck = mSettings->library.getTypeCheck("unusedvar", typeStr); |
4134 | 0 | if (typeCheck == Library::TypeCheck::check || typeCheck == Library::TypeCheck::checkFiniteLifetime) |
4135 | 0 | return true; |
4136 | 0 | return mSettings->library.detectContainerOrIterator(tok); |
4137 | 0 | }; |
4138 | | |
4139 | 763 | const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); |
4140 | 763 | std::string typeStr; |
4141 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
4142 | 23.7k | for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { |
4143 | 22.6k | const Token* ctorTok = getConstructorTok(tok, typeStr); |
4144 | 22.6k | if (ctorTok && (((ctorTok->type() || ctorTok->isStandardType() || (ctorTok->function() && ctorTok->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug |
4145 | 0 | && (!ctorTok->function() || ctorTok->function()->isConstructor()) // // is not a function on this scope or is function in this scope and it's a ctor |
4146 | 0 | && ctorTok->str() != MatchCompiler::makeConstString("void")) || isLibraryConstructor(tok->next(), typeStr))) { |
4147 | 0 | const Token* parTok = ctorTok->next(); |
4148 | 0 | if (match17(parTok) && parTok->link()) |
4149 | 0 | parTok = parTok->link()->next(); |
4150 | 0 | if (const Token* arg = parTok->astOperand2()) { |
4151 | 0 | if (!isConstStatement(arg)) |
4152 | 0 | continue; |
4153 | 0 | if (parTok->str() == MatchCompiler::makeConstString("(")) { |
4154 | 0 | if (arg->varId() && !(arg->variable() && arg->variable()->nameToken() != arg)) |
4155 | 0 | continue; |
4156 | 0 | const Token* rml = nextAfterAstRightmostLeaf(arg); |
4157 | 0 | if (rml && rml->previous() && rml->previous()->varId()) |
4158 | 0 | continue; |
4159 | 0 | } |
4160 | 0 | } |
4161 | 0 | tok = tok->next(); |
4162 | 0 | misusedScopeObjectError(ctorTok, typeStr); |
4163 | 0 | tok = tok->next(); |
4164 | 0 | } |
4165 | 22.6k | if (tok->isAssignmentOp() && match67(tok->astOperand1()) && tok->astOperand1()->astOperand1()) { |
4166 | 0 | if (const Function* ftok = tok->astOperand1()->astOperand1()->function()) { |
4167 | 0 | if (ftok->retType && match122(ftok->retType->classDef) && !Function::returnsReference(ftok, /*unknown*/ false, /*includeRValueRef*/ true)) |
4168 | 0 | misusedScopeObjectError(tok->next(), ftok->retType->name(), /*isAssignment*/ true); |
4169 | 0 | } |
4170 | 0 | } |
4171 | 22.6k | } |
4172 | 1.10k | } |
4173 | 763 | } |
4174 | | |
4175 | | void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname, bool isAssignment) |
4176 | 0 | { |
4177 | 0 | std::string msg = "Instance of '$symbol' object is destroyed immediately"; |
4178 | 0 | msg += isAssignment ? ", assignment has no effect." : "."; |
4179 | 0 | reportError(tok, Severity::style, |
4180 | 0 | "unusedScopedObject", |
4181 | 0 | "$symbol:" + varname + "\n" + |
4182 | 0 | msg, CWE563, Certainty::normal); |
4183 | 0 | } |
4184 | | |
4185 | | static const Token * getSingleExpressionInBlock(const Token * tok) |
4186 | 212 | { |
4187 | 212 | if (!tok) |
4188 | 0 | return nullptr; |
4189 | 212 | const Token * top = tok->astTop(); |
4190 | 212 | const Token * nextExpression = nextAfterAstRightmostLeaf(top); |
4191 | 212 | if (!match123(nextExpression)) |
4192 | 157 | return nullptr; |
4193 | 55 | return top; |
4194 | 212 | } |
4195 | | |
4196 | | //----------------------------------------------------------------------------- |
4197 | | // check for duplicate code in if and else branches |
4198 | | // if (a) { b = true; } else { b = true; } |
4199 | | //----------------------------------------------------------------------------- |
4200 | | void CheckOther::checkDuplicateBranch() |
4201 | 763 | { |
4202 | | // This is inconclusive since in practice most warnings are noise: |
4203 | | // * There can be unfixed low-priority todos. The code is fine as it |
4204 | | // is but it could be possible to enhance it. Writing a warning |
4205 | | // here is noise since the code is fine (see cppcheck, abiword, ..) |
4206 | | // * There can be overspecified code so some conditions can't be true |
4207 | | // and their conditional code is a duplicate of the condition that |
4208 | | // is always true just in case it would be false. See for instance |
4209 | | // abiword. |
4210 | 763 | if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) |
4211 | 0 | return; |
4212 | | |
4213 | 763 | logChecker("CheckOther::checkDuplicateBranch"); // style,inconclusive |
4214 | | |
4215 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
4216 | | |
4217 | 2.79k | for (const Scope & scope : symbolDatabase->scopeList) { |
4218 | 2.79k | if (scope.type != Scope::eIf) |
4219 | 2.29k | continue; |
4220 | | |
4221 | | // check all the code in the function for if (..) else |
4222 | 501 | if (match124(scope.bodyEnd)) { |
4223 | | // Make sure there are no macros (different macros might be expanded |
4224 | | // to the same code) |
4225 | 279 | bool macro = false; |
4226 | 4.41k | for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd->linkAt(2); tok = tok->next()) { |
4227 | 4.13k | if (tok->isExpandedMacro()) { |
4228 | 0 | macro = true; |
4229 | 0 | break; |
4230 | 0 | } |
4231 | 4.13k | } |
4232 | 279 | if (macro) |
4233 | 0 | continue; |
4234 | | |
4235 | 279 | const Token * const tokIf = scope.bodyStart->next(); |
4236 | 279 | const Token * const tokElse = scope.bodyEnd->tokAt(3); |
4237 | | |
4238 | | // compare first tok before stringifying the whole blocks |
4239 | 279 | const std::string tokIfStr = tokIf->stringify(false, true, false); |
4240 | 279 | if (tokIfStr.empty()) |
4241 | 0 | continue; |
4242 | | |
4243 | 279 | const std::string tokElseStr = tokElse->stringify(false, true, false); |
4244 | | |
4245 | 279 | if (tokIfStr == tokElseStr) { |
4246 | | // save if branch code |
4247 | 134 | const std::string branch1 = tokIf->stringifyList(scope.bodyEnd); |
4248 | | |
4249 | 134 | if (branch1.empty()) |
4250 | 119 | continue; |
4251 | | |
4252 | | // save else branch code |
4253 | 15 | const std::string branch2 = tokElse->stringifyList(scope.bodyEnd->linkAt(2)); |
4254 | | |
4255 | | // check for duplicates |
4256 | 15 | if (branch1 == branch2) { |
4257 | 2 | duplicateBranchError(scope.classDef, scope.bodyEnd->next(), ErrorPath{}); |
4258 | 2 | continue; |
4259 | 2 | } |
4260 | 15 | } |
4261 | | |
4262 | | // check for duplicates using isSameExpression |
4263 | 158 | const Token * branchTop1 = getSingleExpressionInBlock(tokIf); |
4264 | 158 | if (!branchTop1) |
4265 | 104 | continue; |
4266 | 54 | const Token * branchTop2 = getSingleExpressionInBlock(tokElse); |
4267 | 54 | if (!branchTop2) |
4268 | 53 | continue; |
4269 | 1 | if (branchTop1->str() != branchTop2->str()) |
4270 | 0 | continue; |
4271 | 1 | ErrorPath errorPath; |
4272 | 1 | if (isSameExpression(false, branchTop1->astOperand1(), branchTop2->astOperand1(), *mSettings, true, true, &errorPath) && |
4273 | 1 | isSameExpression(false, branchTop1->astOperand2(), branchTop2->astOperand2(), *mSettings, true, true, &errorPath)) |
4274 | 0 | duplicateBranchError(scope.classDef, scope.bodyEnd->next(), std::move(errorPath)); |
4275 | 1 | } |
4276 | 501 | } |
4277 | 763 | } |
4278 | | |
4279 | | void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) |
4280 | 2 | { |
4281 | 2 | errors.emplace_back(tok2, ""); |
4282 | 2 | errors.emplace_back(tok1, ""); |
4283 | | |
4284 | 2 | reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" |
4285 | 2 | "Finding the same code in an 'if' and related 'else' branch is suspicious and " |
4286 | 2 | "might indicate a cut and paste or logic error. Please examine this code " |
4287 | 2 | "carefully to determine if it is correct.", CWE398, Certainty::inconclusive); |
4288 | 2 | } |
4289 | | |
4290 | | |
4291 | | //----------------------------------------------------------------------------- |
4292 | | // Check for a free() of an invalid address |
4293 | | // char* p = malloc(100); |
4294 | | // free(p + 10); |
4295 | | //----------------------------------------------------------------------------- |
4296 | | void CheckOther::checkInvalidFree() |
4297 | 763 | { |
4298 | 763 | std::map<int, bool> inconclusive; |
4299 | 763 | std::map<int, std::string> allocation; |
4300 | | |
4301 | 763 | logChecker("CheckOther::checkInvalidFree"); |
4302 | | |
4303 | 763 | const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
4304 | 763 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
4305 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
4306 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { |
4307 | | |
4308 | | // Keep track of which variables were assigned addresses to newly-allocated memory |
4309 | 21.5k | if ((tok->isCpp() && match125(tok)) || |
4310 | 21.5k | (match126(tok) && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { |
4311 | 0 | allocation.emplace(tok->varId(), tok->strAt(2)); |
4312 | 0 | inconclusive.emplace(tok->varId(), false); |
4313 | 0 | } |
4314 | | |
4315 | | // If a previously-allocated pointer is incremented or decremented, any subsequent |
4316 | | // free involving pointer arithmetic may or may not be invalid, so we should only |
4317 | | // report an inconclusive result. |
4318 | 21.5k | else if (match127(tok) && |
4319 | 21.5k | tok->varId() == tok->tokAt(2)->varId() && |
4320 | 21.5k | allocation.find(tok->varId()) != allocation.end()) { |
4321 | 0 | if (printInconclusive) |
4322 | 0 | inconclusive[tok->varId()] = true; |
4323 | 0 | else { |
4324 | 0 | allocation.erase(tok->varId()); |
4325 | 0 | inconclusive.erase(tok->varId()); |
4326 | 0 | } |
4327 | 0 | } |
4328 | | |
4329 | | // If a previously-allocated pointer is assigned a completely new value, |
4330 | | // we can't know if any subsequent free() on that pointer is valid or not. |
4331 | 21.5k | else if (match128(tok)) { |
4332 | 260 | allocation.erase(tok->varId()); |
4333 | 260 | inconclusive.erase(tok->varId()); |
4334 | 260 | } |
4335 | | |
4336 | | // If a variable that was previously assigned a newly-allocated memory location is |
4337 | | // added or subtracted from when used to free the memory, report an error. |
4338 | 21.3k | else if ((match129(tok) && mSettings->library.getDeallocFuncInfo(tok)) || |
4339 | 21.3k | match130(tok) || |
4340 | 21.3k | match131(tok)) { |
4341 | |
|
4342 | 0 | const int varIndex = tok->strAt(1) == MatchCompiler::makeConstString("(") ? 2 : |
4343 | 0 | tok->strAt(3) == MatchCompiler::makeConstString("(") ? 4 : 1; |
4344 | 0 | const int var1 = tok->tokAt(varIndex)->varId(); |
4345 | 0 | const int var2 = tok->tokAt(varIndex + 2)->varId(); |
4346 | 0 | const auto alloc1 = utils::as_const(inconclusive).find(var1); |
4347 | 0 | const auto alloc2 = utils::as_const(inconclusive).find(var2); |
4348 | 0 | if (alloc1 != inconclusive.end()) { |
4349 | 0 | invalidFreeError(tok, allocation[var1], alloc1->second); |
4350 | 0 | } else if (alloc2 != inconclusive.end()) { |
4351 | 0 | invalidFreeError(tok, allocation[var2], alloc2->second); |
4352 | 0 | } |
4353 | 0 | } |
4354 | | |
4355 | | // If the previously-allocated variable is passed in to another function |
4356 | | // as a parameter, it might be modified, so we shouldn't report an error |
4357 | | // if it is later used to free memory |
4358 | 21.3k | else if (match32(tok) && !mSettings->library.isFunctionConst(tok->str(), true)) { |
4359 | 722 | const Token* tok2 = findmatch132(tok->next(), tok->linkAt(1)) ; |
4360 | 2.24k | while (tok2 != nullptr) { |
4361 | 1.52k | allocation.erase(tok->varId()); |
4362 | 1.52k | inconclusive.erase(tok2->varId()); |
4363 | 1.52k | tok2 = findmatch132(tok2->next(), tok->linkAt(1)) ; |
4364 | 1.52k | } |
4365 | 722 | } |
4366 | 21.5k | } |
4367 | 1.10k | } |
4368 | 763 | } |
4369 | | |
4370 | | void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) |
4371 | 0 | { |
4372 | 0 | std::string alloc = allocation; |
4373 | 0 | if (alloc != MatchCompiler::makeConstString("new")) |
4374 | 0 | alloc += "()"; |
4375 | 0 | std::string deallocated = (alloc == MatchCompiler::makeConstString("new")) ? "deleted" : "freed"; |
4376 | 0 | reportError(tok, Severity::error, "invalidFree", "Mismatching address is " + deallocated + ". The address you get from " + alloc + " must be " + deallocated + " without offset.", CWE(0U), inconclusive ? Certainty::inconclusive : Certainty::normal); |
4377 | 0 | } |
4378 | | |
4379 | | |
4380 | | //--------------------------------------------------------------------------- |
4381 | | // check for the same expression on both sides of an operator |
4382 | | // (x == x), (x && x), (x || x) |
4383 | | // (x.y == x.y), (x.y && x.y), (x.y || x.y) |
4384 | | //--------------------------------------------------------------------------- |
4385 | | |
4386 | | namespace { |
4387 | | bool notconst(const Function* func) |
4388 | 1.10k | { |
4389 | 1.10k | return !func->isConst(); |
4390 | 1.10k | } |
4391 | | |
4392 | | void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list<const Function*> &constFunctions) |
4393 | 763 | { |
4394 | 2.79k | for (const Scope &scope : symbolDatabase->scopeList) { |
4395 | | // only add const functions that do not have a non-const overloaded version |
4396 | | // since it is pretty much impossible to tell which is being called. |
4397 | 2.79k | using StringFunctionMap = std::map<std::string, std::list<const Function*>>; |
4398 | 2.79k | StringFunctionMap functionsByName; |
4399 | 2.79k | for (const Function &func : scope.functionList) { |
4400 | 1.10k | functionsByName[func.tokenDef->str()].push_back(&func); |
4401 | 1.10k | } |
4402 | 2.79k | for (std::pair<const std::string, std::list<const Function*>>& it : functionsByName) { |
4403 | 1.10k | const auto nc = std::find_if(it.second.cbegin(), it.second.cend(), notconst); |
4404 | 1.10k | if (nc == it.second.cend()) { |
4405 | | // ok to add all of them |
4406 | 0 | constFunctions.splice(constFunctions.end(), it.second); |
4407 | 0 | } |
4408 | 1.10k | } |
4409 | 2.79k | } |
4410 | 763 | } |
4411 | | } |
4412 | | |
4413 | | static bool |
4414 | | isStaticAssert(const Settings &settings, const Token *tok) |
4415 | 82 | { |
4416 | 82 | if (tok->isCpp() && settings.standards.cpp >= Standards::CPP11 && |
4417 | 82 | match133(tok)) { |
4418 | 0 | return true; |
4419 | 0 | } |
4420 | | |
4421 | 82 | if (tok->isC() && settings.standards.c >= Standards::C11 && |
4422 | 82 | match134(tok)) { |
4423 | 0 | return true; |
4424 | 0 | } |
4425 | | |
4426 | 82 | return false; |
4427 | 82 | } |
4428 | | |
4429 | | void CheckOther::checkDuplicateExpression() |
4430 | 763 | { |
4431 | 763 | { |
4432 | 763 | const bool styleEnabled = mSettings->severity.isEnabled(Severity::style); |
4433 | 763 | const bool premiumEnabled = mSettings->isPremiumEnabled("oppositeExpression") || |
4434 | 763 | mSettings->isPremiumEnabled("duplicateExpression") || |
4435 | 763 | mSettings->isPremiumEnabled("duplicateAssignExpression") || |
4436 | 763 | mSettings->isPremiumEnabled("duplicateExpressionTernary") || |
4437 | 763 | mSettings->isPremiumEnabled("duplicateValueTernary") || |
4438 | 763 | mSettings->isPremiumEnabled("selfAssignment") || |
4439 | 763 | mSettings->isPremiumEnabled("knownConditionTrueFalse"); |
4440 | | |
4441 | 763 | if (!styleEnabled && !premiumEnabled) |
4442 | 0 | return; |
4443 | 763 | } |
4444 | | |
4445 | 763 | logChecker("CheckOther::checkDuplicateExpression"); // style,warning |
4446 | | |
4447 | | // Parse all executing scopes.. |
4448 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
4449 | | |
4450 | 763 | std::list<const Function*> constFunctions; |
4451 | 763 | getConstFunctions(symbolDatabase, constFunctions); |
4452 | | |
4453 | 1.10k | for (const Scope *scope : symbolDatabase->functionScopes) { |
4454 | 23.7k | for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
4455 | 22.6k | if (tok->str() == MatchCompiler::makeConstString("=") && match114(tok->astOperand1())) { |
4456 | 240 | const Token * endStatement = findmatch41(tok) ; |
4457 | 240 | if (match135(endStatement)) { |
4458 | 2 | endStatement = endStatement->tokAt(4); |
4459 | 2 | } |
4460 | 240 | if (match136(endStatement)) { |
4461 | 0 | const Token * nextAssign = endStatement->tokAt(1); |
4462 | 0 | const Token * var1 = tok->astOperand1(); |
4463 | 0 | const Token * var2 = nextAssign->astOperand1(); |
4464 | 0 | if (var1 && var2 && |
4465 | 0 | match137(var1->previous()) && |
4466 | 0 | match137(var2->previous()) && |
4467 | 0 | var2->valueType() && var1->valueType() && |
4468 | 0 | var2->valueType()->originalTypeName == var1->valueType()->originalTypeName && |
4469 | 0 | var2->valueType()->pointer == var1->valueType()->pointer && |
4470 | 0 | var2->valueType()->constness == var1->valueType()->constness && |
4471 | 0 | var2->varId() != var1->varId() && ( |
4472 | 0 | tok->astOperand2()->isArithmeticalOp() || |
4473 | 0 | tok->astOperand2()->str() == MatchCompiler::makeConstString(".") || |
4474 | 0 | match32(tok->astOperand2()->previous()) |
4475 | 0 | ) && |
4476 | 0 | tok->next()->tokType() != Token::eType && |
4477 | 0 | isSameExpression(true, tok->next(), nextAssign->next(), *mSettings, true, false) && |
4478 | 0 | isSameExpression(true, tok->astOperand2(), nextAssign->astOperand2(), *mSettings, true, false) && |
4479 | 0 | tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) { |
4480 | 0 | bool differentDomain = false; |
4481 | 0 | const Scope * varScope = var1->scope() ? var1->scope() : scope; |
4482 | 0 | const Token* assignTok = findmatch41(var2) ; |
4483 | 0 | for (; assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) { |
4484 | 0 | if (!match138(assignTok)) |
4485 | 0 | continue; |
4486 | 0 | if (!assignTok->astOperand1()) |
4487 | 0 | continue; |
4488 | 0 | if (!assignTok->astOperand2()) |
4489 | 0 | continue; |
4490 | | |
4491 | 0 | if (assignTok->astOperand1()->varId() != var1->varId() && |
4492 | 0 | assignTok->astOperand1()->varId() != var2->varId() && |
4493 | 0 | !isSameExpression(true, |
4494 | 0 | tok->astOperand2(), |
4495 | 0 | assignTok->astOperand1(), |
4496 | 0 | *mSettings, |
4497 | 0 | true, |
4498 | 0 | true)) |
4499 | 0 | continue; |
4500 | 0 | if (assignTok->astOperand2()->varId() != var1->varId() && |
4501 | 0 | assignTok->astOperand2()->varId() != var2->varId() && |
4502 | 0 | !isSameExpression(true, |
4503 | 0 | tok->astOperand2(), |
4504 | 0 | assignTok->astOperand2(), |
4505 | 0 | *mSettings, |
4506 | 0 | true, |
4507 | 0 | true)) |
4508 | 0 | continue; |
4509 | 0 | differentDomain = true; |
4510 | 0 | break; |
4511 | 0 | } |
4512 | 0 | if (!differentDomain && !isUniqueExpression(tok->astOperand2())) |
4513 | 0 | duplicateAssignExpressionError(var1, var2, false); |
4514 | 0 | else if (mSettings->certainty.isEnabled(Certainty::inconclusive)) { |
4515 | 0 | diag(assignTok); |
4516 | 0 | duplicateAssignExpressionError(var1, var2, true); |
4517 | 0 | } |
4518 | 0 | } |
4519 | 0 | } |
4520 | 240 | } |
4521 | 22.6k | auto isInsideLambdaCaptureList = [](const Token* tok) { |
4522 | 4.48k | const Token* parent = tok->astParent(); |
4523 | 4.49k | while (match65(parent)) |
4524 | 2 | parent = parent->astParent(); |
4525 | 4.48k | return isLambdaCaptureList(parent); |
4526 | 4.48k | }; |
4527 | 22.6k | ErrorPath errorPath; |
4528 | 22.6k | if (tok->isOp() && tok->astOperand1() && !match139(tok) && !isInsideLambdaCaptureList(tok)) { |
4529 | 4.48k | if (match140(tok) && astIsFloat(tok->astOperand1(), true)) |
4530 | 97 | continue; |
4531 | 4.39k | const bool pointerDereference = (tok->astOperand1() && tok->astOperand1()->isUnaryOp("*")) || |
4532 | 4.39k | (tok->astOperand2() && tok->astOperand2()->isUnaryOp("*")); |
4533 | 4.39k | const bool followVar = (!isConstVarExpression(tok) || match141(tok)) && !pointerDereference; |
4534 | 4.39k | if (isSameExpression(true, |
4535 | 4.39k | tok->astOperand1(), |
4536 | 4.39k | tok->astOperand2(), |
4537 | 4.39k | *mSettings, |
4538 | 4.39k | true, |
4539 | 4.39k | followVar, |
4540 | 4.39k | &errorPath)) { |
4541 | 376 | if (isWithoutSideEffects(tok->astOperand1())) { |
4542 | 376 | const Token* loopTok = isInLoopCondition(tok); |
4543 | 376 | if (!loopTok || |
4544 | 376 | !findExpressionChanged(tok, tok, loopTok->link()->linkAt(1), *mSettings)) { |
4545 | 374 | const bool isEnum = tok->scope()->type == Scope::eEnum; |
4546 | 374 | const bool assignment = !isEnum && tok->str() == MatchCompiler::makeConstString("="); |
4547 | 374 | if (assignment) |
4548 | 77 | selfAssignmentError(tok, tok->astOperand1()->expressionString()); |
4549 | 297 | else if (!isEnum) { |
4550 | 297 | if (tok->str() == MatchCompiler::makeConstString("==")) { |
4551 | 82 | const Token* parent = tok->astParent(); |
4552 | 95 | while (parent && parent->astParent()) { |
4553 | 13 | parent = parent->astParent(); |
4554 | 13 | } |
4555 | 82 | if (parent && parent->previous() && isStaticAssert(*mSettings, parent->previous())) { |
4556 | 0 | continue; |
4557 | 0 | } |
4558 | 82 | } |
4559 | 297 | duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, std::move(errorPath)); |
4560 | 297 | } |
4561 | 374 | } |
4562 | 376 | } |
4563 | 4.01k | } else if (tok->str() == MatchCompiler::makeConstString("=") && match31(tok->astOperand2()) && |
4564 | 4.01k | isSameExpression(false, |
4565 | 53 | tok->astOperand1(), |
4566 | 53 | tok->astOperand2()->astOperand1(), |
4567 | 53 | *mSettings, |
4568 | 53 | true, |
4569 | 53 | false)) { |
4570 | 6 | if (isWithoutSideEffects(tok->astOperand1())) { |
4571 | 6 | selfAssignmentError(tok, tok->astOperand1()->expressionString()); |
4572 | 6 | } |
4573 | 4.01k | } else if (isOppositeExpression(tok->astOperand1(), |
4574 | 4.01k | tok->astOperand2(), |
4575 | 4.01k | *mSettings, |
4576 | 4.01k | false, |
4577 | 4.01k | true, |
4578 | 4.01k | &errorPath) && |
4579 | 4.01k | !match142(tok) && |
4580 | 4.01k | isWithoutSideEffects(tok->astOperand1())) { |
4581 | 1 | oppositeExpressionError(tok, std::move(errorPath)); |
4582 | 4.00k | } else if (!match143(tok)) { // These operators are not associative |
4583 | 3.83k | if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && |
4584 | 3.83k | isSameExpression(true, |
4585 | 48 | tok->astOperand2(), |
4586 | 48 | tok->astOperand1()->astOperand2(), |
4587 | 48 | *mSettings, |
4588 | 48 | true, |
4589 | 48 | followVar, |
4590 | 48 | &errorPath) && |
4591 | 3.83k | isWithoutSideEffects(tok->astOperand2())) |
4592 | 17 | duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, std::move(errorPath)); |
4593 | 3.81k | else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library)) { |
4594 | 2.69k | auto checkDuplicate = [&](const Token* exp1, const Token* exp2, const Token* ast1) { |
4595 | 39 | if (isSameExpression(true, exp1, exp2, *mSettings, true, true, &errorPath) && |
4596 | 39 | isWithoutSideEffects(exp1) && |
4597 | 39 | isWithoutSideEffects(ast1->astOperand2())) |
4598 | 3 | duplicateExpressionError(exp1, exp2, tok, errorPath, /*hasMultipleExpr*/ true); |
4599 | 39 | }; |
4600 | 2.69k | const Token *ast1 = tok->astOperand1(); |
4601 | 2.71k | while (ast1 && tok->str() == ast1->str()) { // chain of identical operators |
4602 | 20 | checkDuplicate(ast1->astOperand2(), tok->astOperand2(), ast1); |
4603 | 20 | if (ast1->astOperand1() && ast1->astOperand1()->str() != tok->str()) // check first condition in the chain |
4604 | 19 | checkDuplicate(ast1->astOperand1(), tok->astOperand2(), ast1); |
4605 | 20 | ast1 = ast1->astOperand1(); |
4606 | 20 | } |
4607 | 2.69k | } |
4608 | 3.83k | } |
4609 | 18.1k | } else if (tok->astOperand1() && tok->astOperand2() && tok->str() == MatchCompiler::makeConstString(":") && tok->astParent() && tok->astParent()->str() == MatchCompiler::makeConstString("?")) { |
4610 | 0 | if (!tok->astOperand1()->values().empty() && !tok->astOperand2()->values().empty() && isEqualKnownValue(tok->astOperand1(), tok->astOperand2()) && |
4611 | 0 | !isVariableChanged(tok->astParent(), /*indirect*/ 0, *mSettings) && |
4612 | 0 | isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2())) |
4613 | 0 | duplicateValueTernaryError(tok); |
4614 | 0 | else if (isSameExpression(true, tok->astOperand1(), tok->astOperand2(), *mSettings, false, true, &errorPath)) |
4615 | 0 | duplicateExpressionTernaryError(tok, std::move(errorPath)); |
4616 | 0 | } |
4617 | 22.6k | } |
4618 | 1.10k | } |
4619 | 763 | } |
4620 | | |
4621 | | void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) |
4622 | 1 | { |
4623 | 1 | errors.emplace_back(opTok, ""); |
4624 | | |
4625 | 1 | const std::string& op = opTok ? opTok->str() : "&&"; |
4626 | | |
4627 | 1 | reportError(errors, Severity::style, "oppositeExpression", "Opposite expression on both sides of \'" + op + "\'.\n" |
4628 | 1 | "Finding the opposite expression on both sides of an operator is suspicious and might " |
4629 | 1 | "indicate a cut and paste or logic error. Please examine this code carefully to " |
4630 | 1 | "determine if it is correct.", CWE398, Certainty::normal); |
4631 | 1 | } |
4632 | | |
4633 | | void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr) |
4634 | 317 | { |
4635 | 317 | errors.emplace_back(opTok, ""); |
4636 | | |
4637 | 317 | const std::string& expr1 = tok1 ? tok1->expressionString() : "x"; |
4638 | 317 | const std::string& expr2 = tok2 ? tok2->expressionString() : "x"; |
4639 | | |
4640 | 317 | const std::string& op = opTok ? opTok->str() : "&&"; |
4641 | 317 | std::string msg = "Same expression " + (hasMultipleExpr ? "\'" + expr1 + "\'" + " found multiple times in chain of \'" + op + "\' operators" : "on both sides of \'" + op + "\'"); |
4642 | 317 | const char *id = "duplicateExpression"; |
4643 | 317 | if (expr1 != expr2 && (!opTok || match144(opTok))) { |
4644 | 0 | id = "knownConditionTrueFalse"; |
4645 | 0 | std::string exprMsg = "The comparison \'" + expr1 + " " + op + " " + expr2 + "\' is always "; |
4646 | 0 | if (match145(opTok)) |
4647 | 0 | msg = exprMsg + "true"; |
4648 | 0 | else if (match146(opTok)) |
4649 | 0 | msg = exprMsg + "false"; |
4650 | 0 | } |
4651 | | |
4652 | 317 | if (expr1 != expr2 && !match147(tok1) && !match147(tok2)) |
4653 | 0 | msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value"; |
4654 | | |
4655 | 317 | reportError(errors, Severity::style, id, msg + |
4656 | 317 | (std::string(".\nFinding the same expression ") + (hasMultipleExpr ? "more than once in a condition" : "on both sides of an operator")) + |
4657 | 317 | " is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " |
4658 | 317 | "determine if it is correct.", CWE398, Certainty::normal); |
4659 | 317 | } |
4660 | | |
4661 | | void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) |
4662 | 0 | { |
4663 | 0 | const std::list<const Token *> toks = { tok2, tok1 }; |
4664 | |
|
4665 | 0 | const std::string& var1 = tok1 ? tok1->str() : "x"; |
4666 | 0 | const std::string& var2 = tok2 ? tok2->str() : "x"; |
4667 | |
|
4668 | 0 | reportError(toks, Severity::style, "duplicateAssignExpression", |
4669 | 0 | "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n" |
4670 | 0 | "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression " |
4671 | 0 | "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " |
4672 | 0 | "determine if it is correct.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); |
4673 | 0 | } |
4674 | | |
4675 | | void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) |
4676 | 0 | { |
4677 | 0 | errors.emplace_back(tok, ""); |
4678 | 0 | reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n" |
4679 | 0 | "Finding the same expression in both branches of ternary operator is suspicious as " |
4680 | 0 | "the same code is executed regardless of the condition.", CWE398, Certainty::normal); |
4681 | 0 | } |
4682 | | |
4683 | | void CheckOther::duplicateValueTernaryError(const Token *tok) |
4684 | 0 | { |
4685 | 0 | reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n" |
4686 | 0 | "Finding the same value in both branches of ternary operator is suspicious as " |
4687 | 0 | "the same code is executed regardless of the condition.", CWE398, Certainty::normal); |
4688 | 0 | } |
4689 | | |
4690 | | void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) |
4691 | 83 | { |
4692 | 83 | reportError(tok, Severity::style, |
4693 | 83 | "selfAssignment", |
4694 | 83 | "$symbol:" + varname + "\n" |
4695 | 83 | "Redundant assignment of '$symbol' to itself.", CWE398, Certainty::normal); |
4696 | 83 | } |
4697 | | |
4698 | | //----------------------------------------------------------------------------- |
4699 | | // Check is a comparison of two variables leads to condition, which is |
4700 | | // always true or false. |
4701 | | // For instance: int a = 1; if(isless(a,a)){...} |
4702 | | // In this case isless(a,a) always evaluates to false. |
4703 | | // |
4704 | | // Reference: |
4705 | | // - http://www.cplusplus.com/reference/cmath/ |
4706 | | //----------------------------------------------------------------------------- |
4707 | | void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() |
4708 | 763 | { |
4709 | 763 | if (!mSettings->severity.isEnabled(Severity::warning)) |
4710 | 0 | return; |
4711 | | |
4712 | 763 | logChecker("CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse"); // warning |
4713 | | |
4714 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
4715 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
4716 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
4717 | 21.5k | if (tok->isName() && match148(tok)) { |
4718 | 0 | const int varidLeft = tok->tokAt(2)->varId();// get the left varid |
4719 | 0 | const int varidRight = tok->tokAt(4)->varId();// get the right varid |
4720 | | // compare varids: if they are not zero but equal |
4721 | | // --> the comparison function is called with the same variables |
4722 | 0 | if (varidLeft == varidRight) { |
4723 | 0 | const std::string& functionName = tok->str(); // store function name |
4724 | 0 | const std::string& varNameLeft = tok->strAt(2); // get the left variable name |
4725 | 0 | if (functionName == MatchCompiler::makeConstString("isgreater") || functionName == MatchCompiler::makeConstString("isless") || functionName == MatchCompiler::makeConstString("islessgreater")) { |
4726 | | // e.g.: isgreater(x,x) --> (x)>(x) --> false |
4727 | 0 | checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, false); |
4728 | 0 | } else { // functionName == MatchCompiler::makeConstString("isgreaterequal") || functionName == MatchCompiler::makeConstString("islessequal") |
4729 | | // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true |
4730 | 0 | checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, true); |
4731 | 0 | } |
4732 | 0 | } |
4733 | 0 | } |
4734 | 21.5k | } |
4735 | 1.10k | } |
4736 | 763 | } |
4737 | | void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) |
4738 | 0 | { |
4739 | 0 | const std::string strResult = bool_to_string(result); |
4740 | 0 | const CWE cweResult = result ? CWE571 : CWE570; |
4741 | |
|
4742 | 0 | reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", |
4743 | 0 | "$symbol:" + functionName + "\n" |
4744 | 0 | "Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n" |
4745 | 0 | "The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") " |
4746 | 0 | "for both parameters leads to a statement which is always " + strResult + ".", cweResult, Certainty::normal); |
4747 | 0 | } |
4748 | | |
4749 | | //--------------------------------------------------------------------------- |
4750 | | // Check testing sign of unsigned variables and pointers. |
4751 | | //--------------------------------------------------------------------------- |
4752 | | void CheckOther::checkSignOfUnsignedVariable() |
4753 | 763 | { |
4754 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unsignedLessThanZero")) |
4755 | 0 | return; |
4756 | | |
4757 | 763 | logChecker("CheckOther::checkSignOfUnsignedVariable"); // style |
4758 | | |
4759 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
4760 | | |
4761 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
4762 | | // check all the code in the function |
4763 | 22.6k | for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
4764 | 21.5k | const ValueFlow::Value *zeroValue = nullptr; |
4765 | 21.5k | const Token *nonZeroExpr = nullptr; |
4766 | 21.5k | if (comparisonNonZeroExpressionLessThanZero(tok, zeroValue, nonZeroExpr)) { |
4767 | 0 | const ValueType* vt = nonZeroExpr->valueType(); |
4768 | 0 | if (vt->pointer) |
4769 | 0 | pointerLessThanZeroError(tok, zeroValue); |
4770 | 0 | else |
4771 | 0 | unsignedLessThanZeroError(tok, zeroValue, nonZeroExpr->expressionString()); |
4772 | 21.5k | } else if (testIfNonZeroExpressionIsPositive(tok, zeroValue, nonZeroExpr)) { |
4773 | 0 | const ValueType* vt = nonZeroExpr->valueType(); |
4774 | 0 | if (vt->pointer) |
4775 | 0 | pointerPositiveError(tok, zeroValue); |
4776 | 0 | else |
4777 | 0 | unsignedPositiveError(tok, zeroValue, nonZeroExpr->expressionString()); |
4778 | 0 | } |
4779 | 21.5k | } |
4780 | 1.10k | } |
4781 | 763 | } |
4782 | | |
4783 | | bool CheckOther::comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr, bool suppress) |
4784 | 21.6k | { |
4785 | 21.6k | if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) |
4786 | 20.6k | return false; |
4787 | | |
4788 | 934 | const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); |
4789 | 934 | const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); |
4790 | | |
4791 | 934 | if (match149(tok) && v2 && v2->isKnown()) { |
4792 | 4 | zeroValue = v2; |
4793 | 4 | nonZeroExpr = tok->astOperand1(); |
4794 | 930 | } else if (match150(tok) && v1 && v1->isKnown()) { |
4795 | 11 | zeroValue = v1; |
4796 | 11 | nonZeroExpr = tok->astOperand2(); |
4797 | 919 | } else { |
4798 | 919 | return false; |
4799 | 919 | } |
4800 | | |
4801 | 15 | if (const Variable* var = nonZeroExpr->variable()) |
4802 | 9 | if (var->typeStartToken()->isTemplateArg()) |
4803 | 0 | return suppress; |
4804 | | |
4805 | 15 | const ValueType* vt = nonZeroExpr->valueType(); |
4806 | 15 | return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); |
4807 | 15 | } |
4808 | | |
4809 | | bool CheckOther::testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr) |
4810 | 21.6k | { |
4811 | 21.6k | if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) |
4812 | 20.6k | return false; |
4813 | | |
4814 | 934 | const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); |
4815 | 934 | const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); |
4816 | | |
4817 | 934 | if (match151(tok) && v2 && v2->isKnown()) { |
4818 | 1 | zeroValue = v2; |
4819 | 1 | nonZeroExpr = tok->astOperand1(); |
4820 | 933 | } else if (match152(tok) && v1 && v1->isKnown()) { |
4821 | 5 | zeroValue = v1; |
4822 | 5 | nonZeroExpr = tok->astOperand2(); |
4823 | 928 | } else { |
4824 | 928 | return false; |
4825 | 928 | } |
4826 | | |
4827 | 6 | const ValueType* vt = nonZeroExpr->valueType(); |
4828 | 6 | return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); |
4829 | 934 | } |
4830 | | |
4831 | | void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) |
4832 | 0 | { |
4833 | 0 | reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero", |
4834 | 0 | "$symbol:" + varname + "\n" |
4835 | 0 | "Checking if unsigned expression '$symbol' is less than zero.\n" |
4836 | 0 | "The unsigned expression '$symbol' will never be negative so it " |
4837 | 0 | "is either pointless or an error to check if it is.", CWE570, Certainty::normal); |
4838 | 0 | } |
4839 | | |
4840 | | void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) |
4841 | 0 | { |
4842 | 0 | reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero", |
4843 | 0 | "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, Certainty::normal); |
4844 | 0 | } |
4845 | | |
4846 | | void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) |
4847 | 0 | { |
4848 | 0 | reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive", |
4849 | 0 | "$symbol:" + varname + "\n" |
4850 | 0 | "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, Certainty::normal); |
4851 | 0 | } |
4852 | | |
4853 | | void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) |
4854 | 0 | { |
4855 | 0 | reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive", |
4856 | 0 | "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, Certainty::normal); |
4857 | 0 | } |
4858 | | |
4859 | | /* check if a constructor in given class scope takes a reference */ |
4860 | | static bool constructorTakesReference(const Scope * const classScope) |
4861 | 0 | { |
4862 | 0 | return std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&](const Function& constructor) { |
4863 | 0 | if (constructor.isConstructor()) { |
4864 | 0 | for (int argnr = 0U; argnr < constructor.argCount(); argnr++) { |
4865 | 0 | const Variable * const argVar = constructor.getArgumentVar(argnr); |
4866 | 0 | if (argVar && argVar->isReference()) { |
4867 | 0 | return true; |
4868 | 0 | } |
4869 | 0 | } |
4870 | 0 | } |
4871 | 0 | return false; |
4872 | 0 | }); |
4873 | 0 | } |
4874 | | |
4875 | | //--------------------------------------------------------------------------- |
4876 | | // This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". |
4877 | | // In most scenarios, "const A & a = getA()" will be more efficient. |
4878 | | //--------------------------------------------------------------------------- |
4879 | | void CheckOther::checkRedundantCopy() |
4880 | 763 | { |
4881 | 763 | if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive)) |
4882 | 0 | return; |
4883 | | |
4884 | 763 | logChecker("CheckOther::checkRedundantCopy"); // c++,performance,inconclusive |
4885 | | |
4886 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
4887 | | |
4888 | 4.57k | for (const Variable* var : symbolDatabase->variableList()) { |
4889 | 4.57k | if (!var || var->isReference() || var->isPointer() || |
4890 | 4.57k | (!var->type() && !var->isStlType() && !(var->valueType() && var->valueType()->container)) || // bailout if var is of standard type, if it is a pointer or non-const |
4891 | 4.57k | (!var->isConst() && isVariableChanged(var, *mSettings))) |
4892 | 4.57k | continue; |
4893 | | |
4894 | 0 | const Token* startTok = var->nameToken(); |
4895 | 0 | if (startTok->strAt(1) == MatchCompiler::makeConstString("=")) // %type% %name% = ... ; |
4896 | 0 | ; |
4897 | 0 | else if (match153(startTok->next()) && var->isClass()) { |
4898 | 0 | if (!var->typeScope() && !(var->valueType() && var->valueType()->container)) |
4899 | 0 | continue; |
4900 | | // Object is instantiated. Warn if constructor takes arguments by value. |
4901 | 0 | if (var->typeScope() && constructorTakesReference(var->typeScope())) |
4902 | 0 | continue; |
4903 | 0 | } else if (match51(startTok->next()) && startTok->next()->isSplittedVarDeclEq()) { |
4904 | 0 | startTok = startTok->tokAt(2); |
4905 | 0 | } else |
4906 | 0 | continue; |
4907 | | |
4908 | 0 | const Token* tok = startTok->next()->astOperand2(); |
4909 | 0 | if (!tok) |
4910 | 0 | continue; |
4911 | 0 | if (!match32(tok->previous())) |
4912 | 0 | continue; |
4913 | 0 | if (!match154(tok->link())) // bailout for usage like "const A a = getA()+3" |
4914 | 0 | continue; |
4915 | | |
4916 | 0 | const Token* dot = tok->astOperand1(); |
4917 | 0 | if (match30(dot)) { |
4918 | 0 | const Token* varTok = dot->astOperand1(); |
4919 | 0 | const int indirect = varTok->valueType() ? varTok->valueType()->pointer : 0; |
4920 | 0 | if (isVariableChanged(tok, tok->scope()->bodyEnd, indirect, varTok->varId(), /*globalvar*/ true, *mSettings)) |
4921 | 0 | continue; |
4922 | 0 | if (isTemporary(dot, &mSettings->library, /*unknown*/ true)) |
4923 | 0 | continue; |
4924 | 0 | } |
4925 | 0 | if (exprDependsOnThis(tok->previous())) |
4926 | 0 | continue; |
4927 | | |
4928 | 0 | const Function* func = tok->previous()->function(); |
4929 | 0 | if (func && func->tokenDef->strAt(-1) == MatchCompiler::makeConstString("&")) { |
4930 | 0 | const Scope* fScope = func->functionScope; |
4931 | 0 | if (fScope && fScope->bodyEnd && match155(fScope->bodyEnd->tokAt(-3))) { |
4932 | 0 | const Token* varTok = fScope->bodyEnd->tokAt(-2); |
4933 | 0 | if (varTok->variable() && !varTok->variable()->isGlobal() && |
4934 | 0 | (!varTok->variable()->type() || !varTok->variable()->type()->classScope || |
4935 | 0 | (varTok->variable()->valueType() && ValueFlow::getSizeOf(*varTok->variable()->valueType(), *mSettings) > 2 * mSettings->platform.sizeof_pointer))) |
4936 | 0 | redundantCopyError(startTok, startTok->str()); |
4937 | 0 | } |
4938 | 0 | } |
4939 | 0 | } |
4940 | 763 | } |
4941 | | |
4942 | | void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) |
4943 | 0 | { |
4944 | 0 | reportError(tok, Severity::performance, "redundantCopyLocalConst", |
4945 | 0 | "$symbol:" + varname + "\n" |
4946 | 0 | "Use const reference for '$symbol' to avoid unnecessary data copying.\n" |
4947 | 0 | "The const variable '$symbol' is assigned a copy of the data. You can avoid " |
4948 | 0 | "the unnecessary data copying by converting '$symbol' to const reference.", |
4949 | 0 | CWE398, |
4950 | 0 | Certainty::inconclusive); // since #5618 that check became inconclusive |
4951 | 0 | } |
4952 | | |
4953 | | //--------------------------------------------------------------------------- |
4954 | | // Checking for shift by negative values |
4955 | | //--------------------------------------------------------------------------- |
4956 | | |
4957 | | static bool isNegative(const Token *tok, const Settings &settings) |
4958 | 0 | { |
4959 | 0 | return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings); |
4960 | 0 | } |
4961 | | |
4962 | | void CheckOther::checkNegativeBitwiseShift() |
4963 | 763 | { |
4964 | 763 | const bool portability = mSettings->severity.isEnabled(Severity::portability); |
4965 | | |
4966 | 763 | logChecker("CheckOther::checkNegativeBitwiseShift"); |
4967 | | |
4968 | 55.2k | for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
4969 | 54.5k | tok = skipUnreachableBranch(tok); |
4970 | | |
4971 | 54.5k | if (!tok->astOperand1() || !tok->astOperand2()) |
4972 | 46.2k | continue; |
4973 | | |
4974 | 8.26k | if (!match156(tok)) |
4975 | 8.26k | continue; |
4976 | | |
4977 | | // don't warn if lhs is a class. this is an overloaded operator then |
4978 | 1 | if (tok->isCpp()) { |
4979 | 1 | const ValueType * lhsType = tok->astOperand1()->valueType(); |
4980 | 1 | if (!lhsType || !lhsType->isIntegral() || lhsType->pointer) |
4981 | 1 | continue; |
4982 | 1 | } |
4983 | | |
4984 | | // bailout if operation is protected by ?: |
4985 | 0 | bool ternary = false; |
4986 | 0 | for (const Token *parent = tok; parent; parent = parent->astParent()) { |
4987 | 0 | if (match47(parent)) { |
4988 | 0 | ternary = true; |
4989 | 0 | break; |
4990 | 0 | } |
4991 | 0 | } |
4992 | 0 | if (ternary) |
4993 | 0 | continue; |
4994 | | |
4995 | | // Get negative rhs value. preferably a value which doesn't have 'condition'. |
4996 | 0 | if (portability && isNegative(tok->astOperand1(), *mSettings)) |
4997 | 0 | negativeBitwiseShiftError(tok, 1); |
4998 | 0 | else if (isNegative(tok->astOperand2(), *mSettings)) |
4999 | 0 | negativeBitwiseShiftError(tok, 2); |
5000 | 0 | } |
5001 | 763 | } |
5002 | | |
5003 | | |
5004 | | void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) |
5005 | 0 | { |
5006 | 0 | if (op == 1) |
5007 | | // LHS - this is used by intention in various software, if it |
5008 | | // is used often in a project and works as expected then this is |
5009 | | // a portability issue |
5010 | 0 | reportError(tok, Severity::portability, "shiftNegativeLHS", "Shifting a negative value is technically undefined behaviour", CWE758, Certainty::normal); |
5011 | 0 | else // RHS |
5012 | 0 | reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour", CWE758, Certainty::normal); |
5013 | 0 | } |
5014 | | |
5015 | | //--------------------------------------------------------------------------- |
5016 | | // Check for incompletely filled buffers. |
5017 | | //--------------------------------------------------------------------------- |
5018 | | void CheckOther::checkIncompleteArrayFill() |
5019 | 763 | { |
5020 | 763 | if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) |
5021 | 0 | return; |
5022 | 763 | const bool printWarning = mSettings->severity.isEnabled(Severity::warning); |
5023 | 763 | const bool printPortability = mSettings->severity.isEnabled(Severity::portability); |
5024 | 763 | if (!printPortability && !printWarning) |
5025 | 0 | return; |
5026 | | |
5027 | 763 | logChecker("CheckOther::checkIncompleteArrayFill"); // warning,portability,inconclusive |
5028 | | |
5029 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5030 | | |
5031 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
5032 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
5033 | 21.5k | if (match157(tok)) { |
5034 | 0 | std::vector<const Token*> args = getArguments(tok); |
5035 | 0 | if (args.size() != 3) |
5036 | 0 | continue; |
5037 | 0 | const Token* tok2 = args[0]; |
5038 | 0 | if (tok2->str() == MatchCompiler::makeConstString("::")) |
5039 | 0 | tok2 = tok2->next(); |
5040 | 0 | while (match158(tok2)) |
5041 | 0 | tok2 = tok2->tokAt(2); |
5042 | 0 | if (!match159(tok2)) |
5043 | 0 | continue; |
5044 | | |
5045 | 0 | const Variable *var = tok2->variable(); |
5046 | 0 | if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) |
5047 | 0 | continue; |
5048 | | |
5049 | 0 | if (!args[2]->hasKnownIntValue() || args[2]->getKnownIntValue() != var->dimension(0)) |
5050 | 0 | continue; |
5051 | 0 | int size = mTokenizer->sizeOfType(var->typeStartToken()); |
5052 | 0 | if (size == 0 && var->valueType()->pointer) |
5053 | 0 | size = mSettings->platform.sizeof_pointer; |
5054 | 0 | else if (size == 0 && var->valueType()) |
5055 | 0 | size = ValueFlow::getSizeOf(*var->valueType(), *mSettings); |
5056 | 0 | const Token* tok3 = tok->next()->astOperand2()->astOperand1()->astOperand1(); |
5057 | 0 | if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { |
5058 | 0 | if (printWarning) |
5059 | 0 | incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), false); |
5060 | 0 | } else if (var->valueType()->type == ValueType::Type::BOOL && printPortability) // sizeof(bool) is not 1 on all platforms |
5061 | 0 | incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), true); |
5062 | 0 | } |
5063 | 21.5k | } |
5064 | 1.10k | } |
5065 | 763 | } |
5066 | | |
5067 | | void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) |
5068 | 0 | { |
5069 | 0 | if (boolean) |
5070 | 0 | reportError(tok, Severity::portability, "incompleteArrayFill", |
5071 | 0 | "$symbol:" + buffer + "\n" |
5072 | 0 | "$symbol:" + function + "\n" |
5073 | 0 | "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" |
5074 | 0 | "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); |
5075 | 0 | else |
5076 | 0 | reportError(tok, Severity::warning, "incompleteArrayFill", |
5077 | 0 | "$symbol:" + buffer + "\n" |
5078 | 0 | "$symbol:" + function + "\n" |
5079 | 0 | "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" |
5080 | 0 | "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); |
5081 | 0 | } |
5082 | | |
5083 | | //--------------------------------------------------------------------------- |
5084 | | // Detect NULL being passed to variadic function. |
5085 | | //--------------------------------------------------------------------------- |
5086 | | |
5087 | | void CheckOther::checkVarFuncNullUB() |
5088 | 763 | { |
5089 | 763 | if (!mSettings->severity.isEnabled(Severity::portability)) |
5090 | 0 | return; |
5091 | | |
5092 | 763 | logChecker("CheckOther::checkVarFuncNullUB"); // portability |
5093 | | |
5094 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5095 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
5096 | 23.7k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
5097 | | // Is NULL passed to a function? |
5098 | 22.6k | if (match160(tok)) { |
5099 | | // Locate function name in this function call. |
5100 | 0 | const Token *ftok = tok; |
5101 | 0 | int argnr = 1; |
5102 | 0 | while (ftok && ftok->str() != MatchCompiler::makeConstString("(")) { |
5103 | 0 | if (ftok->str() == MatchCompiler::makeConstString(")")) |
5104 | 0 | ftok = ftok->link(); |
5105 | 0 | else if (ftok->str() == MatchCompiler::makeConstString(",")) |
5106 | 0 | ++argnr; |
5107 | 0 | ftok = ftok->previous(); |
5108 | 0 | } |
5109 | 0 | ftok = ftok ? ftok->previous() : nullptr; |
5110 | 0 | if (ftok && ftok->isName()) { |
5111 | | // If this is a variadic function then report error |
5112 | 0 | const Function *f = ftok->function(); |
5113 | 0 | if (f && f->argCount() <= argnr) { |
5114 | 0 | const Token *tok2 = f->argDef; |
5115 | 0 | tok2 = tok2 ? tok2->link() : nullptr; // goto ')' |
5116 | 0 | if (tok2 && match161(tok2->tokAt(-1))) |
5117 | 0 | varFuncNullUBError(tok); |
5118 | 0 | } |
5119 | 0 | } |
5120 | 0 | } |
5121 | 22.6k | } |
5122 | 1.10k | } |
5123 | 763 | } |
5124 | | |
5125 | | void CheckOther::varFuncNullUBError(const Token *tok) |
5126 | 0 | { |
5127 | 0 | reportError(tok, |
5128 | 0 | Severity::portability, |
5129 | 0 | "varFuncNullUB", |
5130 | 0 | "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" |
5131 | 0 | "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" |
5132 | 0 | "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" |
5133 | 0 | "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" |
5134 | 0 | "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" |
5135 | 0 | "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" |
5136 | 0 | "#include <stdarg.h>\n" |
5137 | 0 | "#include <stdio.h>\n" |
5138 | 0 | "\n" |
5139 | 0 | "void f(char *s, ...) {\n" |
5140 | 0 | " va_list ap;\n" |
5141 | 0 | " va_start(ap,s);\n" |
5142 | 0 | " for (;;) {\n" |
5143 | 0 | " char *p = va_arg(ap,char*);\n" |
5144 | 0 | " printf(\"%018p, %s\\n\", p, (long)p & 255 ? p : \"\");\n" |
5145 | 0 | " if(!p) break;\n" |
5146 | 0 | " }\n" |
5147 | 0 | " va_end(ap);\n" |
5148 | 0 | "}\n" |
5149 | 0 | "\n" |
5150 | 0 | "void g() {\n" |
5151 | 0 | " char *s2 = \"x\";\n" |
5152 | 0 | " char *s3 = \"ERROR\";\n" |
5153 | 0 | "\n" |
5154 | 0 | " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" |
5155 | 0 | " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" |
5156 | 0 | "}\n" |
5157 | 0 | "\n" |
5158 | 0 | "void h() {\n" |
5159 | 0 | " int i;\n" |
5160 | 0 | " volatile unsigned char a[1000];\n" |
5161 | 0 | " for (i = 0; i<sizeof(a); i++)\n" |
5162 | 0 | " a[i] = -1;\n" |
5163 | 0 | "}\n" |
5164 | 0 | "\n" |
5165 | 0 | "int main() {\n" |
5166 | 0 | " h();\n" |
5167 | 0 | " g();\n" |
5168 | 0 | " return 0;\n" |
5169 | 0 | "}", CWE475, Certainty::normal); |
5170 | 0 | } |
5171 | | |
5172 | | void CheckOther::checkRedundantPointerOp() |
5173 | 763 | { |
5174 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("redundantPointerOp")) |
5175 | 0 | return; |
5176 | | |
5177 | 763 | logChecker("CheckOther::checkRedundantPointerOp"); // style |
5178 | | |
5179 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
5180 | 54.8k | if (tok->isExpandedMacro() && tok->str() == MatchCompiler::makeConstString("(")) |
5181 | 0 | tok = tok->link(); |
5182 | | |
5183 | 54.8k | bool addressOfDeref{}; |
5184 | 54.8k | if (tok->isUnaryOp("&") && tok->astOperand1()->isUnaryOp("*")) |
5185 | 0 | addressOfDeref = true; |
5186 | 54.8k | else if (tok->isUnaryOp("*") && tok->astOperand1()->isUnaryOp("&")) |
5187 | 0 | addressOfDeref = false; |
5188 | 54.8k | else |
5189 | 54.8k | continue; |
5190 | | |
5191 | | // variable |
5192 | 0 | const Token *varTok = tok->astOperand1()->astOperand1(); |
5193 | 0 | if (!varTok || varTok->isExpandedMacro()) |
5194 | 0 | continue; |
5195 | | |
5196 | 0 | if (!addressOfDeref) { // dereference of address |
5197 | 0 | if (tok->isExpandedMacro()) |
5198 | 0 | continue; |
5199 | 0 | if (varTok->valueType() && varTok->valueType()->pointer && varTok->valueType()->reference == Reference::LValue) |
5200 | 0 | continue; |
5201 | 0 | } |
5202 | | |
5203 | 0 | const Variable *var = varTok->variable(); |
5204 | 0 | if (!var || (addressOfDeref && !var->isPointer())) |
5205 | 0 | continue; |
5206 | | |
5207 | 0 | redundantPointerOpError(tok, var->name(), false, addressOfDeref); |
5208 | 0 | } |
5209 | 763 | } |
5210 | | |
5211 | | void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive, bool addressOfDeref) |
5212 | 0 | { |
5213 | 0 | std::string msg = "$symbol:" + varname + "\nRedundant pointer operation on '$symbol' - it's already a "; |
5214 | 0 | msg += addressOfDeref ? "pointer." : "variable."; |
5215 | 0 | reportError(tok, Severity::style, "redundantPointerOp", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); |
5216 | 0 | } |
5217 | | |
5218 | | void CheckOther::checkInterlockedDecrement() |
5219 | 763 | { |
5220 | 763 | if (!mSettings->platform.isWindows()) { |
5221 | 763 | return; |
5222 | 763 | } |
5223 | | |
5224 | 0 | logChecker("CheckOther::checkInterlockedDecrement"); // windows-platform |
5225 | |
|
5226 | 0 | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
5227 | 0 | if (tok->isName() && match162(tok)) { |
5228 | 0 | const Token* interlockedVarTok = tok->tokAt(3); |
5229 | 0 | const Token* checkStartTok = interlockedVarTok->tokAt(5); |
5230 | 0 | if ((match163(checkStartTok) && checkStartTok->strAt(2) == interlockedVarTok->str()) || |
5231 | 0 | (match164(checkStartTok) && checkStartTok->strAt(1) == interlockedVarTok->str()) || |
5232 | 0 | (match165(checkStartTok) && checkStartTok->str() == interlockedVarTok->str()) || |
5233 | 0 | (match166(checkStartTok) && checkStartTok->str() == interlockedVarTok->str())) { |
5234 | 0 | raceAfterInterlockedDecrementError(checkStartTok); |
5235 | 0 | } |
5236 | 0 | } else if (match167(tok)) { |
5237 | 0 | const Token* condEnd = tok->linkAt(1); |
5238 | 0 | const Token* funcTok = tok->tokAt(2); |
5239 | 0 | const Token* firstAccessTok = funcTok->str() == MatchCompiler::makeConstString("::") ? funcTok->tokAt(4) : funcTok->tokAt(3); |
5240 | 0 | if (condEnd && condEnd->next() && condEnd->linkAt(1)) { |
5241 | 0 | const Token* ifEndTok = condEnd->linkAt(1); |
5242 | 0 | if (match168(ifEndTok)) { |
5243 | 0 | const Token* secondAccessTok = ifEndTok->tokAt(2); |
5244 | 0 | if (secondAccessTok->str() == firstAccessTok->str()) { |
5245 | 0 | raceAfterInterlockedDecrementError(secondAccessTok); |
5246 | 0 | } |
5247 | 0 | } else if (match169(ifEndTok)) { |
5248 | 0 | const Token* secondAccessTok = ifEndTok->tokAt(4); |
5249 | 0 | if (secondAccessTok->str() == firstAccessTok->str()) { |
5250 | 0 | raceAfterInterlockedDecrementError(secondAccessTok); |
5251 | 0 | } |
5252 | 0 | } |
5253 | 0 | } |
5254 | 0 | } |
5255 | 0 | } |
5256 | 0 | } |
5257 | | |
5258 | | void CheckOther::raceAfterInterlockedDecrementError(const Token* tok) |
5259 | 0 | { |
5260 | 0 | reportError(tok, Severity::error, "raceAfterInterlockedDecrement", |
5261 | 0 | "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, Certainty::normal); |
5262 | 0 | } |
5263 | | |
5264 | | void CheckOther::checkUnusedLabel() |
5265 | 763 | { |
5266 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("unusedLabel")) |
5267 | 0 | return; |
5268 | | |
5269 | 763 | logChecker("CheckOther::checkUnusedLabel"); // style,warning |
5270 | | |
5271 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5272 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
5273 | 1.10k | const bool hasIfdef = mTokenizer->hasIfdef(scope->bodyStart, scope->bodyEnd); |
5274 | 23.7k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
5275 | 22.6k | if (!tok->scope()->isExecutable()) |
5276 | 0 | tok = tok->scope()->bodyEnd; |
5277 | | |
5278 | 22.6k | if (match170(tok) && !tok->tokAt(1)->isKeyword()) { |
5279 | 0 | const std::string tmp("goto " + tok->strAt(1)); |
5280 | 0 | if (!Token::findsimplematch(scope->bodyStart->next(), tmp.c_str(), tmp.size(), scope->bodyEnd->previous()) && !tok->next()->isExpandedMacro()) |
5281 | 0 | unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch, hasIfdef); |
5282 | 0 | } |
5283 | 22.6k | } |
5284 | 1.10k | } |
5285 | 763 | } |
5286 | | |
5287 | | void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef) |
5288 | 0 | { |
5289 | 0 | if (tok && !mSettings->severity.isEnabled(inSwitch ? Severity::warning : Severity::style)) |
5290 | 0 | return; |
5291 | | |
5292 | 0 | std::string id = "unusedLabel"; |
5293 | 0 | if (inSwitch) |
5294 | 0 | id += "Switch"; |
5295 | 0 | if (hasIfdef) |
5296 | 0 | id += "Configuration"; |
5297 | |
|
5298 | 0 | std::string msg = "$symbol:" + (tok ? tok->str() : "") + "\nLabel '$symbol' is not used."; |
5299 | 0 | if (hasIfdef) |
5300 | 0 | msg += " There is #if in function body so the label might be used in code that is removed by the preprocessor."; |
5301 | 0 | if (inSwitch) |
5302 | 0 | msg += " Should this be a 'case' of the enclosing switch()?"; |
5303 | |
|
5304 | 0 | reportError(tok, |
5305 | 0 | inSwitch ? Severity::warning : Severity::style, |
5306 | 0 | id, |
5307 | 0 | msg, |
5308 | 0 | CWE398, |
5309 | 0 | Certainty::normal); |
5310 | 0 | } |
5311 | | |
5312 | | static bool checkEvaluationOrderC(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings, bool & selfAssignmentError) |
5313 | 0 | { |
5314 | | // self assignment.. |
5315 | 0 | if (tok2 == tok && tok->str() == MatchCompiler::makeConstString("=") && parent->str() == MatchCompiler::makeConstString("=") && isSameExpression(false, tok->astOperand1(), parent->astOperand1(), settings, true, false)) { |
5316 | 0 | if (settings.severity.isEnabled(Severity::warning) && isSameExpression(true, tok->astOperand1(), parent->astOperand1(), settings, true, false)) |
5317 | 0 | selfAssignmentError = true; |
5318 | 0 | return false; |
5319 | 0 | } |
5320 | | // Is expression used? |
5321 | 0 | bool foundError = false; |
5322 | 0 | visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { |
5323 | 0 | if (tok3->str() == MatchCompiler::makeConstString("&") && !tok3->astOperand2()) |
5324 | 0 | return ChildrenToVisit::none; // don't handle address-of for now |
5325 | 0 | if (tok3->str() == MatchCompiler::makeConstString("(") && match171(tok3->previous())) |
5326 | 0 | return ChildrenToVisit::none; // don't care about sizeof usage |
5327 | 0 | if (isSameExpression(false, tok->astOperand1(), tok3, settings, true, false)) |
5328 | 0 | foundError = true; |
5329 | 0 | return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; |
5330 | 0 | }); |
5331 | |
|
5332 | 0 | return foundError; |
5333 | 0 | } |
5334 | | |
5335 | | static bool checkEvaluationOrderCpp11(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings) |
5336 | 0 | { |
5337 | 0 | if (tok->isAssignmentOp()) // TODO check assignment |
5338 | 0 | return false; |
5339 | 0 | if (tok->previous() == tok->astOperand1() && parent->isArithmeticalOp() && parent->isBinaryOp()) { |
5340 | 0 | if (parent->astParent() && parent->astParent()->isAssignmentOp() && isSameExpression(false, tok->astOperand1(), parent->astParent()->astOperand1(), settings, true, false)) |
5341 | 0 | return true; |
5342 | 0 | } |
5343 | 0 | bool foundUndefined{false}; |
5344 | 0 | visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { |
5345 | 0 | if (tok3->str() == MatchCompiler::makeConstString("&") && !tok3->astOperand2()) |
5346 | 0 | return ChildrenToVisit::none; // don't handle address-of for now |
5347 | 0 | if (tok3->str() == MatchCompiler::makeConstString("(") && match171(tok3->previous())) |
5348 | 0 | return ChildrenToVisit::none; // don't care about sizeof usage |
5349 | 0 | if (isSameExpression(false, tok->astOperand1(), tok3, settings, true, false)) |
5350 | 0 | foundUndefined = true; |
5351 | 0 | return foundUndefined ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; |
5352 | 0 | }); |
5353 | |
|
5354 | 0 | return foundUndefined; |
5355 | 0 | } |
5356 | | |
5357 | | static bool checkEvaluationOrderCpp17(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings, bool & foundUnspecified) |
5358 | 1.26k | { |
5359 | 1.26k | if (tok->isAssignmentOp()) |
5360 | 298 | return false; |
5361 | 968 | bool foundUndefined{false}; |
5362 | 1.15k | visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { |
5363 | 1.15k | if (tok3->str() == MatchCompiler::makeConstString("&") && !tok3->astOperand2()) |
5364 | 1 | return ChildrenToVisit::none; // don't handle address-of for now |
5365 | 1.15k | if (tok3->str() == MatchCompiler::makeConstString("(") && match171(tok3->previous())) |
5366 | 0 | return ChildrenToVisit::none; // don't care about sizeof usage |
5367 | 1.15k | if (isSameExpression(false, tok->astOperand1(), tok3, settings, true, false) && parent->isArithmeticalOp() && parent->isBinaryOp()) |
5368 | 19 | foundUndefined = true; |
5369 | 1.15k | if (tok3->tokType() == Token::eIncDecOp && isSameExpression(false, tok->astOperand1(), tok3->astOperand1(), settings, true, false)) { |
5370 | 42 | if (parent->isArithmeticalOp() && parent->isBinaryOp()) |
5371 | 18 | foundUndefined = true; |
5372 | 24 | else |
5373 | 24 | foundUnspecified = true; |
5374 | 42 | } |
5375 | 1.15k | return (foundUndefined || foundUnspecified) ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; |
5376 | 1.15k | }); |
5377 | | |
5378 | 968 | return foundUndefined || foundUnspecified; |
5379 | 1.26k | } |
5380 | | |
5381 | | void CheckOther::checkEvaluationOrder() |
5382 | 763 | { |
5383 | 763 | logChecker("CheckOther::checkEvaluationOrder"); |
5384 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5385 | 1.10k | for (const Scope * functionScope : symbolDatabase->functionScopes) { |
5386 | 23.7k | for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { |
5387 | 22.6k | if (!tok->isIncDecOp() && !tok->isAssignmentOp()) |
5388 | 19.7k | continue; |
5389 | 2.88k | if (!tok->astOperand1()) |
5390 | 1 | continue; |
5391 | 4.08k | for (const Token *tok2 = tok;; tok2 = tok2->astParent()) { |
5392 | | // If ast parent is a sequence point then break |
5393 | 4.08k | const Token * const parent = tok2->astParent(); |
5394 | 4.08k | if (!parent) |
5395 | 2.44k | break; |
5396 | 1.64k | if (match172(parent)) |
5397 | 0 | break; |
5398 | 1.64k | if (parent->str() == MatchCompiler::makeConstString(",")) { |
5399 | 2 | const Token *par = parent; |
5400 | 4 | while (match65(par)) |
5401 | 2 | par = par->astParent(); |
5402 | | // not function or in a while clause => break |
5403 | 2 | if (!(par && par->str() == MatchCompiler::makeConstString("(") && par->astOperand2() && par->strAt(-1) != MatchCompiler::makeConstString("while"))) |
5404 | 2 | break; |
5405 | | // control flow (if|while|etc) => break |
5406 | 0 | if (match69(par->link())) |
5407 | 0 | break; |
5408 | | // sequence point in function argument: dostuff((1,2),3) => break |
5409 | 0 | par = par->next(); |
5410 | 0 | while (par && (par->previous() != parent)) |
5411 | 0 | par = par->nextArgument(); |
5412 | 0 | if (!par) |
5413 | 0 | break; |
5414 | 0 | } |
5415 | 1.64k | if (parent->str() == MatchCompiler::makeConstString("(") && parent->astOperand2()) |
5416 | 376 | break; |
5417 | | |
5418 | 1.26k | bool foundError{false}, foundUnspecified{false}, bSelfAssignmentError{false}; |
5419 | 1.26k | if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) { |
5420 | 1.26k | if (mSettings->standards.cpp >= Standards::CPP17) |
5421 | 1.26k | foundError = checkEvaluationOrderCpp17(tok, tok2, parent, *mSettings, foundUnspecified); |
5422 | 0 | else |
5423 | 0 | foundError = checkEvaluationOrderCpp11(tok, tok2, parent, *mSettings); |
5424 | 1.26k | } |
5425 | 0 | else |
5426 | 0 | foundError = checkEvaluationOrderC(tok, tok2, parent, *mSettings, bSelfAssignmentError); |
5427 | | |
5428 | 1.26k | if (foundError) { |
5429 | 61 | unknownEvaluationOrder(parent, foundUnspecified); |
5430 | 61 | break; |
5431 | 61 | } |
5432 | 1.20k | if (bSelfAssignmentError) { |
5433 | 0 | selfAssignmentError(parent, tok->astOperand1()->expressionString()); |
5434 | 0 | break; |
5435 | 0 | } |
5436 | 1.20k | } |
5437 | 2.87k | } |
5438 | 1.10k | } |
5439 | 763 | } |
5440 | | |
5441 | | void CheckOther::unknownEvaluationOrder(const Token* tok, bool isUnspecifiedBehavior) |
5442 | 61 | { |
5443 | 61 | isUnspecifiedBehavior ? |
5444 | 24 | reportError(tok, Severity::portability, "unknownEvaluationOrder", |
5445 | 24 | "Expression '" + (tok ? tok->expressionString() : std::string("x++, x++")) + "' depends on order of evaluation of side effects. Behavior is Unspecified according to c++17", CWE768, Certainty::normal) |
5446 | 61 | : reportError(tok, Severity::error, "unknownEvaluationOrder", |
5447 | 37 | "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); |
5448 | 61 | } |
5449 | | |
5450 | | void CheckOther::checkAccessOfMovedVariable() |
5451 | 763 | { |
5452 | 763 | if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) |
5453 | 0 | return; |
5454 | 763 | if (!mSettings->isPremiumEnabled("accessMoved") && !mSettings->severity.isEnabled(Severity::warning)) |
5455 | 0 | return; |
5456 | 763 | logChecker("CheckOther::checkAccessOfMovedVariable"); // c++11,warning |
5457 | 763 | const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
5458 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5459 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
5460 | 1.10k | const Token * scopeStart = scope->bodyStart; |
5461 | 1.10k | if (scope->function) { |
5462 | 1.10k | const Token * memberInitializationStart = scope->function->constructorMemberInitialization(); |
5463 | 1.10k | if (memberInitializationStart) |
5464 | 0 | scopeStart = memberInitializationStart; |
5465 | 1.10k | } |
5466 | 22.6k | for (const Token* tok = scopeStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
5467 | 21.5k | if (!tok->astParent()) |
5468 | 10.5k | continue; |
5469 | 11.0k | const ValueFlow::Value * movedValue = tok->getMovedValue(); |
5470 | 11.0k | if (!movedValue || movedValue->moveKind == ValueFlow::Value::MoveKind::NonMovedVariable) |
5471 | 11.0k | continue; |
5472 | 0 | if (movedValue->isInconclusive() && !reportInconclusive) |
5473 | 0 | continue; |
5474 | | |
5475 | 0 | bool inconclusive = false; |
5476 | 0 | bool accessOfMoved = false; |
5477 | 0 | if (tok->strAt(1) == MatchCompiler::makeConstString(".")) { |
5478 | 0 | if (tok->next()->originalName() == MatchCompiler::makeConstString("->")) |
5479 | 0 | accessOfMoved = true; |
5480 | 0 | else |
5481 | 0 | inconclusive = true; |
5482 | 0 | } else { |
5483 | 0 | const ExprUsage usage = getExprUsage(tok, 0, *mSettings); |
5484 | 0 | if (usage == ExprUsage::Used) |
5485 | 0 | accessOfMoved = true; |
5486 | 0 | if (usage == ExprUsage::PassedByReference) |
5487 | 0 | accessOfMoved = !isVariableChangedByFunctionCall(tok, 0, *mSettings, &inconclusive); |
5488 | 0 | else if (usage == ExprUsage::Inconclusive) |
5489 | 0 | inconclusive = true; |
5490 | 0 | } |
5491 | 0 | if (accessOfMoved || (inconclusive && reportInconclusive)) |
5492 | 0 | accessMovedError(tok, tok->str(), movedValue, inconclusive || movedValue->isInconclusive()); |
5493 | 0 | } |
5494 | 1.10k | } |
5495 | 763 | } |
5496 | | |
5497 | | void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) |
5498 | 0 | { |
5499 | 0 | if (!tok) { |
5500 | 0 | reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, Certainty::normal); |
5501 | 0 | reportError(tok, Severity::warning, "accessForwarded", "Access of forwarded variable 'v'.", CWE672, Certainty::normal); |
5502 | 0 | return; |
5503 | 0 | } |
5504 | | |
5505 | 0 | const char * errorId = nullptr; |
5506 | 0 | std::string kindString; |
5507 | 0 | switch (value->moveKind) { |
5508 | 0 | case ValueFlow::Value::MoveKind::MovedVariable: |
5509 | 0 | errorId = "accessMoved"; |
5510 | 0 | kindString = "moved"; |
5511 | 0 | break; |
5512 | 0 | case ValueFlow::Value::MoveKind::ForwardedVariable: |
5513 | 0 | errorId = "accessForwarded"; |
5514 | 0 | kindString = "forwarded"; |
5515 | 0 | break; |
5516 | 0 | default: |
5517 | 0 | return; |
5518 | 0 | } |
5519 | 0 | const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'."); |
5520 | 0 | const ErrorPath errorPath = getErrorPath(tok, value, errmsg); |
5521 | 0 | reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive ? Certainty::inconclusive : Certainty::normal); |
5522 | 0 | } |
5523 | | |
5524 | | |
5525 | | |
5526 | | void CheckOther::checkFuncArgNamesDifferent() |
5527 | 763 | { |
5528 | 763 | const bool style = mSettings->severity.isEnabled(Severity::style); |
5529 | 763 | const bool inconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
5530 | 763 | const bool warning = mSettings->severity.isEnabled(Severity::warning); |
5531 | | |
5532 | 763 | if (!(warning || (style && inconclusive)) && !mSettings->isPremiumEnabled("funcArgNamesDifferent")) |
5533 | 0 | return; |
5534 | | |
5535 | 763 | logChecker("CheckOther::checkFuncArgNamesDifferent"); // style,warning,inconclusive |
5536 | | |
5537 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5538 | | // check every function |
5539 | 1.10k | for (const Scope *scope : symbolDatabase->functionScopes) { |
5540 | 1.10k | const Function * function = scope->function; |
5541 | | // only check functions with arguments |
5542 | 1.10k | if (!function || function->argCount() == 0) |
5543 | 1.10k | continue; |
5544 | | |
5545 | | // only check functions with separate declarations and definitions |
5546 | 0 | if (function->argDef == function->arg) |
5547 | 0 | continue; |
5548 | | |
5549 | | // get the function argument name tokens |
5550 | 0 | std::vector<const Token *> declarations(function->argCount()); |
5551 | 0 | std::vector<const Token *> definitions(function->argCount()); |
5552 | 0 | const Token * decl = function->argDef->next(); |
5553 | 0 | for (int j = 0; j < function->argCount(); ++j) { |
5554 | 0 | declarations[j] = nullptr; |
5555 | 0 | definitions[j] = nullptr; |
5556 | | // get the definition |
5557 | 0 | const Variable * variable = function->getArgumentVar(j); |
5558 | 0 | if (variable) { |
5559 | 0 | definitions[j] = variable->nameToken(); |
5560 | 0 | } |
5561 | | // get the declaration (search for first token with varId) |
5562 | 0 | while (decl && !match173(decl)) { |
5563 | | // skip everything after the assignment because |
5564 | | // it could also have a varId or be the first |
5565 | | // token with a varId if there is no name token |
5566 | 0 | if (decl->str() == MatchCompiler::makeConstString("=")) { |
5567 | 0 | decl = decl->nextArgument(); |
5568 | 0 | break; |
5569 | 0 | } |
5570 | | // skip over template |
5571 | 0 | if (decl->link()) |
5572 | 0 | decl = decl->link(); |
5573 | 0 | else if (decl->varId()) |
5574 | 0 | declarations[j] = decl; |
5575 | 0 | decl = decl->next(); |
5576 | 0 | } |
5577 | 0 | if (match65(decl)) |
5578 | 0 | decl = decl->next(); |
5579 | 0 | } |
5580 | | // check for different argument order |
5581 | 0 | if (warning) { |
5582 | 0 | bool order_different = false; |
5583 | 0 | for (int j = 0; j < function->argCount(); ++j) { |
5584 | 0 | if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str()) |
5585 | 0 | continue; |
5586 | | |
5587 | 0 | for (int k = 0; k < function->argCount(); ++k) { |
5588 | 0 | if (j != k && definitions[k] && declarations[j]->str() == definitions[k]->str()) { |
5589 | 0 | order_different = true; |
5590 | 0 | break; |
5591 | 0 | } |
5592 | 0 | } |
5593 | 0 | } |
5594 | 0 | if (order_different) { |
5595 | 0 | funcArgOrderDifferent(function->name(), function->argDef->next(), function->arg->next(), declarations, definitions); |
5596 | 0 | continue; |
5597 | 0 | } |
5598 | 0 | } |
5599 | | // check for different argument names |
5600 | 0 | if (style && inconclusive) { |
5601 | 0 | for (int j = 0; j < function->argCount(); ++j) { |
5602 | 0 | if (declarations[j] && definitions[j] && declarations[j]->str() != definitions[j]->str()) |
5603 | 0 | funcArgNamesDifferent(function->name(), j, declarations[j], definitions[j]); |
5604 | 0 | } |
5605 | 0 | } |
5606 | 0 | } |
5607 | 763 | } |
5608 | | |
5609 | | void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index, |
5610 | | const Token* declaration, const Token* definition) |
5611 | 0 | { |
5612 | 0 | std::list<const Token *> tokens = { declaration,definition }; |
5613 | 0 | reportError(tokens, Severity::style, "funcArgNamesDifferent", |
5614 | 0 | "$symbol:" + functionName + "\n" |
5615 | 0 | "Function '$symbol' argument " + std::to_string(index + 1) + " names different: declaration '" + |
5616 | 0 | (declaration ? declaration->str() : std::string("A")) + "' definition '" + |
5617 | 0 | (definition ? definition->str() : std::string("B")) + "'.", CWE628, Certainty::inconclusive); |
5618 | 0 | } |
5619 | | |
5620 | | void CheckOther::funcArgOrderDifferent(const std::string & functionName, |
5621 | | const Token* declaration, const Token* definition, |
5622 | | const std::vector<const Token *> & declarations, |
5623 | | const std::vector<const Token *> & definitions) |
5624 | 0 | { |
5625 | 0 | std::list<const Token *> tokens = { |
5626 | 0 | !declarations.empty() ? declarations[0] ? declarations[0] : declaration : nullptr, |
5627 | 0 | !definitions.empty() ? definitions[0] ? definitions[0] : definition : nullptr |
5628 | 0 | }; |
5629 | 0 | std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '"; |
5630 | 0 | for (std::size_t i = 0; i < declarations.size(); ++i) { |
5631 | 0 | if (i != 0) |
5632 | 0 | msg += ", "; |
5633 | 0 | if (declarations[i]) |
5634 | 0 | msg += declarations[i]->str(); |
5635 | 0 | } |
5636 | 0 | msg += "' definition '"; |
5637 | 0 | for (std::size_t i = 0; i < definitions.size(); ++i) { |
5638 | 0 | if (i != 0) |
5639 | 0 | msg += ", "; |
5640 | 0 | if (definitions[i]) |
5641 | 0 | msg += definitions[i]->str(); |
5642 | 0 | } |
5643 | 0 | msg += "'"; |
5644 | 0 | reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, Certainty::normal); |
5645 | 0 | } |
5646 | | |
5647 | | static const Token *findShadowed(const Scope *scope, const Variable& var, int linenr) |
5648 | 0 | { |
5649 | 0 | if (!scope) |
5650 | 0 | return nullptr; |
5651 | 0 | for (const Variable &v : scope->varlist) { |
5652 | 0 | if (scope->isExecutable() && v.nameToken()->linenr() > linenr) |
5653 | 0 | continue; |
5654 | 0 | if (v.name() == var.name()) |
5655 | 0 | return v.nameToken(); |
5656 | 0 | } |
5657 | 0 | auto it = std::find_if(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function& f) { |
5658 | 0 | return f.type == Function::Type::eFunction && f.name() == var.name() && precedes(f.tokenDef, var.nameToken()); |
5659 | 0 | }); |
5660 | 0 | if (it != scope->functionList.end()) |
5661 | 0 | return it->tokenDef; |
5662 | | |
5663 | 0 | if (scope->type == Scope::eLambda) |
5664 | 0 | return nullptr; |
5665 | 0 | const Token* shadowed = findShadowed(scope->nestedIn, var, linenr); |
5666 | 0 | if (!shadowed) |
5667 | 0 | shadowed = findShadowed(scope->functionOf, var, linenr); |
5668 | 0 | return shadowed; |
5669 | 0 | } |
5670 | | |
5671 | | void CheckOther::checkShadowVariables() |
5672 | 763 | { |
5673 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("shadowVariable")) |
5674 | 0 | return; |
5675 | 763 | logChecker("CheckOther::checkShadowVariables"); // style |
5676 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5677 | 2.79k | for (const Scope & scope : symbolDatabase->scopeList) { |
5678 | 2.79k | if (!scope.isExecutable() || scope.type == Scope::eLambda) |
5679 | 763 | continue; |
5680 | 2.03k | const Scope *functionScope = &scope; |
5681 | 3.27k | while (functionScope && functionScope->type != Scope::ScopeType::eFunction && functionScope->type != Scope::ScopeType::eLambda) |
5682 | 1.23k | functionScope = functionScope->nestedIn; |
5683 | 2.03k | for (const Variable &var : scope.varlist) { |
5684 | 0 | if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903 |
5685 | 0 | continue; |
5686 | | |
5687 | 0 | if (functionScope && functionScope->type == Scope::ScopeType::eFunction && functionScope->function) { |
5688 | 0 | const auto & argList = functionScope->function->argumentList; |
5689 | 0 | auto it = std::find_if(argList.cbegin(), argList.cend(), [&](const Variable& arg) { |
5690 | 0 | return arg.nameToken() && var.name() == arg.name(); |
5691 | 0 | }); |
5692 | 0 | if (it != argList.end()) { |
5693 | 0 | shadowError(var.nameToken(), it->nameToken(), "argument"); |
5694 | 0 | continue; |
5695 | 0 | } |
5696 | 0 | } |
5697 | | |
5698 | 0 | const Token *shadowed = findShadowed(scope.nestedIn, var, var.nameToken()->linenr()); |
5699 | 0 | if (!shadowed) |
5700 | 0 | shadowed = findShadowed(scope.functionOf, var, var.nameToken()->linenr()); |
5701 | 0 | if (!shadowed) |
5702 | 0 | continue; |
5703 | 0 | if (scope.type == Scope::eFunction && scope.className == var.name()) |
5704 | 0 | continue; |
5705 | 0 | if (functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && functionScope->function->isStatic() && |
5706 | 0 | shadowed->variable() && !shadowed->variable()->isLocal()) |
5707 | 0 | continue; |
5708 | 0 | shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function"); |
5709 | 0 | } |
5710 | 2.03k | } |
5711 | 763 | } |
5712 | | |
5713 | | void CheckOther::shadowError(const Token *var, const Token *shadowed, const std::string& type) |
5714 | 0 | { |
5715 | 0 | ErrorPath errorPath; |
5716 | 0 | errorPath.emplace_back(shadowed, "Shadowed declaration"); |
5717 | 0 | errorPath.emplace_back(var, "Shadow variable"); |
5718 | 0 | const std::string &varname = var ? var->str() : type; |
5719 | 0 | const std::string Type = char(std::toupper(type[0])) + type.substr(1); |
5720 | 0 | const std::string id = "shadow" + Type; |
5721 | 0 | const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type; |
5722 | 0 | reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); |
5723 | 0 | } |
5724 | | |
5725 | | static bool isVariableExpression(const Token* tok) |
5726 | 0 | { |
5727 | 0 | if (tok->varId() != 0) |
5728 | 0 | return true; |
5729 | 0 | if (match30(tok)) |
5730 | 0 | return isVariableExpression(tok->astOperand1()) && |
5731 | 0 | isVariableExpression(tok->astOperand2()); |
5732 | 0 | if (match77(tok)) |
5733 | 0 | return isVariableExpression(tok->astOperand1()); |
5734 | 0 | return false; |
5735 | 0 | } |
5736 | | |
5737 | | static bool isVariableExprHidden(const Token* tok) |
5738 | 0 | { |
5739 | 0 | if (!tok) |
5740 | 0 | return false; |
5741 | 0 | if (!tok->astParent()) |
5742 | 0 | return false; |
5743 | 0 | if (match88(tok->astParent()) && match174(tok->astSibling())) |
5744 | 0 | return true; |
5745 | 0 | if (match175(tok->astParent()) && match176(tok->astSibling())) |
5746 | 0 | return true; |
5747 | 0 | if (match177(tok->astParent()) && match178(tok->astSibling())) |
5748 | 0 | return true; |
5749 | 0 | return false; |
5750 | 0 | } |
5751 | | |
5752 | | void CheckOther::checkKnownArgument() |
5753 | 763 | { |
5754 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("knownArgument")) |
5755 | 0 | return; |
5756 | 763 | logChecker("CheckOther::checkKnownArgument"); // style |
5757 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5758 | 1.10k | for (const Scope *functionScope : symbolDatabase->functionScopes) { |
5759 | 23.7k | for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { |
5760 | 22.6k | if (!tok->hasKnownIntValue() || tok->isExpandedMacro()) |
5761 | 21.4k | continue; |
5762 | 1.22k | if (match179(tok)) |
5763 | 100 | continue; |
5764 | 1.12k | if (!match180(tok->astParent())) |
5765 | 1.02k | continue; |
5766 | 107 | if (tok->astParent()->isCast() || (tok->isCast() && match179(tok->astOperand2()))) |
5767 | 0 | continue; |
5768 | 107 | int argn = -1; |
5769 | 107 | const Token* ftok = getTokenArgumentFunction(tok, argn); |
5770 | 107 | if (!ftok) |
5771 | 0 | continue; |
5772 | 107 | if (ftok->isCast()) |
5773 | 0 | continue; |
5774 | 107 | if (match181(ftok)) |
5775 | 107 | continue; |
5776 | 0 | if (tok == tok->astParent()->previous()) |
5777 | 0 | continue; |
5778 | 0 | if (isConstVarExpression(tok)) |
5779 | 0 | continue; |
5780 | 0 | if (match32(tok->astOperand1())) |
5781 | 0 | continue; |
5782 | 0 | const Token * tok2 = tok; |
5783 | 0 | if (isCPPCast(tok2)) |
5784 | 0 | tok2 = tok2->astOperand2(); |
5785 | 0 | if (isVariableExpression(tok2)) |
5786 | 0 | continue; |
5787 | 0 | if (tok->isComparisonOp() && |
5788 | 0 | isSameExpression( |
5789 | 0 | true, tok->astOperand1(), tok->astOperand2(), *mSettings, true, true)) |
5790 | 0 | continue; |
5791 | | // ensure that there is a integer variable in expression with unknown value |
5792 | 0 | const Token* vartok = nullptr; |
5793 | 0 | visitAstNodes(tok, [&](const Token* child) { |
5794 | 0 | if (match182(child)) { |
5795 | 0 | if (child->hasKnownIntValue()) |
5796 | 0 | return ChildrenToVisit::none; |
5797 | 0 | if (astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty()) { |
5798 | 0 | vartok = child; |
5799 | 0 | return ChildrenToVisit::done; |
5800 | 0 | } |
5801 | 0 | } |
5802 | 0 | return ChildrenToVisit::op1_and_op2; |
5803 | 0 | }); |
5804 | 0 | if (!vartok) |
5805 | 0 | continue; |
5806 | 0 | if (vartok->astSibling() && |
5807 | 0 | findAstNode(vartok->astSibling(), [](const Token* child) { |
5808 | 0 | return match171(child); |
5809 | 0 | })) |
5810 | 0 | continue; |
5811 | | // ensure that function name does not contain "assert" |
5812 | 0 | std::string funcname = ftok->str(); |
5813 | 0 | strTolower(funcname); |
5814 | 0 | if (funcname.find("assert") != std::string::npos) |
5815 | 0 | continue; |
5816 | 0 | knownArgumentError(tok, ftok, &tok->values().front(), vartok->expressionString(), isVariableExprHidden(vartok)); |
5817 | 0 | } |
5818 | 1.10k | } |
5819 | 763 | } |
5820 | | |
5821 | | void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden) |
5822 | 0 | { |
5823 | 0 | if (!tok) { |
5824 | 0 | reportError(tok, Severity::style, "knownArgument", "Argument 'x-x' to function 'func' is always 0. It does not matter what value 'x' has."); |
5825 | 0 | reportError(tok, Severity::style, "knownArgumentHiddenVariableExpression", "Argument 'x*0' to function 'func' is always 0. Constant literal calculation disable/hide variable expression 'x'."); |
5826 | 0 | return; |
5827 | 0 | } |
5828 | | |
5829 | 0 | const MathLib::bigint intvalue = value->intvalue; |
5830 | 0 | const std::string &expr = tok->expressionString(); |
5831 | 0 | const std::string &fun = ftok->str(); |
5832 | |
|
5833 | 0 | std::string ftype = "function "; |
5834 | 0 | if (ftok->type()) |
5835 | 0 | ftype = "constructor "; |
5836 | 0 | else if (fun == MatchCompiler::makeConstString("{")) |
5837 | 0 | ftype = "init list "; |
5838 | |
|
5839 | 0 | const char *id; |
5840 | 0 | std::string errmsg = "Argument '" + expr + "' to " + ftype + fun + " is always " + MathLib::toString(intvalue) + ". "; |
5841 | 0 | if (!isVariableExpressionHidden) { |
5842 | 0 | id = "knownArgument"; |
5843 | 0 | errmsg += "It does not matter what value '" + varexpr + "' has."; |
5844 | 0 | } else { |
5845 | 0 | id = "knownArgumentHiddenVariableExpression"; |
5846 | 0 | errmsg += "Constant literal calculation disable/hide variable expression '" + varexpr + "'."; |
5847 | 0 | } |
5848 | |
|
5849 | 0 | const ErrorPath errorPath = getErrorPath(tok, value, errmsg); |
5850 | 0 | reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal); |
5851 | 0 | } |
5852 | | |
5853 | | void CheckOther::checkKnownPointerToBool() |
5854 | 763 | { |
5855 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("knownPointerToBool")) |
5856 | 0 | return; |
5857 | 763 | logChecker("CheckOther::checkKnownPointerToBool"); // style |
5858 | 763 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
5859 | 1.10k | for (const Scope* functionScope : symbolDatabase->functionScopes) { |
5860 | 23.7k | for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { |
5861 | 22.6k | if (!tok->hasKnownIntValue()) |
5862 | 21.4k | continue; |
5863 | 1.22k | if (!astIsPointer(tok)) |
5864 | 1.22k | continue; |
5865 | 0 | if (match183(tok->astParent())) |
5866 | 0 | continue; |
5867 | 0 | if (tok->astParent() && match184(tok->astParent()->previous())) |
5868 | 0 | continue; |
5869 | 0 | if (tok->isExpandedMacro()) |
5870 | 0 | continue; |
5871 | 0 | if (findParent(tok, [](const Token* parent) { |
5872 | 0 | return parent->isExpandedMacro(); |
5873 | 0 | })) |
5874 | 0 | continue; |
5875 | 0 | if (!isUsedAsBool(tok, *mSettings)) |
5876 | 0 | continue; |
5877 | 0 | const ValueFlow::Value& value = tok->values().front(); |
5878 | 0 | knownPointerToBoolError(tok, &value); |
5879 | 0 | } |
5880 | 1.10k | } |
5881 | 763 | } |
5882 | | |
5883 | | void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value) |
5884 | 0 | { |
5885 | 0 | if (!tok) { |
5886 | 0 | reportError(tok, Severity::style, "knownPointerToBool", "Pointer expression 'p' converted to bool is always true."); |
5887 | 0 | return; |
5888 | 0 | } |
5889 | 0 | std::string cond = bool_to_string(!!value->intvalue); |
5890 | 0 | const std::string& expr = tok->expressionString(); |
5891 | 0 | std::string errmsg = "Pointer expression '" + expr + "' converted to bool is always " + cond + "."; |
5892 | 0 | const ErrorPath errorPath = getErrorPath(tok, value, errmsg); |
5893 | 0 | reportError(errorPath, Severity::style, "knownPointerToBool", errmsg, CWE570, Certainty::normal); |
5894 | 0 | } |
5895 | | |
5896 | | void CheckOther::checkComparePointers() |
5897 | 763 | { |
5898 | 763 | logChecker("CheckOther::checkComparePointers"); |
5899 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
5900 | 1.10k | for (const Scope *functionScope : symbolDatabase->functionScopes) { |
5901 | 23.7k | for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { |
5902 | 22.6k | if (!match185(tok)) |
5903 | 21.9k | continue; |
5904 | 720 | const Token *tok1 = tok->astOperand1(); |
5905 | 720 | if (!astIsPointer(tok1)) |
5906 | 719 | continue; |
5907 | 1 | const Token *tok2 = tok->astOperand2(); |
5908 | 1 | if (!astIsPointer(tok2)) |
5909 | 1 | continue; |
5910 | 0 | ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1); |
5911 | 0 | if (!v1.isLocalLifetimeValue()) |
5912 | 0 | continue; |
5913 | 0 | ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2); |
5914 | 0 | if (!v2.isLocalLifetimeValue()) |
5915 | 0 | continue; |
5916 | 0 | const Variable *var1 = v1.tokvalue->variable(); |
5917 | 0 | const Variable *var2 = v2.tokvalue->variable(); |
5918 | 0 | if (!var1 || !var2) |
5919 | 0 | continue; |
5920 | 0 | if (v1.tokvalue->varId() == v2.tokvalue->varId()) |
5921 | 0 | continue; |
5922 | 0 | if (var1->isReference() || var2->isReference()) |
5923 | 0 | continue; |
5924 | 0 | if (var1->isRValueReference() || var2->isRValueReference()) |
5925 | 0 | continue; |
5926 | 0 | if (const Token* parent2 = getParentLifetime(v2.tokvalue, mSettings->library)) |
5927 | 0 | if (var1 == parent2->variable()) |
5928 | 0 | continue; |
5929 | 0 | if (const Token* parent1 = getParentLifetime(v1.tokvalue, mSettings->library)) |
5930 | 0 | if (var2 == parent1->variable()) |
5931 | 0 | continue; |
5932 | 0 | comparePointersError(tok, &v1, &v2); |
5933 | 0 | } |
5934 | 1.10k | } |
5935 | 763 | } |
5936 | | |
5937 | | void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) |
5938 | 0 | { |
5939 | 0 | ErrorPath errorPath; |
5940 | 0 | std::string verb = "Comparing"; |
5941 | 0 | if (match186(tok)) |
5942 | 0 | verb = "Subtracting"; |
5943 | 0 | const char * const id = (verb[0] == 'C') ? "comparePointers" : "subtractPointers"; |
5944 | 0 | if (v1) { |
5945 | 0 | errorPath.emplace_back(v1->tokvalue->variable()->nameToken(), "Variable declared here."); |
5946 | 0 | errorPath.insert(errorPath.end(), v1->errorPath.cbegin(), v1->errorPath.cend()); |
5947 | 0 | } |
5948 | 0 | if (v2) { |
5949 | 0 | errorPath.emplace_back(v2->tokvalue->variable()->nameToken(), "Variable declared here."); |
5950 | 0 | errorPath.insert(errorPath.end(), v2->errorPath.cbegin(), v2->errorPath.cend()); |
5951 | 0 | } |
5952 | 0 | errorPath.emplace_back(tok, ""); |
5953 | 0 | reportError( |
5954 | 0 | errorPath, Severity::error, id, verb + " pointers that point to different objects", CWE570, Certainty::normal); |
5955 | 0 | } |
5956 | | |
5957 | | void CheckOther::checkModuloOfOne() |
5958 | 763 | { |
5959 | 763 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("moduloofone")) |
5960 | 0 | return; |
5961 | | |
5962 | 763 | logChecker("CheckOther::checkModuloOfOne"); // style |
5963 | | |
5964 | 55.6k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
5965 | 54.8k | if (!tok->astOperand2() || !tok->astOperand1()) |
5966 | 46.5k | continue; |
5967 | 8.33k | if (tok->str() != MatchCompiler::makeConstString("%")) |
5968 | 8.24k | continue; |
5969 | 91 | if (!tok->valueType() || !tok->valueType()->isIntegral()) |
5970 | 44 | continue; |
5971 | | |
5972 | | // Value flow.. |
5973 | 47 | const ValueFlow::Value *value = tok->astOperand2()->getValue(1LL); |
5974 | 47 | if (value && value->isKnown()) |
5975 | 0 | checkModuloOfOneError(tok); |
5976 | 47 | } |
5977 | 763 | } |
5978 | | |
5979 | | void CheckOther::checkModuloOfOneError(const Token *tok) |
5980 | 0 | { |
5981 | 0 | reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero"); |
5982 | 0 | } |
5983 | | |
5984 | | //----------------------------------------------------------------------------- |
5985 | | // Overlapping write (undefined behavior) |
5986 | | //----------------------------------------------------------------------------- |
5987 | | static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigint *offset, const Settings& settings, MathLib::bigint* sizeValue = nullptr) |
5988 | 0 | { |
5989 | 0 | if (!expr) |
5990 | 0 | return false; |
5991 | 0 | const Token *bufToken, *offsetToken; |
5992 | 0 | MathLib::bigint elementSize = 0; |
5993 | 0 | if (expr->isUnaryOp("&") && match77(expr->astOperand1())) { |
5994 | 0 | bufToken = expr->astOperand1()->astOperand1(); |
5995 | 0 | offsetToken = expr->astOperand1()->astOperand2(); |
5996 | 0 | if (expr->astOperand1()->valueType()) |
5997 | 0 | elementSize = ValueFlow::getSizeOf(*expr->astOperand1()->valueType(), settings); |
5998 | 0 | } else if (match187(expr) && expr->isBinaryOp()) { |
5999 | 0 | const bool pointer1 = (expr->astOperand1()->valueType() && expr->astOperand1()->valueType()->pointer > 0); |
6000 | 0 | const bool pointer2 = (expr->astOperand2()->valueType() && expr->astOperand2()->valueType()->pointer > 0); |
6001 | 0 | if (pointer1 && !pointer2) { |
6002 | 0 | bufToken = expr->astOperand1(); |
6003 | 0 | offsetToken = expr->astOperand2(); |
6004 | 0 | auto vt = *expr->astOperand1()->valueType(); |
6005 | 0 | --vt.pointer; |
6006 | 0 | elementSize = ValueFlow::getSizeOf(vt, settings); |
6007 | 0 | } else if (!pointer1 && pointer2) { |
6008 | 0 | bufToken = expr->astOperand2(); |
6009 | 0 | offsetToken = expr->astOperand1(); |
6010 | 0 | auto vt = *expr->astOperand2()->valueType(); |
6011 | 0 | --vt.pointer; |
6012 | 0 | elementSize = ValueFlow::getSizeOf(vt, settings); |
6013 | 0 | } else { |
6014 | 0 | return false; |
6015 | 0 | } |
6016 | 0 | } else if (expr->valueType() && expr->valueType()->pointer > 0) { |
6017 | 0 | buf = expr; |
6018 | 0 | *offset = 0; |
6019 | 0 | auto vt = *expr->valueType(); |
6020 | 0 | --vt.pointer; |
6021 | 0 | elementSize = ValueFlow::getSizeOf(vt, settings); |
6022 | 0 | if (elementSize > 0) { |
6023 | 0 | *offset *= elementSize; |
6024 | 0 | if (sizeValue) |
6025 | 0 | *sizeValue *= elementSize; |
6026 | 0 | } |
6027 | 0 | return true; |
6028 | 0 | } else { |
6029 | 0 | return false; |
6030 | 0 | } |
6031 | 0 | if (!bufToken->valueType() || !bufToken->valueType()->pointer) |
6032 | 0 | return false; |
6033 | 0 | if (!offsetToken->hasKnownIntValue()) |
6034 | 0 | return false; |
6035 | 0 | buf = bufToken; |
6036 | 0 | *offset = offsetToken->getKnownIntValue(); |
6037 | 0 | if (elementSize > 0) { |
6038 | 0 | *offset *= elementSize; |
6039 | 0 | if (sizeValue) |
6040 | 0 | *sizeValue *= elementSize; |
6041 | 0 | } |
6042 | 0 | return true; |
6043 | 0 | } |
6044 | | |
6045 | | void CheckOther::checkOverlappingWrite() |
6046 | 763 | { |
6047 | 763 | logChecker("CheckOther::checkOverlappingWrite"); |
6048 | 763 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
6049 | 1.10k | for (const Scope *functionScope : symbolDatabase->functionScopes) { |
6050 | 23.7k | for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { |
6051 | 22.6k | if (tok->isAssignmentOp()) { |
6052 | | // check if LHS is a union member.. |
6053 | 2.24k | const Token * const lhs = tok->astOperand1(); |
6054 | 2.24k | if (!match30(lhs) || !lhs->isBinaryOp()) |
6055 | 2.24k | continue; |
6056 | 0 | const Variable * const lhsvar = lhs->astOperand1()->variable(); |
6057 | 0 | if (!lhsvar || !lhsvar->typeScope() || lhsvar->typeScope()->type != Scope::ScopeType::eUnion) |
6058 | 0 | continue; |
6059 | 0 | const Token* const lhsmember = lhs->astOperand2(); |
6060 | 0 | if (!lhsmember) |
6061 | 0 | continue; |
6062 | | |
6063 | | // Is other union member used in RHS? |
6064 | 0 | const Token *errorToken = nullptr; |
6065 | 0 | visitAstNodes(tok->astOperand2(), [lhsvar, lhsmember, &errorToken](const Token *rhs) { |
6066 | 0 | if (!match30(rhs)) |
6067 | 0 | return ChildrenToVisit::op1_and_op2; |
6068 | 0 | if (!rhs->isBinaryOp() || rhs->astOperand1()->variable() != lhsvar) |
6069 | 0 | return ChildrenToVisit::none; |
6070 | 0 | if (lhsmember->str() == rhs->astOperand2()->str()) |
6071 | 0 | return ChildrenToVisit::none; |
6072 | 0 | const Variable* rhsmembervar = rhs->astOperand2()->variable(); |
6073 | 0 | const Scope* varscope1 = lhsmember->variable() ? lhsmember->variable()->typeStartToken()->scope() : nullptr; |
6074 | 0 | const Scope* varscope2 = rhsmembervar ? rhsmembervar->typeStartToken()->scope() : nullptr; |
6075 | 0 | if (varscope1 && varscope1 == varscope2 && varscope1 != lhsvar->typeScope()) |
6076 | | // lhsmember and rhsmember are declared in same anonymous scope inside union |
6077 | 0 | return ChildrenToVisit::none; |
6078 | 0 | errorToken = rhs->astOperand2(); |
6079 | 0 | return ChildrenToVisit::done; |
6080 | 0 | }); |
6081 | 0 | if (errorToken) |
6082 | 0 | overlappingWriteUnion(tok); |
6083 | 20.4k | } else if (match32(tok)) { |
6084 | 722 | const Library::NonOverlappingData *nonOverlappingData = mSettings->library.getNonOverlappingData(tok); |
6085 | 722 | if (!nonOverlappingData) |
6086 | 722 | continue; |
6087 | 0 | const std::vector<const Token *> args = getArguments(tok); |
6088 | 0 | if (nonOverlappingData->ptr1Arg <= 0 || nonOverlappingData->ptr1Arg > args.size()) |
6089 | 0 | continue; |
6090 | 0 | if (nonOverlappingData->ptr2Arg <= 0 || nonOverlappingData->ptr2Arg > args.size()) |
6091 | 0 | continue; |
6092 | | |
6093 | 0 | const Token *ptr1 = args[nonOverlappingData->ptr1Arg - 1]; |
6094 | 0 | if (ptr1->hasKnownIntValue() && ptr1->getKnownIntValue() == 0) |
6095 | 0 | continue; |
6096 | | |
6097 | 0 | const Token *ptr2 = args[nonOverlappingData->ptr2Arg - 1]; |
6098 | 0 | if (ptr2->hasKnownIntValue() && ptr2->getKnownIntValue() == 0) |
6099 | 0 | continue; |
6100 | | |
6101 | | // TODO: nonOverlappingData->strlenArg |
6102 | 0 | const int sizeArg = std::max(nonOverlappingData->sizeArg, nonOverlappingData->countArg); |
6103 | 0 | if (sizeArg <= 0 || sizeArg > args.size()) { |
6104 | 0 | if (nonOverlappingData->sizeArg == -1) { |
6105 | 0 | ErrorPath errorPath; |
6106 | 0 | constexpr bool macro = true; |
6107 | 0 | constexpr bool pure = true; |
6108 | 0 | constexpr bool follow = true; |
6109 | 0 | if (!isSameExpression(macro, ptr1, ptr2, *mSettings, pure, follow, &errorPath)) |
6110 | 0 | continue; |
6111 | 0 | overlappingWriteFunction(tok, tok->str()); |
6112 | 0 | } |
6113 | 0 | continue; |
6114 | 0 | } |
6115 | 0 | const bool isCountArg = nonOverlappingData->countArg > 0; |
6116 | 0 | if (!args[sizeArg-1]->hasKnownIntValue()) |
6117 | 0 | continue; |
6118 | 0 | MathLib::bigint sizeValue = args[sizeArg-1]->getKnownIntValue(); |
6119 | 0 | const Token *buf1, *buf2; |
6120 | 0 | MathLib::bigint offset1, offset2; |
6121 | 0 | if (!getBufAndOffset(ptr1, buf1, &offset1, *mSettings, isCountArg ? &sizeValue : nullptr)) |
6122 | 0 | continue; |
6123 | 0 | if (!getBufAndOffset(ptr2, buf2, &offset2, *mSettings)) |
6124 | 0 | continue; |
6125 | | |
6126 | 0 | if (offset1 < offset2 && offset1 + sizeValue <= offset2) |
6127 | 0 | continue; |
6128 | 0 | if (offset2 < offset1 && offset2 + sizeValue <= offset1) |
6129 | 0 | continue; |
6130 | | |
6131 | 0 | ErrorPath errorPath; |
6132 | 0 | constexpr bool macro = true; |
6133 | 0 | constexpr bool pure = true; |
6134 | 0 | constexpr bool follow = true; |
6135 | 0 | if (!isSameExpression(macro, buf1, buf2, *mSettings, pure, follow, &errorPath)) |
6136 | 0 | continue; |
6137 | 0 | overlappingWriteFunction(tok, tok->str()); |
6138 | 0 | } |
6139 | 22.6k | } |
6140 | 1.10k | } |
6141 | 763 | } |
6142 | | |
6143 | | void CheckOther::overlappingWriteUnion(const Token *tok) |
6144 | 0 | { |
6145 | 0 | reportError(tok, Severity::error, "overlappingWriteUnion", "Overlapping read/write of union is undefined behavior"); |
6146 | 0 | } |
6147 | | |
6148 | | void CheckOther::overlappingWriteFunction(const Token *tok, const std::string& funcname) |
6149 | 0 | { |
6150 | 0 | reportError(tok, Severity::error, "overlappingWriteFunction", "Overlapping read/write in " + funcname + "() is undefined behavior"); |
6151 | 0 | } |
6152 | | |
6153 | | void CheckOther::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) |
6154 | 763 | { |
6155 | 763 | CheckOther checkOther(&tokenizer, &tokenizer.getSettings(), errorLogger); |
6156 | | |
6157 | | // Checks |
6158 | 763 | checkOther.warningOldStylePointerCast(); |
6159 | 763 | checkOther.suspiciousFloatingPointCast(); |
6160 | 763 | checkOther.invalidPointerCast(); |
6161 | 763 | checkOther.checkCharVariable(); |
6162 | 763 | checkOther.redundantBitwiseOperationInSwitchError(); |
6163 | 763 | checkOther.checkSuspiciousCaseInSwitch(); |
6164 | 763 | checkOther.checkDuplicateBranch(); |
6165 | 763 | checkOther.checkDuplicateExpression(); |
6166 | 763 | checkOther.checkRedundantAssignment(); |
6167 | 763 | checkOther.checkUnreachableCode(); |
6168 | 763 | checkOther.checkSuspiciousSemicolon(); |
6169 | 763 | checkOther.checkVariableScope(); |
6170 | 763 | checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) |
6171 | 763 | checkOther.checkIncompleteArrayFill(); |
6172 | 763 | checkOther.checkVarFuncNullUB(); |
6173 | 763 | checkOther.checkNanInArithmeticExpression(); |
6174 | 763 | checkOther.checkCommaSeparatedReturn(); |
6175 | 763 | checkOther.checkRedundantPointerOp(); |
6176 | 763 | checkOther.checkZeroDivision(); |
6177 | 763 | checkOther.checkNegativeBitwiseShift(); |
6178 | 763 | checkOther.checkInterlockedDecrement(); |
6179 | 763 | checkOther.checkUnusedLabel(); |
6180 | 763 | checkOther.checkEvaluationOrder(); |
6181 | 763 | checkOther.checkFuncArgNamesDifferent(); |
6182 | 763 | checkOther.checkShadowVariables(); |
6183 | 763 | checkOther.checkKnownArgument(); |
6184 | 763 | checkOther.checkKnownPointerToBool(); |
6185 | 763 | checkOther.checkComparePointers(); |
6186 | 763 | checkOther.checkIncompleteStatement(); |
6187 | 763 | checkOther.checkRedundantCopy(); |
6188 | 763 | checkOther.clarifyCalculation(); |
6189 | 763 | checkOther.checkPassByReference(); |
6190 | 763 | checkOther.checkConstVariable(); |
6191 | 763 | checkOther.checkConstPointer(); |
6192 | 763 | checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); |
6193 | 763 | checkOther.checkInvalidFree(); |
6194 | 763 | checkOther.clarifyStatement(); |
6195 | 763 | checkOther.checkCastIntToCharAndBack(); |
6196 | 763 | checkOther.checkMisusedScopedObject(); |
6197 | 763 | checkOther.checkAccessOfMovedVariable(); |
6198 | 763 | checkOther.checkModuloOfOne(); |
6199 | 763 | checkOther.checkOverlappingWrite(); |
6200 | 763 | } |
6201 | | |
6202 | | void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const |
6203 | 0 | { |
6204 | 0 | CheckOther c(nullptr, settings, errorLogger); |
6205 | | |
6206 | | // error |
6207 | 0 | c.zerodivError(nullptr, nullptr); |
6208 | 0 | c.misusedScopeObjectError(nullptr, "varname"); |
6209 | 0 | c.invalidPointerCastError(nullptr, "float *", "double *", false, false); |
6210 | 0 | c.negativeBitwiseShiftError(nullptr, 1); |
6211 | 0 | c.negativeBitwiseShiftError(nullptr, 2); |
6212 | 0 | c.raceAfterInterlockedDecrementError(nullptr); |
6213 | 0 | c.invalidFreeError(nullptr, "malloc", false); |
6214 | 0 | c.overlappingWriteUnion(nullptr); |
6215 | 0 | c.overlappingWriteFunction(nullptr, "funcname"); |
6216 | | |
6217 | | //performance |
6218 | 0 | c.redundantCopyError(nullptr, "varname"); |
6219 | 0 | c.redundantCopyError(nullptr, nullptr, "var"); |
6220 | | |
6221 | | // style/warning |
6222 | 0 | c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); |
6223 | 0 | c.checkCastIntToCharAndBackError(nullptr, "func_name"); |
6224 | 0 | c.cstyleCastError(nullptr); |
6225 | 0 | c.suspiciousFloatingPointCastError(nullptr); |
6226 | 0 | c.passedByValueError(nullptr, false); |
6227 | 0 | c.constVariableError(nullptr, nullptr); |
6228 | 0 | c.constStatementError(nullptr, "type", false); |
6229 | 0 | c.signedCharArrayIndexError(nullptr); |
6230 | 0 | c.unknownSignCharArrayIndexError(nullptr); |
6231 | 0 | c.charBitOpError(nullptr); |
6232 | 0 | c.variableScopeError(nullptr, "varname"); |
6233 | 0 | c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); |
6234 | 0 | c.suspiciousCaseInSwitchError(nullptr, "||"); |
6235 | 0 | c.selfAssignmentError(nullptr, "varname"); |
6236 | 0 | c.clarifyCalculationError(nullptr, "+"); |
6237 | 0 | c.clarifyStatementError(nullptr); |
6238 | 0 | c.duplicateBranchError(nullptr, nullptr, ErrorPath{}); |
6239 | 0 | c.duplicateAssignExpressionError(nullptr, nullptr, true); |
6240 | 0 | c.oppositeExpressionError(nullptr, ErrorPath{}); |
6241 | 0 | c.duplicateExpressionError(nullptr, nullptr, nullptr, ErrorPath{}); |
6242 | 0 | c.duplicateValueTernaryError(nullptr); |
6243 | 0 | c.duplicateExpressionTernaryError(nullptr, ErrorPath{}); |
6244 | 0 | c.duplicateBreakError(nullptr, false); |
6245 | 0 | c.unreachableCodeError(nullptr, nullptr, false); |
6246 | 0 | c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); |
6247 | 0 | c.unsignedPositiveError(nullptr, nullptr, "varname"); |
6248 | 0 | c.pointerLessThanZeroError(nullptr, nullptr); |
6249 | 0 | c.pointerPositiveError(nullptr, nullptr); |
6250 | 0 | c.suspiciousSemicolonError(nullptr); |
6251 | 0 | c.incompleteArrayFillError(nullptr, "buffer", "memset", false); |
6252 | 0 | c.varFuncNullUBError(nullptr); |
6253 | 0 | c.nanInArithmeticExpressionError(nullptr); |
6254 | 0 | c.commaSeparatedReturnError(nullptr); |
6255 | 0 | c.redundantPointerOpError(nullptr, "varname", false, /*addressOfDeref*/ true); |
6256 | 0 | c.unusedLabelError(nullptr, false, false); |
6257 | 0 | c.unusedLabelError(nullptr, false, true); |
6258 | 0 | c.unusedLabelError(nullptr, true, false); |
6259 | 0 | c.unusedLabelError(nullptr, true, true); |
6260 | 0 | c.unknownEvaluationOrder(nullptr); |
6261 | 0 | c.accessMovedError(nullptr, "v", nullptr, false); |
6262 | 0 | c.funcArgNamesDifferent("function", 1, nullptr, nullptr); |
6263 | 0 | c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); |
6264 | 0 | c.shadowError(nullptr, nullptr, "variable"); |
6265 | 0 | c.shadowError(nullptr, nullptr, "function"); |
6266 | 0 | c.shadowError(nullptr, nullptr, "argument"); |
6267 | 0 | c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); |
6268 | 0 | c.knownPointerToBoolError(nullptr, nullptr); |
6269 | 0 | c.comparePointersError(nullptr, nullptr, nullptr); |
6270 | 0 | c.redundantAssignmentError(nullptr, nullptr, "var", false); |
6271 | 0 | c.redundantInitializationError(nullptr, nullptr, "var", false); |
6272 | |
|
6273 | 0 | const std::vector<const Token *> nullvec; |
6274 | 0 | c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); |
6275 | 0 | c.checkModuloOfOneError(nullptr); |
6276 | 0 | } |