/src/cppcheck/lib/tokenize.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Cppcheck - A tool for static C/C++ code analysis |
3 | | * Copyright (C) 2007-2023 Cppcheck team. |
4 | | * |
5 | | * This program is free software: you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation, either version 3 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | //--------------------------------------------------------------------------- |
20 | | #include "tokenize.h" |
21 | | |
22 | | #include "check.h" |
23 | | #include "errorlogger.h" |
24 | | #include "library.h" |
25 | | #include "mathlib.h" |
26 | | #include "platform.h" |
27 | | #include "preprocessor.h" |
28 | | #include "settings.h" |
29 | | #include "standards.h" |
30 | | #include "summaries.h" |
31 | | #include "symboldatabase.h" |
32 | | #include "templatesimplifier.h" |
33 | | #include "timer.h" |
34 | | #include "token.h" |
35 | | #include "utils.h" |
36 | | #include "valueflow.h" |
37 | | #include "vfvalue.h" |
38 | | |
39 | | #include <algorithm> |
40 | | #include <cassert> |
41 | | #include <cctype> |
42 | | #include <cstdlib> |
43 | | #include <cstring> |
44 | | #include <ctime> |
45 | | #include <iostream> |
46 | | #include <iterator> |
47 | | #include <exception> |
48 | | #include <memory> |
49 | | #include <set> |
50 | | #include <sstream> // IWYU pragma: keep |
51 | | #include <stack> |
52 | | #include <stdexcept> |
53 | | #include <type_traits> |
54 | | #include <unordered_map> |
55 | | #include <unordered_set> |
56 | | #include <utility> |
57 | | #include <vector> |
58 | | |
59 | | #include <simplecpp.h> |
60 | | |
61 | | //--------------------------------------------------------------------------- |
62 | | |
63 | | namespace { |
64 | | // local struct used in setVarId |
65 | | // in order to store information about the scope |
66 | | struct VarIdScopeInfo { |
67 | 1.36k | VarIdScopeInfo() = default; |
68 | | VarIdScopeInfo(bool isExecutable, bool isStructInit, bool isEnum, nonneg int startVarid) |
69 | 3.58k | : isExecutable(isExecutable), isStructInit(isStructInit), isEnum(isEnum), startVarid(startVarid) {} |
70 | | |
71 | | const bool isExecutable{}; |
72 | | const bool isStructInit{}; |
73 | | const bool isEnum{}; |
74 | | const nonneg int startVarid{}; |
75 | | }; |
76 | | } |
77 | | |
78 | | /** Return whether tok is the "{" that starts an enumerator list */ |
79 | | static bool isEnumStart(const Token* tok) |
80 | 458 | { |
81 | 458 | if (!tok || tok->str() != "{") |
82 | 0 | return false; |
83 | 458 | return (tok->strAt(-1) == "enum") || (tok->strAt(-2) == "enum") || Token::Match(tok->tokAt(-3), "enum class %name%"); |
84 | 458 | } |
85 | | |
86 | | template<typename T> |
87 | | static void skipEnumBody(T **tok) |
88 | 0 | { |
89 | 0 | T *defStart = *tok; |
90 | 0 | while (Token::Match(defStart, "%name%|::|:")) |
91 | 0 | defStart = defStart->next(); |
92 | 0 | if (defStart && defStart->str() == "{") |
93 | 0 | *tok = defStart->link()->next(); |
94 | 0 | } |
95 | | |
96 | | const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith) const |
97 | 12.9k | { |
98 | 12.9k | return Tokenizer::isFunctionHead(tok, endsWith, isCPP()); |
99 | 12.9k | } |
100 | | |
101 | | const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp) |
102 | 16.4k | { |
103 | 16.4k | if (!tok) |
104 | 0 | return nullptr; |
105 | 16.4k | if (tok->str() == "(") |
106 | 7.30k | tok = tok->link(); |
107 | 16.4k | if (Token::Match(tok, ") ;|{|[")) { |
108 | 15.2k | tok = tok->next(); |
109 | 15.2k | while (tok && tok->str() == "[" && tok->link()) { |
110 | 0 | if (endsWith.find(tok->str()) != std::string::npos) |
111 | 0 | return tok; |
112 | 0 | tok = tok->link()->next(); |
113 | 0 | } |
114 | 15.2k | return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; |
115 | 15.2k | } |
116 | 1.23k | if (cpp && tok->str() == ")") { |
117 | 1.23k | tok = tok->next(); |
118 | 1.29k | while (Token::Match(tok, "const|noexcept|override|final|volatile|mutable|&|&& !!(") || |
119 | 1.29k | (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) |
120 | 59 | tok = tok->next(); |
121 | 1.23k | if (tok && tok->str() == ")") |
122 | 364 | tok = tok->next(); |
123 | 1.23k | while (tok && tok->str() == "[") |
124 | 0 | tok = tok->link()->next(); |
125 | 1.23k | if (Token::Match(tok, "throw|noexcept (")) |
126 | 0 | tok = tok->linkAt(1)->next(); |
127 | 1.23k | if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) |
128 | 0 | tok = tok->linkAt(1)->next(); |
129 | 1.23k | if (tok && tok->originalName() == "->") { // trailing return type |
130 | 0 | for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) |
131 | 0 | if (tok->link() && Token::Match(tok, "<|[|(")) |
132 | 0 | tok = tok->link(); |
133 | 0 | } |
134 | 1.23k | while (Token::Match(tok, "override|final !!(") || |
135 | 1.23k | (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) |
136 | 0 | tok = tok->next(); |
137 | 1.23k | if (Token::Match(tok, "= 0|default|delete ;")) |
138 | 0 | tok = tok->tokAt(2); |
139 | | |
140 | 1.23k | return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; |
141 | 1.23k | } |
142 | 0 | return nullptr; |
143 | 1.23k | } |
144 | | |
145 | | /** |
146 | | * is tok the start brace { of a class, struct, union, or enum |
147 | | */ |
148 | | static bool isClassStructUnionEnumStart(const Token * tok) |
149 | 458 | { |
150 | 458 | if (!Token::Match(tok->previous(), "class|struct|union|enum|%name%|>|>> {")) |
151 | 0 | return false; |
152 | 458 | const Token * tok2 = tok->previous(); |
153 | 916 | while (tok2 && !Token::Match(tok2, "class|struct|union|enum|{|}|;")) |
154 | 458 | tok2 = tok2->previous(); |
155 | 458 | return Token::Match(tok2, "class|struct|union|enum"); |
156 | 458 | } |
157 | | |
158 | | //--------------------------------------------------------------------------- |
159 | | |
160 | | Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger, const Preprocessor *preprocessor) : |
161 | | list(settings), |
162 | | mSettings(settings), |
163 | | mErrorLogger(errorLogger), |
164 | | mTemplateSimplifier(new TemplateSimplifier(*this)), |
165 | | mPreprocessor(preprocessor) |
166 | 1.36k | { |
167 | | // make sure settings are specified |
168 | 1.36k | assert(mSettings); |
169 | 1.36k | } |
170 | | |
171 | | Tokenizer::~Tokenizer() |
172 | 1.36k | { |
173 | 1.36k | delete mSymbolDatabase; |
174 | 1.36k | delete mTemplateSimplifier; |
175 | 1.36k | } |
176 | | |
177 | | |
178 | | //--------------------------------------------------------------------------- |
179 | | // SizeOfType - gives the size of a type |
180 | | //--------------------------------------------------------------------------- |
181 | | |
182 | | nonneg int Tokenizer::sizeOfType(const std::string& type) const |
183 | 0 | { |
184 | 0 | const std::map<std::string, int>::const_iterator it = mTypeSize.find(type); |
185 | 0 | if (it == mTypeSize.end()) { |
186 | 0 | const Library::PodType* podtype = mSettings->library.podtype(type); |
187 | 0 | if (!podtype) |
188 | 0 | return 0; |
189 | | |
190 | 0 | return podtype->size; |
191 | 0 | } |
192 | 0 | return it->second; |
193 | 0 | } |
194 | | |
195 | | nonneg int Tokenizer::sizeOfType(const Token *type) const |
196 | 0 | { |
197 | 0 | if (!type || type->str().empty()) |
198 | 0 | return 0; |
199 | | |
200 | 0 | if (type->tokType() == Token::eString) |
201 | 0 | return Token::getStrLength(type) + 1U; |
202 | | |
203 | 0 | const std::map<std::string, int>::const_iterator it = mTypeSize.find(type->str()); |
204 | 0 | if (it == mTypeSize.end()) { |
205 | 0 | const Library::PodType* podtype = mSettings->library.podtype(type->str()); |
206 | 0 | if (!podtype) |
207 | 0 | return 0; |
208 | | |
209 | 0 | return podtype->size; |
210 | 0 | } |
211 | 0 | if (type->isLong()) { |
212 | 0 | if (type->str() == "double") |
213 | 0 | return mSettings->platform.sizeof_long_double; |
214 | 0 | if (type->str() == "long") |
215 | 0 | return mSettings->platform.sizeof_long_long; |
216 | 0 | } |
217 | | |
218 | 0 | return it->second; |
219 | 0 | } |
220 | | //--------------------------------------------------------------------------- |
221 | | |
222 | | // check if this statement is a duplicate definition |
223 | | bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef) const |
224 | 0 | { |
225 | | // check for an end of definition |
226 | 0 | Token * tok = *tokPtr; |
227 | 0 | if (tok && Token::Match(tok->next(), ";|,|[|=|)|>|(|{")) { |
228 | 0 | Token * end = tok->next(); |
229 | |
|
230 | 0 | if (end->str() == "[") { |
231 | 0 | if (!end->link()) |
232 | 0 | syntaxError(end); // invalid code |
233 | 0 | end = end->link()->next(); |
234 | 0 | } else if (end->str() == ",") { |
235 | | // check for derived class |
236 | 0 | if (Token::Match(tok->previous(), "public|private|protected")) |
237 | 0 | return false; |
238 | | |
239 | | // find end of definition |
240 | 0 | while (end && end->next() && !Token::Match(end->next(), ";|)|>")) { |
241 | 0 | if (end->next()->str() == "(") |
242 | 0 | end = end->linkAt(1); |
243 | |
|
244 | 0 | end = (end)?end->next():nullptr; |
245 | 0 | } |
246 | 0 | if (end) |
247 | 0 | end = end->next(); |
248 | 0 | } else if (end->str() == "(") { |
249 | 0 | if (startsWith(tok->previous()->str(), "operator")) |
250 | | // conversion operator |
251 | 0 | return false; |
252 | 0 | if (tok->previous()->str() == "typedef") |
253 | | // typedef of function returning this type |
254 | 0 | return false; |
255 | 0 | if (Token::Match(tok->previous(), "public:|private:|protected:")) |
256 | 0 | return false; |
257 | 0 | if (tok->previous()->str() == ">") { |
258 | 0 | if (!Token::Match(tok->tokAt(-2), "%type%")) |
259 | 0 | return false; |
260 | | |
261 | 0 | if (!Token::Match(tok->tokAt(-3), ",|<")) |
262 | 0 | return false; |
263 | | |
264 | 0 | *tokPtr = end->link(); |
265 | 0 | return true; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | 0 | if (end) { |
270 | 0 | if (Token::simpleMatch(end, ") {")) { // function parameter ? |
271 | | // look backwards |
272 | 0 | if (Token::Match(tok->previous(), "%type%") && |
273 | 0 | !Token::Match(tok->previous(), "return|new|const|struct")) { |
274 | | // duplicate definition so skip entire function |
275 | 0 | *tokPtr = end->next()->link(); |
276 | 0 | return true; |
277 | 0 | } |
278 | 0 | } else if (end->str() == ">") { // template parameter ? |
279 | | // look backwards |
280 | 0 | if (Token::Match(tok->previous(), "%type%") && |
281 | 0 | !Token::Match(tok->previous(), "return|new|const|volatile")) { |
282 | | // duplicate definition so skip entire template |
283 | 0 | while (end && end->str() != "{") |
284 | 0 | end = end->next(); |
285 | 0 | if (end) { |
286 | 0 | *tokPtr = end->link(); |
287 | 0 | return true; |
288 | 0 | } |
289 | 0 | } |
290 | 0 | } else { |
291 | | // look backwards |
292 | 0 | if (Token::Match(tok->previous(), "typedef|}|>") || |
293 | 0 | (end->str() == ";" && tok->previous()->str() == ",") || |
294 | 0 | (tok->previous()->str() == "*" && tok->next()->str() != "(") || |
295 | 0 | (Token::Match(tok->previous(), "%type%") && |
296 | 0 | (!Token::Match(tok->previous(), "return|new|const|friend|public|private|protected|throw|extern") && |
297 | 0 | !Token::simpleMatch(tok->tokAt(-2), "friend class")))) { |
298 | | // scan backwards for the end of the previous statement |
299 | 0 | while (tok && tok->previous() && !Token::Match(tok->previous(), ";|{")) { |
300 | 0 | if (tok->previous()->str() == "}") { |
301 | 0 | tok = tok->previous()->link(); |
302 | 0 | } else if (tok->previous()->str() == "typedef") { |
303 | 0 | return true; |
304 | 0 | } else if (tok->previous()->str() == "enum") { |
305 | 0 | return true; |
306 | 0 | } else if (tok->previous()->str() == "struct") { |
307 | 0 | if (tok->strAt(-2) == "typedef" && |
308 | 0 | tok->next()->str() == "{" && |
309 | 0 | typeDef->strAt(3) != "{") { |
310 | | // declaration after forward declaration |
311 | 0 | return true; |
312 | 0 | } |
313 | 0 | if (tok->next()->str() == "{") |
314 | 0 | return true; |
315 | 0 | if (Token::Match(tok->next(), ")|*")) |
316 | 0 | return true; |
317 | 0 | if (tok->next()->str() == name->str()) |
318 | 0 | return true; |
319 | 0 | if (tok->next()->str() != ";") |
320 | 0 | return true; |
321 | 0 | return false; |
322 | 0 | } else if (tok->previous()->str() == "union") { |
323 | 0 | return tok->next()->str() != ";"; |
324 | 0 | } else if (isCPP() && tok->previous()->str() == "class") { |
325 | 0 | return tok->next()->str() != ";"; |
326 | 0 | } |
327 | 0 | if (tok) |
328 | 0 | tok = tok->previous(); |
329 | 0 | } |
330 | | |
331 | 0 | if ((*tokPtr)->strAt(1) != "(" || !Token::Match((*tokPtr)->linkAt(1), ") .|(|[")) |
332 | 0 | return true; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | 0 | return false; |
339 | 0 | } |
340 | | |
341 | | void Tokenizer::unsupportedTypedef(const Token *tok) const |
342 | 0 | { |
343 | 0 | if (!mSettings->debugwarnings) |
344 | 0 | return; |
345 | | |
346 | 0 | std::ostringstream str; |
347 | 0 | const Token *tok1 = tok; |
348 | 0 | int level = 0; |
349 | 0 | while (tok) { |
350 | 0 | if (level == 0 && tok->str() == ";") |
351 | 0 | break; |
352 | 0 | if (tok->str() == "{") |
353 | 0 | ++level; |
354 | 0 | else if (tok->str() == "}") { |
355 | 0 | if (level == 0) |
356 | 0 | break; |
357 | 0 | --level; |
358 | 0 | } |
359 | | |
360 | 0 | if (tok != tok1) |
361 | 0 | str << " "; |
362 | 0 | str << tok->str(); |
363 | 0 | tok = tok->next(); |
364 | 0 | } |
365 | 0 | if (tok) |
366 | 0 | str << " ;"; |
367 | |
|
368 | 0 | reportError(tok1, Severity::debug, "simplifyTypedef", |
369 | 0 | "Failed to parse \'" + str.str() + "\'. The checking continues anyway."); |
370 | 0 | } |
371 | | |
372 | | Token * Tokenizer::deleteInvalidTypedef(Token *typeDef) |
373 | 0 | { |
374 | 0 | Token *tok = nullptr; |
375 | | |
376 | | // remove typedef but leave ; |
377 | 0 | while (typeDef->next()) { |
378 | 0 | if (typeDef->next()->str() == ";") { |
379 | 0 | typeDef->deleteNext(); |
380 | 0 | break; |
381 | 0 | } |
382 | 0 | if (typeDef->next()->str() == "{") |
383 | 0 | Token::eraseTokens(typeDef, typeDef->linkAt(1)); |
384 | 0 | else if (typeDef->next()->str() == "}") |
385 | 0 | break; |
386 | 0 | typeDef->deleteNext(); |
387 | 0 | } |
388 | |
|
389 | 0 | if (typeDef != list.front()) { |
390 | 0 | tok = typeDef->previous(); |
391 | 0 | tok->deleteNext(); |
392 | 0 | } else { |
393 | 0 | list.front()->deleteThis(); |
394 | 0 | tok = list.front(); |
395 | 0 | } |
396 | |
|
397 | 0 | return tok; |
398 | 0 | } |
399 | | |
400 | | namespace { |
401 | | struct Space { |
402 | | std::string className; |
403 | | const Token* bodyEnd{}; // for body contains typedef define |
404 | | const Token* bodyEnd2{}; // for body contains typedef using |
405 | | bool isNamespace{}; |
406 | | std::set<std::string> recordTypes; |
407 | | }; |
408 | | } |
409 | | |
410 | | static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) |
411 | 0 | { |
412 | 0 | std::string name; |
413 | 0 | bool isConst = false; |
414 | 0 | Token *tok1 = tok->next(); |
415 | | |
416 | | // skip const if present |
417 | 0 | if (tok1->str() == "const") { |
418 | 0 | tok1->deleteThis(); |
419 | 0 | isConst = true; |
420 | 0 | } |
421 | | |
422 | | // skip "class|struct|union|enum" |
423 | 0 | tok1 = tok1->next(); |
424 | |
|
425 | 0 | const bool hasName = Token::Match(tok1, "%name%"); |
426 | | |
427 | | // skip name |
428 | 0 | if (hasName) { |
429 | 0 | name = tok1->str(); |
430 | 0 | tok1 = tok1->next(); |
431 | 0 | } |
432 | | |
433 | | // skip base classes if present |
434 | 0 | if (tok1->str() == ":") { |
435 | 0 | tok1 = tok1->next(); |
436 | 0 | while (tok1 && tok1->str() != "{") |
437 | 0 | tok1 = tok1->next(); |
438 | 0 | if (!tok1) |
439 | 0 | return nullptr; |
440 | 0 | } |
441 | | |
442 | | // skip to end |
443 | 0 | tok1 = tok1->link(); |
444 | |
|
445 | 0 | if (!hasName) { // unnamed |
446 | 0 | if (tok1->next()) { |
447 | | // use typedef name if available |
448 | 0 | if (Token::Match(tok1->next(), "%type%")) |
449 | 0 | name = tok1->next()->str(); |
450 | 0 | else // create a unique name |
451 | 0 | name = "Unnamed" + std::to_string((*unnamedCount)++); |
452 | 0 | tok->next()->insertToken(name); |
453 | 0 | } else |
454 | 0 | return nullptr; |
455 | 0 | } |
456 | | |
457 | 0 | tok1->insertToken(";"); |
458 | 0 | tok1 = tok1->next(); |
459 | |
|
460 | 0 | if (tok1->next() && tok1->next()->str() == ";" && tok1->previous()->str() == "}") { |
461 | 0 | tok->deleteThis(); |
462 | 0 | tok1->deleteThis(); |
463 | 0 | return nullptr; |
464 | 0 | } |
465 | 0 | tok1->insertToken("typedef"); |
466 | 0 | tok1 = tok1->next(); |
467 | 0 | Token * tok3 = tok1; |
468 | 0 | if (isConst) { |
469 | 0 | tok1->insertToken("const"); |
470 | 0 | tok1 = tok1->next(); |
471 | 0 | } |
472 | 0 | tok1->insertToken(tok->next()->str()); // struct, union or enum |
473 | 0 | tok1 = tok1->next(); |
474 | 0 | tok1->insertToken(name); |
475 | 0 | tok->deleteThis(); |
476 | 0 | tok = tok3; |
477 | |
|
478 | 0 | return tok; |
479 | 0 | } |
480 | | |
481 | | /* This function is called when processing function related typedefs. |
482 | | * If simplifyTypedef generates an "Internal Error" message and the |
483 | | * code that generated it deals in some way with functions, then this |
484 | | * function will probably need to be extended to handle a new function |
485 | | * related pattern */ |
486 | | const Token *Tokenizer::processFunc(const Token *tok2, bool inOperator) const |
487 | 0 | { |
488 | 0 | if (tok2->next() && tok2->next()->str() != ")" && |
489 | 0 | tok2->next()->str() != ",") { |
490 | | // skip over tokens for some types of canonicalization |
491 | 0 | if (Token::Match(tok2->next(), "( * %type% ) (")) |
492 | 0 | tok2 = tok2->linkAt(5); |
493 | 0 | else if (Token::Match(tok2->next(), "* ( * %type% ) (")) |
494 | 0 | tok2 = tok2->linkAt(6); |
495 | 0 | else if (Token::Match(tok2->next(), "* ( * %type% ) ;")) |
496 | 0 | tok2 = tok2->tokAt(5); |
497 | 0 | else if (Token::Match(tok2->next(), "* ( %type% [") && |
498 | 0 | Token::Match(tok2->linkAt(4), "] ) ;|=")) |
499 | 0 | tok2 = tok2->linkAt(4)->next(); |
500 | 0 | else if (Token::Match(tok2->next(), "* ( * %type% (")) |
501 | 0 | tok2 = tok2->linkAt(5)->next(); |
502 | 0 | else if (Token::simpleMatch(tok2->next(), "* [") && |
503 | 0 | Token::simpleMatch(tok2->linkAt(2), "] ;")) |
504 | 0 | tok2 = tok2->next(); |
505 | 0 | else { |
506 | 0 | if (tok2->next()->str() == "(") |
507 | 0 | tok2 = tok2->next()->link(); |
508 | 0 | else if (!inOperator && !Token::Match(tok2->next(), "[|>|;")) { |
509 | 0 | tok2 = tok2->next(); |
510 | |
|
511 | 0 | while (Token::Match(tok2, "*|&") && |
512 | 0 | !Token::Match(tok2->next(), ")|>")) |
513 | 0 | tok2 = tok2->next(); |
514 | | |
515 | | // skip over namespace |
516 | 0 | while (Token::Match(tok2, "%name% ::")) |
517 | 0 | tok2 = tok2->tokAt(2); |
518 | |
|
519 | 0 | if (!tok2) |
520 | 0 | return nullptr; |
521 | | |
522 | 0 | if (tok2->str() == "(" && |
523 | 0 | tok2->link()->next() && |
524 | 0 | tok2->link()->next()->str() == "(") { |
525 | 0 | tok2 = tok2->link(); |
526 | |
|
527 | 0 | if (tok2->next()->str() == "(") |
528 | 0 | tok2 = tok2->next()->link(); |
529 | 0 | } |
530 | | |
531 | | // skip over typedef parameter |
532 | 0 | if (tok2->next() && tok2->next()->str() == "(") { |
533 | 0 | tok2 = tok2->next()->link(); |
534 | 0 | if (!tok2->next()) |
535 | 0 | syntaxError(tok2); |
536 | |
|
537 | 0 | if (tok2->next()->str() == "(") |
538 | 0 | tok2 = tok2->next()->link(); |
539 | 0 | } |
540 | 0 | } |
541 | 0 | } |
542 | 0 | } |
543 | 0 | return tok2; |
544 | 0 | } |
545 | | |
546 | | Token *Tokenizer::processFunc(Token *tok2, bool inOperator) |
547 | 0 | { |
548 | 0 | return const_cast<Token*>(processFunc(const_cast<const Token*>(tok2), inOperator)); |
549 | 0 | } |
550 | | |
551 | | void Tokenizer::simplifyUsingToTypedef() |
552 | 1.36k | { |
553 | 1.36k | if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) |
554 | 0 | return; |
555 | | |
556 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
557 | | // using a::b; => typedef a::b b; |
558 | 92.7k | if ((Token::Match(tok, "[;{}] using %name% :: %name% ::|;") && !tok->tokAt(2)->isKeyword()) || |
559 | 92.7k | (Token::Match(tok, "[;{}] using :: %name% :: %name% ::|;") && !tok->tokAt(3)->isKeyword())) { |
560 | 0 | Token *endtok = tok->tokAt(5); |
561 | 0 | if (Token::Match(endtok, "%name%")) |
562 | 0 | endtok = endtok->next(); |
563 | 0 | while (Token::Match(endtok, ":: %name%")) |
564 | 0 | endtok = endtok->tokAt(2); |
565 | 0 | if (endtok && endtok->str() == ";") { |
566 | 0 | tok->next()->str("typedef"); |
567 | 0 | endtok = endtok->previous(); |
568 | 0 | endtok->insertToken(endtok->str()); |
569 | 0 | } |
570 | 0 | } |
571 | 92.7k | } |
572 | 1.36k | } |
573 | | |
574 | | void Tokenizer::simplifyTypedefLHS() |
575 | 1.36k | { |
576 | 1.36k | if (!list.front()) |
577 | 0 | return; |
578 | | |
579 | 92.7k | for (Token* tok = list.front()->next(); tok; tok = tok->next()) { |
580 | 91.3k | if (tok->str() == "typedef") { |
581 | 0 | bool doSimplify = !Token::Match(tok->previous(), ";|{|}|:|public:|private:|protected:"); |
582 | 0 | if (doSimplify && Token::simpleMatch(tok->previous(), ")") && Token::Match(tok->linkAt(-1)->previous(), "if|for|while")) |
583 | 0 | doSimplify = false; |
584 | 0 | bool haveStart = false; |
585 | 0 | Token* start{}; |
586 | 0 | if (!doSimplify && Token::simpleMatch(tok->previous(), "}")) { |
587 | 0 | start = tok->linkAt(-1)->previous(); |
588 | 0 | while (Token::Match(start, "%name%")) { |
589 | 0 | if (Token::Match(start, "class|struct|union|enum")) { |
590 | 0 | start = start->previous(); |
591 | 0 | doSimplify = true; |
592 | 0 | haveStart = true; |
593 | 0 | break; |
594 | 0 | } |
595 | 0 | start = start->previous(); |
596 | 0 | } |
597 | 0 | } |
598 | 0 | if (doSimplify) { |
599 | 0 | if (!haveStart) { |
600 | 0 | start = tok; |
601 | 0 | while (start && !Token::Match(start, "[;{}]")) |
602 | 0 | start = start->previous(); |
603 | 0 | } |
604 | 0 | if (start) |
605 | 0 | start = start->next(); |
606 | 0 | else |
607 | 0 | start = list.front(); |
608 | 0 | start->insertTokenBefore(tok->str()); |
609 | 0 | tok->deleteThis(); |
610 | 0 | } |
611 | 0 | } |
612 | 91.3k | } |
613 | 1.36k | } |
614 | | |
615 | | namespace { |
616 | | class TypedefSimplifier { |
617 | | private: |
618 | | Token* mTypedefToken; // The "typedef" token |
619 | | Token* mEndToken{nullptr}; // Semicolon |
620 | | std::pair<Token*, Token*> mRangeType; |
621 | | std::pair<Token*, Token*> mRangeTypeQualifiers; |
622 | | std::pair<Token*, Token*> mRangeAfterVar; |
623 | | std::string mTypedefName; // Name of typedef type |
624 | | Token* mNameToken{nullptr}; |
625 | | bool mFail = false; |
626 | | bool mReplaceFailed = false; |
627 | | bool mUsed = false; |
628 | | |
629 | | public: |
630 | 0 | TypedefSimplifier(Token* typedefToken, int &num) : mTypedefToken(typedefToken) { |
631 | 0 | Token* start = typedefToken->next(); |
632 | 0 | if (Token::simpleMatch(start, "typename")) |
633 | 0 | start = start->next(); |
634 | | |
635 | | // TODO handle unnamed structs etc |
636 | 0 | if (Token::Match(start, "const| enum|struct|union|class %name% {")) { |
637 | 0 | const std::pair<Token*, Token*> rangeBefore(start, Token::findsimplematch(start, "{")); |
638 | | |
639 | | // find typedef name token |
640 | 0 | Token* nameToken = rangeBefore.second->link()->next(); |
641 | 0 | while (Token::Match(nameToken, "%name%|* %name%|*")) |
642 | 0 | nameToken = nameToken->next(); |
643 | 0 | const std::pair<Token*, Token*> rangeQualifiers(rangeBefore.second->link()->next(), nameToken); |
644 | |
|
645 | 0 | if (Token::Match(nameToken, "%name% ;")) { |
646 | 0 | mRangeType = rangeBefore; |
647 | 0 | mRangeTypeQualifiers = rangeQualifiers; |
648 | 0 | mTypedefName = nameToken->str(); |
649 | 0 | Token* typeName = rangeBefore.second->previous(); |
650 | 0 | if (typeName->isKeyword()) { |
651 | 0 | (void)num; |
652 | | // TODO typeName->insertToken("T:" + std::to_string(num++)); |
653 | 0 | typeName->insertToken(nameToken->str()); |
654 | 0 | } |
655 | 0 | mNameToken = nameToken; |
656 | 0 | mEndToken = nameToken->next(); |
657 | 0 | return; |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | 0 | for (Token* type = start; Token::Match(type, "%name%|*|&"); type = type->next()) { |
662 | 0 | if (type != start && Token::Match(type, "%name% ;") && !type->isStandardType()) { |
663 | 0 | mRangeType.first = start; |
664 | 0 | mRangeType.second = type; |
665 | 0 | mNameToken = type; |
666 | 0 | mEndToken = mNameToken->next(); |
667 | 0 | return; |
668 | 0 | } |
669 | 0 | if (type != start && Token::Match(type, "%name% [")) { |
670 | 0 | Token* end = type->linkAt(1); |
671 | 0 | while (Token::simpleMatch(end, "] [")) |
672 | 0 | end = end->linkAt(1); |
673 | 0 | if (!Token::simpleMatch(end, "] ;")) |
674 | 0 | break; |
675 | 0 | mRangeType.first = start; |
676 | 0 | mRangeType.second = type; |
677 | 0 | mNameToken = type; |
678 | 0 | mEndToken = end->next(); |
679 | 0 | mRangeAfterVar.first = mNameToken->next(); |
680 | 0 | mRangeAfterVar.second = mEndToken; |
681 | 0 | return; |
682 | 0 | } |
683 | 0 | if (Token::Match(type->next(), "( * const| %name% ) (") && Token::simpleMatch(type->linkAt(1)->linkAt(1), ") ;")) { |
684 | 0 | mNameToken = type->linkAt(1)->previous(); |
685 | 0 | mEndToken = type->linkAt(1)->linkAt(1)->next(); |
686 | 0 | mRangeType.first = start; |
687 | 0 | mRangeType.second = mNameToken; |
688 | 0 | mRangeAfterVar.first = mNameToken->next(); |
689 | 0 | mRangeAfterVar.second = mEndToken; |
690 | 0 | return; |
691 | 0 | } |
692 | 0 | if (Token::Match(type, "%name% ( !!(") && Token::simpleMatch(type->linkAt(1), ") ;") && !type->isStandardType()) { |
693 | 0 | mNameToken = type; |
694 | 0 | mEndToken = type->linkAt(1)->next(); |
695 | 0 | mRangeType.first = start; |
696 | 0 | mRangeType.second = type; |
697 | 0 | mRangeAfterVar.first = mNameToken->next(); |
698 | 0 | mRangeAfterVar.second = mEndToken; |
699 | 0 | return; |
700 | 0 | } |
701 | 0 | } |
702 | | // TODO: handle all typedefs |
703 | 0 | if ((false)) |
704 | 0 | printTypedef(typedefToken); |
705 | 0 | mFail = true; |
706 | 0 | } |
707 | | |
708 | 0 | const Token* getTypedefToken() const { |
709 | 0 | return mTypedefToken; |
710 | 0 | } |
711 | | |
712 | 0 | bool isUsed() const { |
713 | 0 | return mUsed; |
714 | 0 | } |
715 | | |
716 | 0 | bool isInvalidConstFunctionType(const std::map<std::string, TypedefSimplifier>& m) const { |
717 | 0 | if (!Token::Match(mTypedefToken, "typedef const %name% %name% ;")) |
718 | 0 | return false; |
719 | 0 | const auto it = m.find(mTypedefToken->strAt(2)); |
720 | 0 | if (it == m.end()) |
721 | 0 | return false; |
722 | 0 | return Token::Match(it->second.mNameToken, "%name% ("); |
723 | 0 | } |
724 | | |
725 | 0 | bool fail() const { |
726 | 0 | return mFail; |
727 | 0 | } |
728 | | |
729 | 0 | bool replaceFailed() const { |
730 | 0 | return mReplaceFailed; |
731 | 0 | } |
732 | | |
733 | 0 | bool isStructEtc() const { |
734 | 0 | return mRangeType.second && mRangeType.second->str() == "{"; |
735 | 0 | } |
736 | | |
737 | 0 | std::string name() const { |
738 | 0 | return mNameToken ? mNameToken->str() : ""; |
739 | 0 | } |
740 | | |
741 | 0 | void replace(Token* tok) { |
742 | 0 | if (tok == mNameToken) |
743 | 0 | return; |
744 | | |
745 | 0 | mUsed = true; |
746 | | |
747 | | // Special handling for T() when T is a pointer |
748 | 0 | if (Token::Match(tok, "%name% ( )")) { |
749 | 0 | bool pointerType = false; |
750 | 0 | for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { |
751 | 0 | if (type->str() == "*" || type->str() == "&") { |
752 | 0 | pointerType = true; |
753 | 0 | break; |
754 | 0 | } |
755 | 0 | } |
756 | 0 | for (const Token* type = mRangeTypeQualifiers.first; type != mRangeTypeQualifiers.second; type = type->next()) { |
757 | 0 | if (type->str() == "*" || type->str() == "&") { |
758 | 0 | pointerType = true; |
759 | 0 | break; |
760 | 0 | } |
761 | 0 | } |
762 | 0 | if (pointerType) { |
763 | 0 | tok->deleteThis(); |
764 | 0 | tok->next()->insertToken("0"); |
765 | 0 | Token* tok2 = insertTokens(tok, mRangeType); |
766 | 0 | insertTokens(tok2, mRangeTypeQualifiers); |
767 | 0 | return; |
768 | 0 | } |
769 | 0 | } |
770 | | |
771 | | // Special handling of function pointer cast |
772 | 0 | const bool isFunctionPointer = Token::Match(mNameToken, "%name% )"); |
773 | 0 | if (isFunctionPointer && isCast(tok->previous())) { |
774 | 0 | tok->insertToken("*"); |
775 | 0 | insertTokens(tok, std::pair<Token*, Token*>(mRangeType.first, mNameToken->linkAt(1))); |
776 | 0 | tok->deleteThis(); |
777 | 0 | return; |
778 | 0 | } |
779 | | |
780 | | // Inherited type => skip "struct" / "class" |
781 | 0 | if (Token::Match(mRangeType.first, "const| struct|class %name% {") && Token::Match(tok->previous(), "public|protected|private")) { |
782 | 0 | tok->originalName(tok->str()); |
783 | 0 | tok->str(mRangeType.second->previous()->str()); |
784 | 0 | return; |
785 | 0 | } |
786 | | |
787 | 0 | if (Token::Match(tok, "%name% ::")) { |
788 | 0 | if (Token::Match(mRangeType.first, "const| struct|class %name% %name% ;")) { |
789 | 0 | tok->originalName(tok->str()); |
790 | 0 | tok->str(mRangeType.second->previous()->str()); |
791 | 0 | } else { |
792 | 0 | mReplaceFailed = true; |
793 | 0 | } |
794 | 0 | return; |
795 | 0 | } |
796 | | |
797 | | // pointer => move "const" |
798 | 0 | if (Token::simpleMatch(tok->previous(), "const")) { |
799 | 0 | bool pointerType = false; |
800 | 0 | for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { |
801 | 0 | if (type->str() == "*") { |
802 | 0 | pointerType = true; |
803 | 0 | break; |
804 | 0 | } |
805 | 0 | } |
806 | 0 | if (pointerType) { |
807 | 0 | tok->insertToken("const"); |
808 | 0 | tok->next()->column(tok->column()); |
809 | 0 | tok->next()->isExpandedMacro(tok->previous()->isExpandedMacro()); |
810 | 0 | tok->deletePrevious(); |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | | // Do not duplicate class/struct/enum/union |
815 | 0 | if (Token::Match(tok->previous(), "enum|union|struct|class")) { |
816 | 0 | bool found = false; |
817 | 0 | const std::string &kw = tok->previous()->str(); |
818 | 0 | for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { |
819 | 0 | if (type->str() == kw) { |
820 | 0 | found = true; |
821 | 0 | break; |
822 | 0 | } |
823 | 0 | } |
824 | 0 | if (found) |
825 | 0 | tok->deletePrevious(); |
826 | 0 | else { |
827 | 0 | mReplaceFailed = true; |
828 | 0 | return; |
829 | 0 | } |
830 | 0 | } |
831 | | |
832 | 0 | Token* const tok2 = insertTokens(tok, mRangeType); |
833 | 0 | Token* const tok3 = insertTokens(tok2, mRangeTypeQualifiers); |
834 | |
|
835 | 0 | Token *after = tok3; |
836 | 0 | while (Token::Match(after, "%name%|*|&|&&|::")) |
837 | 0 | after = after->next(); |
838 | 0 | if (Token::Match(mNameToken, "%name% (") && Token::simpleMatch(tok3->next(), "*")) { |
839 | 0 | while (Token::Match(after, "(|[")) |
840 | 0 | after = after->link()->next(); |
841 | 0 | if (after) { |
842 | 0 | tok3->insertToken("("); |
843 | 0 | after->previous()->insertToken(")"); |
844 | 0 | Token::createMutualLinks(tok3->next(), after->previous()); |
845 | 0 | } |
846 | 0 | } |
847 | |
|
848 | 0 | bool useAfterVarRange = true; |
849 | 0 | if (Token::simpleMatch(mRangeAfterVar.first, "[")) { |
850 | 0 | if (Token::Match(after->previous(), "%name% ( !!*")) { |
851 | 0 | useAfterVarRange = false; |
852 | | // Function return type => replace array with "*" |
853 | 0 | for (const Token* a = mRangeAfterVar.first; Token::simpleMatch(a, "["); a = a->link()->next()) |
854 | 0 | tok3->insertToken("*"); |
855 | 0 | } else if (Token::Match(after->previous(), "%name% ( * %name% ) [")) { |
856 | 0 | after = after->linkAt(4)->next(); |
857 | 0 | } else { |
858 | 0 | Token* prev = after->previous(); |
859 | 0 | if (prev->isName() && prev != tok3) |
860 | 0 | prev = prev->previous(); |
861 | 0 | if (Token::Match(prev, "*|&|&&") && prev != tok3) { |
862 | 0 | while (Token::Match(prev, "*|&|&&") && prev != tok3) |
863 | 0 | prev = prev->previous(); |
864 | 0 | prev->insertToken("("); |
865 | 0 | after->previous()->insertToken(")"); |
866 | 0 | } |
867 | 0 | } |
868 | 0 | } |
869 | |
|
870 | 0 | if (isFunctionPointer) { |
871 | 0 | if (Token::Match(after, "( * %name% ) (")) |
872 | 0 | after = after->link()->linkAt(1)->next(); |
873 | 0 | else if (after->str() == "(") { |
874 | 0 | useAfterVarRange = false; |
875 | 0 | if (Token::simpleMatch(tok3->previous(), "( *")) |
876 | 0 | tok3->deletePrevious(); |
877 | 0 | } |
878 | 0 | else if (after->str() == "[") { |
879 | 0 | while (after && after->str() == "[") |
880 | 0 | after = after->link()->next(); |
881 | 0 | } |
882 | 0 | } |
883 | 0 | else { |
884 | 0 | while (Token::simpleMatch(after, "[")) |
885 | 0 | after = after->link()->next(); |
886 | 0 | } |
887 | |
|
888 | 0 | if (!after) |
889 | 0 | throw InternalError(tok, "Failed to simplify typedef. Is the code valid?"); |
890 | | |
891 | 0 | const Token* const tok4 = useAfterVarRange ? insertTokens(after->previous(), mRangeAfterVar)->next() : tok3->next(); |
892 | |
|
893 | 0 | tok->deleteThis(); |
894 | | |
895 | | // Set links |
896 | 0 | std::stack<Token*> brackets; |
897 | 0 | for (; tok != tok4; tok = tok->next()) { |
898 | 0 | if (Token::Match(tok, "[{([]")) |
899 | 0 | brackets.push(tok); |
900 | 0 | else if (Token::Match(tok, "[})]]")) { |
901 | 0 | Token::createMutualLinks(brackets.top(), tok); |
902 | 0 | brackets.pop(); |
903 | 0 | } |
904 | 0 | } |
905 | 0 | } |
906 | | |
907 | 0 | void removeDeclaration() { |
908 | 0 | if (Token::simpleMatch(mRangeType.second, "{")) { |
909 | 0 | while (Token::Match(mTypedefToken, "typedef|const")) |
910 | 0 | mTypedefToken->deleteThis(); |
911 | 0 | Token::eraseTokens(mRangeType.second->link(), mEndToken); |
912 | 0 | } else { |
913 | 0 | Token::eraseTokens(mTypedefToken, mEndToken); |
914 | 0 | mTypedefToken->deleteThis(); |
915 | 0 | } |
916 | 0 | } |
917 | | |
918 | 0 | bool canReplace(const Token* tok) { |
919 | 0 | if (mNameToken == tok) |
920 | 0 | return false; |
921 | 0 | if (!Token::Match(tok->previous(), "%name%|;|{|}|(|,|<") && !Token::Match(tok->previous(), "!!. %name% (")) |
922 | 0 | return false; |
923 | 0 | if (!Token::Match(tok, "%name% %name%|*|&|&&|;|(|)|,|::")) { |
924 | 0 | if (Token::Match(tok->previous(), "( %name% =") && Token::Match(tok->linkAt(-1), ") %name%|{") && !tok->tokAt(-2)->isKeyword()) |
925 | 0 | return true; |
926 | 0 | if (Token::Match(tok->previous(), ", %name% =")) |
927 | 0 | return true; |
928 | 0 | if (Token::Match(tok->previous(), "new %name% [")) |
929 | 0 | return true; |
930 | 0 | if (Token::Match(tok->previous(), "< %name% >")) |
931 | 0 | return true; |
932 | 0 | if (Token::Match(tok->previous(), "public|protected|private")) |
933 | 0 | return true; |
934 | 0 | if (Token::Match(tok->previous(), ", %name% :")) { |
935 | 0 | bool isGeneric = false; |
936 | 0 | for (; tok; tok = tok->previous()) { |
937 | 0 | if (Token::Match(tok, ")|]")) |
938 | 0 | tok = tok->link(); |
939 | 0 | else if (Token::Match(tok, "[;{}(]")) { |
940 | 0 | isGeneric = Token::simpleMatch(tok->previous(), "_Generic ("); |
941 | 0 | break; |
942 | 0 | } |
943 | 0 | } |
944 | 0 | return isGeneric; |
945 | 0 | } |
946 | 0 | return false; |
947 | 0 | } |
948 | 0 | if (Token::Match(tok->previous(), "%name%") && !tok->previous()->isKeyword()) |
949 | 0 | return false; |
950 | 0 | if (Token::simpleMatch(tok->next(), "(") && Token::Match(tok->linkAt(1), ") %name%|{")) |
951 | 0 | return false; |
952 | 0 | if (Token::Match(tok->previous(), "struct|union|class|enum %name% %name%") && |
953 | 0 | Token::simpleMatch(mRangeType.second, "{") && |
954 | 0 | tok->str() != mRangeType.second->previous()->str()) |
955 | 0 | return true; |
956 | 0 | if (Token::Match(tok->previous(), "; %name% ;")) |
957 | 0 | return false; |
958 | 0 | if (Token::Match(tok->previous(), "<|, %name% * ,|>")) |
959 | 0 | return true; |
960 | 0 | for (const Token* after = tok->next(); after; after = after->next()) { |
961 | 0 | if (Token::Match(after, "%name%|::|&|*|&&")) |
962 | 0 | continue; |
963 | 0 | if (after->str() == "<" && after->link()) |
964 | 0 | break; |
965 | 0 | if (after->isNumber()) |
966 | 0 | return false; |
967 | 0 | if (after->isComparisonOp() || after->isArithmeticalOp()) |
968 | 0 | return false; |
969 | 0 | break; |
970 | 0 | } |
971 | 0 | for (const Token* before = tok->previous(); before; before = before->previous()) { |
972 | 0 | if (Token::Match(before, "[+-*/&|~!]")) |
973 | 0 | return false; |
974 | 0 | if (Token::Match(before, "struct|union|class|enum") || before->isStandardType()) |
975 | 0 | return false; |
976 | 0 | if (before->str() == "::") |
977 | 0 | return false; |
978 | 0 | if (before->isName()) |
979 | 0 | continue; |
980 | 0 | break; |
981 | 0 | } |
982 | 0 | return true; |
983 | 0 | } |
984 | | |
985 | 0 | Token* endToken() const { |
986 | 0 | return mEndToken; |
987 | 0 | } |
988 | | |
989 | | private: |
990 | 0 | static bool isCast(const Token* tok) { |
991 | 0 | if (Token::Match(tok, "( %name% ) (|%name%")) |
992 | 0 | return !tok->tokAt(2)->isKeyword(); |
993 | 0 | if (Token::Match(tok, "< %name% > (") && tok->previous() && endsWith(tok->previous()->str(), "_cast", 5)) |
994 | 0 | return true; |
995 | 0 | return false; |
996 | 0 | } |
997 | | |
998 | 0 | static Token* insertTokens(Token* to, std::pair<Token*,Token*> range) { |
999 | 0 | for (const Token* from = range.first; from != range.second; from = from->next()) { |
1000 | 0 | to->insertToken(from->str()); |
1001 | 0 | to->next()->column(to->column()); |
1002 | 0 | to = to->next(); |
1003 | 0 | to->isSimplifiedTypedef(true); |
1004 | 0 | to->isExternC(from->isExternC()); |
1005 | 0 | } |
1006 | 0 | return to; |
1007 | 0 | } |
1008 | | |
1009 | 0 | static void printTypedef(const Token *tok) { |
1010 | 0 | int indent = 0; |
1011 | 0 | while (tok && (indent > 0 || tok->str() != ";")) { |
1012 | 0 | if (tok->str() == "{") |
1013 | 0 | ++indent; |
1014 | 0 | else if (tok->str() == "}") |
1015 | 0 | --indent; |
1016 | 0 | std::cout << " " << tok->str(); |
1017 | 0 | tok = tok->next(); |
1018 | 0 | } |
1019 | 0 | std::cout << "\n"; |
1020 | 0 | } |
1021 | | }; |
1022 | | } |
1023 | | |
1024 | | void Tokenizer::simplifyTypedef() |
1025 | 1.36k | { |
1026 | | // Simplify global typedefs that are not redefined with the fast 1-pass simplification. |
1027 | | // Then use the slower old typedef simplification. |
1028 | 1.36k | std::map<std::string, int> numberOfTypedefs; |
1029 | 94.1k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
1030 | 92.7k | if (tok->str() == "typedef") { |
1031 | 0 | int dummy = 0; |
1032 | 0 | TypedefSimplifier ts(tok, dummy); |
1033 | 0 | if (!ts.fail()) |
1034 | 0 | numberOfTypedefs[ts.name()]++; |
1035 | 0 | continue; |
1036 | 0 | } |
1037 | 92.7k | } |
1038 | | |
1039 | 1.36k | int indentlevel = 0; |
1040 | 1.36k | int typeNum = 1; |
1041 | 1.36k | std::map<std::string, TypedefSimplifier> typedefs; |
1042 | 94.1k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
1043 | 92.7k | if (!tok->isName()) { |
1044 | 56.9k | if (tok->str()[0] == '{') |
1045 | 3.58k | ++indentlevel; |
1046 | 53.3k | else if (tok->str()[0] == '}') |
1047 | 3.58k | --indentlevel; |
1048 | 56.9k | continue; |
1049 | 56.9k | } |
1050 | | |
1051 | 35.7k | if (indentlevel == 0 && tok->str() == "typedef") { |
1052 | 0 | TypedefSimplifier ts(tok, typeNum); |
1053 | 0 | if (!ts.fail() && numberOfTypedefs[ts.name()] == 1) { |
1054 | 0 | if (mSettings->severity.isEnabled(Severity::portability) && ts.isInvalidConstFunctionType(typedefs)) |
1055 | 0 | reportError(tok->next(), Severity::portability, "invalidConstFunctionType", |
1056 | 0 | "It is unspecified behavior to const qualify a function type."); |
1057 | 0 | typedefs.emplace(ts.name(), ts); |
1058 | 0 | if (!ts.isStructEtc()) |
1059 | 0 | tok = ts.endToken(); |
1060 | 0 | } |
1061 | 0 | continue; |
1062 | 0 | } |
1063 | | |
1064 | 35.7k | auto it = typedefs.find(tok->str()); |
1065 | 35.7k | if (it != typedefs.end() && it->second.canReplace(tok)) { |
1066 | 0 | std::set<std::string> r; |
1067 | 0 | while (it != typedefs.end() && r.insert(tok->str()).second) { |
1068 | 0 | it->second.replace(tok); |
1069 | 0 | it = typedefs.find(tok->str()); |
1070 | 0 | } |
1071 | 35.7k | } else if (tok->str() == "enum") { |
1072 | 0 | while (Token::Match(tok, "%name%|:|::")) |
1073 | 0 | tok = tok->next(); |
1074 | 0 | if (!tok) |
1075 | 0 | break; |
1076 | 0 | if (tok->str() == "{") |
1077 | 0 | tok = tok->link(); |
1078 | 0 | } |
1079 | 35.7k | } |
1080 | | |
1081 | 1.36k | if (!typedefs.empty()) |
1082 | 0 | { |
1083 | | // remove typedefs |
1084 | 0 | for (auto &t: typedefs) { |
1085 | 0 | if (!t.second.replaceFailed()) { |
1086 | 0 | const Token* const typedefToken = t.second.getTypedefToken(); |
1087 | 0 | TypedefInfo typedefInfo; |
1088 | 0 | typedefInfo.name = t.second.name(); |
1089 | 0 | typedefInfo.filename = list.file(typedefToken); |
1090 | 0 | typedefInfo.lineNumber = typedefToken->linenr(); |
1091 | 0 | typedefInfo.column = typedefToken->column(); |
1092 | 0 | typedefInfo.used = t.second.isUsed(); |
1093 | 0 | mTypedefInfo.push_back(std::move(typedefInfo)); |
1094 | |
|
1095 | 0 | t.second.removeDeclaration(); |
1096 | 0 | } |
1097 | 0 | } |
1098 | |
|
1099 | 0 | while (Token::Match(list.front(), "; %any%")) |
1100 | 0 | list.front()->deleteThis(); |
1101 | 0 | } |
1102 | | |
1103 | 1.36k | simplifyTypedefCpp(); |
1104 | 1.36k | } |
1105 | | |
1106 | | void Tokenizer::simplifyTypedefCpp() |
1107 | 1.36k | { |
1108 | 1.36k | std::vector<Space> spaceInfo; |
1109 | 1.36k | bool isNamespace = false; |
1110 | 1.36k | std::string className; |
1111 | 1.36k | std::string fullClassName; |
1112 | 1.36k | bool hasClass = false; |
1113 | 1.36k | bool goback = false; |
1114 | | |
1115 | | // add global namespace |
1116 | 1.36k | spaceInfo.emplace_back(/*Space{}*/); |
1117 | | |
1118 | | // Convert "using a::b;" to corresponding typedef statements |
1119 | 1.36k | simplifyUsingToTypedef(); |
1120 | | |
1121 | 1.36k | const std::time_t maxTime = mSettings->typedefMaxTime > 0 ? std::time(nullptr) + mSettings->typedefMaxTime: 0; |
1122 | | |
1123 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
1124 | 92.7k | if (mErrorLogger && !list.getFiles().empty()) |
1125 | 92.7k | mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (typedef)", tok->progressValue()); |
1126 | | |
1127 | 92.7k | if (Settings::terminated()) |
1128 | 0 | return; |
1129 | | |
1130 | 92.7k | if (maxTime > 0 && std::time(nullptr) > maxTime) { |
1131 | 0 | if (mSettings->debugwarnings) { |
1132 | 0 | ErrorMessage::FileLocation loc; |
1133 | 0 | loc.setfile(list.getFiles()[0]); |
1134 | 0 | ErrorMessage errmsg({std::move(loc)}, |
1135 | 0 | emptyString, |
1136 | 0 | Severity::debug, |
1137 | 0 | "Typedef simplification instantiation maximum time exceeded", |
1138 | 0 | "typedefMaxTime", |
1139 | 0 | Certainty::normal); |
1140 | 0 | mErrorLogger->reportErr(errmsg); |
1141 | 0 | } |
1142 | 0 | return; |
1143 | 0 | } |
1144 | | |
1145 | 92.7k | if (goback) { |
1146 | | //jump back once, see the comment at the end of the function |
1147 | 0 | goback = false; |
1148 | 0 | tok = tok->previous(); |
1149 | 0 | } |
1150 | | |
1151 | 92.7k | if (tok->str() != "typedef") { |
1152 | 92.7k | if (Token::simpleMatch(tok, "( typedef")) { |
1153 | | // Skip typedefs inside parentheses (#2453 and #4002) |
1154 | 0 | tok = tok->next(); |
1155 | 92.7k | } else if (Token::Match(tok, "class|struct|namespace %any%") && |
1156 | 92.7k | (!tok->previous() || tok->previous()->str() != "enum")) { |
1157 | 0 | isNamespace = (tok->str() == "namespace"); |
1158 | 0 | hasClass = true; |
1159 | 0 | className = tok->next()->str(); |
1160 | 0 | const Token *tok1 = tok->next(); |
1161 | 0 | fullClassName = className; |
1162 | 0 | while (Token::Match(tok1, "%name% :: %name%")) { |
1163 | 0 | tok1 = tok1->tokAt(2); |
1164 | 0 | fullClassName += " :: " + tok1->str(); |
1165 | 0 | } |
1166 | 92.7k | } else if (hasClass && tok->str() == ";") { |
1167 | 0 | hasClass = false; |
1168 | 92.7k | } else if (hasClass && tok->str() == "{") { |
1169 | 0 | if (!isNamespace) |
1170 | 0 | spaceInfo.back().recordTypes.insert(fullClassName); |
1171 | |
|
1172 | 0 | Space info; |
1173 | 0 | info.isNamespace = isNamespace; |
1174 | 0 | info.className = className; |
1175 | 0 | info.bodyEnd = tok->link(); |
1176 | 0 | info.bodyEnd2 = tok->link(); |
1177 | 0 | spaceInfo.push_back(std::move(info)); |
1178 | |
|
1179 | 0 | hasClass = false; |
1180 | 92.7k | } else if (spaceInfo.size() > 1 && tok->str() == "}" && spaceInfo.back().bodyEnd == tok) { |
1181 | 0 | spaceInfo.pop_back(); |
1182 | 0 | } |
1183 | 92.7k | continue; |
1184 | 92.7k | } |
1185 | | |
1186 | | // pull struct, union, enum or class definition out of typedef |
1187 | | // use typedef name for unnamed struct, union, enum or class |
1188 | 0 | if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {|:")) { |
1189 | 0 | Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); |
1190 | 0 | if (!tok1) |
1191 | 0 | continue; |
1192 | 0 | tok = tok1; |
1193 | 0 | } |
1194 | | |
1195 | | /** @todo add support for union */ |
1196 | 0 | if (Token::Match(tok->next(), "enum %type% %type% ;") && tok->strAt(2) == tok->strAt(3)) { |
1197 | 0 | tok->deleteNext(3); |
1198 | 0 | tok->deleteThis(); |
1199 | 0 | if (tok->next()) |
1200 | 0 | tok->deleteThis(); |
1201 | | //now the next token to process is 'tok', not 'tok->next()'; |
1202 | 0 | goback = true; |
1203 | 0 | continue; |
1204 | 0 | } |
1205 | | |
1206 | 0 | Token *typeName; |
1207 | 0 | Token *typeStart = nullptr; |
1208 | 0 | Token *typeEnd = nullptr; |
1209 | 0 | Token *argStart = nullptr; |
1210 | 0 | Token *argEnd = nullptr; |
1211 | 0 | Token *arrayStart = nullptr; |
1212 | 0 | Token *arrayEnd = nullptr; |
1213 | 0 | Token *specStart = nullptr; |
1214 | 0 | Token *specEnd = nullptr; |
1215 | 0 | Token *typeDef = tok; |
1216 | 0 | Token *argFuncRetStart = nullptr; |
1217 | 0 | Token *argFuncRetEnd = nullptr; |
1218 | 0 | Token *funcStart = nullptr; |
1219 | 0 | Token *funcEnd = nullptr; |
1220 | 0 | Token *tokOffset = tok->next(); |
1221 | 0 | bool function = false; |
1222 | 0 | bool functionPtr = false; |
1223 | 0 | bool functionRetFuncPtr = false; |
1224 | 0 | bool functionPtrRetFuncPtr = false; |
1225 | 0 | bool ptrToArray = false; |
1226 | 0 | bool refToArray = false; |
1227 | 0 | bool ptrMember = false; |
1228 | 0 | bool typeOf = false; |
1229 | 0 | Token *namespaceStart = nullptr; |
1230 | 0 | Token *namespaceEnd = nullptr; |
1231 | | |
1232 | | // check for invalid input |
1233 | 0 | if (!tokOffset) |
1234 | 0 | syntaxError(tok); |
1235 | | |
1236 | |
|
1237 | 0 | if (tokOffset->str() == "::") { |
1238 | 0 | typeStart = tokOffset; |
1239 | 0 | tokOffset = tokOffset->next(); |
1240 | |
|
1241 | 0 | while (Token::Match(tokOffset, "%type% ::")) |
1242 | 0 | tokOffset = tokOffset->tokAt(2); |
1243 | |
|
1244 | 0 | typeEnd = tokOffset; |
1245 | |
|
1246 | 0 | if (Token::Match(tokOffset, "%type%")) |
1247 | 0 | tokOffset = tokOffset->next(); |
1248 | 0 | } else if (Token::Match(tokOffset, "%type% ::")) { |
1249 | 0 | typeStart = tokOffset; |
1250 | |
|
1251 | 0 | do { |
1252 | 0 | tokOffset = tokOffset->tokAt(2); |
1253 | 0 | } while (Token::Match(tokOffset, "%type% ::")); |
1254 | |
|
1255 | 0 | typeEnd = tokOffset; |
1256 | |
|
1257 | 0 | if (Token::Match(tokOffset, "%type%")) |
1258 | 0 | tokOffset = tokOffset->next(); |
1259 | 0 | } else if (Token::Match(tokOffset, "%type%")) { |
1260 | 0 | typeStart = tokOffset; |
1261 | |
|
1262 | 0 | while (Token::Match(tokOffset, "const|struct|enum %type%") || |
1263 | 0 | (tokOffset->next() && tokOffset->next()->isStandardType() && !Token::Match(tokOffset->next(), "%name% ;"))) |
1264 | 0 | tokOffset = tokOffset->next(); |
1265 | |
|
1266 | 0 | typeEnd = tokOffset; |
1267 | 0 | if (!Token::Match(tokOffset->next(), "%name% ;")) |
1268 | 0 | tokOffset = tokOffset->next(); |
1269 | |
|
1270 | 0 | while (Token::Match(tokOffset, "%type%") && |
1271 | 0 | (tokOffset->isStandardType() || Token::Match(tokOffset, "unsigned|signed")) && |
1272 | 0 | !Token::Match(tokOffset->next(), "%name% ;")) { |
1273 | 0 | typeEnd = tokOffset; |
1274 | 0 | tokOffset = tokOffset->next(); |
1275 | 0 | } |
1276 | |
|
1277 | 0 | bool atEnd = false; |
1278 | 0 | while (!atEnd) { |
1279 | 0 | if (tokOffset && tokOffset->str() == "::") { |
1280 | 0 | typeEnd = tokOffset; |
1281 | 0 | tokOffset = tokOffset->next(); |
1282 | 0 | } |
1283 | |
|
1284 | 0 | if (Token::Match(tokOffset, "%type%") && |
1285 | 0 | tokOffset->next() && !Token::Match(tokOffset->next(), "[|;|,|(")) { |
1286 | 0 | typeEnd = tokOffset; |
1287 | 0 | tokOffset = tokOffset->next(); |
1288 | 0 | } else if (Token::simpleMatch(tokOffset, "const (")) { |
1289 | 0 | typeEnd = tokOffset; |
1290 | 0 | tokOffset = tokOffset->next(); |
1291 | 0 | atEnd = true; |
1292 | 0 | } else |
1293 | 0 | atEnd = true; |
1294 | 0 | } |
1295 | 0 | } else |
1296 | 0 | continue; // invalid input |
1297 | | |
1298 | | // check for invalid input |
1299 | 0 | if (!tokOffset) |
1300 | 0 | syntaxError(tok); |
1301 | | |
1302 | | // check for template |
1303 | 0 | if (!isC() && tokOffset->str() == "<") { |
1304 | 0 | typeEnd = tokOffset->findClosingBracket(); |
1305 | |
|
1306 | 0 | while (typeEnd && Token::Match(typeEnd->next(), ":: %type%")) |
1307 | 0 | typeEnd = typeEnd->tokAt(2); |
1308 | |
|
1309 | 0 | if (!typeEnd) { |
1310 | | // internal error |
1311 | 0 | return; |
1312 | 0 | } |
1313 | | |
1314 | 0 | while (Token::Match(typeEnd->next(), "const|volatile")) |
1315 | 0 | typeEnd = typeEnd->next(); |
1316 | |
|
1317 | 0 | tok = typeEnd; |
1318 | 0 | tokOffset = tok->next(); |
1319 | 0 | } |
1320 | | |
1321 | 0 | std::list<std::string> pointers; |
1322 | | // check for pointers and references |
1323 | 0 | while (Token::Match(tokOffset, "*|&|&&|const")) { |
1324 | 0 | pointers.push_back(tokOffset->str()); |
1325 | 0 | tokOffset = tokOffset->next(); |
1326 | 0 | } |
1327 | | |
1328 | | // check for invalid input |
1329 | 0 | if (!tokOffset) |
1330 | 0 | syntaxError(tok); |
1331 | |
|
1332 | 0 | if (tokOffset->isName() && !tokOffset->isKeyword()) { |
1333 | | // found the type name |
1334 | 0 | typeName = tokOffset; |
1335 | 0 | tokOffset = tokOffset->next(); |
1336 | | |
1337 | | // check for array |
1338 | 0 | while (tokOffset && tokOffset->str() == "[") { |
1339 | 0 | if (!arrayStart) |
1340 | 0 | arrayStart = tokOffset; |
1341 | 0 | arrayEnd = tokOffset->link(); |
1342 | 0 | tokOffset = arrayEnd->next(); |
1343 | 0 | } |
1344 | | |
1345 | | // check for end or another |
1346 | 0 | if (Token::Match(tokOffset, ";|,")) |
1347 | 0 | tok = tokOffset; |
1348 | | |
1349 | | // or a function typedef |
1350 | 0 | else if (tokOffset && tokOffset->str() == "(") { |
1351 | 0 | Token *tokOffset2 = nullptr; |
1352 | 0 | if (Token::Match(tokOffset, "( *|%name%")) { |
1353 | 0 | tokOffset2 = tokOffset->next(); |
1354 | 0 | if (tokOffset2->str() == "typename") |
1355 | 0 | tokOffset2 = tokOffset2->next(); |
1356 | 0 | while (Token::Match(tokOffset2, "%type% ::")) |
1357 | 0 | tokOffset2 = tokOffset2->tokAt(2); |
1358 | 0 | } |
1359 | | |
1360 | | // unhandled typedef, skip it and continue |
1361 | 0 | if (typeName->str() == "void") { |
1362 | 0 | unsupportedTypedef(typeDef); |
1363 | 0 | tok = deleteInvalidTypedef(typeDef); |
1364 | 0 | if (tok == list.front()) |
1365 | | //now the next token to process is 'tok', not 'tok->next()'; |
1366 | 0 | goback = true; |
1367 | 0 | continue; |
1368 | 0 | } |
1369 | | |
1370 | | // function pointer |
1371 | 0 | if (Token::Match(tokOffset2, "* %name% ) (")) { |
1372 | | // name token wasn't a name, it was part of the type |
1373 | 0 | typeEnd = typeEnd->next(); |
1374 | 0 | functionPtr = true; |
1375 | 0 | funcStart = funcEnd = tokOffset2; // * |
1376 | 0 | tokOffset = tokOffset2->tokAt(3); // ( |
1377 | 0 | typeName = tokOffset->tokAt(-2); |
1378 | 0 | argStart = tokOffset; |
1379 | 0 | argEnd = tokOffset->link(); |
1380 | 0 | tok = argEnd->next(); |
1381 | 0 | } |
1382 | | |
1383 | | // function |
1384 | 0 | else if (isFunctionHead(tokOffset->link(), ";,")) { |
1385 | 0 | function = true; |
1386 | 0 | if (tokOffset->link()->next()->str() == "const") { |
1387 | 0 | specStart = tokOffset->link()->next(); |
1388 | 0 | specEnd = specStart; |
1389 | 0 | } |
1390 | 0 | argStart = tokOffset; |
1391 | 0 | argEnd = tokOffset->link(); |
1392 | 0 | tok = argEnd->next(); |
1393 | 0 | if (specStart) |
1394 | 0 | tok = tok->next(); |
1395 | 0 | } |
1396 | | |
1397 | | // syntax error |
1398 | 0 | else |
1399 | 0 | syntaxError(tok); |
1400 | 0 | } |
1401 | | |
1402 | | // unhandled typedef, skip it and continue |
1403 | 0 | else { |
1404 | 0 | unsupportedTypedef(typeDef); |
1405 | 0 | tok = deleteInvalidTypedef(typeDef); |
1406 | 0 | if (tok == list.front()) |
1407 | | //now the next token to process is 'tok', not 'tok->next()'; |
1408 | 0 | goback = true; |
1409 | 0 | continue; |
1410 | 0 | } |
1411 | 0 | } |
1412 | | |
1413 | | // typeof: typedef typeof ( ... ) type; |
1414 | 0 | else if (Token::simpleMatch(tokOffset->previous(), "typeof (") && |
1415 | 0 | Token::Match(tokOffset->link(), ") %type% ;")) { |
1416 | 0 | argStart = tokOffset; |
1417 | 0 | argEnd = tokOffset->link(); |
1418 | 0 | typeName = tokOffset->link()->next(); |
1419 | 0 | tok = typeName->next(); |
1420 | 0 | typeOf = true; |
1421 | 0 | } |
1422 | | |
1423 | | // function: typedef ... ( ... type )( ... ); |
1424 | | // typedef ... (( ... type )( ... )); |
1425 | | // typedef ... ( * ( ... type )( ... )); |
1426 | 0 | else if (tokOffset->str() == "(" && ( |
1427 | 0 | (tokOffset->link() && Token::Match(tokOffset->link()->previous(), "%type% ) (") && |
1428 | 0 | Token::Match(tokOffset->link()->next()->link(), ") const|volatile|;")) || |
1429 | 0 | (Token::simpleMatch(tokOffset, "( (") && |
1430 | 0 | tokOffset->next() && Token::Match(tokOffset->next()->link()->previous(), "%type% ) (") && |
1431 | 0 | Token::Match(tokOffset->next()->link()->next()->link(), ") const|volatile| ) ;|,")) || |
1432 | 0 | (Token::simpleMatch(tokOffset, "( * (") && |
1433 | 0 | tokOffset->linkAt(2) && Token::Match(tokOffset->linkAt(2)->previous(), "%type% ) (") && |
1434 | 0 | Token::Match(tokOffset->linkAt(2)->next()->link(), ") const|volatile| ) ;|,")))) { |
1435 | 0 | if (tokOffset->next()->str() == "(") |
1436 | 0 | tokOffset = tokOffset->next(); |
1437 | 0 | else if (Token::simpleMatch(tokOffset, "( * (")) { |
1438 | 0 | pointers.emplace_back("*"); |
1439 | 0 | tokOffset = tokOffset->tokAt(2); |
1440 | 0 | } |
1441 | |
|
1442 | 0 | if (tokOffset->link()->strAt(-2) == "*") |
1443 | 0 | functionPtr = true; |
1444 | 0 | else |
1445 | 0 | function = true; |
1446 | 0 | funcStart = tokOffset->next(); |
1447 | 0 | tokOffset = tokOffset->link(); |
1448 | 0 | funcEnd = tokOffset->tokAt(-2); |
1449 | 0 | typeName = tokOffset->previous(); |
1450 | 0 | argStart = tokOffset->next(); |
1451 | 0 | argEnd = tokOffset->next()->link(); |
1452 | 0 | if (!argEnd) |
1453 | 0 | syntaxError(argStart); |
1454 | |
|
1455 | 0 | tok = argEnd->next(); |
1456 | 0 | Token *spec = tok; |
1457 | 0 | if (Token::Match(spec, "const|volatile")) { |
1458 | 0 | specStart = spec; |
1459 | 0 | specEnd = spec; |
1460 | 0 | while (Token::Match(spec->next(), "const|volatile")) { |
1461 | 0 | specEnd = spec->next(); |
1462 | 0 | spec = specEnd; |
1463 | 0 | } |
1464 | 0 | tok = specEnd->next(); |
1465 | 0 | } |
1466 | 0 | if (!tok) |
1467 | 0 | syntaxError(specEnd); |
1468 | |
|
1469 | 0 | if (tok->str() == ")") |
1470 | 0 | tok = tok->next(); |
1471 | 0 | } |
1472 | | |
1473 | 0 | else if (Token::Match(tokOffset, "( %type% (")) { |
1474 | 0 | function = true; |
1475 | 0 | if (tokOffset->link()->next()) { |
1476 | 0 | tok = tokOffset->link()->next(); |
1477 | 0 | tokOffset = tokOffset->tokAt(2); |
1478 | 0 | typeName = tokOffset->previous(); |
1479 | 0 | argStart = tokOffset; |
1480 | 0 | argEnd = tokOffset->link(); |
1481 | 0 | } else { |
1482 | | // internal error |
1483 | 0 | continue; |
1484 | 0 | } |
1485 | 0 | } |
1486 | | |
1487 | | // pointer to function returning pointer to function |
1488 | 0 | else if (Token::Match(tokOffset, "( * ( * %type% ) (") && |
1489 | 0 | Token::simpleMatch(tokOffset->linkAt(6), ") ) (") && |
1490 | 0 | Token::Match(tokOffset->linkAt(6)->linkAt(2), ") ;|,")) { |
1491 | 0 | functionPtrRetFuncPtr = true; |
1492 | |
|
1493 | 0 | tokOffset = tokOffset->tokAt(6); |
1494 | 0 | typeName = tokOffset->tokAt(-2); |
1495 | 0 | argStart = tokOffset; |
1496 | 0 | argEnd = tokOffset->link(); |
1497 | 0 | if (!argEnd) |
1498 | 0 | syntaxError(arrayStart); |
1499 | |
|
1500 | 0 | argFuncRetStart = argEnd->tokAt(2); |
1501 | 0 | argFuncRetEnd = argFuncRetStart->link(); |
1502 | 0 | if (!argFuncRetEnd) |
1503 | 0 | syntaxError(argFuncRetStart); |
1504 | |
|
1505 | 0 | tok = argFuncRetEnd->next(); |
1506 | 0 | } |
1507 | | |
1508 | | // function returning pointer to function |
1509 | 0 | else if (Token::Match(tokOffset, "( * %type% (") && |
1510 | 0 | Token::simpleMatch(tokOffset->linkAt(3), ") ) (") && |
1511 | 0 | Token::Match(tokOffset->linkAt(3)->linkAt(2), ") ;|,")) { |
1512 | 0 | functionRetFuncPtr = true; |
1513 | |
|
1514 | 0 | tokOffset = tokOffset->tokAt(3); |
1515 | 0 | typeName = tokOffset->previous(); |
1516 | 0 | argStart = tokOffset; |
1517 | 0 | argEnd = tokOffset->link(); |
1518 | |
|
1519 | 0 | argFuncRetStart = argEnd->tokAt(2); |
1520 | 0 | if (!argFuncRetStart) |
1521 | 0 | syntaxError(tokOffset); |
1522 | |
|
1523 | 0 | argFuncRetEnd = argFuncRetStart->link(); |
1524 | 0 | if (!argFuncRetEnd) |
1525 | 0 | syntaxError(tokOffset); |
1526 | |
|
1527 | 0 | tok = argFuncRetEnd->next(); |
1528 | 0 | } else if (Token::Match(tokOffset, "( * ( %type% ) (")) { |
1529 | 0 | functionRetFuncPtr = true; |
1530 | |
|
1531 | 0 | tokOffset = tokOffset->tokAt(5); |
1532 | 0 | typeName = tokOffset->tokAt(-2); |
1533 | 0 | argStart = tokOffset; |
1534 | 0 | argEnd = tokOffset->link(); |
1535 | 0 | if (!argEnd) |
1536 | 0 | syntaxError(arrayStart); |
1537 | |
|
1538 | 0 | argFuncRetStart = argEnd->tokAt(2); |
1539 | 0 | if (!argFuncRetStart) |
1540 | 0 | syntaxError(tokOffset); |
1541 | |
|
1542 | 0 | argFuncRetEnd = argFuncRetStart->link(); |
1543 | 0 | if (!argFuncRetEnd) |
1544 | 0 | syntaxError(tokOffset); |
1545 | |
|
1546 | 0 | tok = argFuncRetEnd->next(); |
1547 | 0 | } |
1548 | | |
1549 | | // pointer/reference to array |
1550 | 0 | else if (Token::Match(tokOffset, "( *|& %type% ) [")) { |
1551 | 0 | ptrToArray = (tokOffset->next()->str() == "*"); |
1552 | 0 | refToArray = !ptrToArray; |
1553 | 0 | tokOffset = tokOffset->tokAt(2); |
1554 | 0 | typeName = tokOffset; |
1555 | 0 | arrayStart = tokOffset->tokAt(2); |
1556 | 0 | arrayEnd = arrayStart->link(); |
1557 | 0 | if (!arrayEnd) |
1558 | 0 | syntaxError(arrayStart); |
1559 | |
|
1560 | 0 | tok = arrayEnd->next(); |
1561 | 0 | } |
1562 | | |
1563 | | // pointer to class member |
1564 | 0 | else if (Token::Match(tokOffset, "( %type% :: * %type% ) ;")) { |
1565 | 0 | tokOffset = tokOffset->tokAt(2); |
1566 | 0 | namespaceStart = tokOffset->previous(); |
1567 | 0 | namespaceEnd = tokOffset; |
1568 | 0 | ptrMember = true; |
1569 | 0 | tokOffset = tokOffset->tokAt(2); |
1570 | 0 | typeName = tokOffset; |
1571 | 0 | tok = tokOffset->tokAt(2); |
1572 | 0 | } |
1573 | | |
1574 | | // unhandled typedef, skip it and continue |
1575 | 0 | else { |
1576 | 0 | unsupportedTypedef(typeDef); |
1577 | 0 | tok = deleteInvalidTypedef(typeDef); |
1578 | 0 | if (tok == list.front()) |
1579 | | //now the next token to process is 'tok', not 'tok->next()'; |
1580 | 0 | goback = true; |
1581 | 0 | continue; |
1582 | 0 | } |
1583 | | |
1584 | 0 | bool done = false; |
1585 | 0 | bool ok = true; |
1586 | |
|
1587 | 0 | TypedefInfo typedefInfo; |
1588 | 0 | typedefInfo.name = typeName->str(); |
1589 | 0 | typedefInfo.filename = list.file(typeName); |
1590 | 0 | typedefInfo.lineNumber = typeName->linenr(); |
1591 | 0 | typedefInfo.column = typeName->column(); |
1592 | 0 | typedefInfo.used = false; |
1593 | 0 | mTypedefInfo.push_back(std::move(typedefInfo)); |
1594 | |
|
1595 | 0 | while (!done) { |
1596 | 0 | std::string pattern = typeName->str(); |
1597 | 0 | int scope = 0; |
1598 | 0 | bool simplifyType = false; |
1599 | 0 | bool inMemberFunc = false; |
1600 | 0 | int memberScope = 0; |
1601 | 0 | bool globalScope = false; |
1602 | 0 | int classLevel = spaceInfo.size(); |
1603 | 0 | bool inTypeDef = false; |
1604 | 0 | bool inEnumClass = false; |
1605 | 0 | std::string removed; |
1606 | 0 | std::string classPath; |
1607 | 0 | for (size_t i = 1; i < spaceInfo.size(); ++i) { |
1608 | 0 | if (!classPath.empty()) |
1609 | 0 | classPath += " :: "; |
1610 | 0 | classPath += spaceInfo[i].className; |
1611 | 0 | } |
1612 | |
|
1613 | 0 | for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { |
1614 | 0 | if (Settings::terminated()) |
1615 | 0 | return; |
1616 | | |
1617 | 0 | removed.clear(); |
1618 | |
|
1619 | 0 | if (Token::simpleMatch(tok2, "typedef")) |
1620 | 0 | inTypeDef = true; |
1621 | |
|
1622 | 0 | if (inTypeDef && Token::simpleMatch(tok2, ";")) |
1623 | 0 | inTypeDef = false; |
1624 | | |
1625 | | // Check for variable declared with the same name |
1626 | 0 | if (!inTypeDef && spaceInfo.size() == 1 && Token::Match(tok2->previous(), "%name%") && |
1627 | 0 | !tok2->previous()->isKeyword()) { |
1628 | 0 | Token* varDecl = tok2; |
1629 | 0 | while (Token::Match(varDecl, "*|&|&&|const")) |
1630 | 0 | varDecl = varDecl->next(); |
1631 | 0 | if (Token::Match(varDecl, "%name% ;|,|)|=") && varDecl->str() == typeName->str()) { |
1632 | | // Skip to the next closing brace |
1633 | 0 | if (Token::Match(varDecl, "%name% ) {")) { // is argument variable |
1634 | 0 | tok2 = varDecl->linkAt(2)->next(); |
1635 | 0 | } else { |
1636 | 0 | tok2 = varDecl; |
1637 | 0 | while (tok2 && !Token::simpleMatch(tok2, "}")) { |
1638 | 0 | if (Token::Match(tok2, "(|{|[")) |
1639 | 0 | tok2 = tok2->link(); |
1640 | 0 | tok2 = tok2->next(); |
1641 | 0 | } |
1642 | 0 | } |
1643 | 0 | if (!tok2) |
1644 | 0 | break; |
1645 | 0 | continue; |
1646 | 0 | } |
1647 | 0 | } |
1648 | | |
1649 | 0 | if (tok2->link()) { // Pre-check for performance |
1650 | | // check for end of scope |
1651 | 0 | if (tok2->str() == "}") { |
1652 | | // check for end of member function |
1653 | 0 | if (inMemberFunc) { |
1654 | 0 | --memberScope; |
1655 | 0 | if (memberScope == 0) |
1656 | 0 | inMemberFunc = false; |
1657 | 0 | } |
1658 | 0 | inEnumClass = false; |
1659 | |
|
1660 | 0 | if (classLevel > 1 && tok2 == spaceInfo[classLevel - 1].bodyEnd2) { |
1661 | 0 | --classLevel; |
1662 | 0 | pattern.clear(); |
1663 | |
|
1664 | 0 | for (int i = classLevel; i < spaceInfo.size(); ++i) |
1665 | 0 | pattern += (spaceInfo[i].className + " :: "); |
1666 | |
|
1667 | 0 | pattern += typeName->str(); |
1668 | 0 | } else { |
1669 | 0 | if (scope == 0 && !(classLevel > 1 && tok2 == spaceInfo[classLevel - 1].bodyEnd)) |
1670 | 0 | break; |
1671 | 0 | --scope; |
1672 | 0 | } |
1673 | 0 | } |
1674 | | |
1675 | | // check for member functions |
1676 | 0 | else if (isCPP() && tok2->str() == "(" && isFunctionHead(tok2, "{:")) { |
1677 | 0 | const Token *func = tok2->previous(); |
1678 | | |
1679 | | /** @todo add support for multi-token operators */ |
1680 | 0 | if (func->previous()->str() == "operator") |
1681 | 0 | func = func->previous(); |
1682 | |
|
1683 | 0 | if (!func->previous()) |
1684 | 0 | syntaxError(func); |
1685 | | |
1686 | | // check for qualifier |
1687 | 0 | if (Token::Match(func->tokAt(-2), "%name% ::")) { |
1688 | 0 | int offset = -2; |
1689 | 0 | while (Token::Match(func->tokAt(offset - 2), "%name% ::")) |
1690 | 0 | offset -= 2; |
1691 | | // check for available and matching class name |
1692 | 0 | if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && |
1693 | 0 | func->strAt(offset) == spaceInfo[classLevel].className) { |
1694 | 0 | memberScope = 0; |
1695 | 0 | inMemberFunc = true; |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | } |
1699 | | |
1700 | | // check for entering a new scope |
1701 | 0 | else if (tok2->str() == "{") { |
1702 | | // check for entering a new namespace |
1703 | 0 | if (isCPP()) { |
1704 | 0 | if (tok2->strAt(-2) == "namespace") { |
1705 | 0 | if (classLevel < spaceInfo.size() && |
1706 | 0 | spaceInfo[classLevel].isNamespace && |
1707 | 0 | spaceInfo[classLevel].className == tok2->previous()->str()) { |
1708 | 0 | spaceInfo[classLevel].bodyEnd2 = tok2->link(); |
1709 | 0 | ++classLevel; |
1710 | 0 | pattern.clear(); |
1711 | 0 | for (int i = classLevel; i < spaceInfo.size(); ++i) |
1712 | 0 | pattern += spaceInfo[i].className + " :: "; |
1713 | |
|
1714 | 0 | pattern += typeName->str(); |
1715 | 0 | } |
1716 | 0 | ++scope; |
1717 | 0 | } |
1718 | 0 | if (Token::Match(tok2->tokAt(-3), "enum class %name%")) |
1719 | 0 | inEnumClass = true; |
1720 | 0 | } |
1721 | | |
1722 | | // keep track of scopes within member function |
1723 | 0 | if (inMemberFunc) |
1724 | 0 | ++memberScope; |
1725 | |
|
1726 | 0 | ++scope; |
1727 | 0 | } |
1728 | 0 | } |
1729 | | |
1730 | | // check for operator typedef |
1731 | | /** @todo add support for multi-token operators */ |
1732 | 0 | else if (isCPP() && |
1733 | 0 | tok2->str() == "operator" && |
1734 | 0 | tok2->next() && |
1735 | 0 | tok2->next()->str() == typeName->str() && |
1736 | 0 | tok2->linkAt(2) && |
1737 | 0 | tok2->strAt(2) == "(" && |
1738 | 0 | Token::Match(tok2->linkAt(2), ") const| {")) { |
1739 | | // check for qualifier |
1740 | 0 | if (tok2->previous()->str() == "::") { |
1741 | | // check for available and matching class name |
1742 | 0 | if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && |
1743 | 0 | tok2->strAt(-2) == spaceInfo[classLevel].className) { |
1744 | 0 | tok2 = tok2->next(); |
1745 | 0 | simplifyType = true; |
1746 | 0 | } |
1747 | 0 | } |
1748 | 0 | } |
1749 | | |
1750 | 0 | else if (Token::Match(tok2->previous(), "class|struct %name% [:{]")) { |
1751 | | // don't replace names in struct/class definition |
1752 | 0 | } |
1753 | | |
1754 | | // check for typedef that can be substituted |
1755 | 0 | else if ((tok2->isNameOnly() || (tok2->isName() && (tok2->isExpandedMacro() || tok2->isInline()))) && |
1756 | 0 | (Token::simpleMatch(tok2, pattern.c_str(), pattern.size()) || |
1757 | 0 | (inMemberFunc && tok2->str() == typeName->str()))) { |
1758 | | // member function class variables don't need qualification |
1759 | 0 | if (!(inMemberFunc && tok2->str() == typeName->str()) && pattern.find("::") != std::string::npos) { // has a "something ::" |
1760 | 0 | Token *start = tok2; |
1761 | 0 | int count = 0; |
1762 | 0 | int back = classLevel - 1; |
1763 | 0 | bool good = true; |
1764 | | // check for extra qualification |
1765 | 0 | while (back >= 1) { |
1766 | 0 | Token *qualificationTok = start->tokAt(-2); |
1767 | 0 | if (!Token::Match(qualificationTok, "%type% ::")) |
1768 | 0 | break; |
1769 | 0 | if (qualificationTok->str() == spaceInfo[back].className) { |
1770 | 0 | start = qualificationTok; |
1771 | 0 | back--; |
1772 | 0 | count++; |
1773 | 0 | } else { |
1774 | 0 | good = false; |
1775 | 0 | break; |
1776 | 0 | } |
1777 | 0 | } |
1778 | | // check global namespace |
1779 | 0 | if (good && back == 1 && start->strAt(-1) == "::") |
1780 | 0 | good = false; |
1781 | |
|
1782 | 0 | if (good) { |
1783 | | // remove any extra qualification if present |
1784 | 0 | while (count) { |
1785 | 0 | if (!removed.empty()) |
1786 | 0 | removed.insert(0, " "); |
1787 | 0 | removed.insert(0, tok2->strAt(-2) + " " + tok2->strAt(-1)); |
1788 | 0 | tok2->tokAt(-3)->deleteNext(2); |
1789 | 0 | --count; |
1790 | 0 | } |
1791 | | |
1792 | | // remove global namespace if present |
1793 | 0 | if (tok2->strAt(-1) == "::") { |
1794 | 0 | removed.insert(0, ":: "); |
1795 | 0 | tok2->tokAt(-2)->deleteNext(); |
1796 | 0 | globalScope = true; |
1797 | 0 | } |
1798 | | |
1799 | | // remove qualification if present |
1800 | 0 | for (int i = classLevel; i < spaceInfo.size(); ++i) { |
1801 | 0 | if (!removed.empty()) |
1802 | 0 | removed += " "; |
1803 | 0 | removed += (tok2->str() + " " + tok2->strAt(1)); |
1804 | 0 | tok2->deleteThis(); |
1805 | 0 | tok2->deleteThis(); |
1806 | 0 | } |
1807 | 0 | simplifyType = true; |
1808 | 0 | } |
1809 | 0 | } else { |
1810 | 0 | if (tok2->strAt(-1) == "::") { |
1811 | 0 | int relativeSpaceInfoSize = spaceInfo.size(); |
1812 | 0 | Token * tokBeforeType = tok2->previous(); |
1813 | 0 | while (relativeSpaceInfoSize > 1 && |
1814 | 0 | tokBeforeType && tokBeforeType->str() == "::" && |
1815 | 0 | tokBeforeType->strAt(-1) == spaceInfo[relativeSpaceInfoSize-1].className) { |
1816 | 0 | tokBeforeType = tokBeforeType->tokAt(-2); |
1817 | 0 | --relativeSpaceInfoSize; |
1818 | 0 | } |
1819 | 0 | if (tokBeforeType && tokBeforeType->str() != "::") { |
1820 | 0 | Token::eraseTokens(tokBeforeType, tok2); |
1821 | 0 | simplifyType = true; |
1822 | 0 | } |
1823 | 0 | } else if (Token::Match(tok2->previous(), "case|;|{|} %type% :")) { |
1824 | 0 | tok2 = tok2->next(); |
1825 | 0 | } else if (duplicateTypedef(&tok2, typeName, typeDef)) { |
1826 | | // skip to end of scope if not already there |
1827 | 0 | if (tok2->str() != "}") { |
1828 | 0 | while (tok2->next()) { |
1829 | 0 | if (tok2->next()->str() == "{") |
1830 | 0 | tok2 = tok2->linkAt(1)->previous(); |
1831 | 0 | else if (tok2->next()->str() == "}") |
1832 | 0 | break; |
1833 | | |
1834 | 0 | tok2 = tok2->next(); |
1835 | 0 | } |
1836 | 0 | } |
1837 | 0 | } else if (Token::Match(tok2->tokAt(-2), "%type% *|&")) { |
1838 | | // Ticket #5868: Don't substitute variable names |
1839 | 0 | } else if (tok2->previous()->str() != ".") { |
1840 | 0 | simplifyType = true; |
1841 | 0 | } |
1842 | 0 | } |
1843 | 0 | } |
1844 | | |
1845 | 0 | simplifyType = simplifyType && (!inEnumClass || Token::simpleMatch(tok2->previous(), "=")); |
1846 | |
|
1847 | 0 | if (simplifyType) { |
1848 | 0 | mTypedefInfo.back().used = true; |
1849 | | |
1850 | | // can't simplify 'operator functionPtr ()' and 'functionPtr operator ... ()' |
1851 | 0 | if (functionPtr && (tok2->previous()->str() == "operator" || |
1852 | 0 | (tok2->next() && tok2->next()->str() == "operator"))) { |
1853 | 0 | simplifyType = false; |
1854 | 0 | tok2 = tok2->next(); |
1855 | 0 | continue; |
1856 | 0 | } |
1857 | | |
1858 | | // There are 2 categories of typedef substitutions: |
1859 | | // 1. variable declarations that preserve the variable name like |
1860 | | // global, local, and function parameters |
1861 | | // 2. not variable declarations that have no name like derived |
1862 | | // classes, casts, operators, and template parameters |
1863 | | |
1864 | | // try to determine which category this substitution is |
1865 | 0 | bool inCast = false; |
1866 | 0 | bool inTemplate = false; |
1867 | 0 | bool inOperator = false; |
1868 | 0 | bool inSizeof = false; |
1869 | |
|
1870 | 0 | const bool sameStartEnd = (typeStart == typeEnd); |
1871 | | |
1872 | | // check for derived class: class A : some_typedef { |
1873 | 0 | const bool isDerived = Token::Match(tok2->previous(), "public|protected|private|: %type% {|,"); |
1874 | | |
1875 | | // check for cast: (some_typedef) A or static_cast<some_typedef>(A) |
1876 | | // todo: check for more complicated casts like: (const some_typedef *)A |
1877 | 0 | if ((tok2->previous()->str() == "(" && tok2->next()->str() == ")" && tok2->strAt(-2) != "sizeof") || |
1878 | 0 | (tok2->previous()->str() == "<" && Token::simpleMatch(tok2->next(), "> (")) || |
1879 | 0 | Token::Match(tok2->tokAt(-2), "( const %name% )")) |
1880 | 0 | inCast = true; |
1881 | | |
1882 | | // check for template parameters: t<some_typedef> t1 |
1883 | 0 | else if (Token::Match(tok2->previous(), "<|,") && |
1884 | 0 | Token::Match(tok2->next(), "&|*| &|*| >|,")) |
1885 | 0 | inTemplate = true; |
1886 | | |
1887 | 0 | else if (Token::Match(tok2->tokAt(-2), "sizeof ( %type% )")) |
1888 | 0 | inSizeof = true; |
1889 | | |
1890 | | // check for operator |
1891 | 0 | if (tok2->strAt(-1) == "operator" || |
1892 | 0 | Token::simpleMatch(tok2->tokAt(-2), "operator const")) |
1893 | 0 | inOperator = true; |
1894 | |
|
1895 | 0 | if (typeStart->str() == "typename" && tok2->strAt(-1)=="typename") { |
1896 | | // Remove one typename if it is already contained in the goal |
1897 | 0 | typeStart = typeStart->next(); |
1898 | 0 | } |
1899 | | |
1900 | | // skip over class or struct in derived class declaration |
1901 | 0 | bool structRemoved = false; |
1902 | 0 | if ((isDerived || inTemplate) && Token::Match(typeStart, "class|struct")) { |
1903 | 0 | if (typeStart->str() == "struct") |
1904 | 0 | structRemoved = true; |
1905 | 0 | typeStart = typeStart->next(); |
1906 | 0 | } |
1907 | 0 | if (Token::Match(typeStart, "struct|class|union") && Token::Match(tok2, "%name% ::")) |
1908 | 0 | typeStart = typeStart->next(); |
1909 | |
|
1910 | 0 | if (sameStartEnd) |
1911 | 0 | typeEnd = typeStart; |
1912 | | |
1913 | | // Is this a "T()" expression where T is a pointer type? |
1914 | 0 | const bool isPointerTypeCall = !inOperator && Token::Match(tok2, "%name% ( )") && !pointers.empty(); |
1915 | | |
1916 | | // start substituting at the typedef name by replacing it with the type |
1917 | 0 | Token* replStart = tok2; // track first replaced token |
1918 | 0 | for (Token* tok3 = typeStart; tok3->str() != ";"; tok3 = tok3->next()) |
1919 | 0 | tok3->isSimplifiedTypedef(true); |
1920 | 0 | if (isPointerTypeCall) { |
1921 | 0 | tok2->deleteThis(); |
1922 | 0 | tok2->insertToken("0"); |
1923 | 0 | tok2 = tok2->next(); |
1924 | 0 | tok2->next()->insertToken("0"); |
1925 | 0 | } |
1926 | 0 | tok2->str(typeStart->str()); |
1927 | | |
1928 | | // restore qualification if it was removed |
1929 | 0 | if (typeStart->str() == "struct" || structRemoved) { |
1930 | 0 | if (structRemoved) |
1931 | 0 | tok2 = tok2->previous(); |
1932 | |
|
1933 | 0 | if (globalScope) { |
1934 | 0 | replStart = tok2->insertToken("::"); |
1935 | 0 | tok2 = tok2->next(); |
1936 | 0 | } |
1937 | |
|
1938 | 0 | for (int i = classLevel; i < spaceInfo.size(); ++i) { |
1939 | 0 | tok2->insertToken(spaceInfo[i].className); |
1940 | 0 | tok2 = tok2->next(); |
1941 | 0 | tok2->insertToken("::"); |
1942 | 0 | tok2 = tok2->next(); |
1943 | 0 | } |
1944 | 0 | } |
1945 | | |
1946 | | // add some qualification back if needed |
1947 | 0 | Token *start = tok2; |
1948 | 0 | std::string removed1 = removed; |
1949 | 0 | std::string::size_type idx = removed1.rfind(" ::"); |
1950 | |
|
1951 | 0 | if (idx != std::string::npos) |
1952 | 0 | removed1.resize(idx); |
1953 | 0 | if (removed1 == classPath && !removed1.empty()) { |
1954 | 0 | for (std::vector<Space>::const_reverse_iterator it = spaceInfo.crbegin(); it != spaceInfo.crend(); ++it) { |
1955 | 0 | if (it->recordTypes.find(start->str()) != it->recordTypes.end()) { |
1956 | 0 | std::string::size_type spaceIdx = 0; |
1957 | 0 | std::string::size_type startIdx = 0; |
1958 | 0 | while ((spaceIdx = removed1.find(' ', startIdx)) != std::string::npos) { |
1959 | 0 | tok2->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); |
1960 | 0 | startIdx = spaceIdx + 1; |
1961 | 0 | } |
1962 | 0 | tok2->previous()->insertToken(removed1.substr(startIdx)); |
1963 | 0 | replStart = tok2->previous()->insertToken("::"); |
1964 | 0 | break; |
1965 | 0 | } |
1966 | 0 | idx = removed1.rfind(" ::"); |
1967 | 0 | if (idx == std::string::npos) |
1968 | 0 | break; |
1969 | | |
1970 | 0 | removed1.resize(idx); |
1971 | 0 | } |
1972 | 0 | } |
1973 | 0 | replStart->isSimplifiedTypedef(true); |
1974 | 0 | Token* constTok = Token::simpleMatch(tok2->previous(), "const") ? tok2->previous() : nullptr; |
1975 | | // add remainder of type |
1976 | 0 | tok2 = TokenList::copyTokens(tok2, typeStart->next(), typeEnd); |
1977 | |
|
1978 | 0 | if (!pointers.empty()) { |
1979 | 0 | for (const std::string &p : pointers) { |
1980 | 0 | tok2->insertToken(p); |
1981 | 0 | tok2->isSimplifiedTypedef(true); |
1982 | 0 | tok2 = tok2->next(); |
1983 | 0 | } |
1984 | 0 | if (constTok) { |
1985 | 0 | constTok->deleteThis(); |
1986 | 0 | tok2->insertToken("const"); |
1987 | 0 | tok2->isSimplifiedTypedef(true); |
1988 | 0 | tok2 = tok2->next(); |
1989 | 0 | } |
1990 | 0 | } |
1991 | |
|
1992 | 0 | if (funcStart && funcEnd) { |
1993 | 0 | tok2->insertToken("("); |
1994 | 0 | tok2 = tok2->next(); |
1995 | 0 | Token *paren = tok2; |
1996 | 0 | tok2 = TokenList::copyTokens(tok2, funcStart, funcEnd); |
1997 | |
|
1998 | 0 | if (!inCast) |
1999 | 0 | tok2 = processFunc(tok2, inOperator); |
2000 | |
|
2001 | 0 | if (!tok2) |
2002 | 0 | break; |
2003 | | |
2004 | 0 | while (Token::Match(tok2, "%name%|] [")) |
2005 | 0 | tok2 = tok2->linkAt(1); |
2006 | |
|
2007 | 0 | tok2->insertToken(")"); |
2008 | 0 | tok2 = tok2->next(); |
2009 | 0 | Token::createMutualLinks(tok2, paren); |
2010 | |
|
2011 | 0 | tok2 = TokenList::copyTokens(tok2, argStart, argEnd); |
2012 | |
|
2013 | 0 | if (specStart) { |
2014 | 0 | Token *spec = specStart; |
2015 | 0 | tok2->insertToken(spec->str()); |
2016 | 0 | tok2 = tok2->next(); |
2017 | 0 | while (spec != specEnd) { |
2018 | 0 | spec = spec->next(); |
2019 | 0 | tok2->insertToken(spec->str()); |
2020 | 0 | tok2 = tok2->next(); |
2021 | 0 | } |
2022 | 0 | } |
2023 | 0 | } |
2024 | | |
2025 | 0 | else if (functionPtr || function) { |
2026 | | // don't add parentheses around function names because it |
2027 | | // confuses other simplifications |
2028 | 0 | bool needParen = true; |
2029 | 0 | if (!inTemplate && function && tok2->next() && tok2->next()->str() != "*") |
2030 | 0 | needParen = false; |
2031 | 0 | if (needParen) { |
2032 | 0 | tok2->insertToken("("); |
2033 | 0 | tok2 = tok2->next(); |
2034 | 0 | } |
2035 | 0 | Token *tok3 = tok2; |
2036 | 0 | if (namespaceStart) { |
2037 | 0 | const Token *tok4 = namespaceStart; |
2038 | |
|
2039 | 0 | while (tok4 != namespaceEnd) { |
2040 | 0 | tok2->insertToken(tok4->str()); |
2041 | 0 | tok2 = tok2->next(); |
2042 | 0 | tok4 = tok4->next(); |
2043 | 0 | } |
2044 | 0 | tok2->insertToken(namespaceEnd->str()); |
2045 | 0 | tok2 = tok2->next(); |
2046 | 0 | } |
2047 | 0 | if (functionPtr) { |
2048 | 0 | tok2->insertToken("*"); |
2049 | 0 | tok2 = tok2->next(); |
2050 | 0 | } |
2051 | |
|
2052 | 0 | if (!inCast) |
2053 | 0 | tok2 = processFunc(tok2, inOperator); |
2054 | |
|
2055 | 0 | if (needParen) { |
2056 | 0 | if (!tok2) |
2057 | 0 | syntaxError(nullptr); |
2058 | |
|
2059 | 0 | tok2->insertToken(")"); |
2060 | 0 | tok2 = tok2->next(); |
2061 | 0 | Token::createMutualLinks(tok2, tok3); |
2062 | 0 | } |
2063 | 0 | if (!tok2) |
2064 | 0 | syntaxError(nullptr); |
2065 | |
|
2066 | 0 | tok2 = TokenList::copyTokens(tok2, argStart, argEnd); |
2067 | 0 | if (inTemplate) { |
2068 | 0 | if (!tok2) |
2069 | 0 | syntaxError(nullptr); |
2070 | |
|
2071 | 0 | tok2 = tok2->next(); |
2072 | 0 | } |
2073 | |
|
2074 | 0 | if (specStart) { |
2075 | 0 | Token *spec = specStart; |
2076 | 0 | tok2->insertToken(spec->str()); |
2077 | 0 | tok2 = tok2->next(); |
2078 | 0 | while (spec != specEnd) { |
2079 | 0 | spec = spec->next(); |
2080 | 0 | tok2->insertToken(spec->str()); |
2081 | 0 | tok2 = tok2->next(); |
2082 | 0 | } |
2083 | 0 | } |
2084 | 0 | } else if (functionRetFuncPtr || functionPtrRetFuncPtr) { |
2085 | 0 | tok2->insertToken("("); |
2086 | 0 | tok2 = tok2->next(); |
2087 | 0 | Token *tok3 = tok2; |
2088 | 0 | tok2->insertToken("*"); |
2089 | 0 | tok2 = tok2->next(); |
2090 | |
|
2091 | 0 | Token * tok4 = nullptr; |
2092 | 0 | if (functionPtrRetFuncPtr) { |
2093 | 0 | tok2->insertToken("("); |
2094 | 0 | tok2 = tok2->next(); |
2095 | 0 | tok4 = tok2; |
2096 | 0 | tok2->insertToken("*"); |
2097 | 0 | tok2 = tok2->next(); |
2098 | 0 | } |
2099 | | |
2100 | | // skip over variable name if there |
2101 | 0 | if (!inCast) { |
2102 | 0 | if (!tok2 || !tok2->next()) |
2103 | 0 | syntaxError(nullptr); |
2104 | |
|
2105 | 0 | if (tok2->next()->str() != ")") |
2106 | 0 | tok2 = tok2->next(); |
2107 | 0 | } |
2108 | |
|
2109 | 0 | if (tok4 && functionPtrRetFuncPtr) { |
2110 | 0 | tok2->insertToken(")"); |
2111 | 0 | tok2 = tok2->next(); |
2112 | 0 | Token::createMutualLinks(tok2, tok4); |
2113 | 0 | } |
2114 | |
|
2115 | 0 | tok2 = TokenList::copyTokens(tok2, argStart, argEnd); |
2116 | |
|
2117 | 0 | tok2->insertToken(")"); |
2118 | 0 | tok2 = tok2->next(); |
2119 | 0 | Token::createMutualLinks(tok2, tok3); |
2120 | |
|
2121 | 0 | tok2 = TokenList::copyTokens(tok2, argFuncRetStart, argFuncRetEnd); |
2122 | 0 | } else if (ptrToArray || refToArray) { |
2123 | 0 | tok2->insertToken("("); |
2124 | 0 | tok2 = tok2->next(); |
2125 | 0 | Token *tok3 = tok2; |
2126 | |
|
2127 | 0 | if (ptrToArray) |
2128 | 0 | tok2->insertToken("*"); |
2129 | 0 | else |
2130 | 0 | tok2->insertToken("&"); |
2131 | 0 | tok2 = tok2->next(); |
2132 | |
|
2133 | 0 | bool hasName = false; |
2134 | | // skip over name |
2135 | 0 | if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != "," && |
2136 | 0 | tok2->next()->str() != ">") { |
2137 | 0 | hasName = true; |
2138 | 0 | if (tok2->next()->str() != "(") |
2139 | 0 | tok2 = tok2->next(); |
2140 | | |
2141 | | // check for function and skip over args |
2142 | 0 | if (tok2 && tok2->next() && tok2->next()->str() == "(") |
2143 | 0 | tok2 = tok2->next()->link(); |
2144 | | |
2145 | | // check for array |
2146 | 0 | if (tok2 && tok2->next() && tok2->next()->str() == "[") |
2147 | 0 | tok2 = tok2->next()->link(); |
2148 | 0 | } |
2149 | |
|
2150 | 0 | tok2->insertToken(")"); |
2151 | 0 | Token::createMutualLinks(tok2->next(), tok3); |
2152 | |
|
2153 | 0 | if (!hasName) |
2154 | 0 | tok2 = tok2->next(); |
2155 | 0 | } else if (ptrMember) { |
2156 | 0 | if (Token::simpleMatch(tok2, "* (")) { |
2157 | 0 | tok2->insertToken("*"); |
2158 | 0 | tok2 = tok2->next(); |
2159 | 0 | } else { |
2160 | | // This is the case of casting operator. |
2161 | | // Name is not available, and () should not be |
2162 | | // inserted |
2163 | 0 | const bool castOperator = inOperator && Token::Match(tok2, "%type% ("); |
2164 | 0 | Token *openParenthesis = nullptr; |
2165 | |
|
2166 | 0 | if (!castOperator) { |
2167 | 0 | tok2->insertToken("("); |
2168 | 0 | tok2 = tok2->next(); |
2169 | |
|
2170 | 0 | openParenthesis = tok2; |
2171 | 0 | } |
2172 | |
|
2173 | 0 | const Token *tok4 = namespaceStart; |
2174 | |
|
2175 | 0 | while (tok4 != namespaceEnd) { |
2176 | 0 | tok2->insertToken(tok4->str()); |
2177 | 0 | tok2 = tok2->next(); |
2178 | 0 | tok4 = tok4->next(); |
2179 | 0 | } |
2180 | 0 | tok2->insertToken(namespaceEnd->str()); |
2181 | 0 | tok2 = tok2->next(); |
2182 | |
|
2183 | 0 | tok2->insertToken("*"); |
2184 | 0 | tok2 = tok2->next(); |
2185 | |
|
2186 | 0 | if (openParenthesis) { |
2187 | | // Skip over name, if any |
2188 | 0 | if (Token::Match(tok2->next(), "%name%")) |
2189 | 0 | tok2 = tok2->next(); |
2190 | |
|
2191 | 0 | tok2->insertToken(")"); |
2192 | 0 | tok2 = tok2->next(); |
2193 | |
|
2194 | 0 | Token::createMutualLinks(tok2, openParenthesis); |
2195 | 0 | } |
2196 | 0 | } |
2197 | 0 | } else if (typeOf) { |
2198 | 0 | tok2 = TokenList::copyTokens(tok2, argStart, argEnd); |
2199 | 0 | } else if (Token::Match(tok2, "%name% [")) { |
2200 | 0 | while (Token::Match(tok2, "%name%|] [")) { |
2201 | 0 | tok2 = tok2->linkAt(1); |
2202 | 0 | } |
2203 | 0 | tok2 = tok2->previous(); |
2204 | 0 | } |
2205 | | |
2206 | 0 | if (arrayStart && arrayEnd) { |
2207 | 0 | do { |
2208 | 0 | if (!tok2->next()) |
2209 | 0 | syntaxError(tok2); // can't recover so quit |
2210 | |
|
2211 | 0 | if (!inCast && !inSizeof && !inTemplate) |
2212 | 0 | tok2 = tok2->next(); |
2213 | |
|
2214 | 0 | if (tok2->str() == "const") |
2215 | 0 | tok2 = tok2->next(); |
2216 | | |
2217 | | // reference or pointer to array? |
2218 | 0 | if (Token::Match(tok2, "&|*|&&")) { |
2219 | 0 | tok2 = tok2->previous(); |
2220 | 0 | tok2->insertToken("("); |
2221 | 0 | Token *tok3 = tok2->next(); |
2222 | | |
2223 | | // handle missing variable name |
2224 | 0 | if (Token::Match(tok3, "( *|&|&& *|&|&& %name%")) |
2225 | 0 | tok2 = tok3->tokAt(3); |
2226 | 0 | else if (Token::Match(tok2->tokAt(3), "[(),;]")) |
2227 | 0 | tok2 = tok2->tokAt(2); |
2228 | 0 | else |
2229 | 0 | tok2 = tok2->tokAt(3); |
2230 | 0 | if (!tok2) |
2231 | 0 | syntaxError(nullptr); |
2232 | |
|
2233 | 0 | while (tok2->strAt(1) == "::") |
2234 | 0 | tok2 = tok2->tokAt(2); |
2235 | | |
2236 | | // skip over function parameters |
2237 | 0 | if (tok2->str() == "(") |
2238 | 0 | tok2 = tok2->link(); |
2239 | |
|
2240 | 0 | if (tok2->strAt(1) == "(") |
2241 | 0 | tok2 = tok2->linkAt(1); |
2242 | | |
2243 | | // skip over const/noexcept |
2244 | 0 | while (Token::Match(tok2->next(), "const|noexcept")) { |
2245 | 0 | tok2 = tok2->next(); |
2246 | 0 | if (Token::Match(tok2->next(), "( true|false )")) |
2247 | 0 | tok2 = tok2->tokAt(3); |
2248 | 0 | } |
2249 | |
|
2250 | 0 | tok2->insertToken(")"); |
2251 | 0 | tok2 = tok2->next(); |
2252 | 0 | Token::createMutualLinks(tok2, tok3); |
2253 | 0 | } |
2254 | |
|
2255 | 0 | if (!tok2->next()) |
2256 | 0 | syntaxError(tok2); // can't recover so quit |
2257 | | |
2258 | | // skip over array dimensions |
2259 | 0 | while (tok2->next()->str() == "[") |
2260 | 0 | tok2 = tok2->linkAt(1); |
2261 | |
|
2262 | 0 | tok2 = TokenList::copyTokens(tok2, arrayStart, arrayEnd); |
2263 | 0 | if (!tok2->next()) |
2264 | 0 | syntaxError(tok2); |
2265 | |
|
2266 | 0 | if (tok2->str() == "=") { |
2267 | 0 | if (tok2->next()->str() == "{") |
2268 | 0 | tok2 = tok2->next()->link()->next(); |
2269 | 0 | else if (tok2->next()->str().at(0) == '\"') |
2270 | 0 | tok2 = tok2->tokAt(2); |
2271 | 0 | } |
2272 | 0 | } while (Token::Match(tok2, ", %name% ;|=|,")); |
2273 | 0 | } |
2274 | |
|
2275 | 0 | simplifyType = false; |
2276 | 0 | } |
2277 | 0 | if (!tok2) |
2278 | 0 | break; |
2279 | 0 | } |
2280 | | |
2281 | 0 | if (!tok) |
2282 | 0 | syntaxError(nullptr); |
2283 | |
|
2284 | 0 | if (tok->str() == ";") |
2285 | 0 | done = true; |
2286 | 0 | else if (tok->str() == ",") { |
2287 | 0 | arrayStart = nullptr; |
2288 | 0 | arrayEnd = nullptr; |
2289 | 0 | tokOffset = tok->next(); |
2290 | 0 | pointers.clear(); |
2291 | |
|
2292 | 0 | while (Token::Match(tokOffset, "*|&")) { |
2293 | 0 | pointers.push_back(tokOffset->str()); |
2294 | 0 | tokOffset = tokOffset->next(); |
2295 | 0 | } |
2296 | |
|
2297 | 0 | if (Token::Match(tokOffset, "%type%")) { |
2298 | 0 | typeName = tokOffset; |
2299 | 0 | tokOffset = tokOffset->next(); |
2300 | |
|
2301 | 0 | if (tokOffset && tokOffset->str() == "[") { |
2302 | 0 | arrayStart = tokOffset; |
2303 | |
|
2304 | 0 | for (;;) { |
2305 | 0 | while (tokOffset->next() && !Token::Match(tokOffset->next(), ";|,")) |
2306 | 0 | tokOffset = tokOffset->next(); |
2307 | |
|
2308 | 0 | if (!tokOffset->next()) |
2309 | 0 | return; // invalid input |
2310 | 0 | if (tokOffset->next()->str() == ";") |
2311 | 0 | break; |
2312 | 0 | if (tokOffset->str() == "]") |
2313 | 0 | break; |
2314 | 0 | tokOffset = tokOffset->next(); |
2315 | 0 | } |
2316 | | |
2317 | 0 | arrayEnd = tokOffset; |
2318 | 0 | tokOffset = tokOffset->next(); |
2319 | 0 | } |
2320 | | |
2321 | 0 | if (Token::Match(tokOffset, ";|,")) |
2322 | 0 | tok = tokOffset; |
2323 | 0 | else { |
2324 | | // we encountered a typedef we don't support yet so just continue |
2325 | 0 | done = true; |
2326 | 0 | ok = false; |
2327 | 0 | } |
2328 | 0 | } else { |
2329 | | // we encountered a typedef we don't support yet so just continue |
2330 | 0 | done = true; |
2331 | 0 | ok = false; |
2332 | 0 | } |
2333 | 0 | } else { |
2334 | | // something is really wrong (internal error) |
2335 | 0 | done = true; |
2336 | 0 | ok = false; |
2337 | 0 | } |
2338 | 0 | } |
2339 | | |
2340 | 0 | if (ok) { |
2341 | | // remove typedef |
2342 | 0 | Token::eraseTokens(typeDef, tok); |
2343 | |
|
2344 | 0 | if (typeDef != list.front()) { |
2345 | 0 | tok = typeDef->previous(); |
2346 | 0 | tok->deleteNext(); |
2347 | | //no need to remove last token in the list |
2348 | 0 | if (tok->tokAt(2)) |
2349 | 0 | tok->deleteNext(); |
2350 | 0 | } else { |
2351 | 0 | list.front()->deleteThis(); |
2352 | | //no need to remove last token in the list |
2353 | 0 | if (list.front()->next()) |
2354 | 0 | list.front()->deleteThis(); |
2355 | 0 | tok = list.front(); |
2356 | | //now the next token to process is 'tok', not 'tok->next()'; |
2357 | 0 | goback = true; |
2358 | 0 | } |
2359 | 0 | } |
2360 | 0 | } |
2361 | 1.36k | } |
2362 | | |
2363 | | namespace { |
2364 | | struct ScopeInfo3 { |
2365 | | enum Type { Global, Namespace, Record, MemberFunction, Other }; |
2366 | 1.36k | ScopeInfo3() : parent(nullptr), type(Global), bodyStart(nullptr), bodyEnd(nullptr) {} |
2367 | | ScopeInfo3(ScopeInfo3 *parent_, Type type_, std::string name_, const Token *bodyStart_, const Token *bodyEnd_) |
2368 | 3.58k | : parent(parent_), type(type_), name(std::move(name_)), bodyStart(bodyStart_), bodyEnd(bodyEnd_) { |
2369 | 3.58k | if (name.empty()) |
2370 | 3.58k | return; |
2371 | 0 | fullName = name; |
2372 | 0 | ScopeInfo3 *scope = parent; |
2373 | 0 | while (scope && scope->parent) { |
2374 | 0 | if (scope->name.empty()) |
2375 | 0 | break; |
2376 | 0 | fullName = scope->name + " :: " + fullName; |
2377 | 0 | scope = scope->parent; |
2378 | 0 | } |
2379 | 0 | } |
2380 | | ScopeInfo3 *parent; |
2381 | | std::list<ScopeInfo3> children; |
2382 | | Type type; |
2383 | | std::string fullName; |
2384 | | std::string name; |
2385 | | const Token * bodyStart; |
2386 | | const Token * bodyEnd; |
2387 | | std::set<std::string> usingNamespaces; |
2388 | | std::set<std::string> recordTypes; |
2389 | | std::set<std::string> baseTypes; |
2390 | | |
2391 | 3.58k | ScopeInfo3 *addChild(Type scopeType, const std::string &scopeName, const Token *bodyStartToken, const Token *bodyEndToken) { |
2392 | 3.58k | children.emplace_back(this, scopeType, scopeName, bodyStartToken, bodyEndToken); |
2393 | 3.58k | return &children.back(); |
2394 | 3.58k | } |
2395 | | |
2396 | 0 | bool hasChild(const std::string &childName) const { |
2397 | 0 | return std::any_of(children.cbegin(), children.cend(), [&](const ScopeInfo3& child) { |
2398 | 0 | return child.name == childName; |
2399 | 0 | }); |
2400 | 0 | } |
2401 | | |
2402 | 0 | const ScopeInfo3 * findInChildren(const std::string & scope) const { |
2403 | 0 | for (const auto & child : children) { |
2404 | 0 | if (child.type == Record && (child.name == scope || child.fullName == scope)) |
2405 | 0 | return &child; |
2406 | | |
2407 | 0 | const ScopeInfo3 * temp = child.findInChildren(scope); |
2408 | 0 | if (temp) |
2409 | 0 | return temp; |
2410 | 0 | } |
2411 | 0 | return nullptr; |
2412 | 0 | } |
2413 | | |
2414 | 0 | const ScopeInfo3 * findScope(const std::string & scope) const { |
2415 | 0 | const ScopeInfo3 * tempScope = this; |
2416 | 0 | while (tempScope) { |
2417 | | // check children |
2418 | 0 | auto it = std::find_if(tempScope->children.cbegin(), tempScope->children.cend(), [&](const ScopeInfo3& child) { |
2419 | 0 | return &child != this && child.type == Record && (child.name == scope || child.fullName == scope); |
2420 | 0 | }); |
2421 | 0 | if (it != tempScope->children.end()) |
2422 | 0 | return &*it; |
2423 | | // check siblings for same name |
2424 | 0 | if (tempScope->parent) { |
2425 | 0 | for (const auto &sibling : tempScope->parent->children) { |
2426 | 0 | if (sibling.name == tempScope->name && &sibling != this) { |
2427 | 0 | const ScopeInfo3 * temp = sibling.findInChildren(scope); |
2428 | 0 | if (temp) |
2429 | 0 | return temp; |
2430 | 0 | } |
2431 | 0 | } |
2432 | 0 | } |
2433 | 0 | tempScope = tempScope->parent; |
2434 | 0 | } |
2435 | 0 | return nullptr; |
2436 | 0 | } |
2437 | | |
2438 | 0 | bool findTypeInBase(const std::string &scope) const { |
2439 | 0 | if (scope.empty()) |
2440 | 0 | return false; |
2441 | | // check in base types first |
2442 | 0 | if (baseTypes.find(scope) != baseTypes.end()) |
2443 | 0 | return true; |
2444 | | // check in base types base types |
2445 | 0 | for (const std::string & base : baseTypes) { |
2446 | 0 | const ScopeInfo3 * baseScope = findScope(base); |
2447 | | // bail on uninstantiated recursive template |
2448 | 0 | if (baseScope == this) |
2449 | 0 | return false; |
2450 | 0 | if (baseScope && baseScope->fullName == scope) |
2451 | 0 | return true; |
2452 | 0 | if (baseScope && baseScope->findTypeInBase(scope)) |
2453 | 0 | return true; |
2454 | 0 | } |
2455 | 0 | return false; |
2456 | 0 | } |
2457 | | |
2458 | 0 | ScopeInfo3 * findScope(const ScopeInfo3 * scope) { |
2459 | 0 | if (scope->bodyStart == bodyStart) |
2460 | 0 | return this; |
2461 | 0 | for (auto & child : children) { |
2462 | 0 | ScopeInfo3 * temp = child.findScope(scope); |
2463 | 0 | if (temp) |
2464 | 0 | return temp; |
2465 | 0 | } |
2466 | 0 | return nullptr; |
2467 | 0 | } |
2468 | | }; |
2469 | | |
2470 | | void setScopeInfo(Token *tok, ScopeInfo3 **scopeInfo, bool debug=false) |
2471 | 7.17k | { |
2472 | 7.17k | if (!tok) |
2473 | 0 | return; |
2474 | 7.17k | if (tok->str() == "{" && (*scopeInfo)->parent && tok == (*scopeInfo)->bodyStart) |
2475 | 0 | return; |
2476 | 7.17k | if (tok->str() == "}") { |
2477 | 3.58k | if ((*scopeInfo)->parent && tok == (*scopeInfo)->bodyEnd) |
2478 | 3.58k | *scopeInfo = (*scopeInfo)->parent; |
2479 | 0 | else { |
2480 | | // Try to find parent scope |
2481 | 0 | ScopeInfo3 *parent = (*scopeInfo)->parent; |
2482 | 0 | while (parent && parent->bodyEnd != tok) |
2483 | 0 | parent = parent->parent; |
2484 | 0 | if (parent) { |
2485 | 0 | *scopeInfo = parent; |
2486 | 0 | if (debug) |
2487 | 0 | throw std::runtime_error("Internal error: unmatched }"); |
2488 | 0 | } |
2489 | 0 | } |
2490 | 3.58k | return; |
2491 | 3.58k | } |
2492 | 3.58k | if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::|<")) { |
2493 | | // check for using namespace |
2494 | 3.58k | if (Token::Match(tok, "using namespace %name% ;|::")) { |
2495 | 0 | const Token * tok1 = tok->tokAt(2); |
2496 | 0 | std::string nameSpace; |
2497 | 0 | while (tok1 && tok1->str() != ";") { |
2498 | 0 | if (!nameSpace.empty()) |
2499 | 0 | nameSpace += " "; |
2500 | 0 | nameSpace += tok1->str(); |
2501 | 0 | tok1 = tok1->next(); |
2502 | 0 | } |
2503 | 0 | (*scopeInfo)->usingNamespaces.insert(std::move(nameSpace)); |
2504 | 0 | } |
2505 | | // check for member function |
2506 | 3.58k | else if (tok->str() == "{") { |
2507 | 3.58k | bool added = false; |
2508 | 3.58k | Token *tok1 = tok; |
2509 | 3.58k | while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) |
2510 | 0 | tok1 = tok1->previous(); |
2511 | 3.58k | if (tok1->previous() && (tok1->strAt(-1) == ")" || tok->strAt(-1) == "}")) { |
2512 | 3.12k | tok1 = tok1->linkAt(-1); |
2513 | 3.12k | if (Token::Match(tok1->previous(), "throw|noexcept (")) { |
2514 | 0 | tok1 = tok1->previous(); |
2515 | 0 | while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) |
2516 | 0 | tok1 = tok1->previous(); |
2517 | 0 | if (tok1->strAt(-1) != ")") |
2518 | 0 | return; |
2519 | 0 | tok1 = tok1->linkAt(-1); |
2520 | 3.12k | } else { |
2521 | 3.12k | while (Token::Match(tok1->tokAt(-2), ":|, %name%")) { |
2522 | 0 | tok1 = tok1->tokAt(-2); |
2523 | 0 | if (tok1->strAt(-1) != ")" && tok1->strAt(-1) != "}") |
2524 | 0 | return; |
2525 | 0 | tok1 = tok1->linkAt(-1); |
2526 | 0 | } |
2527 | 3.12k | } |
2528 | 3.12k | if (tok1->strAt(-1) == ">") |
2529 | 0 | tok1 = tok1->previous()->findOpeningBracket(); |
2530 | 3.12k | if (tok1 && (Token::Match(tok1->tokAt(-3), "%name% :: %name%") || |
2531 | 3.12k | Token::Match(tok1->tokAt(-4), "%name% :: ~ %name%"))) { |
2532 | 0 | tok1 = tok1->tokAt(-2); |
2533 | 0 | if (tok1->str() == "~") |
2534 | 0 | tok1 = tok1->previous(); |
2535 | 0 | std::string scope = tok1->strAt(-1); |
2536 | 0 | while (Token::Match(tok1->tokAt(-2), ":: %name%")) { |
2537 | 0 | scope = tok1->strAt(-3) + " :: " + scope; |
2538 | 0 | tok1 = tok1->tokAt(-2); |
2539 | 0 | } |
2540 | 0 | *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::MemberFunction, scope, tok, tok->link()); |
2541 | 0 | added = true; |
2542 | 0 | } |
2543 | 3.12k | } |
2544 | | |
2545 | 3.58k | if (!added) |
2546 | 3.58k | *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::Other, emptyString, tok, tok->link()); |
2547 | 3.58k | } |
2548 | 3.58k | return; |
2549 | 3.58k | } |
2550 | | |
2551 | 0 | const bool record = Token::Match(tok, "class|struct|union %name%"); |
2552 | 0 | tok = tok->next(); |
2553 | 0 | std::string classname = tok->str(); |
2554 | 0 | while (Token::Match(tok, "%name% :: %name%")) { |
2555 | 0 | tok = tok->tokAt(2); |
2556 | 0 | classname += " :: " + tok->str(); |
2557 | 0 | } |
2558 | | |
2559 | | // add record type to scope info |
2560 | 0 | if (record) |
2561 | 0 | (*scopeInfo)->recordTypes.insert(classname); |
2562 | 0 | tok = tok->next(); |
2563 | | |
2564 | | // skip template parameters |
2565 | 0 | if (tok && tok->str() == "<") { |
2566 | 0 | tok = tok->findClosingBracket(); |
2567 | 0 | if (tok) |
2568 | 0 | tok = tok->next(); |
2569 | 0 | } |
2570 | | |
2571 | | // get base class types |
2572 | 0 | std::set<std::string> baseTypes; |
2573 | 0 | if (tok && tok->str() == ":") { |
2574 | 0 | do { |
2575 | 0 | tok = tok->next(); |
2576 | 0 | while (Token::Match(tok, "public|protected|private|virtual")) |
2577 | 0 | tok = tok->next(); |
2578 | 0 | std::string base; |
2579 | 0 | while (tok && !Token::Match(tok, ";|,|{")) { |
2580 | 0 | if (!base.empty()) |
2581 | 0 | base += ' '; |
2582 | 0 | base += tok->str(); |
2583 | 0 | tok = tok->next(); |
2584 | | // add template parameters |
2585 | 0 | if (tok && tok->str() == "<") { |
2586 | 0 | const Token* endTok = tok->findClosingBracket(); |
2587 | 0 | if (endTok) { |
2588 | 0 | endTok = endTok->next(); |
2589 | 0 | while (tok != endTok) { |
2590 | 0 | base += tok->str(); |
2591 | 0 | tok = tok->next(); |
2592 | 0 | } |
2593 | 0 | } |
2594 | 0 | } |
2595 | 0 | } |
2596 | 0 | baseTypes.insert(std::move(base)); |
2597 | 0 | } while (tok && !Token::Match(tok, ";|{")); |
2598 | 0 | } |
2599 | |
|
2600 | 0 | if (tok && tok->str() == "{") { |
2601 | 0 | *scopeInfo = (*scopeInfo)->addChild(record ? ScopeInfo3::Record : ScopeInfo3::Namespace, classname, tok, tok->link()); |
2602 | 0 | (*scopeInfo)->baseTypes = baseTypes; |
2603 | 0 | } |
2604 | 0 | } |
2605 | | |
2606 | | Token *findSemicolon(Token *tok) |
2607 | 0 | { |
2608 | 0 | int level = 0; |
2609 | |
|
2610 | 0 | for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) { |
2611 | 0 | if (tok->str() == "{") |
2612 | 0 | ++level; |
2613 | 0 | else if (level > 0 && tok->str() == "}") |
2614 | 0 | --level; |
2615 | 0 | } |
2616 | |
|
2617 | 0 | return tok; |
2618 | 0 | } |
2619 | | |
2620 | | bool usingMatch( |
2621 | | const Token *nameToken, |
2622 | | const std::string &scope, |
2623 | | Token **tok, |
2624 | | const std::string &scope1, |
2625 | | const ScopeInfo3 *currentScope, |
2626 | | const ScopeInfo3 *memberClassScope) |
2627 | 0 | { |
2628 | 0 | Token *tok1 = *tok; |
2629 | |
|
2630 | 0 | if (tok1 && tok1->str() != nameToken->str()) |
2631 | 0 | return false; |
2632 | | |
2633 | | // skip this using |
2634 | 0 | if (tok1 == nameToken) { |
2635 | 0 | *tok = findSemicolon(tok1); |
2636 | 0 | return false; |
2637 | 0 | } |
2638 | | |
2639 | | // skip other using with this name |
2640 | 0 | if (tok1->strAt(-1) == "using") { |
2641 | | // fixme: this is wrong |
2642 | | // skip to end of scope |
2643 | 0 | if (currentScope->bodyEnd) |
2644 | 0 | *tok = const_cast<Token*>(currentScope->bodyEnd->previous()); |
2645 | 0 | return false; |
2646 | 0 | } |
2647 | | |
2648 | 0 | if (Token::Match(tok1->tokAt(-1), "class|struct|union|enum|namespace")) { |
2649 | | // fixme |
2650 | 0 | return false; |
2651 | 0 | } |
2652 | | |
2653 | | // get qualification |
2654 | 0 | std::string qualification; |
2655 | 0 | const Token* tok2 = tok1; |
2656 | 0 | std::string::size_type index = scope.size(); |
2657 | 0 | std::string::size_type new_index = std::string::npos; |
2658 | 0 | bool match = true; |
2659 | 0 | while (Token::Match(tok2->tokAt(-2), "%name% ::") && !tok2->tokAt(-2)->isKeyword()) { |
2660 | 0 | std::string last; |
2661 | 0 | if (match && !scope1.empty()) { |
2662 | 0 | new_index = scope1.rfind(' ', index - 1); |
2663 | 0 | if (new_index != std::string::npos) |
2664 | 0 | last = scope1.substr(new_index, index - new_index); |
2665 | 0 | else if (!qualification.empty()) |
2666 | 0 | last.clear(); |
2667 | 0 | else |
2668 | 0 | last = scope1; |
2669 | 0 | } else |
2670 | 0 | match = false; |
2671 | 0 | if (match && tok2->strAt(-2) == last) |
2672 | 0 | index = new_index; |
2673 | 0 | else { |
2674 | 0 | if (!qualification.empty()) |
2675 | 0 | qualification = " :: " + qualification; |
2676 | 0 | qualification = tok2->strAt(-2) + qualification; |
2677 | 0 | } |
2678 | 0 | tok2 = tok2->tokAt(-2); |
2679 | 0 | } |
2680 | |
|
2681 | 0 | std::string fullScope1 = scope1; |
2682 | 0 | if (!scope1.empty() && !qualification.empty()) |
2683 | 0 | fullScope1 += " :: "; |
2684 | 0 | fullScope1 += qualification; |
2685 | |
|
2686 | 0 | if (scope == fullScope1) |
2687 | 0 | return true; |
2688 | | |
2689 | 0 | const ScopeInfo3 *scopeInfo = memberClassScope ? memberClassScope : currentScope; |
2690 | | |
2691 | | // check in base types |
2692 | 0 | if (qualification.empty() && scopeInfo->findTypeInBase(scope)) |
2693 | 0 | return true; |
2694 | | |
2695 | | // check using namespace |
2696 | 0 | const ScopeInfo3 * tempScope = scopeInfo; |
2697 | 0 | while (tempScope) { |
2698 | | //if (!tempScope->parent->usingNamespaces.empty()) { |
2699 | 0 | const std::set<std::string>& usingNS = tempScope->usingNamespaces; |
2700 | 0 | if (!usingNS.empty()) { |
2701 | 0 | if (qualification.empty()) { |
2702 | 0 | if (usingNS.find(scope) != usingNS.end()) |
2703 | 0 | return true; |
2704 | 0 | } else { |
2705 | 0 | const std::string suffix = " :: " + qualification; |
2706 | 0 | if (std::any_of(usingNS.cbegin(), usingNS.cend(), [&](const std::string& ns) { |
2707 | 0 | return scope == ns + suffix; |
2708 | 0 | })) |
2709 | 0 | return true; |
2710 | 0 | } |
2711 | 0 | } |
2712 | 0 | tempScope = tempScope->parent; |
2713 | 0 | } |
2714 | | |
2715 | 0 | std::string newScope1 = scope1; |
2716 | | |
2717 | | // scopes didn't match so try higher scopes |
2718 | 0 | index = newScope1.size(); |
2719 | 0 | while (!newScope1.empty()) { |
2720 | 0 | const std::string::size_type separator = newScope1.rfind(" :: ", index - 1); |
2721 | 0 | if (separator != std::string::npos) |
2722 | 0 | newScope1.resize(separator); |
2723 | 0 | else |
2724 | 0 | newScope1.clear(); |
2725 | |
|
2726 | 0 | std::string newFullScope1 = newScope1; |
2727 | 0 | if (!newScope1.empty() && !qualification.empty()) |
2728 | 0 | newFullScope1 += " :: "; |
2729 | 0 | newFullScope1 += qualification; |
2730 | |
|
2731 | 0 | if (scope == newFullScope1) |
2732 | 0 | return true; |
2733 | 0 | } |
2734 | | |
2735 | 0 | return false; |
2736 | 0 | } |
2737 | | |
2738 | | std::string memberFunctionScope(const Token *tok) |
2739 | 0 | { |
2740 | 0 | std::string qualification; |
2741 | 0 | const Token *qualTok = tok->strAt(-2) == "~" ? tok->tokAt(-4) : tok->tokAt(-3); |
2742 | 0 | while (Token::Match(qualTok, "%type% ::")) { |
2743 | 0 | if (!qualification.empty()) |
2744 | 0 | qualification = " :: " + qualification; |
2745 | 0 | qualification = qualTok->str() + qualification; |
2746 | 0 | qualTok = qualTok->tokAt(-2); |
2747 | 0 | } |
2748 | 0 | return qualification; |
2749 | 0 | } |
2750 | | |
2751 | | const Token * memberFunctionEnd(const Token *tok) |
2752 | 0 | { |
2753 | 0 | if (tok->str() != "(") |
2754 | 0 | return nullptr; |
2755 | 0 | const Token *end = tok->link()->next(); |
2756 | 0 | while (end) { |
2757 | 0 | if (end->str() == "{" && !Token::Match(end->tokAt(-2), ":|, %name%")) |
2758 | 0 | return end; |
2759 | 0 | if (end->str() == ";") |
2760 | 0 | break; |
2761 | 0 | end = end->next(); |
2762 | 0 | } |
2763 | 0 | return nullptr; |
2764 | 0 | } |
2765 | | } // namespace |
2766 | | |
2767 | | bool Tokenizer::isMemberFunction(const Token *openParen) const |
2768 | 0 | { |
2769 | 0 | return (Token::Match(openParen->tokAt(-2), ":: %name% (") || |
2770 | 0 | Token::Match(openParen->tokAt(-3), ":: ~ %name% (")) && |
2771 | 0 | isFunctionHead(openParen, "{|:"); |
2772 | 0 | } |
2773 | | |
2774 | | static bool scopesMatch(const std::string &scope1, const std::string &scope2, const ScopeInfo3 *globalScope) |
2775 | 0 | { |
2776 | 0 | if (scope1.empty() || scope2.empty()) |
2777 | 0 | return false; |
2778 | | |
2779 | | // check if scopes match |
2780 | 0 | if (scope1 == scope2) |
2781 | 0 | return true; |
2782 | | |
2783 | | // check if scopes only differ by global qualification |
2784 | 0 | if (scope1 == (":: " + scope2)) { |
2785 | 0 | std::string::size_type end = scope2.find_first_of(' '); |
2786 | 0 | if (end == std::string::npos) |
2787 | 0 | end = scope2.size(); |
2788 | 0 | if (globalScope->hasChild(scope2.substr(0, end))) |
2789 | 0 | return true; |
2790 | 0 | } else if (scope2 == (":: " + scope1)) { |
2791 | 0 | std::string::size_type end = scope1.find_first_of(' '); |
2792 | 0 | if (end == std::string::npos) |
2793 | 0 | end = scope1.size(); |
2794 | 0 | if (globalScope->hasChild(scope1.substr(0, end))) |
2795 | 0 | return true; |
2796 | 0 | } |
2797 | | |
2798 | 0 | return false; |
2799 | 0 | } |
2800 | | |
2801 | 0 | static unsigned int tokDistance(const Token* tok1, const Token* tok2) { |
2802 | 0 | unsigned int dist = 0; |
2803 | 0 | const Token* tok = tok1; |
2804 | 0 | while (tok != tok2) { |
2805 | 0 | ++dist; |
2806 | 0 | tok = tok->next(); |
2807 | 0 | } |
2808 | 0 | return dist; |
2809 | 0 | } |
2810 | | |
2811 | | bool Tokenizer::simplifyUsing() |
2812 | 1.36k | { |
2813 | 1.36k | if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) |
2814 | 0 | return false; |
2815 | | |
2816 | 1.36k | const unsigned int maxReplacementTokens = 1000; // limit the number of tokens we replace |
2817 | | |
2818 | 1.36k | bool substitute = false; |
2819 | 1.36k | ScopeInfo3 scopeInfo; |
2820 | 1.36k | ScopeInfo3 *currentScope = &scopeInfo; |
2821 | 1.36k | struct Using { |
2822 | 1.36k | Using(Token *start, Token *end) : startTok(start), endTok(end) {} |
2823 | 1.36k | Token *startTok; |
2824 | 1.36k | Token *endTok; |
2825 | 1.36k | }; |
2826 | 1.36k | std::list<Using> usingList; |
2827 | | |
2828 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
2829 | 92.7k | if (mErrorLogger && !list.getFiles().empty()) |
2830 | 92.7k | mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue()); |
2831 | | |
2832 | 92.7k | if (Settings::terminated()) |
2833 | 0 | return substitute; |
2834 | | |
2835 | 92.7k | if (Token::Match(tok, "enum class|struct")) { |
2836 | 0 | Token *bodyStart = tok; |
2837 | 0 | while (Token::Match(bodyStart, "%name%|:|::|<")) { |
2838 | 0 | if (bodyStart->str() == "<") |
2839 | 0 | bodyStart = bodyStart->findClosingBracket(); |
2840 | 0 | bodyStart = bodyStart ? bodyStart->next() : nullptr; |
2841 | 0 | } |
2842 | 0 | if (Token::simpleMatch(bodyStart, "{")) |
2843 | 0 | tok = bodyStart->link(); |
2844 | 0 | continue; |
2845 | 0 | } |
2846 | | |
2847 | 92.7k | if (Token::Match(tok, "{|}|namespace|class|struct|union") || |
2848 | 92.7k | Token::Match(tok, "using namespace %name% ;|::")) { |
2849 | 7.17k | try { |
2850 | 7.17k | setScopeInfo(tok, ¤tScope, mSettings->debugwarnings); |
2851 | 7.17k | } catch (const std::runtime_error &) { |
2852 | 0 | reportError(tok, Severity::debug, "simplifyUsingUnmatchedBodyEnd", |
2853 | 0 | "simplifyUsing: unmatched body end"); |
2854 | 0 | } |
2855 | 7.17k | continue; |
2856 | 7.17k | } |
2857 | | |
2858 | | // skip template declarations |
2859 | 85.5k | if (Token::Match(tok, "template < !!>")) { |
2860 | | // add template record type to scope info |
2861 | 0 | const Token *end = tok->next()->findClosingBracket(); |
2862 | 0 | if (end && Token::Match(end->next(), "class|struct|union %name%")) |
2863 | 0 | currentScope->recordTypes.insert(end->strAt(2)); |
2864 | |
|
2865 | 0 | Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); |
2866 | 0 | if (declEndToken) |
2867 | 0 | tok = declEndToken; |
2868 | 0 | continue; |
2869 | 0 | } |
2870 | | |
2871 | | // look for non-template type aliases |
2872 | 85.5k | if (!(tok->strAt(-1) != ">" && |
2873 | 85.5k | (Token::Match(tok, "using %name% = ::| %name%") || |
2874 | 85.0k | (Token::Match(tok, "using %name% [ [") && |
2875 | 85.0k | Token::Match(tok->linkAt(2), "] ] = ::| %name%"))))) |
2876 | 85.5k | continue; |
2877 | | |
2878 | 0 | const std::string& name = tok->strAt(1); |
2879 | 0 | const Token *nameToken = tok->next(); |
2880 | 0 | std::string scope = currentScope->fullName; |
2881 | 0 | Token *usingStart = tok; |
2882 | 0 | Token *start; |
2883 | 0 | if (tok->strAt(2) == "=") |
2884 | 0 | start = tok->tokAt(3); |
2885 | 0 | else |
2886 | 0 | start = tok->linkAt(2)->tokAt(3); |
2887 | 0 | Token *usingEnd = findSemicolon(start); |
2888 | 0 | if (!usingEnd) |
2889 | 0 | continue; |
2890 | | |
2891 | | // Move struct defined in using out of using. |
2892 | | // using T = struct t { }; => struct t { }; using T = struct t; |
2893 | | // fixme: this doesn't handle attributes |
2894 | 0 | if (Token::Match(start, "class|struct|union|enum %name%| {|:")) { |
2895 | 0 | Token *structEnd = start->tokAt(1); |
2896 | 0 | const bool hasName = Token::Match(structEnd, "%name%"); |
2897 | | |
2898 | | // skip over name if present |
2899 | 0 | if (hasName) |
2900 | 0 | structEnd = structEnd->next(); |
2901 | | |
2902 | | // skip over base class information |
2903 | 0 | if (structEnd->str() == ":") { |
2904 | 0 | structEnd = structEnd->next(); // skip over ":" |
2905 | 0 | while (structEnd && structEnd->str() != "{") |
2906 | 0 | structEnd = structEnd->next(); |
2907 | 0 | if (!structEnd) |
2908 | 0 | continue; |
2909 | 0 | } |
2910 | | |
2911 | | // use link to go to end |
2912 | 0 | structEnd = structEnd->link(); |
2913 | | |
2914 | | // add ';' after end of struct |
2915 | 0 | structEnd->insertToken(";", emptyString); |
2916 | | |
2917 | | // add name for anonymous struct |
2918 | 0 | if (!hasName) { |
2919 | 0 | std::string newName; |
2920 | 0 | if (structEnd->strAt(2) == ";") |
2921 | 0 | newName = name; |
2922 | 0 | else |
2923 | 0 | newName = "Unnamed" + std::to_string(mUnnamedCount++); |
2924 | 0 | TokenList::copyTokens(structEnd->next(), tok, start); |
2925 | 0 | structEnd->tokAt(5)->insertToken(newName, emptyString); |
2926 | 0 | start->insertToken(newName, emptyString); |
2927 | 0 | } else |
2928 | 0 | TokenList::copyTokens(structEnd->next(), tok, start->next()); |
2929 | | |
2930 | | // add using after end of struct |
2931 | 0 | usingStart = structEnd->tokAt(2); |
2932 | 0 | nameToken = usingStart->next(); |
2933 | 0 | if (usingStart->strAt(2) == "=") |
2934 | 0 | start = usingStart->tokAt(3); |
2935 | 0 | else |
2936 | 0 | start = usingStart->linkAt(2)->tokAt(3); |
2937 | 0 | usingEnd = findSemicolon(start); |
2938 | | |
2939 | | // delete original using before struct |
2940 | 0 | tok->deleteThis(); |
2941 | 0 | tok->deleteThis(); |
2942 | 0 | tok->deleteThis(); |
2943 | 0 | tok = usingStart; |
2944 | 0 | } |
2945 | | |
2946 | | // remove 'typename' and 'template' |
2947 | 0 | else if (start->str() == "typename") { |
2948 | 0 | start->deleteThis(); |
2949 | 0 | Token *temp = start; |
2950 | 0 | while (Token::Match(temp, "%name% ::")) |
2951 | 0 | temp = temp->tokAt(2); |
2952 | 0 | if (Token::Match(temp, "template %name%")) |
2953 | 0 | temp->deleteThis(); |
2954 | 0 | } |
2955 | | |
2956 | 0 | if (usingEnd) |
2957 | 0 | tok = usingEnd; |
2958 | | |
2959 | | // Unfortunately we have to start searching from the beginning |
2960 | | // of the token stream because templates are instantiated at |
2961 | | // the end of the token stream and it may be used before then. |
2962 | 0 | ScopeInfo3 scopeInfo1; |
2963 | 0 | ScopeInfo3 *currentScope1 = &scopeInfo1; |
2964 | 0 | Token *startToken = list.front(); |
2965 | 0 | Token *endToken = nullptr; |
2966 | 0 | bool inMemberFunc = false; |
2967 | 0 | const ScopeInfo3 * memberFuncScope = nullptr; |
2968 | 0 | const Token * memberFuncEnd = nullptr; |
2969 | | |
2970 | | // We can limit the search to the current function when the type alias |
2971 | | // is defined in that function. |
2972 | 0 | if (currentScope->type == ScopeInfo3::Other || |
2973 | 0 | currentScope->type == ScopeInfo3::MemberFunction) { |
2974 | 0 | scopeInfo1 = scopeInfo; |
2975 | 0 | currentScope1 = scopeInfo1.findScope(currentScope); |
2976 | 0 | if (!currentScope1) |
2977 | 0 | return substitute; // something bad happened |
2978 | 0 | startToken = usingEnd->next(); |
2979 | 0 | endToken = const_cast<Token*>(currentScope->bodyEnd->next()); |
2980 | 0 | if (currentScope->type == ScopeInfo3::MemberFunction) { |
2981 | 0 | const ScopeInfo3 * temp = currentScope->findScope(currentScope->fullName); |
2982 | 0 | if (temp) { |
2983 | 0 | inMemberFunc = true; |
2984 | 0 | memberFuncScope = temp; |
2985 | 0 | memberFuncEnd = endToken; |
2986 | 0 | } |
2987 | 0 | } |
2988 | 0 | } |
2989 | | |
2990 | 0 | std::string scope1 = currentScope1->fullName; |
2991 | 0 | bool skip = false; // don't erase type aliases we can't parse |
2992 | 0 | Token *enumOpenBrace = nullptr; |
2993 | 0 | for (Token* tok1 = startToken; !skip && tok1 && tok1 != endToken; tok1 = tok1->next()) { |
2994 | | // skip enum body |
2995 | 0 | if (tok1 && tok1 == enumOpenBrace) { |
2996 | 0 | tok1 = tok1->link(); |
2997 | 0 | enumOpenBrace = nullptr; |
2998 | 0 | continue; |
2999 | 0 | } |
3000 | | |
3001 | 0 | if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || |
3002 | 0 | Token::Match(tok1, "using namespace %name% ;|::")) { |
3003 | 0 | try { |
3004 | 0 | setScopeInfo(tok1, ¤tScope1, mSettings->debugwarnings); |
3005 | 0 | } catch (const std::runtime_error &) { |
3006 | 0 | reportError(tok1, Severity::debug, "simplifyUsingUnmatchedBodyEnd", |
3007 | 0 | "simplifyUsing: unmatched body end"); |
3008 | 0 | } |
3009 | 0 | scope1 = currentScope1->fullName; |
3010 | 0 | if (inMemberFunc && memberFuncEnd && tok1 == memberFuncEnd) { |
3011 | 0 | inMemberFunc = false; |
3012 | 0 | memberFuncScope = nullptr; |
3013 | 0 | memberFuncEnd = nullptr; |
3014 | 0 | } |
3015 | 0 | continue; |
3016 | 0 | } |
3017 | | |
3018 | | // skip template definitions |
3019 | 0 | if (Token::Match(tok1, "template < !!>")) { |
3020 | 0 | Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok1); |
3021 | 0 | if (declEndToken) |
3022 | 0 | tok1 = declEndToken; |
3023 | 0 | continue; |
3024 | 0 | } |
3025 | | |
3026 | | // check for enum with body |
3027 | 0 | if (tok1->str() == "enum") { |
3028 | 0 | if (Token::Match(tok1, "enum class|struct")) |
3029 | 0 | tok1 = tok1->next(); |
3030 | 0 | Token *defStart = tok1; |
3031 | 0 | while (Token::Match(defStart, "%name%|::|:")) |
3032 | 0 | defStart = defStart->next(); |
3033 | 0 | if (Token::simpleMatch(defStart, "{")) |
3034 | 0 | enumOpenBrace = defStart; |
3035 | 0 | continue; |
3036 | 0 | } |
3037 | | |
3038 | | // check for member function and adjust scope |
3039 | 0 | if (isMemberFunction(tok1)) { |
3040 | 0 | if (!scope1.empty()) |
3041 | 0 | scope1 += " :: "; |
3042 | 0 | scope1 += memberFunctionScope(tok1); |
3043 | 0 | const ScopeInfo3 * temp = currentScope1->findScope(scope1); |
3044 | 0 | if (temp) { |
3045 | 0 | const Token *end = memberFunctionEnd(tok1); |
3046 | 0 | if (end) { |
3047 | 0 | inMemberFunc = true; |
3048 | 0 | memberFuncScope = temp; |
3049 | 0 | memberFuncEnd = end; |
3050 | 0 | } |
3051 | 0 | } |
3052 | 0 | continue; |
3053 | 0 | } |
3054 | 0 | if (inMemberFunc && memberFuncScope) { |
3055 | 0 | if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, memberFuncScope)) |
3056 | 0 | continue; |
3057 | 0 | } else if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, nullptr)) |
3058 | 0 | continue; |
3059 | | |
3060 | 0 | const auto nReplace = tokDistance(start, usingEnd); |
3061 | 0 | if (nReplace > maxReplacementTokens) { |
3062 | 0 | simplifyUsingError(usingStart, usingEnd); |
3063 | 0 | continue; |
3064 | 0 | } |
3065 | | |
3066 | | // remove the qualification |
3067 | 0 | std::string fullScope = scope; |
3068 | 0 | std::string removed; |
3069 | 0 | while (Token::Match(tok1->tokAt(-2), "%name% ::") && !tok1->tokAt(-2)->isKeyword()) { |
3070 | 0 | removed = (tok1->strAt(-2) + " :: ") + removed; |
3071 | 0 | if (fullScope == tok1->strAt(-2)) { |
3072 | 0 | tok1->deletePrevious(); |
3073 | 0 | tok1->deletePrevious(); |
3074 | 0 | break; |
3075 | 0 | } |
3076 | 0 | const std::string::size_type idx = fullScope.rfind("::"); |
3077 | |
|
3078 | 0 | if (idx == std::string::npos) |
3079 | 0 | break; |
3080 | | |
3081 | 0 | if (tok1->strAt(-2) == fullScope.substr(idx + 3)) { |
3082 | 0 | tok1->deletePrevious(); |
3083 | 0 | tok1->deletePrevious(); |
3084 | 0 | fullScope.resize(idx - 1); |
3085 | 0 | } else |
3086 | 0 | break; |
3087 | 0 | } |
3088 | | |
3089 | | // remove global namespace if present |
3090 | 0 | if (tok1->strAt(-1) == "::") { |
3091 | 0 | removed.insert(0, ":: "); |
3092 | 0 | tok1->deletePrevious(); |
3093 | 0 | } |
3094 | |
|
3095 | 0 | Token * arrayStart = nullptr; |
3096 | | |
3097 | | // parse the type |
3098 | 0 | Token *type = start; |
3099 | 0 | if (type->str() == "::") { |
3100 | 0 | type = type->next(); |
3101 | 0 | while (Token::Match(type, "%type% ::")) |
3102 | 0 | type = type->tokAt(2); |
3103 | 0 | if (Token::Match(type, "%type%")) |
3104 | 0 | type = type->next(); |
3105 | 0 | } else if (Token::Match(type, "%type% ::")) { |
3106 | 0 | do { |
3107 | 0 | type = type->tokAt(2); |
3108 | 0 | } while (Token::Match(type, "%type% ::")); |
3109 | 0 | if (Token::Match(type, "%type%")) |
3110 | 0 | type = type->next(); |
3111 | 0 | } else if (Token::Match(type, "%type%")) { |
3112 | 0 | while (Token::Match(type, "const|class|struct|union|enum %type%") || |
3113 | 0 | (type->next() && type->next()->isStandardType())) |
3114 | 0 | type = type->next(); |
3115 | |
|
3116 | 0 | type = type->next(); |
3117 | |
|
3118 | 0 | while (Token::Match(type, "%type%") && |
3119 | 0 | (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { |
3120 | 0 | type = type->next(); |
3121 | 0 | } |
3122 | |
|
3123 | 0 | bool atEnd = false; |
3124 | 0 | while (!atEnd) { |
3125 | 0 | if (type && type->str() == "::") { |
3126 | 0 | type = type->next(); |
3127 | 0 | } |
3128 | |
|
3129 | 0 | if (Token::Match(type, "%type%") && |
3130 | 0 | type->next() && !Token::Match(type->next(), "[|,|(")) { |
3131 | 0 | type = type->next(); |
3132 | 0 | } else if (Token::simpleMatch(type, "const (")) { |
3133 | 0 | type = type->next(); |
3134 | 0 | atEnd = true; |
3135 | 0 | } else |
3136 | 0 | atEnd = true; |
3137 | 0 | } |
3138 | 0 | } else |
3139 | 0 | syntaxError(type); |
3140 | | |
3141 | | // check for invalid input |
3142 | 0 | if (!type) |
3143 | 0 | syntaxError(tok1); |
3144 | | |
3145 | | // check for template |
3146 | 0 | if (type->str() == "<") { |
3147 | 0 | type = type->findClosingBracket(); |
3148 | |
|
3149 | 0 | while (type && Token::Match(type->next(), ":: %type%")) |
3150 | 0 | type = type->tokAt(2); |
3151 | |
|
3152 | 0 | if (!type) { |
3153 | 0 | syntaxError(tok1); |
3154 | 0 | } |
3155 | |
|
3156 | 0 | while (Token::Match(type->next(), "const|volatile")) |
3157 | 0 | type = type->next(); |
3158 | |
|
3159 | 0 | type = type->next(); |
3160 | 0 | } |
3161 | | |
3162 | | // check for pointers and references |
3163 | 0 | std::list<std::string> pointers; |
3164 | 0 | while (Token::Match(type, "*|&|&&|const")) { |
3165 | 0 | pointers.push_back(type->str()); |
3166 | 0 | type = type->next(); |
3167 | 0 | } |
3168 | | |
3169 | | // check for array |
3170 | 0 | if (type && type->str() == "[") { |
3171 | 0 | do { |
3172 | 0 | if (!arrayStart) |
3173 | 0 | arrayStart = type; |
3174 | |
|
3175 | 0 | bool atEnd = false; |
3176 | 0 | while (!atEnd) { |
3177 | 0 | while (type->next() && !Token::Match(type->next(), ";|,")) { |
3178 | 0 | type = type->next(); |
3179 | 0 | } |
3180 | |
|
3181 | 0 | if (!type->next()) |
3182 | 0 | syntaxError(type); // invalid input |
3183 | 0 | else if (type->next()->str() == ";") |
3184 | 0 | atEnd = true; |
3185 | 0 | else if (type->str() == "]") |
3186 | 0 | atEnd = true; |
3187 | 0 | else |
3188 | 0 | type = type->next(); |
3189 | 0 | } |
3190 | |
|
3191 | 0 | type = type->next(); |
3192 | 0 | } while (type && type->str() == "["); |
3193 | 0 | } |
3194 | | |
3195 | | // make sure we are in a good state |
3196 | 0 | if (!tok1 || !tok1->next()) |
3197 | 0 | break; // bail |
3198 | | |
3199 | 0 | Token* after = tok1->next(); |
3200 | | // check if type was parsed |
3201 | 0 | if (type && type == usingEnd) { |
3202 | | // check for array syntax and add type around variable |
3203 | 0 | if (arrayStart) { |
3204 | 0 | if (Token::Match(tok1->next(), "%name%")) { |
3205 | 0 | TokenList::copyTokens(tok1->next(), arrayStart, usingEnd->previous()); |
3206 | 0 | TokenList::copyTokens(tok1, start, arrayStart->previous()); |
3207 | 0 | tok1->deleteThis(); |
3208 | 0 | substitute = true; |
3209 | 0 | } |
3210 | 0 | } else { |
3211 | | // add some qualification back if needed |
3212 | 0 | std::string removed1 = removed; |
3213 | 0 | std::string::size_type idx = removed1.rfind(" ::"); |
3214 | 0 | if (idx != std::string::npos) |
3215 | 0 | removed1.resize(idx); |
3216 | 0 | if (scopesMatch(removed1, scope, &scopeInfo1)) { |
3217 | 0 | ScopeInfo3 * tempScope = currentScope; |
3218 | 0 | while (tempScope->parent) { |
3219 | 0 | if (tempScope->recordTypes.find(start->str()) != tempScope->recordTypes.end()) { |
3220 | 0 | std::string::size_type spaceIdx = 0; |
3221 | 0 | std::string::size_type startIdx = 0; |
3222 | 0 | while ((spaceIdx = removed1.find(' ', startIdx)) != std::string::npos) { |
3223 | 0 | tok1->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); |
3224 | 0 | startIdx = spaceIdx + 1; |
3225 | 0 | } |
3226 | 0 | tok1->previous()->insertToken(removed1.substr(startIdx)); |
3227 | 0 | tok1->previous()->insertToken("::"); |
3228 | 0 | break; |
3229 | 0 | } |
3230 | 0 | idx = removed1.rfind(" ::"); |
3231 | 0 | if (idx == std::string::npos) |
3232 | 0 | break; |
3233 | | |
3234 | 0 | removed1.resize(idx); |
3235 | 0 | tempScope = tempScope->parent; |
3236 | 0 | } |
3237 | 0 | } |
3238 | | |
3239 | | // Is this a "T()" expression where T is a pointer type? |
3240 | 0 | if (Token::Match(tok1, "%name% ( )") && !pointers.empty()) { |
3241 | 0 | Token* tok2 = tok1->linkAt(1); |
3242 | 0 | tok1->deleteThis(); |
3243 | 0 | TokenList::copyTokens(tok1, start, usingEnd->previous()); |
3244 | 0 | tok2->insertToken("0"); |
3245 | 0 | after = tok2->next(); |
3246 | 0 | } |
3247 | 0 | else { // just replace simple type aliases |
3248 | 0 | TokenList::copyTokens(tok1, start, usingEnd->previous()); |
3249 | 0 | tok1->deleteThis(); |
3250 | 0 | } |
3251 | 0 | substitute = true; |
3252 | 0 | } |
3253 | 0 | } else { |
3254 | 0 | skip = true; |
3255 | 0 | simplifyUsingError(usingStart, usingEnd); |
3256 | 0 | } |
3257 | 0 | tok1 = after->previous(); |
3258 | 0 | } |
3259 | | |
3260 | 0 | if (!skip) |
3261 | 0 | usingList.emplace_back(usingStart, usingEnd); |
3262 | 0 | } |
3263 | | |
3264 | | // delete all used type alias definitions |
3265 | 1.36k | for (std::list<Using>::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) { |
3266 | 0 | Token *usingStart = it->startTok; |
3267 | 0 | Token *usingEnd = it->endTok; |
3268 | 0 | if (usingStart->previous()) { |
3269 | 0 | if (usingEnd->next()) |
3270 | 0 | Token::eraseTokens(usingStart->previous(), usingEnd->next()); |
3271 | 0 | else { |
3272 | 0 | Token::eraseTokens(usingStart->previous(), usingEnd); |
3273 | 0 | usingEnd->deleteThis(); |
3274 | 0 | } |
3275 | 0 | } else { |
3276 | 0 | if (usingEnd->next()) { |
3277 | 0 | Token::eraseTokens(usingStart, usingEnd->next()); |
3278 | 0 | usingStart->deleteThis(); |
3279 | 0 | } else { |
3280 | | // this is the only code being checked so leave ';' |
3281 | 0 | Token::eraseTokens(usingStart, usingEnd); |
3282 | 0 | usingStart->deleteThis(); |
3283 | 0 | } |
3284 | 0 | } |
3285 | 0 | } |
3286 | | |
3287 | 1.36k | return substitute; |
3288 | 1.36k | } |
3289 | | |
3290 | | void Tokenizer::simplifyUsingError(const Token* usingStart, const Token* usingEnd) |
3291 | 0 | { |
3292 | 0 | if (mSettings->debugwarnings && mErrorLogger) { |
3293 | 0 | std::string str; |
3294 | 0 | for (const Token *tok = usingStart; tok && tok != usingEnd; tok = tok->next()) { |
3295 | 0 | if (!str.empty()) |
3296 | 0 | str += ' '; |
3297 | 0 | str += tok->str(); |
3298 | 0 | } |
3299 | 0 | str += " ;"; |
3300 | 0 | std::list<const Token *> callstack(1, usingStart); |
3301 | 0 | mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing", |
3302 | 0 | "Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal)); |
3303 | 0 | } |
3304 | 0 | } |
3305 | | |
3306 | | bool Tokenizer::createTokens(std::istream &code, |
3307 | | const std::string& FileName) |
3308 | 0 | { |
3309 | 0 | return list.createTokens(code, FileName); |
3310 | 0 | } |
3311 | | |
3312 | | void Tokenizer::createTokens(simplecpp::TokenList&& tokenList) |
3313 | 1.36k | { |
3314 | 1.36k | list.createTokens(std::move(tokenList)); |
3315 | 1.36k | } |
3316 | | |
3317 | | bool Tokenizer::simplifyTokens1(const std::string &configuration) |
3318 | 1.36k | { |
3319 | | // Fill the map mTypeSize.. |
3320 | 1.36k | fillTypeSizes(); |
3321 | | |
3322 | 1.36k | mConfiguration = configuration; |
3323 | | |
3324 | 1.36k | if (mTimerResults) { |
3325 | 0 | Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1", mSettings->showtime, mTimerResults); |
3326 | 0 | if (!simplifyTokenList1(list.getFiles().front().c_str())) |
3327 | 0 | return false; |
3328 | 1.36k | } else { |
3329 | 1.36k | if (!simplifyTokenList1(list.getFiles().front().c_str())) |
3330 | 0 | return false; |
3331 | 1.36k | } |
3332 | | |
3333 | 1.36k | if (mTimerResults) { |
3334 | 0 | Timer t("Tokenizer::simplifyTokens1::createAst", mSettings->showtime, mTimerResults); |
3335 | 0 | list.createAst(); |
3336 | 0 | list.validateAst(); |
3337 | 1.36k | } else { |
3338 | 1.36k | list.createAst(); |
3339 | 1.36k | list.validateAst(); |
3340 | 1.36k | } |
3341 | | |
3342 | 1.36k | if (mTimerResults) { |
3343 | 0 | Timer t("Tokenizer::simplifyTokens1::createSymbolDatabase", mSettings->showtime, mTimerResults); |
3344 | 0 | createSymbolDatabase(); |
3345 | 1.36k | } else { |
3346 | 1.36k | createSymbolDatabase(); |
3347 | 1.36k | } |
3348 | | |
3349 | 1.36k | if (mTimerResults) { |
3350 | 0 | Timer t("Tokenizer::simplifyTokens1::setValueType", mSettings->showtime, mTimerResults); |
3351 | 0 | mSymbolDatabase->setValueTypeInTokenList(false); |
3352 | 0 | mSymbolDatabase->setValueTypeInTokenList(true); |
3353 | 1.36k | } else { |
3354 | 1.36k | mSymbolDatabase->setValueTypeInTokenList(false); |
3355 | 1.36k | mSymbolDatabase->setValueTypeInTokenList(true); |
3356 | 1.36k | } |
3357 | | |
3358 | 1.36k | if (!mSettings->buildDir.empty()) |
3359 | 0 | Summaries::create(this, configuration); |
3360 | | |
3361 | | // TODO: do not run valueflow if no checks are being performed at all - e.g. unusedFunctions only |
3362 | 1.36k | const char* disableValueflowEnv = std::getenv("DISABLE_VALUEFLOW"); |
3363 | 1.36k | const bool doValueFlow = !disableValueflowEnv || (std::strcmp(disableValueflowEnv, "1") != 0); |
3364 | | |
3365 | 1.36k | if (doValueFlow) { |
3366 | 1.36k | if (mTimerResults) { |
3367 | 0 | Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings->showtime, mTimerResults); |
3368 | 0 | ValueFlow::setValues(list, *mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); |
3369 | 1.36k | } else { |
3370 | 1.36k | ValueFlow::setValues(list, *mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); |
3371 | 1.36k | } |
3372 | 1.36k | } |
3373 | | |
3374 | | // Warn about unhandled character literals |
3375 | 1.36k | if (mSettings->severity.isEnabled(Severity::portability)) { |
3376 | 93.5k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
3377 | 92.2k | if (tok->tokType() == Token::eChar && tok->values().empty()) { |
3378 | 0 | try { |
3379 | 0 | simplecpp::characterLiteralToLL(tok->str()); |
3380 | 0 | } catch (const std::exception &e) { |
3381 | 0 | unhandledCharLiteral(tok, e.what()); |
3382 | 0 | } |
3383 | 0 | } |
3384 | 92.2k | } |
3385 | 1.36k | } |
3386 | | |
3387 | 1.36k | if (doValueFlow) { |
3388 | 1.36k | mSymbolDatabase->setArrayDimensionsUsingValueFlow(); |
3389 | 1.36k | } |
3390 | | |
3391 | 1.36k | printDebugOutput(1); |
3392 | | |
3393 | 1.36k | return true; |
3394 | 1.36k | } |
3395 | | |
3396 | | bool Tokenizer::tokenize(std::istream &code, |
3397 | | const char FileName[], |
3398 | | const std::string &configuration) |
3399 | 0 | { |
3400 | 0 | if (!createTokens(code, FileName)) |
3401 | 0 | return false; |
3402 | | |
3403 | 0 | return simplifyTokens1(configuration); |
3404 | 0 | } |
3405 | | //--------------------------------------------------------------------------- |
3406 | | |
3407 | | void Tokenizer::findComplicatedSyntaxErrorsInTemplates() |
3408 | 1.36k | { |
3409 | 1.36k | validate(); |
3410 | 1.36k | mTemplateSimplifier->checkComplicatedSyntaxErrorsInTemplates(); |
3411 | 1.36k | } |
3412 | | |
3413 | | void Tokenizer::checkForEnumsWithTypedef() |
3414 | 1.36k | { |
3415 | 80.4k | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
3416 | 79.1k | if (Token::Match(tok, "enum %name% {")) { |
3417 | 0 | tok = tok->tokAt(2); |
3418 | 0 | const Token *tok2 = Token::findsimplematch(tok, "typedef", tok->link()); |
3419 | 0 | if (tok2) |
3420 | 0 | syntaxError(tok2); |
3421 | 0 | tok = tok->link(); |
3422 | 0 | } |
3423 | 79.1k | } |
3424 | 1.36k | } |
3425 | | |
3426 | | void Tokenizer::fillTypeSizes() |
3427 | 1.36k | { |
3428 | 1.36k | mTypeSize.clear(); |
3429 | 1.36k | mTypeSize["char"] = 1; |
3430 | 1.36k | mTypeSize["_Bool"] = mSettings->platform.sizeof_bool; |
3431 | 1.36k | mTypeSize["bool"] = mSettings->platform.sizeof_bool; |
3432 | 1.36k | mTypeSize["short"] = mSettings->platform.sizeof_short; |
3433 | 1.36k | mTypeSize["int"] = mSettings->platform.sizeof_int; |
3434 | 1.36k | mTypeSize["long"] = mSettings->platform.sizeof_long; |
3435 | 1.36k | mTypeSize["long long"] = mSettings->platform.sizeof_long_long; |
3436 | 1.36k | mTypeSize["float"] = mSettings->platform.sizeof_float; |
3437 | 1.36k | mTypeSize["double"] = mSettings->platform.sizeof_double; |
3438 | 1.36k | mTypeSize["long double"] = mSettings->platform.sizeof_long_double; |
3439 | 1.36k | mTypeSize["wchar_t"] = mSettings->platform.sizeof_wchar_t; |
3440 | 1.36k | mTypeSize["size_t"] = mSettings->platform.sizeof_size_t; |
3441 | 1.36k | mTypeSize["*"] = mSettings->platform.sizeof_pointer; |
3442 | 1.36k | } |
3443 | | |
3444 | | void Tokenizer::combineOperators() |
3445 | 1.36k | { |
3446 | 1.36k | const bool cpp = isCPP(); |
3447 | | |
3448 | | // Combine tokens.. |
3449 | 80.4k | for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { |
3450 | 79.1k | const char c1 = tok->str()[0]; |
3451 | | |
3452 | 79.1k | if (tok->str().length() == 1 && tok->next()->str().length() == 1) { |
3453 | 27.0k | const char c2 = tok->next()->str()[0]; |
3454 | | |
3455 | | // combine +-*/ and = |
3456 | 27.0k | if (c2 == '=' && (std::strchr("+-*/%|^=!<>", c1)) && !Token::Match(tok->previous(), "%type% *")) { |
3457 | | // skip templates |
3458 | 0 | if (cpp && (tok->str() == ">" || Token::simpleMatch(tok->previous(), "> *"))) { |
3459 | 0 | const Token* opening = |
3460 | 0 | tok->str() == ">" ? tok->findOpeningBracket() : tok->previous()->findOpeningBracket(); |
3461 | 0 | if (opening && Token::Match(opening->previous(), "%name%")) |
3462 | 0 | continue; |
3463 | 0 | } |
3464 | 0 | tok->str(tok->str() + c2); |
3465 | 0 | tok->deleteNext(); |
3466 | 0 | continue; |
3467 | 0 | } |
3468 | 52.0k | } else if (tok->next()->str() == "=") { |
3469 | 8.71k | if (tok->str() == ">>") { |
3470 | 0 | tok->str(">>="); |
3471 | 0 | tok->deleteNext(); |
3472 | 8.71k | } else if (tok->str() == "<<") { |
3473 | 0 | tok->str("<<="); |
3474 | 0 | tok->deleteNext(); |
3475 | 0 | } |
3476 | 43.3k | } else if (cpp && (c1 == 'p' || c1 == '_') && |
3477 | 43.3k | Token::Match(tok, "private|protected|public|__published : !!:")) { |
3478 | 0 | bool simplify = false; |
3479 | 0 | int par = 0; |
3480 | 0 | for (const Token *prev = tok->previous(); prev; prev = prev->previous()) { |
3481 | 0 | if (prev->str() == ")") { |
3482 | 0 | ++par; |
3483 | 0 | } else if (prev->str() == "(") { |
3484 | 0 | if (par == 0U) |
3485 | 0 | break; |
3486 | 0 | --par; |
3487 | 0 | } |
3488 | 0 | if (par != 0U || prev->str() == "(") |
3489 | 0 | continue; |
3490 | 0 | if (Token::Match(prev, "[;{}]")) { |
3491 | 0 | simplify = true; |
3492 | 0 | break; |
3493 | 0 | } |
3494 | 0 | if (prev->isName() && prev->isUpperCaseName()) |
3495 | 0 | continue; |
3496 | 0 | if (prev->isName() && endsWith(prev->str(), ':')) |
3497 | 0 | simplify = true; |
3498 | 0 | break; |
3499 | 0 | } |
3500 | 0 | if (simplify) { |
3501 | 0 | tok->str(tok->str() + ":"); |
3502 | 0 | tok->deleteNext(); |
3503 | 0 | } |
3504 | 43.3k | } else if (tok->str() == "->") { |
3505 | | // If the preceding sequence is "( & %name% )", replace it by "%name%" |
3506 | 0 | Token *t = tok->tokAt(-4); |
3507 | 0 | if (Token::Match(t, "( & %name% )") && !Token::simpleMatch(t->previous(), ">")) { |
3508 | 0 | t->deleteThis(); |
3509 | 0 | t->deleteThis(); |
3510 | 0 | t->deleteNext(); |
3511 | 0 | tok->str("."); |
3512 | 0 | } else { |
3513 | 0 | tok->str("."); |
3514 | 0 | tok->originalName("->"); |
3515 | 0 | } |
3516 | 0 | } |
3517 | 79.1k | } |
3518 | 1.36k | } |
3519 | | |
3520 | | void Tokenizer::combineStringAndCharLiterals() |
3521 | 1.36k | { |
3522 | | // Combine strings |
3523 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3524 | 80.4k | if (!isStringLiteral(tok->str())) |
3525 | 80.4k | continue; |
3526 | | |
3527 | 0 | tok->str(simplifyString(tok->str())); |
3528 | |
|
3529 | 0 | while (Token::Match(tok->next(), "%str%") || Token::Match(tok->next(), "_T|_TEXT|TEXT ( %str% )")) { |
3530 | 0 | if (tok->next()->isName()) { |
3531 | 0 | if (!mSettings->platform.isWindows()) |
3532 | 0 | break; |
3533 | 0 | tok->deleteNext(2); |
3534 | 0 | tok->next()->deleteNext(); |
3535 | 0 | } |
3536 | | // Two strings after each other, combine them |
3537 | 0 | tok->concatStr(simplifyString(tok->next()->str())); |
3538 | 0 | tok->deleteNext(); |
3539 | 0 | } |
3540 | 0 | } |
3541 | 1.36k | } |
3542 | | |
3543 | | void Tokenizer::concatenateNegativeNumberAndAnyPositive() |
3544 | 1.36k | { |
3545 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3546 | 79.1k | if (!Token::Match(tok, "?|:|,|(|[|{|return|case|sizeof|%op% +|-") || tok->tokType() == Token::eIncDecOp) |
3547 | 77.7k | continue; |
3548 | | |
3549 | 1.36k | while (tok->str() != ">" && tok->next() && tok->next()->str() == "+" && (!Token::Match(tok->tokAt(2), "%name% (|;") || Token::Match(tok, "%op%"))) |
3550 | 0 | tok->deleteNext(); |
3551 | | |
3552 | 1.36k | if (Token::Match(tok->next(), "- %num%")) { |
3553 | 1.36k | tok->deleteNext(); |
3554 | 1.36k | tok->next()->str("-" + tok->next()->str()); |
3555 | 1.36k | } |
3556 | 1.36k | } |
3557 | 1.36k | } |
3558 | | |
3559 | | void Tokenizer::simplifyExternC() |
3560 | 1.36k | { |
3561 | 1.36k | if (isC()) |
3562 | 0 | return; |
3563 | | |
3564 | | // Add attributes to all tokens within `extern "C"` inlines and blocks, and remove the `extern "C"` tokens. |
3565 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3566 | 79.1k | if (Token::simpleMatch(tok, "extern \"C\"")) { |
3567 | 0 | Token *tok2 = tok->next(); |
3568 | 0 | if (tok->strAt(2) == "{") { |
3569 | 0 | tok2 = tok2->next(); // skip { |
3570 | 0 | while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2)) |
3571 | 0 | tok2->isExternC(true); |
3572 | 0 | tok->linkAt(2)->deleteThis(); // } |
3573 | 0 | tok->deleteNext(2); // "C" { |
3574 | 0 | } else { |
3575 | 0 | while ((tok2 = tok2->next()) && !Token::Match(tok2, "[;{]")) |
3576 | 0 | tok2->isExternC(true); |
3577 | 0 | tok->deleteNext(); // "C" |
3578 | 0 | } |
3579 | 0 | tok->deleteThis(); // extern |
3580 | 0 | } |
3581 | 79.1k | } |
3582 | 1.36k | } |
3583 | | |
3584 | | void Tokenizer::simplifyRoundCurlyParentheses() |
3585 | 1.36k | { |
3586 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3587 | 79.1k | while (Token::Match(tok, "[;{}:] ( {") && |
3588 | 79.1k | Token::simpleMatch(tok->linkAt(2), "} ) ;")) { |
3589 | 0 | if (tok->str() == ":" && !Token::Match(tok->tokAt(-2),"[;{}] %type% :")) |
3590 | 0 | break; |
3591 | 0 | Token *end = tok->linkAt(2)->tokAt(-3); |
3592 | 0 | if (Token::Match(end, "[;{}] %num%|%str% ;")) |
3593 | 0 | end->deleteNext(2); |
3594 | 0 | tok->linkAt(2)->previous()->deleteNext(3); |
3595 | 0 | tok->deleteNext(2); |
3596 | 0 | } |
3597 | 79.1k | if (Token::Match(tok, "( { %bool%|%char%|%num%|%str%|%name% ; } )")) { |
3598 | 0 | tok->deleteNext(); |
3599 | 0 | tok->deleteThis(); |
3600 | 0 | tok->deleteNext(3); |
3601 | 0 | } |
3602 | 79.1k | } |
3603 | 1.36k | } |
3604 | | |
3605 | | void Tokenizer::simplifySQL() |
3606 | 1.36k | { |
3607 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3608 | 80.4k | if (!Token::simpleMatch(tok, "__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL")) |
3609 | 80.4k | continue; |
3610 | | |
3611 | 0 | const Token *end = findSQLBlockEnd(tok); |
3612 | 0 | if (end == nullptr) |
3613 | 0 | syntaxError(nullptr); |
3614 | |
|
3615 | 0 | const std::string instruction = tok->stringifyList(end); |
3616 | | // delete all tokens until the embedded SQL block end |
3617 | 0 | Token::eraseTokens(tok, end); |
3618 | | |
3619 | | // insert "asm ( "instruction" ) ;" |
3620 | 0 | tok->str("asm"); |
3621 | | // it can happen that 'end' is NULL when wrong code is inserted |
3622 | 0 | if (!tok->next()) |
3623 | 0 | tok->insertToken(";"); |
3624 | 0 | tok->insertToken(")"); |
3625 | 0 | tok->insertToken("\"" + instruction + "\""); |
3626 | 0 | tok->insertToken("("); |
3627 | | // jump to ';' and continue |
3628 | 0 | tok = tok->tokAt(3); |
3629 | 0 | } |
3630 | 1.36k | } |
3631 | | |
3632 | | void Tokenizer::simplifyArrayAccessSyntax() |
3633 | 1.36k | { |
3634 | | // 0[a] -> a[0] |
3635 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3636 | 92.3k | if (tok->isNumber() && Token::Match(tok, "%num% [ %name% ]")) { |
3637 | 0 | const std::string number(tok->str()); |
3638 | 0 | Token* indexTok = tok->tokAt(2); |
3639 | 0 | tok->str(indexTok->str()); |
3640 | 0 | tok->varId(indexTok->varId()); |
3641 | 0 | indexTok->str(number); |
3642 | 0 | } |
3643 | 92.3k | } |
3644 | 1.36k | } |
3645 | | |
3646 | | void Tokenizer::simplifyParameterVoid() |
3647 | 1.36k | { |
3648 | 93.6k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
3649 | 92.3k | if (Token::Match(tok, "%name% ( void )") && !Token::Match(tok, "sizeof|decltype|typeof|return")) { |
3650 | 0 | tok->next()->deleteNext(); |
3651 | 0 | tok->next()->setRemovedVoidParameter(true); |
3652 | 0 | } |
3653 | 92.3k | } |
3654 | 1.36k | } |
3655 | | |
3656 | | void Tokenizer::simplifyRedundantConsecutiveBraces() |
3657 | 1.36k | { |
3658 | | // Remove redundant consecutive braces, i.e. '.. { { .. } } ..' -> '.. { .. } ..'. |
3659 | 93.6k | for (Token *tok = list.front(); tok;) { |
3660 | 92.3k | if (Token::simpleMatch(tok, "= {")) { |
3661 | 0 | tok = tok->linkAt(1); |
3662 | 92.3k | } else if (Token::simpleMatch(tok, "{ {") && Token::simpleMatch(tok->next()->link(), "} }")) { |
3663 | | //remove internal parentheses |
3664 | 0 | tok->next()->link()->deleteThis(); |
3665 | 0 | tok->deleteNext(); |
3666 | 0 | } else |
3667 | 92.3k | tok = tok->next(); |
3668 | 92.3k | } |
3669 | 1.36k | } |
3670 | | |
3671 | | void Tokenizer::simplifyDoublePlusAndDoubleMinus() |
3672 | 1.36k | { |
3673 | | // Convert - - into + and + - into - |
3674 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3675 | 92.3k | while (tok->next()) { |
3676 | 90.9k | if (tok->str() == "+") { |
3677 | 279 | if (tok->next()->str()[0] == '-') { |
3678 | 12 | tok = tok->next(); |
3679 | 12 | if (tok->str().size() == 1) { |
3680 | 0 | tok = tok->previous(); |
3681 | 0 | tok->str("-"); |
3682 | 0 | tok->deleteNext(); |
3683 | 12 | } else if (tok->isNumber()) { |
3684 | 0 | tok->str(tok->str().substr(1)); |
3685 | 0 | tok = tok->previous(); |
3686 | 0 | tok->str("-"); |
3687 | 0 | } |
3688 | 12 | continue; |
3689 | 12 | } |
3690 | 90.6k | } else if (tok->str() == "-") { |
3691 | 250 | if (tok->next()->str()[0] == '-') { |
3692 | 1 | tok = tok->next(); |
3693 | 1 | if (tok->str().size() == 1) { |
3694 | 0 | tok = tok->previous(); |
3695 | 0 | tok->str("+"); |
3696 | 0 | tok->deleteNext(); |
3697 | 1 | } else if (tok->isNumber()) { |
3698 | 0 | tok->str(tok->str().substr(1)); |
3699 | 0 | tok = tok->previous(); |
3700 | 0 | tok->str("+"); |
3701 | 0 | } |
3702 | 1 | continue; |
3703 | 1 | } |
3704 | 250 | } |
3705 | | |
3706 | 90.9k | break; |
3707 | 90.9k | } |
3708 | 92.3k | } |
3709 | 1.36k | } |
3710 | | |
3711 | | /** Specify array size if it hasn't been given */ |
3712 | | |
3713 | | void Tokenizer::arraySize() |
3714 | 1.36k | { |
3715 | 1.36k | auto getStrTok = [](Token* tok, bool addLength, Token** endStmt) -> Token* { |
3716 | 0 | if (addLength) { |
3717 | 0 | *endStmt = tok->tokAt(5); |
3718 | 0 | return tok->tokAt(4); |
3719 | 0 | } |
3720 | 0 | if (Token::Match(tok, "%var% [ ] =")) { |
3721 | 0 | tok = tok->tokAt(4); |
3722 | 0 | int parCount = 0; |
3723 | 0 | while (Token::simpleMatch(tok, "(")) { |
3724 | 0 | ++parCount; |
3725 | 0 | tok = tok->next(); |
3726 | 0 | } |
3727 | 0 | if (Token::Match(tok, "%str%")) { |
3728 | 0 | *endStmt = tok->tokAt(parCount + 1); |
3729 | 0 | return tok; |
3730 | 0 | } |
3731 | 0 | } |
3732 | 0 | return nullptr; |
3733 | 0 | }; |
3734 | | |
3735 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3736 | 92.3k | if (!tok->isName() || !Token::Match(tok, "%var% [ ] =")) |
3737 | 92.3k | continue; |
3738 | 0 | bool addlength = false; |
3739 | 0 | if (Token::Match(tok->previous(), "!!* %var% [ ] = { %str% } ;")) { |
3740 | 0 | Token *t = tok->tokAt(3); |
3741 | 0 | t->deleteNext(); |
3742 | 0 | t->next()->deleteNext(); |
3743 | 0 | addlength = true; |
3744 | 0 | } |
3745 | |
|
3746 | 0 | Token* endStmt{}; |
3747 | 0 | if (const Token* strTok = getStrTok(tok, addlength, &endStmt)) { |
3748 | 0 | const int sz = Token::getStrArraySize(strTok); |
3749 | 0 | tok->next()->insertToken(std::to_string(sz)); |
3750 | 0 | tok = endStmt; |
3751 | 0 | } |
3752 | | |
3753 | 0 | else if (Token::Match(tok, "%var% [ ] = {")) { |
3754 | 0 | MathLib::biguint sz = 1; |
3755 | 0 | tok = tok->next(); |
3756 | 0 | Token *end = tok->linkAt(3); |
3757 | 0 | for (Token *tok2 = tok->tokAt(4); tok2 && tok2 != end; tok2 = tok2->next()) { |
3758 | 0 | if (tok2->link() && Token::Match(tok2, "{|(|[|<")) { |
3759 | 0 | if (tok2->str() == "[" && tok2->link()->strAt(1) == "=") { // designated initializer |
3760 | 0 | if (Token::Match(tok2, "[ %num% ]")) |
3761 | 0 | sz = std::max(sz, MathLib::toULongNumber(tok2->strAt(1)) + 1U); |
3762 | 0 | else { |
3763 | 0 | sz = 0; |
3764 | 0 | break; |
3765 | 0 | } |
3766 | 0 | } |
3767 | 0 | tok2 = tok2->link(); |
3768 | 0 | } else if (tok2->str() == ",") { |
3769 | 0 | if (!Token::Match(tok2->next(), "[},]")) |
3770 | 0 | ++sz; |
3771 | 0 | else { |
3772 | 0 | tok2 = tok2->previous(); |
3773 | 0 | tok2->deleteNext(); |
3774 | 0 | } |
3775 | 0 | } |
3776 | 0 | } |
3777 | |
|
3778 | 0 | if (sz != 0) |
3779 | 0 | tok->insertToken(std::to_string(sz)); |
3780 | |
|
3781 | 0 | tok = end->next() ? end->next() : end; |
3782 | 0 | } |
3783 | 0 | } |
3784 | 1.36k | } |
3785 | | |
3786 | | static Token *skipTernaryOp(Token *tok) |
3787 | 0 | { |
3788 | 0 | int colonLevel = 1; |
3789 | 0 | while (nullptr != (tok = tok->next())) { |
3790 | 0 | if (tok->str() == "?") { |
3791 | 0 | ++colonLevel; |
3792 | 0 | } else if (tok->str() == ":") { |
3793 | 0 | --colonLevel; |
3794 | 0 | if (colonLevel == 0) { |
3795 | 0 | tok = tok->next(); |
3796 | 0 | break; |
3797 | 0 | } |
3798 | 0 | } |
3799 | 0 | if (tok->link() && Token::Match(tok, "[(<]")) |
3800 | 0 | tok = tok->link(); |
3801 | 0 | else if (Token::Match(tok->next(), "[{};)]")) |
3802 | 0 | break; |
3803 | 0 | } |
3804 | 0 | if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' |
3805 | 0 | return nullptr; |
3806 | 0 | return tok; |
3807 | 0 | } |
3808 | | |
3809 | | // Skips until the colon at the end of the case label, the argument must point to the "case" token. |
3810 | | // In case of success returns the colon token. |
3811 | | // In case of failure returns the token that caused the error. |
3812 | | static Token *skipCaseLabel(Token *tok) |
3813 | 0 | { |
3814 | 0 | assert(tok->str() == "case"); |
3815 | 0 | while (nullptr != (tok = tok->next())) { |
3816 | 0 | if (Token::Match(tok, "(|[")) |
3817 | 0 | tok = tok->link(); |
3818 | 0 | else if (tok->str() == "?") { |
3819 | 0 | Token * tok1 = skipTernaryOp(tok); |
3820 | 0 | if (!tok1) |
3821 | 0 | return tok; |
3822 | 0 | tok = tok1; |
3823 | 0 | } |
3824 | 0 | if (Token::Match(tok, "[:{};]")) |
3825 | 0 | return tok; |
3826 | 0 | } |
3827 | 0 | return nullptr; |
3828 | 0 | } |
3829 | | |
3830 | | const Token * Tokenizer::startOfExecutableScope(const Token * tok) |
3831 | 99.1k | { |
3832 | 99.1k | if (tok->str() != ")") |
3833 | 95.6k | return nullptr; |
3834 | | |
3835 | 3.48k | tok = isFunctionHead(tok, ":{", true); |
3836 | | |
3837 | 3.48k | if (Token::Match(tok, ": %name% [({]")) { |
3838 | 0 | while (Token::Match(tok, "[:,] %name% [({]")) |
3839 | 0 | tok = tok->linkAt(2)->next(); |
3840 | 0 | } |
3841 | | |
3842 | 3.48k | return (tok && tok->str() == "{") ? tok : nullptr; |
3843 | 99.1k | } |
3844 | | |
3845 | | |
3846 | | /** simplify labels and case|default in the code: add a ";" if not already in.*/ |
3847 | | |
3848 | | void Tokenizer::simplifyLabelsCaseDefault() |
3849 | 1.36k | { |
3850 | 1.36k | const bool cpp = isCPP(); |
3851 | 1.36k | bool executablescope = false; |
3852 | 1.36k | int indentLevel = 0; |
3853 | 66.3k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3854 | | // Simplify labels in the executable scope.. |
3855 | 64.9k | Token *start = const_cast<Token *>(startOfExecutableScope(tok)); |
3856 | 64.9k | if (start) { |
3857 | 1.74k | tok = start; |
3858 | 1.74k | executablescope = true; |
3859 | 1.74k | } |
3860 | | |
3861 | 64.9k | if (!executablescope) |
3862 | 39.2k | continue; |
3863 | | |
3864 | 25.6k | if (tok->str() == "{") { |
3865 | 3.58k | if (tok->previous()->str() == "=") |
3866 | 0 | tok = tok->link(); |
3867 | 3.58k | else |
3868 | 3.58k | ++indentLevel; |
3869 | 22.0k | } else if (tok->str() == "}") { |
3870 | 3.58k | --indentLevel; |
3871 | 3.58k | if (indentLevel == 0) { |
3872 | 1.74k | executablescope = false; |
3873 | 1.74k | continue; |
3874 | 1.74k | } |
3875 | 18.5k | } else if (Token::Match(tok, "(|[")) |
3876 | 1.76k | tok = tok->link(); |
3877 | | |
3878 | 23.9k | if (Token::Match(tok, "[;{}:] case")) { |
3879 | 0 | tok = skipCaseLabel(tok->next()); |
3880 | 0 | if (!tok) |
3881 | 0 | break; |
3882 | 0 | if (tok->str() != ":" || tok->strAt(-1) == "case" || !tok->next()) |
3883 | 0 | syntaxError(tok); |
3884 | 0 | if (tok->next()->str() != ";" && tok->next()->str() != "case") |
3885 | 0 | tok->insertToken(";"); |
3886 | 0 | else |
3887 | 0 | tok = tok->previous(); |
3888 | 23.9k | } else if (Token::Match(tok, "[;{}] %name% : !!;")) { |
3889 | 0 | if (!cpp || !Token::Match(tok->next(), "class|struct|enum")) { |
3890 | 0 | tok = tok->tokAt(2); |
3891 | 0 | tok->insertToken(";"); |
3892 | 0 | } |
3893 | 0 | } |
3894 | 23.9k | } |
3895 | 1.36k | } |
3896 | | |
3897 | | |
3898 | | void Tokenizer::simplifyCaseRange() |
3899 | 1.36k | { |
3900 | 80.4k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
3901 | 79.1k | if (Token::Match(tok, "case %num%|%char% ... %num%|%char% :")) { |
3902 | 0 | const MathLib::bigint start = MathLib::toLongNumber(tok->strAt(1)); |
3903 | 0 | MathLib::bigint end = MathLib::toLongNumber(tok->strAt(3)); |
3904 | 0 | end = std::min(start + 50, end); // Simplify it 50 times at maximum |
3905 | 0 | if (start < end) { |
3906 | 0 | tok = tok->tokAt(2); |
3907 | 0 | tok->str(":"); |
3908 | 0 | tok->insertToken("case"); |
3909 | 0 | for (MathLib::bigint i = end-1; i > start; i--) { |
3910 | 0 | tok->insertToken(":"); |
3911 | 0 | tok->insertToken(std::to_string(i)); |
3912 | 0 | tok->insertToken("case"); |
3913 | 0 | } |
3914 | 0 | } |
3915 | 0 | } |
3916 | 79.1k | } |
3917 | 1.36k | } |
3918 | | |
3919 | | void Tokenizer::calculateScopes() |
3920 | 1.36k | { |
3921 | 93.6k | for (auto *tok = list.front(); tok; tok = tok->next()) |
3922 | 92.3k | tok->scopeInfo(nullptr); |
3923 | | |
3924 | 1.36k | std::string nextScopeNameAddition; |
3925 | 1.36k | std::shared_ptr<ScopeInfo2> primaryScope = std::make_shared<ScopeInfo2>("", nullptr); |
3926 | 1.36k | list.front()->scopeInfo(primaryScope); |
3927 | | |
3928 | 93.6k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
3929 | 92.3k | if (tok == list.front() || !tok->scopeInfo()) { |
3930 | 88.7k | if (tok != list.front()) |
3931 | 87.3k | tok->scopeInfo(tok->previous()->scopeInfo()); |
3932 | | |
3933 | 88.7k | if (Token::Match(tok, "using namespace %name% ::|<|;")) { |
3934 | 0 | std::string usingNamespaceName; |
3935 | 0 | for (const Token* namespaceNameToken = tok->tokAt(2); |
3936 | 0 | namespaceNameToken && namespaceNameToken->str() != ";"; |
3937 | 0 | namespaceNameToken = namespaceNameToken->next()) { |
3938 | 0 | usingNamespaceName += namespaceNameToken->str(); |
3939 | 0 | usingNamespaceName += " "; |
3940 | 0 | } |
3941 | 0 | if (!usingNamespaceName.empty()) |
3942 | 0 | usingNamespaceName.pop_back(); |
3943 | 0 | tok->scopeInfo()->usingNamespaces.insert(std::move(usingNamespaceName)); |
3944 | 88.7k | } else if (Token::Match(tok, "namespace|class|struct|union %name% {|::|:|<")) { |
3945 | 0 | for (Token* nameTok = tok->next(); nameTok && !Token::Match(nameTok, "{|:"); nameTok = nameTok->next()) { |
3946 | 0 | if (Token::Match(nameTok, ";|<")) { |
3947 | 0 | nextScopeNameAddition = ""; |
3948 | 0 | break; |
3949 | 0 | } |
3950 | 0 | nextScopeNameAddition.append(nameTok->str()); |
3951 | 0 | nextScopeNameAddition.append(" "); |
3952 | 0 | } |
3953 | 0 | if (!nextScopeNameAddition.empty()) |
3954 | 0 | nextScopeNameAddition.pop_back(); |
3955 | 0 | } |
3956 | | |
3957 | 88.7k | if (Token::simpleMatch(tok, "{")) { |
3958 | | // This might be the opening of a member function |
3959 | 3.58k | Token *tok1 = tok; |
3960 | 3.58k | while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) |
3961 | 0 | tok1 = tok1->previous(); |
3962 | 3.58k | if (tok1->previous() && tok1->strAt(-1) == ")") { |
3963 | 3.12k | bool member = true; |
3964 | 3.12k | tok1 = tok1->linkAt(-1); |
3965 | 3.12k | if (Token::Match(tok1->previous(), "throw|noexcept")) { |
3966 | 0 | tok1 = tok1->previous(); |
3967 | 0 | while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) |
3968 | 0 | tok1 = tok1->previous(); |
3969 | 0 | if (tok1->strAt(-1) != ")") |
3970 | 0 | member = false; |
3971 | 3.12k | } else if (Token::Match(tok->tokAt(-2), ":|, %name%")) { |
3972 | 0 | tok1 = tok1->tokAt(-2); |
3973 | 0 | if (tok1->strAt(-1) != ")") |
3974 | 0 | member = false; |
3975 | 0 | } |
3976 | 3.12k | if (member) { |
3977 | 3.12k | if (tok1->strAt(-1) == ">") |
3978 | 0 | tok1 = tok1->previous()->findOpeningBracket(); |
3979 | 3.12k | if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { |
3980 | 0 | tok1 = tok1->tokAt(-2); |
3981 | 0 | std::string scope = tok1->strAt(-1); |
3982 | 0 | while (Token::Match(tok1->tokAt(-2), ":: %name%")) { |
3983 | 0 | scope = tok1->strAt(-3) + " :: " + scope; |
3984 | 0 | tok1 = tok1->tokAt(-2); |
3985 | 0 | } |
3986 | |
|
3987 | 0 | if (!nextScopeNameAddition.empty() && !scope.empty()) |
3988 | 0 | nextScopeNameAddition += " :: "; |
3989 | 0 | nextScopeNameAddition += scope; |
3990 | 0 | } |
3991 | 3.12k | } |
3992 | 3.12k | } |
3993 | | |
3994 | | // New scope is opening, record it here |
3995 | 3.58k | std::shared_ptr<ScopeInfo2> newScopeInfo = std::make_shared<ScopeInfo2>(tok->scopeInfo()->name, tok->link(), tok->scopeInfo()->usingNamespaces); |
3996 | | |
3997 | 3.58k | if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) |
3998 | 0 | newScopeInfo->name.append(" :: "); |
3999 | 3.58k | newScopeInfo->name.append(nextScopeNameAddition); |
4000 | 3.58k | nextScopeNameAddition = ""; |
4001 | | |
4002 | 3.58k | if (tok->link()) |
4003 | 3.58k | tok->link()->scopeInfo(tok->scopeInfo()); |
4004 | 3.58k | tok->scopeInfo(newScopeInfo); |
4005 | 3.58k | } |
4006 | 88.7k | } |
4007 | 92.3k | } |
4008 | 1.36k | } |
4009 | | |
4010 | | void Tokenizer::simplifyTemplates() |
4011 | 1.36k | { |
4012 | 1.36k | if (isC()) |
4013 | 0 | return; |
4014 | | |
4015 | 1.36k | const std::time_t maxTime = mSettings->templateMaxTime > 0 ? std::time(nullptr) + mSettings->templateMaxTime : 0; |
4016 | 1.36k | mTemplateSimplifier->simplifyTemplates( |
4017 | 1.36k | maxTime); |
4018 | 1.36k | } |
4019 | | //--------------------------------------------------------------------------- |
4020 | | |
4021 | | |
4022 | | /** Class used in Tokenizer::setVarIdPass1 */ |
4023 | | class VariableMap { |
4024 | | private: |
4025 | | std::unordered_map<std::string, nonneg int> mVariableId; |
4026 | | std::unordered_map<std::string, nonneg int> mVariableId_global; |
4027 | | std::stack<std::vector<std::pair<std::string, nonneg int>>> mScopeInfo; |
4028 | | mutable nonneg int mVarId{}; |
4029 | | public: |
4030 | 1.36k | VariableMap() = default; |
4031 | | void enterScope(); |
4032 | | bool leaveScope(); |
4033 | | void addVariable(const std::string& varname, bool globalNamespace); |
4034 | 0 | bool hasVariable(const std::string& varname) const { |
4035 | 0 | return mVariableId.find(varname) != mVariableId.end(); |
4036 | 0 | } |
4037 | | |
4038 | 74.9k | const std::unordered_map<std::string, nonneg int>& map(bool global) const { |
4039 | 74.9k | return global ? mVariableId_global : mVariableId; |
4040 | 74.9k | } |
4041 | 0 | nonneg int getVarId() const { |
4042 | 0 | return mVarId; |
4043 | 0 | } |
4044 | 24.8k | nonneg int& getVarId() { |
4045 | 24.8k | return mVarId; |
4046 | 24.8k | } |
4047 | | }; |
4048 | | |
4049 | | |
4050 | | void VariableMap::enterScope() |
4051 | 4.50k | { |
4052 | 4.50k | mScopeInfo.emplace(/*std::vector<std::pair<std::string, nonneg int>>()*/); |
4053 | 4.50k | } |
4054 | | |
4055 | | bool VariableMap::leaveScope() |
4056 | 4.50k | { |
4057 | 4.50k | if (mScopeInfo.empty()) |
4058 | 0 | return false; |
4059 | | |
4060 | 4.50k | for (const std::pair<std::string, nonneg int>& outerVariable : mScopeInfo.top()) { |
4061 | 1 | if (outerVariable.second != 0) |
4062 | 1 | mVariableId[outerVariable.first] = outerVariable.second; |
4063 | 0 | else |
4064 | 0 | mVariableId.erase(outerVariable.first); |
4065 | 1 | } |
4066 | 4.50k | mScopeInfo.pop(); |
4067 | 4.50k | return true; |
4068 | 4.50k | } |
4069 | | |
4070 | | void VariableMap::addVariable(const std::string& varname, bool globalNamespace) |
4071 | 6.81k | { |
4072 | 6.81k | if (mScopeInfo.empty()) { |
4073 | 6.81k | mVariableId[varname] = ++mVarId; |
4074 | 6.81k | if (globalNamespace) |
4075 | 6.81k | mVariableId_global[varname] = mVariableId[varname]; |
4076 | 6.81k | return; |
4077 | 6.81k | } |
4078 | 1 | std::unordered_map<std::string, nonneg int>::iterator it = mVariableId.find(varname); |
4079 | 1 | if (it == mVariableId.end()) { |
4080 | 0 | mScopeInfo.top().emplace_back(varname, 0); |
4081 | 0 | mVariableId[varname] = ++mVarId; |
4082 | 0 | if (globalNamespace) |
4083 | 0 | mVariableId_global[varname] = mVariableId[varname]; |
4084 | 0 | return; |
4085 | 0 | } |
4086 | 1 | mScopeInfo.top().emplace_back(varname, it->second); |
4087 | 1 | it->second = ++mVarId; |
4088 | 1 | } |
4089 | | |
4090 | | static bool setVarIdParseDeclaration(Token** tok, const VariableMap& variableMap, bool executableScope, bool cpp, bool c) |
4091 | 25.5k | { |
4092 | 25.5k | const Token* const tok1 = *tok; |
4093 | 25.5k | Token* tok2 = *tok; |
4094 | 25.5k | if (!tok2->isName()) |
4095 | 6.11k | return false; |
4096 | | |
4097 | 19.4k | nonneg int typeCount = 0; |
4098 | 19.4k | nonneg int singleNameCount = 0; |
4099 | 19.4k | bool hasstruct = false; // Is there a "struct" or "class"? |
4100 | 19.4k | bool bracket = false; |
4101 | 19.4k | bool ref = false; |
4102 | 39.0k | while (tok2) { |
4103 | 39.0k | if (tok2->isName()) { |
4104 | 28.0k | if (Token::simpleMatch(tok2, "alignas (")) { |
4105 | 0 | tok2 = tok2->linkAt(1)->next(); |
4106 | 0 | continue; |
4107 | 0 | } |
4108 | 28.0k | if (cpp && Token::Match(tok2, "namespace|public|private|protected")) |
4109 | 0 | return false; |
4110 | 28.0k | if (cpp && Token::simpleMatch(tok2, "decltype (")) { |
4111 | 0 | typeCount = 1; |
4112 | 0 | tok2 = tok2->linkAt(1)->next(); |
4113 | 0 | continue; |
4114 | 0 | } |
4115 | 28.0k | if (Token::Match(tok2, "struct|union|enum") || (!c && Token::Match(tok2, "class|typename"))) { |
4116 | 0 | hasstruct = true; |
4117 | 0 | typeCount = 0; |
4118 | 0 | singleNameCount = 0; |
4119 | 28.0k | } else if (Token::Match(tok2, "const|extern")) { |
4120 | | // just skip "const", "extern" |
4121 | 28.0k | } else if (!hasstruct && variableMap.map(false).count(tok2->str()) && tok2->previous()->str() != "::") { |
4122 | 8.43k | ++typeCount; |
4123 | 8.43k | tok2 = tok2->next(); |
4124 | 8.43k | if (!tok2 || tok2->str() != "::") |
4125 | 8.43k | break; |
4126 | 19.5k | } else { |
4127 | 19.5k | if (tok2->str() != "void" || Token::Match(tok2, "void const| *|(")) // just "void" cannot be a variable type |
4128 | 19.5k | ++typeCount; |
4129 | 19.5k | ++singleNameCount; |
4130 | 19.5k | } |
4131 | 28.0k | } else if (!c && ((TemplateSimplifier::templateParameters(tok2) > 0) || |
4132 | 11.0k | Token::simpleMatch(tok2, "< >") /* Ticket #4764 */)) { |
4133 | 3 | const Token *start = *tok; |
4134 | 3 | if (Token::Match(start->previous(), "%or%|%oror%|&&|&|^|+|-|*|/")) |
4135 | 0 | return false; |
4136 | 3 | Token* const closingBracket = tok2->findClosingBracket(); |
4137 | 3 | if (closingBracket == nullptr) { /* Ticket #8151 */ |
4138 | 0 | throw tok2; |
4139 | 0 | } |
4140 | 3 | tok2 = closingBracket; |
4141 | 3 | if (tok2->str() != ">") |
4142 | 0 | break; |
4143 | 3 | singleNameCount = 1; |
4144 | 3 | if (Token::Match(tok2, "> %name% %or%|%oror%|&&|&|^|+|-|*|/") && !Token::Match(tok2, "> const [*&]")) |
4145 | 0 | return false; |
4146 | 3 | if (Token::Match(tok2, "> %name% )")) { |
4147 | 2 | if (Token::Match(tok2->linkAt(2)->previous(), "if|for|while (")) |
4148 | 2 | return false; |
4149 | 0 | if (!Token::Match(tok2->linkAt(2)->previous(), "%name%|] (")) |
4150 | 0 | return false; |
4151 | 0 | } |
4152 | 11.0k | } else if (Token::Match(tok2, "&|&&")) { |
4153 | 8 | ref = !bracket; |
4154 | 11.0k | } else if (singleNameCount >= 1 && Token::Match(tok2, "( [*&]") && Token::Match(tok2->link(), ") (|[")) { |
4155 | 0 | for (const Token* tok3 = tok2->tokAt(2); Token::Match(tok3, "!!)"); tok3 = tok3->next()) { |
4156 | 0 | if (Token::Match(tok3, "(|[")) |
4157 | 0 | tok3 = tok3->link(); |
4158 | 0 | if (tok3->str() == ",") |
4159 | 0 | return false; |
4160 | 0 | } |
4161 | 0 | bracket = true; // Skip: Seems to be valid pointer to array or function pointer |
4162 | 11.0k | } else if (singleNameCount >= 1 && Token::Match(tok2, "( * %name% [") && Token::Match(tok2->linkAt(3), "] ) [;,]")) { |
4163 | 0 | bracket = true; |
4164 | 11.0k | } else if (singleNameCount >= 1 && tok2->previous() && tok2->previous()->isStandardType() && Token::Match(tok2, "( *|&| %name% ) ;")) { |
4165 | 0 | bracket = true; |
4166 | 11.0k | } else if (tok2->str() == "::") { |
4167 | 0 | singleNameCount = 0; |
4168 | 11.0k | } else if (tok2->str() != "*" && tok2->str() != "::" && tok2->str() != "...") { |
4169 | 11.0k | break; |
4170 | 11.0k | } |
4171 | 19.6k | tok2 = tok2->next(); |
4172 | 19.6k | } |
4173 | | |
4174 | 19.4k | if (tok2) { |
4175 | 19.4k | bool isLambdaArg = false; |
4176 | 19.4k | { |
4177 | 19.4k | const Token *tok3 = (*tok)->previous(); |
4178 | 19.4k | if (tok3 && tok3->str() == ",") { |
4179 | 0 | while (tok3 && !Token::Match(tok3,";|(|[|{")) { |
4180 | 0 | if (Token::Match(tok3, ")|]")) |
4181 | 0 | tok3 = tok3->link(); |
4182 | 0 | tok3 = tok3->previous(); |
4183 | 0 | } |
4184 | |
|
4185 | 0 | if (tok3 && executableScope && Token::Match(tok3->previous(), "%name% (")) { |
4186 | 0 | const Token *fdecl = tok3->previous(); |
4187 | 0 | int count = 0; |
4188 | 0 | while (Token::Match(fdecl, "%name%|*")) { |
4189 | 0 | fdecl = fdecl->previous(); |
4190 | 0 | count++; |
4191 | 0 | } |
4192 | 0 | if (!Token::Match(fdecl, "[;{}] %name%") || count <= 1) |
4193 | 0 | return false; |
4194 | 0 | } |
4195 | 0 | } |
4196 | | |
4197 | 19.4k | if (cpp && tok3 && Token::simpleMatch(tok3->previous(), "] (") && |
4198 | 19.4k | (Token::simpleMatch(tok3->link(), ") {") || Token::Match(tok3->link(), ") . %name%"))) |
4199 | 0 | isLambdaArg = true; |
4200 | 19.4k | } |
4201 | | |
4202 | | |
4203 | 0 | *tok = tok2; |
4204 | | |
4205 | | // In executable scopes, references must be assigned |
4206 | | // Catching by reference is an exception |
4207 | 19.4k | if (executableScope && ref && !isLambdaArg) { |
4208 | 8 | if (Token::Match(tok2, "(|=|{|:")) |
4209 | 1 | ; // reference is assigned => ok |
4210 | 7 | else if (tok2->str() != ")" || tok2->link()->strAt(-1) != "catch") |
4211 | 7 | return false; // not catching by reference => not declaration |
4212 | 8 | } |
4213 | 19.4k | } |
4214 | | |
4215 | | // Check if array declaration is valid (#2638) |
4216 | | // invalid declaration: AAA a[4] = 0; |
4217 | 19.4k | if (typeCount >= 2 && executableScope && Token::Match(tok2, ")| [")) { |
4218 | 0 | const Token *tok3 = tok2->str() == ")" ? tok2->next() : tok2; |
4219 | 0 | while (tok3 && tok3->str() == "[") { |
4220 | 0 | tok3 = tok3->link()->next(); |
4221 | 0 | } |
4222 | 0 | if (Token::Match(tok3, "= %num%")) |
4223 | 0 | return false; |
4224 | 0 | if (bracket && Token::Match(tok1->previous(), "[(,]") && Token::Match(tok3, "[,)]")) |
4225 | 0 | return false; |
4226 | 0 | } |
4227 | | |
4228 | 19.4k | return (typeCount >= 2 && tok2 && Token::Match(tok2->tokAt(-2), "!!:: %type%")); |
4229 | 19.4k | } |
4230 | | |
4231 | | |
4232 | | void Tokenizer::setVarIdStructMembers(Token **tok1, |
4233 | | std::map<nonneg int, std::map<std::string, nonneg int>>& structMembers, |
4234 | | nonneg int &varId) const |
4235 | 19.8k | { |
4236 | 19.8k | Token *tok = *tok1; |
4237 | | |
4238 | 19.8k | if (Token::Match(tok, "%name% = { . %name% =|{")) { |
4239 | 0 | const nonneg int struct_varid = tok->varId(); |
4240 | 0 | if (struct_varid == 0) |
4241 | 0 | return; |
4242 | | |
4243 | 0 | std::map<std::string, nonneg int>& members = structMembers[struct_varid]; |
4244 | |
|
4245 | 0 | tok = tok->tokAt(3); |
4246 | 0 | while (tok->str() != "}") { |
4247 | 0 | if (Token::Match(tok, "{|[|(")) |
4248 | 0 | tok = tok->link(); |
4249 | 0 | if (Token::Match(tok->previous(), "[,{] . %name% =|{")) { |
4250 | 0 | tok = tok->next(); |
4251 | 0 | const std::map<std::string, nonneg int>::iterator it = members.find(tok->str()); |
4252 | 0 | if (it == members.end()) { |
4253 | 0 | members[tok->str()] = ++varId; |
4254 | 0 | tok->varId(varId); |
4255 | 0 | } else { |
4256 | 0 | tok->varId(it->second); |
4257 | 0 | } |
4258 | 0 | } |
4259 | 0 | tok = tok->next(); |
4260 | 0 | } |
4261 | |
|
4262 | 0 | return; |
4263 | 0 | } |
4264 | | |
4265 | 19.8k | while (Token::Match(tok->next(), ")| . %name% !!(")) { |
4266 | | // Don't set varid for trailing return type |
4267 | 0 | if (tok->strAt(1) == ")" && (tok->linkAt(1)->previous()->isName() || tok->linkAt(1)->strAt(-1) == "]") && |
4268 | 0 | isFunctionHead(tok->linkAt(1), "{|;")) { |
4269 | 0 | tok = tok->tokAt(3); |
4270 | 0 | continue; |
4271 | 0 | } |
4272 | 0 | const nonneg int struct_varid = tok->varId(); |
4273 | 0 | tok = tok->tokAt(2); |
4274 | 0 | if (struct_varid == 0) |
4275 | 0 | continue; |
4276 | | |
4277 | 0 | if (tok->str() == ".") |
4278 | 0 | tok = tok->next(); |
4279 | | |
4280 | | // Don't set varid for template function |
4281 | 0 | if (TemplateSimplifier::templateParameters(tok->next()) > 0) |
4282 | 0 | break; |
4283 | | |
4284 | 0 | std::map<std::string, nonneg int>& members = structMembers[struct_varid]; |
4285 | 0 | const std::map<std::string, nonneg int>::iterator it = members.find(tok->str()); |
4286 | 0 | if (it == members.end()) { |
4287 | 0 | members[tok->str()] = ++varId; |
4288 | 0 | tok->varId(varId); |
4289 | 0 | } else { |
4290 | 0 | tok->varId(it->second); |
4291 | 0 | } |
4292 | 0 | } |
4293 | | // tok can't be null |
4294 | 19.8k | *tok1 = tok; |
4295 | 19.8k | } |
4296 | | |
4297 | | void Tokenizer::setVarIdClassDeclaration(Token* const startToken, |
4298 | | VariableMap& variableMap, |
4299 | | const nonneg int scopeStartVarId, |
4300 | | std::map<nonneg int, std::map<std::string, nonneg int>>& structMembers) |
4301 | 0 | { |
4302 | | // end of scope |
4303 | 0 | const Token* const endToken = startToken->link(); |
4304 | | |
4305 | | // determine class name |
4306 | 0 | std::string className; |
4307 | 0 | for (const Token *tok = startToken->previous(); tok; tok = tok->previous()) { |
4308 | 0 | if (!tok->isName() && tok->str() != ":") |
4309 | 0 | break; |
4310 | 0 | if (Token::Match(tok, "class|struct|enum %type% [:{]")) { |
4311 | 0 | className = tok->next()->str(); |
4312 | 0 | break; |
4313 | 0 | } |
4314 | 0 | } |
4315 | | |
4316 | | // replace varids.. |
4317 | 0 | int indentlevel = 0; |
4318 | 0 | bool initList = false; |
4319 | 0 | bool inEnum = false; |
4320 | 0 | const Token *initListArgLastToken = nullptr; |
4321 | 0 | for (Token *tok = startToken->next(); tok != endToken; tok = tok->next()) { |
4322 | 0 | if (!tok) |
4323 | 0 | syntaxError(nullptr); |
4324 | 0 | if (initList) { |
4325 | 0 | if (tok == initListArgLastToken) |
4326 | 0 | initListArgLastToken = nullptr; |
4327 | 0 | else if (!initListArgLastToken && |
4328 | 0 | Token::Match(tok->previous(), "%name%|>|>> {|(") && |
4329 | 0 | Token::Match(tok->link(), "}|) ,|{")) |
4330 | 0 | initListArgLastToken = tok->link(); |
4331 | 0 | } |
4332 | 0 | if (tok->str() == "{") { |
4333 | 0 | inEnum = isEnumStart(tok); |
4334 | 0 | if (initList && !initListArgLastToken) |
4335 | 0 | initList = false; |
4336 | 0 | ++indentlevel; |
4337 | 0 | } else if (tok->str() == "}") { |
4338 | 0 | --indentlevel; |
4339 | 0 | inEnum = false; |
4340 | 0 | } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { |
4341 | 0 | const std::unordered_map<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str()); |
4342 | 0 | if (it != variableMap.map(false).end()) { |
4343 | 0 | tok->varId(it->second); |
4344 | 0 | } |
4345 | 0 | } else if (tok->isName() && tok->varId() <= scopeStartVarId) { |
4346 | 0 | if (indentlevel > 0 || initList) { |
4347 | 0 | if (Token::Match(tok->previous(), "::|.") && tok->strAt(-2) != "this" && !Token::simpleMatch(tok->tokAt(-5), "( * this ) .")) |
4348 | 0 | continue; |
4349 | 0 | if (!tok->next()) |
4350 | 0 | syntaxError(nullptr); |
4351 | 0 | if (tok->next()->str() == "::") { |
4352 | 0 | if (tok->str() == className) |
4353 | 0 | tok = tok->tokAt(2); |
4354 | 0 | else |
4355 | 0 | continue; |
4356 | 0 | } |
4357 | | |
4358 | 0 | if (!inEnum) { |
4359 | 0 | const std::unordered_map<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str()); |
4360 | 0 | if (it != variableMap.map(false).end()) { |
4361 | 0 | tok->varId(it->second); |
4362 | 0 | setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); |
4363 | 0 | } |
4364 | 0 | } |
4365 | 0 | } |
4366 | 0 | } else if (indentlevel == 0 && tok->str() == ":" && !initListArgLastToken) |
4367 | 0 | initList = true; |
4368 | 0 | } |
4369 | 0 | } |
4370 | | |
4371 | | |
4372 | | |
4373 | | // Update the variable ids.. |
4374 | | // Parse each function.. |
4375 | | void Tokenizer::setVarIdClassFunction(const std::string &classname, |
4376 | | Token * const startToken, |
4377 | | const Token * const endToken, |
4378 | | const std::map<std::string, nonneg int> &varlist, |
4379 | | std::map<nonneg int, std::map<std::string, nonneg int>>& structMembers, |
4380 | | nonneg int &varId_) |
4381 | 0 | { |
4382 | 0 | for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { |
4383 | 0 | if (tok2->varId() != 0 || !tok2->isName()) |
4384 | 0 | continue; |
4385 | 0 | if (Token::Match(tok2->tokAt(-2), ("!!" + classname + " ::").c_str())) |
4386 | 0 | continue; |
4387 | 0 | if (Token::Match(tok2->tokAt(-4), "%name% :: %name% ::")) // Currently unsupported |
4388 | 0 | continue; |
4389 | 0 | if (Token::Match(tok2->tokAt(-2), "!!this .") && !Token::simpleMatch(tok2->tokAt(-5), "( * this ) .")) |
4390 | 0 | continue; |
4391 | 0 | if (Token::Match(tok2, "%name% ::")) |
4392 | 0 | continue; |
4393 | | |
4394 | 0 | const std::map<std::string, nonneg int>::const_iterator it = varlist.find(tok2->str()); |
4395 | 0 | if (it != varlist.end()) { |
4396 | 0 | tok2->varId(it->second); |
4397 | 0 | setVarIdStructMembers(&tok2, structMembers, varId_); |
4398 | 0 | } |
4399 | 0 | } |
4400 | 0 | } |
4401 | | |
4402 | | |
4403 | | |
4404 | | void Tokenizer::setVarId() |
4405 | 1.36k | { |
4406 | | // Clear all variable ids |
4407 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
4408 | 92.3k | if (tok->isName()) |
4409 | 35.7k | tok->varId(0); |
4410 | 92.3k | } |
4411 | | |
4412 | 1.36k | setVarIdPass1(); |
4413 | | |
4414 | 1.36k | setPodTypes(); |
4415 | | |
4416 | 1.36k | setVarIdPass2(); |
4417 | 1.36k | } |
4418 | | |
4419 | | |
4420 | | // Variable declarations can't start with "return" etc. |
4421 | | #define NOTSTART_C "NOT", "case", "default", "goto", "not", "return", "sizeof", "typedef" |
4422 | | static const std::unordered_set<std::string> notstart_c = { NOTSTART_C }; |
4423 | | static const std::unordered_set<std::string> notstart_cpp = { NOTSTART_C, |
4424 | | "delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template" |
4425 | | }; |
4426 | | |
4427 | | void Tokenizer::setVarIdPass1() |
4428 | 1.36k | { |
4429 | | // Variable declarations can't start with "return" etc. |
4430 | 1.36k | const std::unordered_set<std::string>& notstart = (isC()) ? notstart_c : notstart_cpp; |
4431 | | |
4432 | 1.36k | VariableMap variableMap; |
4433 | 1.36k | std::map<nonneg int, std::map<std::string, nonneg int>> structMembers; |
4434 | | |
4435 | 1.36k | std::stack<VarIdScopeInfo> scopeStack; |
4436 | | |
4437 | 1.36k | scopeStack.emplace(/*VarIdScopeInfo()*/); |
4438 | 1.36k | std::stack<const Token *> functionDeclEndStack; |
4439 | 1.36k | const Token *functionDeclEndToken = nullptr; |
4440 | 1.36k | bool initlist = false; |
4441 | 1.36k | bool inlineFunction = false; |
4442 | 80.0k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
4443 | 80.0k | if (tok->isOp()) |
4444 | 15.0k | continue; |
4445 | 64.9k | if (isCPP() && Token::simpleMatch(tok, "template <")) { |
4446 | 0 | Token* closingBracket = tok->next()->findClosingBracket(); |
4447 | 0 | if (closingBracket) |
4448 | 0 | tok = closingBracket; |
4449 | 0 | continue; |
4450 | 0 | } |
4451 | | |
4452 | 64.9k | if (tok == functionDeclEndToken) { |
4453 | 3.12k | functionDeclEndStack.pop(); |
4454 | 3.12k | functionDeclEndToken = functionDeclEndStack.empty() ? nullptr : functionDeclEndStack.top(); |
4455 | 3.12k | if (tok->str() == ":") |
4456 | 0 | initlist = true; |
4457 | 3.12k | else if (tok->str() == ";") { |
4458 | 0 | if (!variableMap.leaveScope()) |
4459 | 0 | cppcheckError(tok); |
4460 | 3.12k | } else if (tok->str() == "{") { |
4461 | 3.12k | scopeStack.emplace(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/ false, variableMap.getVarId()); |
4462 | | |
4463 | | // check if this '{' is a start of an "if" body |
4464 | 3.12k | const Token * ifToken = tok->previous(); |
4465 | 3.12k | if (ifToken && ifToken->str() == ")") |
4466 | 3.12k | ifToken = ifToken->link(); |
4467 | 0 | else |
4468 | 0 | ifToken = nullptr; |
4469 | 3.12k | if (ifToken) |
4470 | 3.12k | ifToken = ifToken->previous(); |
4471 | 3.12k | if (ifToken && ifToken->str() == "if") { |
4472 | | // open another scope to differentiate between variables declared in the "if" condition and in the "if" body |
4473 | 920 | variableMap.enterScope(); |
4474 | 920 | } |
4475 | 3.12k | } |
4476 | 61.8k | } else if (!initlist && tok->str()=="(") { |
4477 | 3.82k | const Token * newFunctionDeclEnd = nullptr; |
4478 | 3.82k | if (!scopeStack.top().isExecutable) |
4479 | 1.74k | newFunctionDeclEnd = isFunctionHead(tok, "{:;"); |
4480 | 2.08k | else { |
4481 | 2.08k | const Token* tokenLinkNext = tok->link()->next(); |
4482 | 2.08k | if (Token::simpleMatch(tokenLinkNext, ".")) { // skip trailing return type |
4483 | 0 | tokenLinkNext = tokenLinkNext->next(); |
4484 | 0 | while (Token::Match(tokenLinkNext, "%name%|::")) { |
4485 | 0 | tokenLinkNext = tokenLinkNext->next(); |
4486 | 0 | if (Token::simpleMatch(tokenLinkNext, "<") && tokenLinkNext->link()) |
4487 | 0 | tokenLinkNext = tokenLinkNext->link()->next(); |
4488 | 0 | } |
4489 | 0 | } |
4490 | 2.08k | if (tokenLinkNext && tokenLinkNext->str() == "{") // might be for- or while-loop or if-statement |
4491 | 1.38k | newFunctionDeclEnd = tokenLinkNext; |
4492 | 2.08k | } |
4493 | 3.82k | if (newFunctionDeclEnd && newFunctionDeclEnd != functionDeclEndToken) { |
4494 | 3.12k | functionDeclEndStack.push(newFunctionDeclEnd); |
4495 | 3.12k | functionDeclEndToken = newFunctionDeclEnd; |
4496 | 3.12k | variableMap.enterScope(); |
4497 | 3.12k | } |
4498 | 58.0k | } else if (Token::Match(tok, "{|}")) { |
4499 | 4.04k | inlineFunction = false; |
4500 | | |
4501 | 4.04k | const Token * const startToken = (tok->str() == "{") ? tok : tok->link(); |
4502 | | |
4503 | | // parse anonymous namespaces as part of the current scope |
4504 | 4.04k | if (!Token::Match(startToken->previous(), "union|struct|enum|namespace {") && |
4505 | 4.04k | !(initlist && Token::Match(startToken->previous(), "%name%|>|>>|(") && Token::Match(startToken->link(), "} ,|{|)"))) { |
4506 | | |
4507 | 4.04k | if (tok->str() == "{") { |
4508 | 458 | bool isExecutable; |
4509 | 458 | const Token *prev = tok->previous(); |
4510 | 916 | while (Token::Match(prev, "%name%|.")) |
4511 | 458 | prev = prev->previous(); |
4512 | 458 | const bool isLambda = prev && prev->str() == ")" && Token::simpleMatch(prev->link()->previous(), "] ("); |
4513 | 458 | if ((!isLambda && (tok->strAt(-1) == ")" || Token::Match(tok->tokAt(-2), ") %type%"))) || |
4514 | 458 | (initlist && tok->strAt(-1) == "}")) { |
4515 | 0 | isExecutable = true; |
4516 | 458 | } else { |
4517 | 458 | isExecutable = ((scopeStack.top().isExecutable || initlist || tok->strAt(-1) == "else") && |
4518 | 458 | !isClassStructUnionEnumStart(tok)); |
4519 | 458 | if (!(scopeStack.top().isStructInit || tok->strAt(-1) == "=")) |
4520 | 458 | variableMap.enterScope(); |
4521 | 458 | } |
4522 | 458 | initlist = false; |
4523 | 458 | scopeStack.emplace(isExecutable, scopeStack.top().isStructInit || tok->strAt(-1) == "=", isEnumStart(tok), variableMap.getVarId()); |
4524 | 3.58k | } else { /* if (tok->str() == "}") */ |
4525 | 3.58k | bool isNamespace = false; |
4526 | 4.04k | for (const Token *tok1 = tok->link()->previous(); tok1 && tok1->isName(); tok1 = tok1->previous()) { |
4527 | 458 | if (tok1->str() == "namespace") { |
4528 | 0 | isNamespace = true; |
4529 | 0 | break; |
4530 | 0 | } |
4531 | 458 | } |
4532 | | // Set variable ids in class declaration.. |
4533 | 3.58k | if (!initlist && !isC() && !scopeStack.top().isExecutable && tok->link() && !isNamespace) { |
4534 | 0 | setVarIdClassDeclaration(tok->link(), |
4535 | 0 | variableMap, |
4536 | 0 | scopeStack.top().startVarid, |
4537 | 0 | structMembers); |
4538 | 0 | } |
4539 | | |
4540 | 3.58k | if (!scopeStack.top().isStructInit) { |
4541 | 3.58k | variableMap.leaveScope(); |
4542 | | |
4543 | | // check if this '}' is an end of an "else" body or an "if" body without an "else" part |
4544 | 3.58k | const Token * ifToken = startToken->previous(); |
4545 | 3.58k | if (ifToken && ifToken->str() == ")") |
4546 | 3.12k | ifToken = ifToken->link()->previous(); |
4547 | 458 | else |
4548 | 458 | ifToken = nullptr; |
4549 | 3.58k | if (startToken->strAt(-1) == "else" || (ifToken && ifToken->str() == "if" && tok->strAt(1) != "else")) { |
4550 | | // leave the extra scope used to differentiate between variables declared in the "if" condition and in the "if" body |
4551 | 920 | variableMap.leaveScope(); |
4552 | 920 | } |
4553 | 3.58k | } |
4554 | | |
4555 | 3.58k | scopeStack.pop(); |
4556 | 3.58k | if (scopeStack.empty()) { // should be impossible |
4557 | 0 | scopeStack.emplace(/*VarIdScopeInfo()*/); |
4558 | 0 | } |
4559 | 3.58k | } |
4560 | 4.04k | } |
4561 | 4.04k | } |
4562 | | |
4563 | 64.9k | if (!scopeStack.top().isStructInit && |
4564 | 64.9k | (tok == list.front() || |
4565 | 64.9k | Token::Match(tok, "[;{}]") || |
4566 | 64.9k | (tok->str() == "(" && isFunctionHead(tok,"{")) || |
4567 | 64.9k | (tok->str() == "(" && !scopeStack.top().isExecutable && isFunctionHead(tok,";:")) || |
4568 | 64.9k | (tok->str() == "," && (!scopeStack.top().isExecutable || inlineFunction || !tok->previous()->varId())) || |
4569 | 64.9k | (tok->isName() && endsWith(tok->str(), ':')))) { |
4570 | | |
4571 | | // No variable declarations in sizeof |
4572 | 28.8k | if (Token::simpleMatch(tok->previous(), "sizeof (")) { |
4573 | 0 | continue; |
4574 | 0 | } |
4575 | | |
4576 | 28.8k | if (Settings::terminated()) |
4577 | 0 | return; |
4578 | | |
4579 | | // locate the variable name.. |
4580 | 28.8k | Token* tok2 = (tok->isName()) ? tok : tok->next(); |
4581 | | |
4582 | | // private: protected: public: etc |
4583 | 28.8k | while (tok2 && endsWith(tok2->str(), ':')) { |
4584 | 0 | tok2 = tok2->next(); |
4585 | 0 | } |
4586 | 28.8k | if (!tok2) |
4587 | 1.36k | break; |
4588 | | |
4589 | | // Variable declaration can't start with "return", etc |
4590 | 27.4k | if (notstart.find(tok2->str()) != notstart.end()) |
4591 | 1.91k | continue; |
4592 | | |
4593 | 25.5k | if (!isC() && Token::simpleMatch(tok2, "const new")) |
4594 | 0 | continue; |
4595 | | |
4596 | 25.5k | bool decl; |
4597 | 25.5k | if (isCPP() && mSettings->standards.cpp >= Standards::CPP17 && Token::Match(tok, "[(;{}] const| auto &|&&| [")) { |
4598 | | // Structured bindings |
4599 | 0 | tok2 = Token::findsimplematch(tok, "["); |
4600 | 0 | if ((Token::simpleMatch(tok->previous(), "for (") && Token::simpleMatch(tok2->link(), "] :")) || |
4601 | 0 | Token::simpleMatch(tok2->link(), "] =")) { |
4602 | 0 | while (tok2 && tok2->str() != "]") { |
4603 | 0 | if (Token::Match(tok2, "%name% [,]]")) |
4604 | 0 | variableMap.addVariable(tok2->str(), false); |
4605 | 0 | tok2 = tok2->next(); |
4606 | 0 | } |
4607 | 0 | continue; |
4608 | 0 | } |
4609 | 0 | } |
4610 | | |
4611 | 25.5k | try { /* Ticket #8151 */ |
4612 | 25.5k | decl = setVarIdParseDeclaration(&tok2, variableMap, scopeStack.top().isExecutable, isCPP(), isC()); |
4613 | 25.5k | } catch (const Token * errTok) { |
4614 | 0 | syntaxError(errTok); |
4615 | 0 | } |
4616 | 25.5k | if (decl) { |
4617 | 8.55k | if (isCPP()) { |
4618 | 8.55k | if (Token *declTypeTok = Token::findsimplematch(tok, "decltype (", tok2)) { |
4619 | 0 | for (Token *declTok = declTypeTok->linkAt(1); declTok != declTypeTok; declTok = declTok->previous()) { |
4620 | 0 | if (declTok->isName() && !Token::Match(declTok->previous(), "::|.") && variableMap.hasVariable(declTok->str())) |
4621 | 0 | declTok->varId(variableMap.map(false).find(declTok->str())->second); |
4622 | 0 | } |
4623 | 0 | } |
4624 | 8.55k | } |
4625 | | |
4626 | 8.55k | if (tok->str() == "(" && isFunctionHead(tok,"{") && scopeStack.top().isExecutable) |
4627 | 4 | inlineFunction = true; |
4628 | | |
4629 | 8.55k | const Token* prev2 = tok2->previous(); |
4630 | 8.55k | if (Token::Match(prev2, "%type% [;[=,)]") && tok2->previous()->str() != "const") |
4631 | 6.81k | ; |
4632 | 1.74k | else if (Token::Match(prev2, "%type% :") && tok->strAt(-1) == "for") |
4633 | 0 | ; |
4634 | 1.74k | else if (Token::Match(prev2, "%type% ( !!)") && Token::simpleMatch(tok2->link(), ") ;")) { |
4635 | | // In C++ , a variable can't be called operator+ or something like that. |
4636 | 0 | if (isCPP() && |
4637 | 0 | prev2->isOperatorKeyword()) |
4638 | 0 | continue; |
4639 | | |
4640 | 0 | const Token *tok3 = tok2->next(); |
4641 | 0 | if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && !Token::Match(tok2->link()->previous(), "[&*]")) { |
4642 | 0 | if (!scopeStack.top().isExecutable) { |
4643 | | // Detecting initializations with () in non-executable scope is hard and often impossible to be done safely. Thus, only treat code as a variable that definitely is one. |
4644 | 0 | decl = false; |
4645 | 0 | bool rhs = false; |
4646 | 0 | for (; tok3; tok3 = tok3->nextArgumentBeforeCreateLinks2()) { |
4647 | 0 | if (tok3->str() == "=") { |
4648 | 0 | rhs = true; |
4649 | 0 | continue; |
4650 | 0 | } |
4651 | | |
4652 | 0 | if (tok3->str() == ",") { |
4653 | 0 | rhs = false; |
4654 | 0 | continue; |
4655 | 0 | } |
4656 | | |
4657 | 0 | if (rhs) |
4658 | 0 | continue; |
4659 | | |
4660 | 0 | if (tok3->isLiteral() || |
4661 | 0 | (tok3->isName() && (variableMap.hasVariable(tok3->str()) || |
4662 | 0 | (tok3->strAt(-1) == "(" && Token::simpleMatch(tok3->next(), "(") && !Token::simpleMatch(tok3->linkAt(1)->next(), "(")))) || |
4663 | 0 | tok3->isOp() || |
4664 | 0 | tok3->str() == "(" || |
4665 | 0 | notstart.find(tok3->str()) != notstart.end()) { |
4666 | 0 | decl = true; |
4667 | 0 | break; |
4668 | 0 | } |
4669 | 0 | } |
4670 | 0 | } |
4671 | 0 | } else |
4672 | 0 | decl = false; |
4673 | 1.74k | } else if (isCPP() && Token::Match(prev2, "%type% {") && Token::simpleMatch(tok2->link(), "} ;")) { // C++11 initialization style |
4674 | 0 | if (tok2->link() != tok2->next() && // add value-initialized variable T x{}; |
4675 | 0 | (Token::Match(prev2, "do|try|else") || Token::Match(prev2->tokAt(-2), "struct|class|:"))) |
4676 | 0 | continue; |
4677 | 0 | } else |
4678 | 1.74k | decl = false; |
4679 | | |
4680 | 8.55k | if (decl) { |
4681 | 6.81k | if (isC() && Token::Match(prev2->previous(), "&|&&")) |
4682 | 0 | syntaxErrorC(prev2, prev2->strAt(-2) + prev2->strAt(-1) + " " + prev2->str()); |
4683 | 6.81k | variableMap.addVariable(prev2->str(), scopeStack.size() <= 1); |
4684 | | |
4685 | 6.81k | if (Token::simpleMatch(tok->previous(), "for (") && Token::Match(prev2, "%name% [=,]")) { |
4686 | 0 | for (const Token *tok3 = prev2->next(); tok3 && tok3->str() != ";"; tok3 = tok3->next()) { |
4687 | 0 | if (Token::Match(tok3, "[([]")) |
4688 | 0 | tok3 = tok3->link(); |
4689 | 0 | if (Token::Match(tok3, ", %name% [,=;]")) |
4690 | 0 | variableMap.addVariable(tok3->next()->str(), false); |
4691 | 0 | } |
4692 | 0 | } |
4693 | | |
4694 | | // set varid for template parameters.. |
4695 | 6.81k | tok = tok->next(); |
4696 | 19.0k | while (Token::Match(tok, "%name%|::")) |
4697 | 12.2k | tok = tok->next(); |
4698 | 6.81k | if (tok && tok->str() == "<") { |
4699 | 0 | const Token *end = tok->findClosingBracket(); |
4700 | 0 | while (tok != end) { |
4701 | 0 | if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && |
4702 | 0 | Token::Match(tok->tokAt(-1), ":: %name%"))) { |
4703 | 0 | const std::unordered_map<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str()); |
4704 | 0 | if (it != variableMap.map(false).end()) |
4705 | 0 | tok->varId(it->second); |
4706 | 0 | } |
4707 | 0 | tok = tok->next(); |
4708 | 0 | } |
4709 | 0 | } |
4710 | | |
4711 | 6.81k | tok = tok2->previous(); |
4712 | 6.81k | } |
4713 | 8.55k | } |
4714 | 25.5k | } |
4715 | | |
4716 | 61.7k | if (tok->isName() && !tok->isKeyword() && !tok->isStandardType()) { |
4717 | | // don't set variable id after a struct|enum|union |
4718 | 23.4k | if (Token::Match(tok->previous(), "struct|enum|union") || (isCPP() && tok->strAt(-1) == "class")) |
4719 | 0 | continue; |
4720 | | |
4721 | 23.4k | bool globalNamespace = false; |
4722 | 23.4k | if (!isC()) { |
4723 | 23.4k | if (tok->previous() && tok->previous()->str() == "::") { |
4724 | 0 | if (Token::Match(tok->tokAt(-2), ")|]|%name%")) |
4725 | 0 | continue; |
4726 | 0 | globalNamespace = true; |
4727 | 0 | } |
4728 | 23.4k | if (tok->next() && tok->next()->str() == "::") |
4729 | 0 | continue; |
4730 | 23.4k | if (Token::simpleMatch(tok->tokAt(-2), ":: template")) |
4731 | 0 | continue; |
4732 | 23.4k | } |
4733 | | |
4734 | | // function declaration inside executable scope? Function declaration is of form: type name "(" args ")" |
4735 | 23.4k | if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)]")) { |
4736 | 1.68k | bool par = false; |
4737 | 1.68k | const Token *start, *end; |
4738 | | |
4739 | | // search begin of function declaration |
4740 | 3.45k | for (start = tok; Token::Match(start, "%name%|*|&|,|("); start = start->previous()) { |
4741 | 2.03k | if (start->str() == "(") { |
4742 | 242 | if (par) |
4743 | 1 | break; |
4744 | 241 | par = true; |
4745 | 241 | } |
4746 | 2.03k | if (Token::Match(start, "[(,]")) { |
4747 | 241 | if (!Token::Match(start, "[(,] %type% %name%|*|&")) |
4748 | 236 | break; |
4749 | 241 | } |
4750 | 1.79k | if (start->varId() > 0) |
4751 | 27 | break; |
4752 | 1.79k | } |
4753 | | |
4754 | | // search end of function declaration |
4755 | 1.68k | for (end = tok->next(); Token::Match(end, "%name%|*|&|,"); end = end->next()) {} |
4756 | | |
4757 | | // there are tokens which can't appear at the begin of a function declaration such as "return" |
4758 | 1.68k | const bool isNotstartKeyword = start->next() && notstart.find(start->next()->str()) != notstart.end(); |
4759 | | |
4760 | | // now check if it is a function declaration |
4761 | 1.68k | if (Token::Match(start, "[;{}] %type% %name%|*") && par && Token::simpleMatch(end, ") ;") && !isNotstartKeyword) |
4762 | | // function declaration => don't set varid |
4763 | 0 | continue; |
4764 | 1.68k | } |
4765 | | |
4766 | 23.4k | if ((!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) && |
4767 | 23.4k | !Token::simpleMatch(tok->next(), ": ;")) { |
4768 | 23.4k | const std::unordered_map<std::string, nonneg int>::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); |
4769 | 23.4k | if (it != variableMap.map(globalNamespace).end()) { |
4770 | 19.8k | tok->varId(it->second); |
4771 | 19.8k | setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); |
4772 | 19.8k | } |
4773 | 23.4k | } |
4774 | 38.2k | } else if (Token::Match(tok, "::|. %name%") && Token::Match(tok->previous(), ")|]|>|%name%")) { |
4775 | | // Don't set varid after a :: or . token |
4776 | 0 | tok = tok->next(); |
4777 | 38.2k | } else if (tok->str() == ":" && Token::Match(tok->tokAt(-2), "class %type%")) { |
4778 | 0 | do { |
4779 | 0 | tok = tok->next(); |
4780 | 0 | } while (tok && (tok->isName() || tok->str() == ",")); |
4781 | 0 | if (!tok) |
4782 | 0 | break; |
4783 | 0 | tok = tok->previous(); |
4784 | 0 | } |
4785 | 61.7k | } |
4786 | | |
4787 | 1.36k | mVarId = variableMap.getVarId(); |
4788 | 1.36k | } |
4789 | | |
4790 | | namespace { |
4791 | | struct Member { |
4792 | 0 | Member(std::list<std::string> s, std::list<const Token *> ns, Token *t) : usingnamespaces(std::move(ns)), scope(std::move(s)), tok(t) {} |
4793 | | std::list<const Token *> usingnamespaces; |
4794 | | std::list<std::string> scope; |
4795 | | Token *tok; |
4796 | | }; |
4797 | | } |
4798 | | |
4799 | | static std::string getScopeName(const std::list<ScopeInfo2> &scopeInfo) |
4800 | 0 | { |
4801 | 0 | std::string ret; |
4802 | 0 | for (const ScopeInfo2 &si : scopeInfo) |
4803 | 0 | ret += (ret.empty() ? "" : " :: ") + (si.name); |
4804 | 0 | return ret; |
4805 | 0 | } |
4806 | | |
4807 | | static Token * matchMemberName(const std::list<std::string> &scope, const Token *nsToken, Token *memberToken, const std::list<ScopeInfo2> &scopeInfo) |
4808 | 0 | { |
4809 | 0 | std::list<ScopeInfo2>::const_iterator scopeIt = scopeInfo.cbegin(); |
4810 | | |
4811 | | // Current scope.. |
4812 | 0 | for (std::list<std::string>::const_iterator it = scope.cbegin(); it != scope.cend(); ++it) { |
4813 | 0 | if (scopeIt == scopeInfo.cend() || scopeIt->name != *it) |
4814 | 0 | return nullptr; |
4815 | 0 | ++scopeIt; |
4816 | 0 | } |
4817 | | |
4818 | | // using namespace.. |
4819 | 0 | if (nsToken) { |
4820 | 0 | while (Token::Match(nsToken, "%name% ::")) { |
4821 | 0 | if (scopeIt != scopeInfo.end() && nsToken->str() == scopeIt->name) { |
4822 | 0 | nsToken = nsToken->tokAt(2); |
4823 | 0 | ++scopeIt; |
4824 | 0 | } else { |
4825 | 0 | return nullptr; |
4826 | 0 | } |
4827 | 0 | } |
4828 | 0 | if (!Token::Match(nsToken, "%name% ;")) |
4829 | 0 | return nullptr; |
4830 | 0 | if (scopeIt == scopeInfo.end() || nsToken->str() != scopeIt->name) |
4831 | 0 | return nullptr; |
4832 | 0 | ++scopeIt; |
4833 | 0 | } |
4834 | | |
4835 | | // Parse member tokens.. |
4836 | 0 | while (scopeIt != scopeInfo.end()) { |
4837 | 0 | if (!Token::Match(memberToken, "%name% ::|<")) |
4838 | 0 | return nullptr; |
4839 | 0 | if (memberToken->str() != scopeIt->name) |
4840 | 0 | return nullptr; |
4841 | 0 | if (memberToken->next()->str() == "<") { |
4842 | 0 | memberToken = memberToken->next()->findClosingBracket(); |
4843 | 0 | if (!Token::simpleMatch(memberToken, "> ::")) |
4844 | 0 | return nullptr; |
4845 | 0 | } |
4846 | 0 | memberToken = memberToken->tokAt(2); |
4847 | 0 | ++scopeIt; |
4848 | 0 | } |
4849 | | |
4850 | 0 | return Token::Match(memberToken, "~| %name%") ? memberToken : nullptr; |
4851 | 0 | } |
4852 | | |
4853 | | static Token * matchMemberName(const Member &member, const std::list<ScopeInfo2> &scopeInfo) |
4854 | 0 | { |
4855 | 0 | if (scopeInfo.empty()) |
4856 | 0 | return nullptr; |
4857 | | |
4858 | | // Does this member match without "using namespace".. |
4859 | 0 | Token *ret = matchMemberName(member.scope, nullptr, member.tok, scopeInfo); |
4860 | 0 | if (ret) |
4861 | 0 | return ret; |
4862 | | |
4863 | | // Try to match member using the "using namespace ..." namespaces.. |
4864 | 0 | for (const Token *ns : member.usingnamespaces) { |
4865 | 0 | ret = matchMemberName(member.scope, ns, member.tok, scopeInfo); |
4866 | 0 | if (ret) |
4867 | 0 | return ret; |
4868 | 0 | } |
4869 | | |
4870 | 0 | return nullptr; |
4871 | 0 | } |
4872 | | |
4873 | | static Token * matchMemberVarName(const Member &var, const std::list<ScopeInfo2> &scopeInfo) |
4874 | 0 | { |
4875 | 0 | Token *tok = matchMemberName(var, scopeInfo); |
4876 | 0 | if (Token::Match(tok, "%name%")) { |
4877 | 0 | if (!tok->next() || tok->strAt(1) != "(" || (tok->tokAt(2) && tok->tokAt(2)->isLiteral())) |
4878 | 0 | return tok; |
4879 | 0 | } |
4880 | 0 | return nullptr; |
4881 | 0 | } |
4882 | | |
4883 | | static Token * matchMemberFunctionName(const Member &func, const std::list<ScopeInfo2> &scopeInfo) |
4884 | 0 | { |
4885 | 0 | Token *tok = matchMemberName(func, scopeInfo); |
4886 | 0 | return Token::Match(tok, "~| %name% (") ? tok : nullptr; |
4887 | 0 | } |
4888 | | |
4889 | | template<typename T> |
4890 | | static T* skipInitializerList(T* tok) |
4891 | 0 | { |
4892 | 0 | T* const start = tok; |
4893 | 0 | while (Token::Match(tok, "[:,] ::| %name%")) { |
4894 | 0 | tok = tok->tokAt(tok->strAt(1) == "::" ? 1 : 2); |
4895 | 0 | while (Token::Match(tok, ":: %name%")) |
4896 | 0 | tok = tok->tokAt(2); |
4897 | 0 | if (!Token::Match(tok, "[({<]") || !tok->link()) |
4898 | 0 | return start; |
4899 | 0 | const bool isTemplate = tok->str() == "<"; |
4900 | 0 | tok = tok->link()->next(); |
4901 | 0 | if (isTemplate && tok && tok->link()) |
4902 | 0 | tok = tok->link()->next(); |
4903 | 0 | } |
4904 | 0 | return tok; |
4905 | 0 | } |
4906 | | |
4907 | | void Tokenizer::setVarIdPass2() |
4908 | 1.36k | { |
4909 | 1.36k | std::map<nonneg int, std::map<std::string, nonneg int>> structMembers; |
4910 | | |
4911 | | // Member functions and variables in this source |
4912 | 1.36k | std::list<Member> allMemberFunctions; |
4913 | 1.36k | std::list<Member> allMemberVars; |
4914 | 1.36k | if (!isC()) { |
4915 | 1.36k | std::map<const Token *, std::string> endOfScope; |
4916 | 1.36k | std::list<std::string> scope; |
4917 | 1.36k | std::list<const Token *> usingnamespaces; |
4918 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
4919 | 92.3k | if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { |
4920 | 24.2k | if (Token::Match(tok, "using namespace %name% ::|;")) { |
4921 | 0 | Token *endtok = tok->tokAt(2); |
4922 | 0 | while (Token::Match(endtok, "%name% ::")) |
4923 | 0 | endtok = endtok->tokAt(2); |
4924 | 0 | if (Token::Match(endtok, "%name% ;")) |
4925 | 0 | usingnamespaces.push_back(tok->tokAt(2)); |
4926 | 0 | tok = endtok; |
4927 | 0 | continue; |
4928 | 0 | } |
4929 | 24.2k | if (Token::Match(tok, "namespace %name% {")) { |
4930 | 0 | scope.push_back(tok->strAt(1)); |
4931 | 0 | endOfScope[tok->linkAt(2)] = tok->strAt(1); |
4932 | 0 | } |
4933 | 24.2k | } |
4934 | | |
4935 | 92.3k | if (tok->str() == "}") { |
4936 | 3.58k | const std::map<const Token *, std::string>::iterator it = endOfScope.find(tok); |
4937 | 3.58k | if (it != endOfScope.end()) |
4938 | 0 | scope.remove(it->second); |
4939 | 3.58k | } |
4940 | | |
4941 | 92.3k | Token* const tok1 = tok; |
4942 | 92.3k | if (Token::Match(tok, "%name% :: ~| %name%")) |
4943 | 0 | tok = tok->next(); |
4944 | 92.3k | else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) |
4945 | 0 | tok = tok->next()->findClosingBracket()->next(); |
4946 | 92.3k | else if (usingnamespaces.empty() || tok->varId() || !tok->isName() || tok->isStandardType() || tok->tokType() == Token::eKeyword || tok->tokType() == Token::eBoolean || |
4947 | 92.3k | Token::Match(tok->previous(), ".|namespace|class|struct|&|&&|*|> %name%") || Token::Match(tok->previous(), "%type%| %name% ( %type%|)") || Token::Match(tok, "public:|private:|protected:") || |
4948 | 92.3k | (!tok->next() && Token::Match(tok->previous(), "}|; %name%"))) |
4949 | 92.3k | continue; |
4950 | | |
4951 | 0 | if (tok->strAt(-1) == "::" && tok->tokAt(-2) && tok->tokAt(-2)->isName()) |
4952 | 0 | continue; |
4953 | | |
4954 | 0 | while (Token::Match(tok, ":: ~| %name%")) { |
4955 | 0 | tok = tok->next(); |
4956 | 0 | if (tok->str() == "~") |
4957 | 0 | tok = tok->next(); |
4958 | 0 | else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) |
4959 | 0 | tok = tok->next()->findClosingBracket()->next(); |
4960 | 0 | else if (Token::Match(tok, "%name% ::")) |
4961 | 0 | tok = tok->next(); |
4962 | 0 | else |
4963 | 0 | break; |
4964 | 0 | } |
4965 | 0 | if (!tok->next()) |
4966 | 0 | syntaxError(tok); |
4967 | 0 | if (Token::Match(tok, "%name% (") && !(tok->tokAt(2) && tok->tokAt(2)->isLiteral())) |
4968 | 0 | allMemberFunctions.emplace_back(scope, usingnamespaces, tok1); |
4969 | 0 | else |
4970 | 0 | allMemberVars.emplace_back(scope, usingnamespaces, tok1); |
4971 | 0 | } |
4972 | 1.36k | } |
4973 | | |
4974 | 1.36k | std::list<ScopeInfo2> scopeInfo; |
4975 | | |
4976 | | // class members.. |
4977 | 1.36k | std::map<std::string, std::map<std::string, nonneg int>> varsByClass; |
4978 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
4979 | 92.3k | while (tok->str() == "}" && !scopeInfo.empty() && tok == scopeInfo.back().bodyEnd) |
4980 | 0 | scopeInfo.pop_back(); |
4981 | | |
4982 | 92.3k | if (!Token::Match(tok, "namespace|class|struct %name% {|:|::|<")) |
4983 | 92.3k | continue; |
4984 | | |
4985 | 0 | const std::string &scopeName(getScopeName(scopeInfo)); |
4986 | 0 | const std::string scopeName2(scopeName.empty() ? std::string() : (scopeName + " :: ")); |
4987 | |
|
4988 | 0 | std::list<const Token *> classnameTokens; |
4989 | 0 | classnameTokens.push_back(tok->next()); |
4990 | 0 | Token* tokStart = tok->tokAt(2); |
4991 | 0 | while (Token::Match(tokStart, ":: %name%") || tokStart->str() == "<") { |
4992 | 0 | if (tokStart->str() == "<") { |
4993 | | // skip the template part |
4994 | 0 | Token* closeTok = tokStart->findClosingBracket(); |
4995 | 0 | if (!closeTok) |
4996 | 0 | syntaxError(tok); |
4997 | 0 | tokStart = closeTok->next(); |
4998 | 0 | } else { |
4999 | 0 | classnameTokens.push_back(tokStart->next()); |
5000 | 0 | tokStart = tokStart->tokAt(2); |
5001 | 0 | } |
5002 | 0 | } |
5003 | |
|
5004 | 0 | std::string classname; |
5005 | 0 | for (const Token *it : classnameTokens) |
5006 | 0 | classname += (classname.empty() ? "" : " :: ") + it->str(); |
5007 | |
|
5008 | 0 | std::map<std::string, nonneg int> &thisClassVars = varsByClass[scopeName2 + classname]; |
5009 | 0 | while (Token::Match(tokStart, ":|::|,|%name%")) { |
5010 | 0 | if (Token::Match(tokStart, "%name% <")) { // TODO: why skip templates? |
5011 | 0 | tokStart = tokStart->next()->findClosingBracket(); |
5012 | 0 | if (tokStart) |
5013 | 0 | tokStart = tokStart->next(); |
5014 | 0 | continue; |
5015 | 0 | } |
5016 | 0 | if (Token::Match(tokStart, "%name% ,|{")) { |
5017 | 0 | std::string baseClassName = tokStart->str(); |
5018 | 0 | const Token* baseStart = tokStart; |
5019 | 0 | while (Token::Match(baseStart->tokAt(-2), "%name% ::")) { // build base class name |
5020 | 0 | baseClassName.insert(0, baseStart->strAt(-2) + " :: "); |
5021 | 0 | baseStart = baseStart->tokAt(-2); |
5022 | 0 | } |
5023 | 0 | std::string scopeName3(scopeName2); |
5024 | 0 | while (!scopeName3.empty()) { |
5025 | 0 | const std::string name = scopeName3 + baseClassName; |
5026 | 0 | if (varsByClass.find(name) != varsByClass.end()) { |
5027 | 0 | baseClassName = name; |
5028 | 0 | break; |
5029 | 0 | } |
5030 | | // Remove last scope name |
5031 | 0 | if (scopeName3.size() <= 8) |
5032 | 0 | break; |
5033 | 0 | scopeName3.erase(scopeName3.size() - 4); |
5034 | 0 | const std::string::size_type pos = scopeName3.rfind(" :: "); |
5035 | 0 | if (pos == std::string::npos) |
5036 | 0 | break; |
5037 | 0 | scopeName3.erase(pos + 4); |
5038 | 0 | } |
5039 | 0 | const std::map<std::string, nonneg int>& baseClassVars = varsByClass[baseClassName]; |
5040 | 0 | thisClassVars.insert(baseClassVars.cbegin(), baseClassVars.cend()); |
5041 | 0 | } |
5042 | 0 | tokStart = tokStart->next(); |
5043 | 0 | } |
5044 | 0 | if (!Token::simpleMatch(tokStart, "{")) |
5045 | 0 | continue; |
5046 | | |
5047 | | // What member variables are there in this class? |
5048 | 0 | std::transform(classnameTokens.cbegin(), classnameTokens.cend(), std::back_inserter(scopeInfo), [&](const Token* tok) { |
5049 | 0 | return ScopeInfo2(tok->str(), tokStart->link()); |
5050 | 0 | }); |
5051 | |
|
5052 | 0 | for (Token *tok2 = tokStart->next(); tok2 && tok2 != tokStart->link(); tok2 = tok2->next()) { |
5053 | | // skip parentheses.. |
5054 | 0 | if (tok2->link()) { |
5055 | 0 | if (tok2->str() == "(") { |
5056 | 0 | Token *funcstart = const_cast<Token*>(isFunctionHead(tok2, "{")); |
5057 | 0 | if (funcstart) { |
5058 | 0 | setVarIdClassFunction(scopeName2 + classname, funcstart, funcstart->link(), thisClassVars, structMembers, mVarId); |
5059 | 0 | tok2 = funcstart->link(); |
5060 | 0 | continue; |
5061 | 0 | } |
5062 | 0 | } |
5063 | 0 | if (tok2->str() == "{") { |
5064 | 0 | if (tok2->strAt(-1) == ")") |
5065 | 0 | setVarIdClassFunction(scopeName2 + classname, tok2, tok2->link(), thisClassVars, structMembers, mVarId); |
5066 | 0 | tok2 = tok2->link(); |
5067 | 0 | } else if (Token::Match(tok2, "( %name%|)") && !Token::Match(tok2->link(), "(|[")) { |
5068 | 0 | tok2 = tok2->link(); |
5069 | | |
5070 | | // Skip initialization list |
5071 | 0 | if (Token::simpleMatch(tok2, ") :")) |
5072 | 0 | tok2 = skipInitializerList(tok2->next()); |
5073 | 0 | } |
5074 | 0 | } |
5075 | | |
5076 | | // Found a member variable.. |
5077 | 0 | else if (tok2->varId() > 0) |
5078 | 0 | thisClassVars[tok2->str()] = tok2->varId(); |
5079 | 0 | } |
5080 | | |
5081 | | // Are there any member variables in this class? |
5082 | 0 | if (thisClassVars.empty()) |
5083 | 0 | continue; |
5084 | | |
5085 | | // Member variables |
5086 | 0 | for (const Member &var : allMemberVars) { |
5087 | 0 | Token *tok2 = matchMemberVarName(var, scopeInfo); |
5088 | 0 | if (!tok2) |
5089 | 0 | continue; |
5090 | 0 | if (tok2->varId() == 0) |
5091 | 0 | tok2->varId(thisClassVars[tok2->str()]); |
5092 | 0 | } |
5093 | |
|
5094 | 0 | if (isC() || tok->str() == "namespace") |
5095 | 0 | continue; |
5096 | | |
5097 | | // Set variable ids in member functions for this class.. |
5098 | 0 | for (const Member &func : allMemberFunctions) { |
5099 | 0 | Token *tok2 = matchMemberFunctionName(func, scopeInfo); |
5100 | 0 | if (!tok2) |
5101 | 0 | continue; |
5102 | | |
5103 | 0 | if (tok2->str() == "~") |
5104 | 0 | tok2 = tok2->linkAt(2); |
5105 | 0 | else |
5106 | 0 | tok2 = tok2->linkAt(1); |
5107 | | |
5108 | | // If this is a function implementation.. add it to funclist |
5109 | 0 | Token * start = const_cast<Token *>(isFunctionHead(tok2, "{")); |
5110 | 0 | if (start) { |
5111 | 0 | setVarIdClassFunction(classname, start, start->link(), thisClassVars, structMembers, mVarId); |
5112 | 0 | } |
5113 | |
|
5114 | 0 | if (Token::Match(tok2, ") %name% (")) |
5115 | 0 | tok2 = tok2->linkAt(2); |
5116 | | |
5117 | | // constructor with initializer list |
5118 | 0 | if (!Token::Match(tok2, ") : ::| %name%")) |
5119 | 0 | continue; |
5120 | | |
5121 | 0 | Token *tok3 = tok2; |
5122 | 0 | while (Token::Match(tok3, "[)}] [,:]")) { |
5123 | 0 | tok3 = tok3->tokAt(2); |
5124 | 0 | if (Token::Match(tok3, ":: %name%")) |
5125 | 0 | tok3 = tok3->next(); |
5126 | 0 | while (Token::Match(tok3, "%name% :: %name%")) |
5127 | 0 | tok3 = tok3->tokAt(2); |
5128 | 0 | if (!Token::Match(tok3, "%name% (|{|<")) |
5129 | 0 | break; |
5130 | | |
5131 | | // set varid |
5132 | 0 | const std::map<std::string, nonneg int>::const_iterator varpos = thisClassVars.find(tok3->str()); |
5133 | 0 | if (varpos != thisClassVars.end()) |
5134 | 0 | tok3->varId(varpos->second); |
5135 | | |
5136 | | // goto end of var |
5137 | 0 | if (tok3->strAt(1) == "<") { |
5138 | 0 | tok3 = tok3->next()->findClosingBracket(); |
5139 | 0 | if (tok3 && tok3->next() && tok3->next()->link()) |
5140 | 0 | tok3 = tok3->next()->link(); |
5141 | 0 | } else |
5142 | 0 | tok3 = tok3->linkAt(1); |
5143 | 0 | } |
5144 | 0 | if (Token::Match(tok3, ")|} {")) { |
5145 | 0 | setVarIdClassFunction(classname, tok2, tok3->next()->link(), thisClassVars, structMembers, mVarId); |
5146 | 0 | } |
5147 | 0 | } |
5148 | 0 | } |
5149 | 1.36k | } |
5150 | | |
5151 | | static void linkBrackets(const Tokenizer * const tokenizer, std::stack<const Token*>& type, std::stack<Token*>& links, Token * const token, const char open, const char close) |
5152 | 241k | { |
5153 | 241k | if (token->str()[0] == open) { |
5154 | 7.63k | links.push(token); |
5155 | 7.63k | type.push(token); |
5156 | 233k | } else if (token->str()[0] == close) { |
5157 | 7.63k | if (links.empty()) { |
5158 | | // Error, { and } don't match. |
5159 | 0 | tokenizer->unmatchedToken(token); |
5160 | 0 | } |
5161 | 7.63k | if (type.top()->str()[0] != open) { |
5162 | 0 | tokenizer->unmatchedToken(type.top()); |
5163 | 0 | } |
5164 | 7.63k | type.pop(); |
5165 | | |
5166 | 7.63k | Token::createMutualLinks(links.top(), token); |
5167 | 7.63k | links.pop(); |
5168 | 7.63k | } |
5169 | 241k | } |
5170 | | |
5171 | | void Tokenizer::createLinks() |
5172 | 1.36k | { |
5173 | 1.36k | std::stack<const Token*> type; |
5174 | 1.36k | std::stack<Token*> links1; |
5175 | 1.36k | std::stack<Token*> links2; |
5176 | 1.36k | std::stack<Token*> links3; |
5177 | 81.8k | for (Token *token = list.front(); token; token = token->next()) { |
5178 | 80.4k | if (token->link()) { |
5179 | 0 | token->link(nullptr); |
5180 | 0 | } |
5181 | | |
5182 | 80.4k | linkBrackets(this, type, links1, token, '{', '}'); |
5183 | | |
5184 | 80.4k | linkBrackets(this, type, links2, token, '(', ')'); |
5185 | | |
5186 | 80.4k | linkBrackets(this, type, links3, token, '[', ']'); |
5187 | 80.4k | } |
5188 | | |
5189 | 1.36k | if (!links1.empty()) { |
5190 | | // Error, { and } don't match. |
5191 | 0 | unmatchedToken(links1.top()); |
5192 | 0 | } |
5193 | | |
5194 | 1.36k | if (!links2.empty()) { |
5195 | | // Error, ( and ) don't match. |
5196 | 0 | unmatchedToken(links2.top()); |
5197 | 0 | } |
5198 | | |
5199 | 1.36k | if (!links3.empty()) { |
5200 | | // Error, [ and ] don't match. |
5201 | 0 | unmatchedToken(links3.top()); |
5202 | 0 | } |
5203 | 1.36k | } |
5204 | | |
5205 | | void Tokenizer::createLinks2() |
5206 | 1.36k | { |
5207 | 1.36k | if (isC()) |
5208 | 0 | return; |
5209 | | |
5210 | 1.36k | bool isStruct = false; |
5211 | | |
5212 | 1.36k | std::stack<Token*> type; |
5213 | 1.36k | std::stack<Token*> templateTokens; |
5214 | 93.6k | for (Token *token = list.front(); token; token = token->next()) { |
5215 | 92.3k | if (Token::Match(token, "%name%|> %name% [:<]")) |
5216 | 21 | isStruct = true; |
5217 | 92.2k | else if (Token::Match(token, "[;{}]")) |
5218 | 24.2k | isStruct = false; |
5219 | | |
5220 | 92.3k | if (token->link()) { |
5221 | 14.8k | if (Token::Match(token, "{|[|(")) |
5222 | 7.40k | type.push(token); |
5223 | 7.40k | else if (!type.empty() && Token::Match(token, "}|]|)")) { |
5224 | 7.48k | while (type.top()->str() == "<") { |
5225 | 72 | if (!templateTokens.empty() && templateTokens.top()->next() == type.top()) |
5226 | 0 | templateTokens.pop(); |
5227 | 72 | type.pop(); |
5228 | 72 | } |
5229 | 7.40k | type.pop(); |
5230 | 7.40k | } |
5231 | 77.4k | } else if (templateTokens.empty() && !isStruct && Token::Match(token, "%oror%|&&|;")) { |
5232 | 17.0k | if (Token::Match(token, "&& [,>]")) |
5233 | 0 | continue; |
5234 | | // If there is some such code: A<B||C>.. |
5235 | | // Then this is probably a template instantiation if either "B" or "C" has comparisons |
5236 | 17.0k | if (token->tokType() == Token::eLogicalOp && !type.empty() && type.top()->str() == "<") { |
5237 | 0 | const Token *prev = token->previous(); |
5238 | 0 | bool foundComparison = false; |
5239 | 0 | while (Token::Match(prev, "%name%|%num%|%str%|%cop%|)|]") && prev != type.top()) { |
5240 | 0 | if (prev->str() == ")" || prev->str() == "]") |
5241 | 0 | prev = prev->link(); |
5242 | 0 | else if (prev->tokType() == Token::eLogicalOp) |
5243 | 0 | break; |
5244 | 0 | else if (prev->isComparisonOp()) |
5245 | 0 | foundComparison = true; |
5246 | 0 | prev = prev->previous(); |
5247 | 0 | } |
5248 | 0 | if (prev == type.top() && foundComparison) |
5249 | 0 | continue; |
5250 | 0 | const Token *next = token->next(); |
5251 | 0 | foundComparison = false; |
5252 | 0 | while (Token::Match(next, "%name%|%num%|%str%|%cop%|(|[") && next->str() != ">") { |
5253 | 0 | if (next->str() == "(" || next->str() == "[") |
5254 | 0 | next = next->link(); |
5255 | 0 | else if (next->tokType() == Token::eLogicalOp) |
5256 | 0 | break; |
5257 | 0 | else if (next->isComparisonOp()) |
5258 | 0 | foundComparison = true; |
5259 | 0 | next = next->next(); |
5260 | 0 | } |
5261 | 0 | if (next && next->str() == ">" && foundComparison) |
5262 | 0 | continue; |
5263 | 0 | } |
5264 | | |
5265 | 17.1k | while (!type.empty() && type.top()->str() == "<") { |
5266 | 27 | const Token* end = type.top()->findClosingBracket(); |
5267 | 27 | if (Token::Match(end, "> %comp%|;|.|=|{|::")) |
5268 | 0 | break; |
5269 | | // Variable declaration |
5270 | 27 | if (Token::Match(end, "> %var% ;") && (type.top()->tokAt(-2) == nullptr || Token::Match(type.top()->tokAt(-2), ";|}|{"))) |
5271 | 0 | break; |
5272 | 27 | type.pop(); |
5273 | 27 | } |
5274 | 60.4k | } else if (token->str() == "<" && |
5275 | 60.4k | ((token->previous() && (token->previous()->isTemplate() || |
5276 | 524 | (token->previous()->isName() && !token->previous()->varId()) || |
5277 | 524 | (token->strAt(-1) == "]" && (!Token::Match(token->linkAt(-1)->previous(), "%name%|)") || token->linkAt(-1)->previous()->isKeyword())) || |
5278 | 524 | (token->strAt(-1) == ")" && token->linkAt(-1)->strAt(-1) == "operator"))) || |
5279 | 524 | Token::Match(token->next(), ">|>>"))) { |
5280 | 115 | type.push(token); |
5281 | 115 | if (token->previous()->str() == "template") |
5282 | 0 | templateTokens.push(token); |
5283 | 60.3k | } else if (token->str() == ">" || token->str() == ">>") { |
5284 | 501 | if (type.empty() || type.top()->str() != "<") // < and > don't match. |
5285 | 482 | continue; |
5286 | 19 | Token * const top1 = type.top(); |
5287 | 19 | type.pop(); |
5288 | 19 | Token * const top2 = type.empty() ? nullptr : type.top(); |
5289 | 19 | type.push(top1); |
5290 | 19 | if (!top2 || top2->str() != "<") { |
5291 | 19 | if (token->str() == ">>") |
5292 | 0 | continue; |
5293 | 19 | if (!Token::Match(token->next(), "%name%|%cop%|%assign%|::|,|(|)|{|}|;|[|]|:|.|=|...") && |
5294 | 19 | !Token::Match(token->next(), "&& %name% =")) |
5295 | 3 | continue; |
5296 | 19 | } |
5297 | | |
5298 | 16 | if (token->str() == ">>" && top1 && top2) { |
5299 | 0 | type.pop(); |
5300 | 0 | type.pop(); |
5301 | | // Split the angle brackets |
5302 | 0 | token->str(">"); |
5303 | 0 | Token::createMutualLinks(top1, token->insertTokenBefore(">")); |
5304 | 0 | Token::createMutualLinks(top2, token); |
5305 | 0 | if (templateTokens.size() == 2 && (top1 == templateTokens.top() || top2 == templateTokens.top())) { |
5306 | 0 | templateTokens.pop(); |
5307 | 0 | templateTokens.pop(); |
5308 | 0 | } |
5309 | 16 | } else { |
5310 | 16 | type.pop(); |
5311 | 16 | if (Token::Match(token, "> %name%") && !token->next()->isKeyword() && |
5312 | 16 | Token::Match(top1->tokAt(-2), "%op% %name% <") && top1->strAt(-2) != "<" && |
5313 | 16 | (templateTokens.empty() || top1 != templateTokens.top())) |
5314 | 12 | continue; |
5315 | 4 | Token::createMutualLinks(top1, token); |
5316 | 4 | if (!templateTokens.empty() && top1 == templateTokens.top()) |
5317 | 0 | templateTokens.pop(); |
5318 | 4 | } |
5319 | 16 | } |
5320 | 92.3k | } |
5321 | 1.36k | } |
5322 | | |
5323 | | void Tokenizer::sizeofAddParentheses() |
5324 | 1.36k | { |
5325 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
5326 | 79.1k | if (!Token::Match(tok, "sizeof !!(")) |
5327 | 79.1k | continue; |
5328 | 0 | if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!|&")) { |
5329 | 0 | Token *endToken = tok->next(); |
5330 | 0 | while (Token::simpleMatch(endToken, "* *")) |
5331 | 0 | endToken = endToken->next(); |
5332 | 0 | while (Token::Match(endToken->next(), "%name%|%num%|%str%|[|(|.|::|++|--|!|~") || (Token::Match(endToken, "%type% * %op%|?|:|const|;|,"))) { |
5333 | 0 | if (Token::Match(endToken->next(), "(|[")) |
5334 | 0 | endToken = endToken->linkAt(1); |
5335 | 0 | else |
5336 | 0 | endToken = endToken->next(); |
5337 | 0 | } |
5338 | | |
5339 | | // Add ( after sizeof and ) behind endToken |
5340 | 0 | tok->insertToken("("); |
5341 | 0 | endToken->insertToken(")"); |
5342 | 0 | Token::createMutualLinks(tok->next(), endToken->next()); |
5343 | 0 | } |
5344 | 0 | } |
5345 | 1.36k | } |
5346 | | |
5347 | | bool Tokenizer::simplifyTokenList1(const char FileName[]) |
5348 | 1.36k | { |
5349 | 1.36k | if (Settings::terminated()) |
5350 | 0 | return false; |
5351 | | |
5352 | | // if MACRO |
5353 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
5354 | 80.4k | if (Token::Match(tok, "if|for|while|BOOST_FOREACH %name% (")) { |
5355 | 0 | if (Token::simpleMatch(tok, "for each")) { |
5356 | | // 'for each ( )' -> 'asm ( )' |
5357 | 0 | tok->str("asm"); |
5358 | 0 | tok->deleteNext(); |
5359 | 0 | } else if (tok->strAt(1) == "constexpr") { |
5360 | 0 | tok->deleteNext(); |
5361 | 0 | tok->isConstexpr(true); |
5362 | 0 | } else { |
5363 | 0 | syntaxError(tok); |
5364 | 0 | } |
5365 | 0 | } |
5366 | 80.4k | } |
5367 | | |
5368 | | // Is there C++ code in C file? |
5369 | 1.36k | validateC(); |
5370 | | |
5371 | | // Combine strings and character literals, e.g. L"string", L'c', "string1" "string2" |
5372 | 1.36k | combineStringAndCharLiterals(); |
5373 | | |
5374 | | // replace inline SQL with "asm()" (Oracle PRO*C). Ticket: #1959 |
5375 | 1.36k | simplifySQL(); |
5376 | | |
5377 | 1.36k | createLinks(); |
5378 | | |
5379 | | // Simplify debug intrinsics |
5380 | 1.36k | simplifyDebug(); |
5381 | | |
5382 | 1.36k | removePragma(); |
5383 | | |
5384 | | // Simplify the C alternative tokens (and, or, etc.) |
5385 | 1.36k | simplifyCAlternativeTokens(); |
5386 | | |
5387 | 1.36k | simplifyFunctionTryCatch(); |
5388 | | |
5389 | 1.36k | simplifyHeadersAndUnusedTemplates(); |
5390 | | |
5391 | | // Remove __asm.. |
5392 | 1.36k | simplifyAsm(); |
5393 | | |
5394 | | // foo < bar < >> => foo < bar < > > |
5395 | 1.36k | if (isCPP() || mSettings->daca) |
5396 | 1.36k | splitTemplateRightAngleBrackets(!isCPP()); |
5397 | | |
5398 | | // Remove extra "template" tokens that are not used by cppcheck |
5399 | 1.36k | removeExtraTemplateKeywords(); |
5400 | | |
5401 | 1.36k | simplifySpaceshipOperator(); |
5402 | | |
5403 | | // Bail out if code is garbage |
5404 | 1.36k | if (mTimerResults) { |
5405 | 0 | Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::findGarbageCode", mSettings->showtime, mTimerResults); |
5406 | 0 | findGarbageCode(); |
5407 | 1.36k | } else { |
5408 | 1.36k | findGarbageCode(); |
5409 | 1.36k | } |
5410 | | |
5411 | 1.36k | checkConfiguration(); |
5412 | | |
5413 | | // if (x) MACRO() .. |
5414 | 73.6k | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
5415 | 72.3k | if (Token::simpleMatch(tok, "if (")) { |
5416 | 920 | tok = tok->next()->link(); |
5417 | 920 | if (Token::Match(tok, ") %name% (") && |
5418 | 920 | tok->next()->isUpperCaseName() && |
5419 | 920 | Token::Match(tok->linkAt(2), ") {|else")) { |
5420 | 0 | syntaxError(tok->next()); |
5421 | 0 | } |
5422 | 920 | } |
5423 | 72.3k | } |
5424 | | |
5425 | 1.36k | if (Settings::terminated()) |
5426 | 0 | return false; |
5427 | | |
5428 | | // convert C++17 style nested namespaces to old style namespaces |
5429 | 1.36k | simplifyNestedNamespace(); |
5430 | | |
5431 | | // convert c++20 coroutines |
5432 | 1.36k | simplifyCoroutines(); |
5433 | | |
5434 | | // simplify namespace aliases |
5435 | 1.36k | simplifyNamespaceAliases(); |
5436 | | |
5437 | | // Remove [[attribute]] |
5438 | 1.36k | simplifyCPPAttribute(); |
5439 | | |
5440 | | // remove __attribute__((?)) |
5441 | 1.36k | simplifyAttribute(); |
5442 | | |
5443 | | // simplify cppcheck attributes __cppcheck_?__(?) |
5444 | 1.36k | simplifyCppcheckAttribute(); |
5445 | | |
5446 | | // Combine tokens.. |
5447 | 1.36k | combineOperators(); |
5448 | | |
5449 | | // combine "- %num%" |
5450 | 1.36k | concatenateNegativeNumberAndAnyPositive(); |
5451 | | |
5452 | | // remove extern "C" and extern "C" {} |
5453 | 1.36k | if (isCPP()) |
5454 | 1.36k | simplifyExternC(); |
5455 | | |
5456 | | // simplify weird but legal code: "[;{}] ( { code; } ) ;"->"[;{}] code;" |
5457 | 1.36k | simplifyRoundCurlyParentheses(); |
5458 | | |
5459 | | // check for simple syntax errors.. |
5460 | 80.4k | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
5461 | 79.1k | if (Token::simpleMatch(tok, "> struct {") && |
5462 | 79.1k | Token::simpleMatch(tok->linkAt(2), "} ;")) { |
5463 | 0 | syntaxError(tok); |
5464 | 0 | } |
5465 | 79.1k | } |
5466 | | |
5467 | 1.36k | if (!simplifyAddBraces()) |
5468 | 0 | return false; |
5469 | | |
5470 | 1.36k | sizeofAddParentheses(); |
5471 | | |
5472 | | // Simplify: 0[foo] -> *(foo) |
5473 | 80.4k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
5474 | 79.1k | if (Token::simpleMatch(tok, "0 [") && tok->linkAt(1)) { |
5475 | 0 | tok->str("*"); |
5476 | 0 | tok->next()->str("("); |
5477 | 0 | tok->linkAt(1)->str(")"); |
5478 | 0 | } |
5479 | 79.1k | } |
5480 | | |
5481 | 1.36k | if (Settings::terminated()) |
5482 | 0 | return false; |
5483 | | |
5484 | | // Remove __declspec() |
5485 | 1.36k | simplifyDeclspec(); |
5486 | 1.36k | validate(); |
5487 | | |
5488 | | // Remove "inline", "register", and "restrict" |
5489 | 1.36k | simplifyKeyword(); |
5490 | | |
5491 | | // simplify simple calculations inside <..> |
5492 | 1.36k | if (isCPP()) { |
5493 | 1.36k | Token *lt = nullptr; |
5494 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
5495 | 79.1k | if (Token::Match(tok, "[;{}]")) |
5496 | 17.4k | lt = nullptr; |
5497 | 61.7k | else if (Token::Match(tok, "%type% <")) |
5498 | 395 | lt = tok->next(); |
5499 | 61.3k | else if (lt && Token::Match(tok, ">|>> %name%|::|(")) { |
5500 | 41 | const Token * const end = tok; |
5501 | 181 | for (tok = lt; tok != end; tok = tok->next()) { |
5502 | 140 | if (tok->isNumber()) |
5503 | 14 | TemplateSimplifier::simplifyNumericCalculations(tok); |
5504 | 140 | } |
5505 | 41 | lt = tok->next(); |
5506 | 41 | } |
5507 | 79.1k | } |
5508 | 1.36k | } |
5509 | | |
5510 | | // Convert K&R function declarations to modern C |
5511 | 1.36k | simplifyVarDecl(true); |
5512 | 1.36k | simplifyFunctionParameters(); |
5513 | | |
5514 | | // simplify case ranges (gcc extension) |
5515 | 1.36k | simplifyCaseRange(); |
5516 | | |
5517 | | // simplify labels and 'case|default'-like syntaxes |
5518 | 1.36k | simplifyLabelsCaseDefault(); |
5519 | | |
5520 | 1.36k | if (!isC() && !mSettings->library.markupFile(FileName)) { |
5521 | 1.36k | findComplicatedSyntaxErrorsInTemplates(); |
5522 | 1.36k | } |
5523 | | |
5524 | 1.36k | if (Settings::terminated()) |
5525 | 0 | return false; |
5526 | | |
5527 | | // remove calling conventions __cdecl, __stdcall.. |
5528 | 1.36k | simplifyCallingConvention(); |
5529 | | |
5530 | 1.36k | addSemicolonAfterUnknownMacro(); |
5531 | | |
5532 | | // remove some unhandled macros in global scope |
5533 | 1.36k | removeMacrosInGlobalScope(); |
5534 | | |
5535 | | // remove undefined macro in class definition: |
5536 | | // class DLLEXPORT Fred { }; |
5537 | | // class Fred FINAL : Base { }; |
5538 | 1.36k | removeMacroInClassDef(); |
5539 | | |
5540 | | // That call here fixes #7190 |
5541 | 1.36k | validate(); |
5542 | | |
5543 | | // remove unnecessary member qualification.. |
5544 | 1.36k | removeUnnecessaryQualification(); |
5545 | | |
5546 | | // convert Microsoft memory functions |
5547 | 1.36k | simplifyMicrosoftMemoryFunctions(); |
5548 | | |
5549 | | // convert Microsoft string functions |
5550 | 1.36k | simplifyMicrosoftStringFunctions(); |
5551 | | |
5552 | 1.36k | if (Settings::terminated()) |
5553 | 0 | return false; |
5554 | | |
5555 | | // remove Borland stuff.. |
5556 | 1.36k | simplifyBorland(); |
5557 | | |
5558 | | // syntax error: enum with typedef in it |
5559 | 1.36k | checkForEnumsWithTypedef(); |
5560 | | |
5561 | | // Add parentheses to ternary operator where necessary |
5562 | 1.36k | prepareTernaryOpForAST(); |
5563 | | |
5564 | | // Change initialisation of variable to assignment |
5565 | 1.36k | simplifyInitVar(); |
5566 | | |
5567 | | // Split up variable declarations. |
5568 | 1.36k | simplifyVarDecl(false); |
5569 | | |
5570 | 1.36k | reportUnknownMacros(); |
5571 | | |
5572 | 1.36k | simplifyTypedefLHS(); |
5573 | | |
5574 | | // typedef.. |
5575 | 1.36k | if (mTimerResults) { |
5576 | 0 | Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::simplifyTypedef", mSettings->showtime, mTimerResults); |
5577 | 0 | simplifyTypedef(); |
5578 | 1.36k | } else { |
5579 | 1.36k | simplifyTypedef(); |
5580 | 1.36k | } |
5581 | | |
5582 | | // using A = B; |
5583 | 1.36k | while (simplifyUsing()) |
5584 | 0 | ; |
5585 | | |
5586 | | // Add parentheses to ternary operator where necessary |
5587 | | // TODO: this is only necessary if one typedef simplification had a comma and was used within ?: |
5588 | | // If typedef handling is refactored and moved to symboldatabase someday we can remove this |
5589 | 1.36k | prepareTernaryOpForAST(); |
5590 | | |
5591 | 94.1k | for (Token* tok = list.front(); tok;) { |
5592 | 92.7k | if (Token::Match(tok, "union|struct|class union|struct|class")) |
5593 | 0 | tok->deleteNext(); |
5594 | 92.7k | else |
5595 | 92.7k | tok = tok->next(); |
5596 | 92.7k | } |
5597 | | |
5598 | | // class x y { |
5599 | 1.36k | if (isCPP() && mSettings->severity.isEnabled(Severity::information)) { |
5600 | 94.1k | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
5601 | 92.7k | if (Token::Match(tok, "class %type% %type% [:{]")) { |
5602 | 0 | unhandled_macro_class_x_y(tok); |
5603 | 0 | } |
5604 | 92.7k | } |
5605 | 1.36k | } |
5606 | | |
5607 | | // catch bad typedef canonicalization |
5608 | | // |
5609 | | // to reproduce bad typedef, download upx-ucl from: |
5610 | | // http://packages.debian.org/sid/upx-ucl |
5611 | | // analyse the file src/stub/src/i386-linux.elf.interp-main.c |
5612 | 1.36k | validate(); |
5613 | | |
5614 | | // The simplify enum have inner loops |
5615 | 1.36k | if (Settings::terminated()) |
5616 | 0 | return false; |
5617 | | |
5618 | | // Put ^{} statements in asm() |
5619 | 1.36k | simplifyAsm2(); |
5620 | | |
5621 | | // @.. |
5622 | 1.36k | simplifyAt(); |
5623 | | |
5624 | | // When the assembly code has been cleaned up, no @ is allowed |
5625 | 79.9k | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
5626 | 78.5k | if (tok->str() == "(") { |
5627 | 3.50k | const Token *tok1 = tok; |
5628 | 3.50k | tok = tok->link(); |
5629 | 3.50k | if (!tok) |
5630 | 0 | syntaxError(tok1); |
5631 | 75.0k | } else if (tok->str() == "@") { |
5632 | 0 | syntaxError(tok); |
5633 | 0 | } |
5634 | 78.5k | } |
5635 | | |
5636 | | // Order keywords "static" and "const" |
5637 | 1.36k | simplifyStaticConst(); |
5638 | | |
5639 | | // convert platform dependent types to standard types |
5640 | | // 32 bits: size_t -> unsigned long |
5641 | | // 64 bits: size_t -> unsigned long long |
5642 | 1.36k | list.simplifyPlatformTypes(); |
5643 | | |
5644 | | // collapse compound standard types into a single token |
5645 | | // unsigned long long int => long (with _isUnsigned=true,_isLong=true) |
5646 | 1.36k | list.simplifyStdType(); |
5647 | | |
5648 | 1.36k | if (Settings::terminated()) |
5649 | 0 | return false; |
5650 | | |
5651 | | // simplify bit fields.. |
5652 | 1.36k | simplifyBitfields(); |
5653 | | |
5654 | 1.36k | if (Settings::terminated()) |
5655 | 0 | return false; |
5656 | | |
5657 | | // struct simplification "struct S {} s; => struct S { } ; S s ; |
5658 | 1.36k | simplifyStructDecl(); |
5659 | | |
5660 | 1.36k | if (Settings::terminated()) |
5661 | 0 | return false; |
5662 | | |
5663 | | // x = ({ 123; }); => { x = 123; } |
5664 | 1.36k | simplifyAssignmentBlock(); |
5665 | | |
5666 | 1.36k | if (Settings::terminated()) |
5667 | 0 | return false; |
5668 | | |
5669 | 1.36k | simplifyVariableMultipleAssign(); |
5670 | | |
5671 | | // Collapse operator name tokens into single token |
5672 | | // operator = => operator= |
5673 | 1.36k | simplifyOperatorName(); |
5674 | | |
5675 | | // Remove redundant parentheses |
5676 | 1.36k | simplifyRedundantParentheses(); |
5677 | | |
5678 | 1.36k | if (isCPP()) |
5679 | 1.36k | simplifyTypeIntrinsics(); |
5680 | | |
5681 | 1.36k | if (!isC()) { |
5682 | | // Handle templates.. |
5683 | 1.36k | if (mTimerResults) { |
5684 | 0 | Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::simplifyTemplates", mSettings->showtime, mTimerResults); |
5685 | 0 | simplifyTemplates(); |
5686 | 1.36k | } else { |
5687 | 1.36k | simplifyTemplates(); |
5688 | 1.36k | } |
5689 | | |
5690 | | // The simplifyTemplates have inner loops |
5691 | 1.36k | if (Settings::terminated()) |
5692 | 0 | return false; |
5693 | | |
5694 | 1.36k | validate(); // #6847 - invalid code |
5695 | 1.36k | } |
5696 | | |
5697 | | // Simplify pointer to standard types (C only) |
5698 | 1.36k | simplifyPointerToStandardType(); |
5699 | | |
5700 | | // simplify function pointers |
5701 | 1.36k | simplifyFunctionPointers(); |
5702 | | |
5703 | | // Change initialisation of variable to assignment |
5704 | 1.36k | simplifyInitVar(); |
5705 | | |
5706 | | // Split up variable declarations. |
5707 | 1.36k | simplifyVarDecl(false); |
5708 | | |
5709 | 1.36k | elseif(); |
5710 | | |
5711 | 1.36k | validate(); // #6772 "segmentation fault (invalid code) in Tokenizer::setVarId" |
5712 | | |
5713 | 1.36k | if (mTimerResults) { |
5714 | 0 | Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::setVarId", mSettings->showtime, mTimerResults); |
5715 | 0 | setVarId(); |
5716 | 1.36k | } else { |
5717 | 1.36k | setVarId(); |
5718 | 1.36k | } |
5719 | | |
5720 | | // Link < with > |
5721 | 1.36k | createLinks2(); |
5722 | | |
5723 | | // Mark C++ casts |
5724 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
5725 | 92.3k | if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <") && Token::simpleMatch(tok->linkAt(1), "> (")) { |
5726 | 0 | tok = tok->linkAt(1)->next(); |
5727 | 0 | tok->isCast(true); |
5728 | 0 | } |
5729 | 92.3k | } |
5730 | | |
5731 | | // specify array size |
5732 | 1.36k | arraySize(); |
5733 | | |
5734 | | // The simplify enum might have inner loops |
5735 | 1.36k | if (Settings::terminated()) |
5736 | 0 | return false; |
5737 | | |
5738 | | // Add std:: in front of std classes, when using namespace std; was given |
5739 | 1.36k | simplifyNamespaceStd(); |
5740 | | |
5741 | | // Change initialisation of variable to assignment |
5742 | 1.36k | simplifyInitVar(); |
5743 | | |
5744 | 1.36k | simplifyDoublePlusAndDoubleMinus(); |
5745 | | |
5746 | 1.36k | simplifyArrayAccessSyntax(); |
5747 | | |
5748 | 1.36k | Token::assignProgressValues(list.front()); |
5749 | | |
5750 | 1.36k | removeRedundantSemicolons(); |
5751 | | |
5752 | 1.36k | simplifyParameterVoid(); |
5753 | | |
5754 | 1.36k | simplifyRedundantConsecutiveBraces(); |
5755 | | |
5756 | 1.36k | simplifyEmptyNamespaces(); |
5757 | | |
5758 | 1.36k | simplifyIfSwitchForInit(); |
5759 | | |
5760 | 1.36k | simplifyOverloadedOperators(); |
5761 | | |
5762 | 1.36k | validate(); |
5763 | | |
5764 | 1.36k | list.front()->assignIndexes(); |
5765 | | |
5766 | 1.36k | return true; |
5767 | 1.36k | } |
5768 | | //--------------------------------------------------------------------------- |
5769 | | |
5770 | | void Tokenizer::printDebugOutput(int simplification) const |
5771 | 1.36k | { |
5772 | 1.36k | const bool debug = (simplification != 1U && mSettings->debugSimplified) || |
5773 | 1.36k | (simplification != 2U && mSettings->debugnormal); |
5774 | | |
5775 | 1.36k | if (debug && list.front()) { |
5776 | 0 | list.front()->printOut(nullptr, list.getFiles()); |
5777 | |
|
5778 | 0 | if (mSettings->xml) |
5779 | 0 | std::cout << "<debug>" << std::endl; |
5780 | |
|
5781 | 0 | if (mSymbolDatabase) { |
5782 | 0 | if (mSettings->xml) |
5783 | 0 | mSymbolDatabase->printXml(std::cout); |
5784 | 0 | else if (mSettings->verbose) { |
5785 | 0 | mSymbolDatabase->printOut("Symbol database"); |
5786 | 0 | } |
5787 | 0 | } |
5788 | |
|
5789 | 0 | if (mSettings->verbose) |
5790 | 0 | list.front()->printAst(mSettings->verbose, mSettings->xml, list.getFiles(), std::cout); |
5791 | |
|
5792 | 0 | list.front()->printValueFlow(mSettings->xml, std::cout); |
5793 | |
|
5794 | 0 | if (mSettings->xml) |
5795 | 0 | std::cout << "</debug>" << std::endl; |
5796 | 0 | } |
5797 | | |
5798 | 1.36k | if (mSymbolDatabase && simplification == 2U && mSettings->debugwarnings) { |
5799 | 0 | printUnknownTypes(); |
5800 | | |
5801 | | // the typeStartToken() should come before typeEndToken() |
5802 | 0 | for (const Variable *var : mSymbolDatabase->variableList()) { |
5803 | 0 | if (!var) |
5804 | 0 | continue; |
5805 | | |
5806 | 0 | const Token * typetok = var->typeStartToken(); |
5807 | 0 | while (typetok && typetok != var->typeEndToken()) |
5808 | 0 | typetok = typetok->next(); |
5809 | |
|
5810 | 0 | if (typetok != var->typeEndToken()) { |
5811 | 0 | reportError(var->typeStartToken(), |
5812 | 0 | Severity::debug, |
5813 | 0 | "debug", |
5814 | 0 | "Variable::typeStartToken() of variable '" + var->name() + "' is not located before Variable::typeEndToken(). The location of the typeStartToken() is '" + var->typeStartToken()->str() + "' at line " + std::to_string(var->typeStartToken()->linenr())); |
5815 | 0 | } |
5816 | 0 | } |
5817 | 0 | } |
5818 | 1.36k | } |
5819 | | |
5820 | | void Tokenizer::dump(std::ostream &out) const |
5821 | 0 | { |
5822 | | // Create a xml data dump. |
5823 | | // The idea is not that this will be readable for humans. It's a |
5824 | | // data dump that 3rd party tools could load and get useful info from. |
5825 | |
|
5826 | 0 | std::string outs; |
5827 | |
|
5828 | 0 | std::set<const Library::Container*> containers; |
5829 | | |
5830 | | // tokens.. |
5831 | 0 | outs += " <tokenlist>"; |
5832 | 0 | outs += '\n'; |
5833 | 0 | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
5834 | 0 | outs += " <token id=\""; |
5835 | 0 | outs += id_string(tok); |
5836 | 0 | outs += "\" file=\""; |
5837 | 0 | outs += ErrorLogger::toxml(list.file(tok)); |
5838 | 0 | outs += "\" linenr=\""; |
5839 | 0 | outs += std::to_string(tok->linenr()); |
5840 | 0 | outs += "\" column=\""; |
5841 | 0 | outs += std::to_string(tok->column()); |
5842 | 0 | outs += "\""; |
5843 | |
|
5844 | 0 | outs += " str=\""; |
5845 | 0 | outs += ErrorLogger::toxml(tok->str()); |
5846 | 0 | outs += '\"'; |
5847 | |
|
5848 | 0 | outs += " scope=\""; |
5849 | 0 | outs += id_string(tok->scope()); |
5850 | 0 | outs += '\"'; |
5851 | 0 | if (tok->isName()) { |
5852 | 0 | outs += " type=\"name\""; |
5853 | 0 | if (tok->isUnsigned()) |
5854 | 0 | outs += " isUnsigned=\"true\""; |
5855 | 0 | else if (tok->isSigned()) |
5856 | 0 | outs += " isSigned=\"true\""; |
5857 | 0 | } else if (tok->isNumber()) { |
5858 | 0 | outs += " type=\"number\""; |
5859 | 0 | if (MathLib::isInt(tok->str())) |
5860 | 0 | outs += " isInt=\"true\""; |
5861 | 0 | if (MathLib::isFloat(tok->str())) |
5862 | 0 | outs += " isFloat=\"true\""; |
5863 | 0 | } else if (tok->tokType() == Token::eString) { |
5864 | 0 | outs += " type=\"string\" strlen=\""; |
5865 | 0 | outs += std::to_string(Token::getStrLength(tok)); |
5866 | 0 | outs += '\"'; |
5867 | 0 | } |
5868 | 0 | else if (tok->tokType() == Token::eChar) |
5869 | 0 | outs += " type=\"char\""; |
5870 | 0 | else if (tok->isBoolean()) |
5871 | 0 | outs += " type=\"boolean\""; |
5872 | 0 | else if (tok->isOp()) { |
5873 | 0 | outs += " type=\"op\""; |
5874 | 0 | if (tok->isArithmeticalOp()) |
5875 | 0 | outs += " isArithmeticalOp=\"true\""; |
5876 | 0 | else if (tok->isAssignmentOp()) |
5877 | 0 | outs += " isAssignmentOp=\"true\""; |
5878 | 0 | else if (tok->isComparisonOp()) |
5879 | 0 | outs += " isComparisonOp=\"true\""; |
5880 | 0 | else if (tok->tokType() == Token::eLogicalOp) |
5881 | 0 | outs += " isLogicalOp=\"true\""; |
5882 | 0 | } |
5883 | 0 | if (tok->isCast()) |
5884 | 0 | outs += " isCast=\"true\""; |
5885 | 0 | if (tok->isExternC()) |
5886 | 0 | outs += " externLang=\"C\""; |
5887 | 0 | if (tok->isExpandedMacro()) |
5888 | 0 | outs += " isExpandedMacro=\"true\""; |
5889 | 0 | if (tok->isTemplateArg()) |
5890 | 0 | outs += " isTemplateArg=\"true\""; |
5891 | 0 | if (tok->isRemovedVoidParameter()) |
5892 | 0 | outs += " isRemovedVoidParameter=\"true\""; |
5893 | 0 | if (tok->isSplittedVarDeclComma()) |
5894 | 0 | outs += " isSplittedVarDeclComma=\"true\""; |
5895 | 0 | if (tok->isSplittedVarDeclEq()) |
5896 | 0 | outs += " isSplittedVarDeclEq=\"true\""; |
5897 | 0 | if (tok->isImplicitInt()) |
5898 | 0 | outs += " isImplicitInt=\"true\""; |
5899 | 0 | if (tok->isComplex()) |
5900 | 0 | outs += " isComplex=\"true\""; |
5901 | 0 | if (tok->isRestrict()) |
5902 | 0 | outs += " isRestrict=\"true\""; |
5903 | 0 | if (tok->isAtomic()) |
5904 | 0 | outs += " isAtomic=\"true\""; |
5905 | 0 | if (tok->isAttributeExport()) |
5906 | 0 | outs += " isAttributeExport=\"true\""; |
5907 | 0 | if (tok->link()) { |
5908 | 0 | outs += " link=\""; |
5909 | 0 | outs += id_string(tok->link()); |
5910 | 0 | outs += '\"'; |
5911 | 0 | } |
5912 | 0 | if (tok->varId() > 0) { |
5913 | 0 | outs += " varId=\""; |
5914 | 0 | outs += std::to_string(tok->varId()); |
5915 | 0 | outs += '\"'; |
5916 | 0 | } |
5917 | 0 | if (tok->exprId() > 0) { |
5918 | 0 | outs += " exprId=\""; |
5919 | 0 | outs += std::to_string(tok->exprId()); |
5920 | 0 | outs += '\"'; |
5921 | 0 | } |
5922 | 0 | if (tok->variable()) { |
5923 | 0 | outs += " variable=\""; |
5924 | 0 | outs += id_string(tok->variable()); |
5925 | 0 | outs += '\"'; |
5926 | 0 | } |
5927 | 0 | if (tok->function()) { |
5928 | 0 | outs += " function=\""; |
5929 | 0 | outs += id_string(tok->function()); |
5930 | 0 | outs += '\"'; |
5931 | 0 | } |
5932 | 0 | if (!tok->values().empty()) { |
5933 | 0 | outs += " values=\""; |
5934 | 0 | outs += id_string(&tok->values()); |
5935 | 0 | outs += '\"'; |
5936 | 0 | } |
5937 | 0 | if (tok->type()) { |
5938 | 0 | outs += " type-scope=\""; |
5939 | 0 | outs += id_string(tok->type()->classScope); |
5940 | 0 | outs += '\"'; |
5941 | 0 | } |
5942 | 0 | if (tok->astParent()) { |
5943 | 0 | outs += " astParent=\""; |
5944 | 0 | outs += id_string(tok->astParent()); |
5945 | 0 | outs += '\"'; |
5946 | 0 | } |
5947 | 0 | if (tok->astOperand1()) { |
5948 | 0 | outs += " astOperand1=\""; |
5949 | 0 | outs += id_string(tok->astOperand1()); |
5950 | 0 | outs += '\"'; |
5951 | 0 | } |
5952 | 0 | if (tok->astOperand2()) { |
5953 | 0 | outs += " astOperand2=\""; |
5954 | 0 | outs += id_string(tok->astOperand2()); |
5955 | 0 | outs += '\"'; |
5956 | 0 | } |
5957 | 0 | if (!tok->originalName().empty()) { |
5958 | 0 | outs += " originalName=\""; |
5959 | 0 | outs += tok->originalName(); |
5960 | 0 | outs += '\"'; |
5961 | 0 | } |
5962 | 0 | if (tok->valueType()) { |
5963 | 0 | const std::string vt = tok->valueType()->dump(); |
5964 | 0 | if (!vt.empty()) { |
5965 | 0 | outs += ' '; |
5966 | 0 | outs += vt; |
5967 | 0 | } |
5968 | 0 | containers.insert(tok->valueType()->container); |
5969 | 0 | } |
5970 | 0 | if (!tok->varId() && tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { |
5971 | 0 | if (mSettings->library.isnoreturn(tok)) |
5972 | 0 | outs += " noreturn=\"true\""; |
5973 | 0 | } |
5974 | |
|
5975 | 0 | outs += "/>"; |
5976 | 0 | outs += '\n'; |
5977 | 0 | } |
5978 | 0 | outs += " </tokenlist>"; |
5979 | 0 | outs += '\n'; |
5980 | |
|
5981 | 0 | out << outs; |
5982 | 0 | outs.clear(); |
5983 | |
|
5984 | 0 | mSymbolDatabase->printXml(out); |
5985 | |
|
5986 | 0 | containers.erase(nullptr); |
5987 | 0 | if (!containers.empty()) { |
5988 | 0 | outs += " <containers>"; |
5989 | 0 | outs += '\n'; |
5990 | 0 | for (const Library::Container* c: containers) { |
5991 | 0 | outs += " <container id=\""; |
5992 | 0 | outs += id_string(c); |
5993 | 0 | outs += "\" array-like-index-op=\""; |
5994 | 0 | outs += bool_to_string(c->arrayLike_indexOp); |
5995 | 0 | outs += "\" "; |
5996 | 0 | outs += "std-string-like=\""; |
5997 | 0 | outs += bool_to_string(c->stdStringLike); |
5998 | 0 | outs += "\"/>"; |
5999 | 0 | outs += '\n'; |
6000 | 0 | } |
6001 | 0 | outs += " </containers>"; |
6002 | 0 | outs += '\n'; |
6003 | 0 | } |
6004 | |
|
6005 | 0 | if (list.front()) |
6006 | 0 | list.front()->printValueFlow(true, out); |
6007 | |
|
6008 | 0 | if (!mTypedefInfo.empty()) { |
6009 | 0 | outs += " <typedef-info>"; |
6010 | 0 | outs += '\n'; |
6011 | 0 | for (const TypedefInfo &typedefInfo: mTypedefInfo) { |
6012 | 0 | outs += " <info"; |
6013 | |
|
6014 | 0 | outs += " name=\""; |
6015 | 0 | outs += typedefInfo.name; |
6016 | 0 | outs += "\""; |
6017 | |
|
6018 | 0 | outs += " file=\""; |
6019 | 0 | outs += ErrorLogger::toxml(typedefInfo.filename); |
6020 | 0 | outs += "\""; |
6021 | |
|
6022 | 0 | outs += " line=\""; |
6023 | 0 | outs += std::to_string(typedefInfo.lineNumber); |
6024 | 0 | outs += "\""; |
6025 | |
|
6026 | 0 | outs += " column=\""; |
6027 | 0 | outs += std::to_string(typedefInfo.column); |
6028 | 0 | outs += "\""; |
6029 | |
|
6030 | 0 | outs += " used=\""; |
6031 | 0 | outs += std::to_string(typedefInfo.used?1:0); |
6032 | 0 | outs += "\""; |
6033 | |
|
6034 | 0 | outs += "/>"; |
6035 | 0 | outs += '\n'; |
6036 | 0 | } |
6037 | 0 | outs += " </typedef-info>"; |
6038 | 0 | outs += '\n'; |
6039 | 0 | } |
6040 | 0 | outs += mTemplateSimplifier->dump(); |
6041 | |
|
6042 | 0 | out << outs; |
6043 | 0 | } |
6044 | | |
6045 | | void Tokenizer::simplifyHeadersAndUnusedTemplates() |
6046 | 1.36k | { |
6047 | 1.36k | if (mSettings->checkHeaders && mSettings->checkUnusedTemplates) |
6048 | | // Full analysis. All information in the headers are kept. |
6049 | 1.36k | return; |
6050 | | |
6051 | 0 | const bool checkHeaders = mSettings->checkHeaders; |
6052 | 0 | const bool removeUnusedIncludedFunctions = !mSettings->checkHeaders; |
6053 | 0 | const bool removeUnusedIncludedClasses = !mSettings->checkHeaders; |
6054 | 0 | const bool removeUnusedIncludedTemplates = !mSettings->checkUnusedTemplates || !mSettings->checkHeaders; |
6055 | 0 | const bool removeUnusedTemplates = !mSettings->checkUnusedTemplates; |
6056 | | |
6057 | | // checkHeaders: |
6058 | | // |
6059 | | // If it is true then keep all code in the headers. It's possible |
6060 | | // to remove unused types/variables if false positives / false |
6061 | | // negatives can be avoided. |
6062 | | // |
6063 | | // If it is false, then we want to remove selected stuff from the |
6064 | | // headers but not *everything*. The intention here is to not damage |
6065 | | // the analysis of the source file. You should get all warnings in |
6066 | | // the source file. You should not get false positives. |
6067 | | |
6068 | | // functions and types to keep |
6069 | 0 | std::set<std::string> keep; |
6070 | 0 | for (const Token *tok = list.front(); tok; tok = tok->next()) { |
6071 | 0 | if (isCPP() && Token::simpleMatch(tok, "template <")) { |
6072 | 0 | const Token *closingBracket = tok->next()->findClosingBracket(); |
6073 | 0 | if (Token::Match(closingBracket, "> class|struct %name% {")) |
6074 | 0 | tok = closingBracket->linkAt(3); |
6075 | 0 | } |
6076 | |
|
6077 | 0 | if (!tok->isName() || tok->isKeyword()) |
6078 | 0 | continue; |
6079 | | |
6080 | 0 | if (!checkHeaders && tok->fileIndex() != 0) |
6081 | 0 | continue; |
6082 | | |
6083 | 0 | if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { |
6084 | 0 | keep.insert(tok->str()); |
6085 | 0 | continue; |
6086 | 0 | } |
6087 | | |
6088 | 0 | if (Token::Match(tok, "%name% %name%|::|*|&|<")) { |
6089 | 0 | keep.insert(tok->str()); |
6090 | 0 | } |
6091 | 0 | } |
6092 | |
|
6093 | 0 | const std::set<std::string> functionStart{"static", "const", "unsigned", "signed", "void", "bool", "char", "short", "int", "long", "float", "*"}; |
6094 | |
|
6095 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6096 | 0 | const bool isIncluded = (tok->fileIndex() != 0); |
6097 | | |
6098 | | // Remove executable code |
6099 | 0 | if (isIncluded && !mSettings->checkHeaders && tok->str() == "{") { |
6100 | | // TODO: We probably need to keep the executable code if this function is called from the source file. |
6101 | 0 | const Token *prev = tok->previous(); |
6102 | 0 | while (prev && prev->isName()) |
6103 | 0 | prev = prev->previous(); |
6104 | 0 | if (Token::simpleMatch(prev, ")")) { |
6105 | | // Replace all tokens from { to } with a ";". |
6106 | 0 | Token::eraseTokens(tok,tok->link()->next()); |
6107 | 0 | tok->str(";"); |
6108 | 0 | tok->link(nullptr); |
6109 | 0 | } |
6110 | 0 | } |
6111 | |
|
6112 | 0 | if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { |
6113 | | // Remove unused function declarations |
6114 | 0 | if (isIncluded && removeUnusedIncludedFunctions) { |
6115 | 0 | while (true) { |
6116 | 0 | Token *start = tok; |
6117 | 0 | while (start && functionStart.find(start->str()) != functionStart.end()) |
6118 | 0 | start = start->next(); |
6119 | 0 | if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) { |
6120 | 0 | Token::eraseTokens(tok, start->linkAt(1)->tokAt(2)); |
6121 | 0 | tok->deleteThis(); |
6122 | 0 | } else |
6123 | 0 | break; |
6124 | 0 | } |
6125 | 0 | } |
6126 | |
|
6127 | 0 | if (isIncluded && removeUnusedIncludedClasses) { |
6128 | 0 | if (Token::Match(tok, "class|struct %name% [:{]") && keep.find(tok->strAt(1)) == keep.end()) { |
6129 | | // Remove this class/struct |
6130 | 0 | const Token *endToken = tok->tokAt(2); |
6131 | 0 | if (endToken->str() == ":") { |
6132 | 0 | endToken = endToken->next(); |
6133 | 0 | while (Token::Match(endToken, "%name%|,")) |
6134 | 0 | endToken = endToken->next(); |
6135 | 0 | } |
6136 | 0 | if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) { |
6137 | 0 | Token::eraseTokens(tok, endToken->link()->next()); |
6138 | 0 | tok->deleteThis(); |
6139 | 0 | } |
6140 | 0 | } |
6141 | 0 | } |
6142 | |
|
6143 | 0 | if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) { |
6144 | 0 | if (Token::Match(tok, "template < %name%")) { |
6145 | 0 | const Token *closingBracket = tok->next()->findClosingBracket(); |
6146 | 0 | if (Token::Match(closingBracket, "> class|struct %name% [;:{]") && keep.find(closingBracket->strAt(2)) == keep.end()) { |
6147 | 0 | const Token *endToken = closingBracket->tokAt(3); |
6148 | 0 | if (endToken->str() == ":") { |
6149 | 0 | endToken = endToken->next(); |
6150 | 0 | while (Token::Match(endToken, "%name%|,")) |
6151 | 0 | endToken = endToken->next(); |
6152 | 0 | } |
6153 | 0 | if (endToken && endToken->str() == "{") |
6154 | 0 | endToken = endToken->link()->next(); |
6155 | 0 | if (endToken && endToken->str() == ";") { |
6156 | 0 | Token::eraseTokens(tok, endToken); |
6157 | 0 | tok->deleteThis(); |
6158 | 0 | } |
6159 | 0 | } else if (Token::Match(closingBracket, "> %type% %name% (") && Token::simpleMatch(closingBracket->linkAt(3), ") {") && keep.find(closingBracket->strAt(2)) == keep.end()) { |
6160 | 0 | const Token *endToken = closingBracket->linkAt(3)->linkAt(1)->next(); |
6161 | 0 | Token::eraseTokens(tok, endToken); |
6162 | 0 | tok->deleteThis(); |
6163 | 0 | } |
6164 | 0 | } |
6165 | 0 | } |
6166 | 0 | } |
6167 | 0 | } |
6168 | 0 | } |
6169 | | |
6170 | | void Tokenizer::removeExtraTemplateKeywords() |
6171 | 1.36k | { |
6172 | 1.36k | if (isCPP()) { |
6173 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6174 | 80.4k | if (Token::Match(tok, "%name%|>|) .|:: template %name%")) { |
6175 | 0 | tok->next()->deleteNext(); |
6176 | 0 | Token* templateName = tok->tokAt(2); |
6177 | 0 | while (Token::Match(templateName, "%name%|::")) { |
6178 | 0 | templateName->isTemplate(true); |
6179 | 0 | templateName = templateName->next(); |
6180 | 0 | } |
6181 | 0 | if (Token::Match(templateName->previous(), "operator %op%|(")) { |
6182 | 0 | templateName->isTemplate(true); |
6183 | 0 | if (templateName->str() == "(" && templateName->link()) |
6184 | 0 | templateName->link()->isTemplate(true); |
6185 | 0 | } |
6186 | 0 | } |
6187 | 80.4k | } |
6188 | 1.36k | } |
6189 | 1.36k | } |
6190 | | |
6191 | | static std::string getExpression(const Token *tok) |
6192 | 0 | { |
6193 | 0 | std::string line; |
6194 | 0 | for (const Token *prev = tok->previous(); prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) |
6195 | 0 | line = prev->str() + " " + line; |
6196 | 0 | line += "!!!" + tok->str() + "!!!"; |
6197 | 0 | for (const Token *next = tok->next(); next && !Token::Match(next, "[;{}]"); next = next->next()) |
6198 | 0 | line += " " + next->str(); |
6199 | 0 | return line; |
6200 | 0 | } |
6201 | | |
6202 | | void Tokenizer::splitTemplateRightAngleBrackets(bool check) |
6203 | 1.36k | { |
6204 | 1.36k | std::vector<std::pair<std::string, int>> vars; |
6205 | | |
6206 | 1.36k | int scopeLevel = 0; |
6207 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6208 | 80.4k | if (tok->str() == "{") |
6209 | 3.58k | ++scopeLevel; |
6210 | 76.9k | else if (tok->str() == "}") { |
6211 | 14.3k | vars.erase(std::remove_if(vars.begin(), vars.end(), [scopeLevel](const std::pair<std::string, int>& v) { |
6212 | 14.3k | return v.second == scopeLevel; |
6213 | 14.3k | }), vars.end()); |
6214 | 3.58k | --scopeLevel; |
6215 | 3.58k | } |
6216 | 80.4k | if (Token::Match(tok, "[;{}] %type% %type% [;,=]") && tok->next()->isStandardType()) |
6217 | 5.44k | vars.emplace_back(tok->strAt(2), scopeLevel); |
6218 | | |
6219 | | // Ticket #6181: normalize C++11 template parameter list closing syntax |
6220 | 80.4k | if (tok->previous() && tok->str() == "<" && TemplateSimplifier::templateParameters(tok) && std::none_of(vars.begin(), vars.end(), [&](const std::pair<std::string, int>& v) { |
6221 | 184 | return v.first == tok->previous()->str(); |
6222 | 184 | })) { |
6223 | 37 | Token *endTok = tok->findClosingBracket(); |
6224 | 37 | if (check) { |
6225 | 0 | if (Token::Match(endTok, ">>|>>=")) |
6226 | 0 | reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!<!!!: " + getExpression(tok), false); |
6227 | 0 | continue; |
6228 | 0 | } |
6229 | 37 | if (endTok && endTok->str() == ">>") { |
6230 | 0 | endTok->str(">"); |
6231 | 0 | endTok->insertToken(">"); |
6232 | 37 | } else if (endTok && endTok->str() == ">>=") { |
6233 | 0 | endTok->str(">"); |
6234 | 0 | endTok->insertToken("="); |
6235 | 0 | endTok->insertToken(">"); |
6236 | 0 | } |
6237 | 80.4k | } else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <") && std::none_of(vars.begin(), vars.end(), [&](const std::pair<std::string, int>& v) { |
6238 | 151 | return v.first == tok->next()->str(); |
6239 | 151 | })) { |
6240 | 33 | Token *endTok = tok->tokAt(2)->findClosingBracket(); |
6241 | 33 | if (check) { |
6242 | 0 | if (Token::simpleMatch(endTok, ">>")) |
6243 | 0 | reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!<!!!: " + getExpression(tok), false); |
6244 | 0 | continue; |
6245 | 0 | } |
6246 | 33 | if (Token::Match(endTok, ">> ;|{|%type%")) { |
6247 | 0 | endTok->str(">"); |
6248 | 0 | endTok->insertToken(">"); |
6249 | 0 | } |
6250 | 33 | } |
6251 | 80.4k | } |
6252 | 1.36k | } |
6253 | | |
6254 | | void Tokenizer::removeMacrosInGlobalScope() |
6255 | 1.36k | { |
6256 | 42.3k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6257 | 41.0k | if (tok->str() == "(") { |
6258 | 1.74k | tok = tok->link(); |
6259 | 1.74k | if (Token::Match(tok, ") %type% {") && |
6260 | 1.74k | !tok->next()->isStandardType() && |
6261 | 1.74k | !tok->next()->isKeyword() && |
6262 | 1.74k | !Token::Match(tok->next(), "override|final") && |
6263 | 1.74k | tok->next()->isUpperCaseName()) |
6264 | 0 | tok->deleteNext(); |
6265 | 1.74k | } |
6266 | | |
6267 | 41.0k | if (Token::Match(tok, "%type%") && tok->isUpperCaseName() && |
6268 | 41.0k | (!tok->previous() || Token::Match(tok->previous(), "[;{}]") || (tok->previous()->isName() && endsWith(tok->previous()->str(), ':')))) { |
6269 | 0 | const Token *tok2 = tok->next(); |
6270 | 0 | if (tok2 && tok2->str() == "(") |
6271 | 0 | tok2 = tok2->link()->next(); |
6272 | | |
6273 | | // Several unknown macros... |
6274 | 0 | while (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) |
6275 | 0 | tok2 = tok2->linkAt(1)->next(); |
6276 | |
|
6277 | 0 | if (Token::Match(tok, "%name% (") && Token::Match(tok2, "%name% *|&|::|<| %name%") && !Token::Match(tok2, "namespace|class|struct|union|private:|protected:|public:")) |
6278 | 0 | unknownMacroError(tok); |
6279 | |
|
6280 | 0 | if (Token::Match(tok, "%type% (") && Token::Match(tok2, "%type% (") && !Token::Match(tok2, "noexcept|throw") && isFunctionHead(tok2->next(), ":;{")) |
6281 | 0 | unknownMacroError(tok); |
6282 | | |
6283 | | // remove unknown macros before namespace|class|struct|union |
6284 | 0 | if (Token::Match(tok2, "namespace|class|struct|union")) { |
6285 | | // is there a "{" for? |
6286 | 0 | const Token *tok3 = tok2; |
6287 | 0 | while (tok3 && !Token::Match(tok3,"[;{}()]")) |
6288 | 0 | tok3 = tok3->next(); |
6289 | 0 | if (tok3 && tok3->str() == "{") { |
6290 | 0 | Token::eraseTokens(tok, tok2); |
6291 | 0 | tok->deleteThis(); |
6292 | 0 | } |
6293 | 0 | continue; |
6294 | 0 | } |
6295 | | |
6296 | | // replace unknown macros before foo( |
6297 | | /* |
6298 | | if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { |
6299 | | std::string typeName; |
6300 | | for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) |
6301 | | typeName += tok3->str(); |
6302 | | Token::eraseTokens(tok, tok2); |
6303 | | tok->str(typeName); |
6304 | | } |
6305 | | */ |
6306 | | // remove unknown macros before foo::foo( |
6307 | 0 | if (Token::Match(tok2, "%type% :: %type%")) { |
6308 | 0 | const Token *tok3 = tok2; |
6309 | 0 | while (Token::Match(tok3, "%type% :: %type% ::")) |
6310 | 0 | tok3 = tok3->tokAt(2); |
6311 | 0 | if (Token::Match(tok3, "%type% :: %type% (") && tok3->str() == tok3->strAt(2)) { |
6312 | 0 | Token::eraseTokens(tok, tok2); |
6313 | 0 | tok->deleteThis(); |
6314 | 0 | } |
6315 | 0 | continue; |
6316 | 0 | } |
6317 | 0 | } |
6318 | | |
6319 | | // Skip executable scopes |
6320 | 41.0k | if (tok->str() == "{") { |
6321 | 1.74k | const Token *prev = tok->previous(); |
6322 | 1.74k | while (prev && prev->isName()) |
6323 | 0 | prev = prev->previous(); |
6324 | 1.74k | if (prev && prev->str() == ")") |
6325 | 1.74k | tok = tok->link(); |
6326 | 1.74k | } |
6327 | 41.0k | } |
6328 | 1.36k | } |
6329 | | |
6330 | | //--------------------------------------------------------------------------- |
6331 | | |
6332 | | void Tokenizer::removePragma() |
6333 | 1.36k | { |
6334 | 1.36k | if (isC() && mSettings->standards.c == Standards::C89) |
6335 | 0 | return; |
6336 | 1.36k | if (isCPP() && mSettings->standards.cpp == Standards::CPP03) |
6337 | 0 | return; |
6338 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6339 | 80.4k | while (Token::simpleMatch(tok, "_Pragma (")) { |
6340 | 0 | Token::eraseTokens(tok, tok->linkAt(1)->next()); |
6341 | 0 | tok->deleteThis(); |
6342 | 0 | } |
6343 | 80.4k | } |
6344 | 1.36k | } |
6345 | | |
6346 | | //--------------------------------------------------------------------------- |
6347 | | |
6348 | | void Tokenizer::removeMacroInClassDef() |
6349 | 1.36k | { |
6350 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6351 | 79.1k | if (!Token::Match(tok, "class|struct %name% %name% final| {|:")) |
6352 | 79.1k | continue; |
6353 | | |
6354 | 0 | const bool nextIsUppercase = tok->next()->isUpperCaseName(); |
6355 | 0 | const bool afterNextIsUppercase = tok->tokAt(2)->isUpperCaseName(); |
6356 | 0 | if (nextIsUppercase && !afterNextIsUppercase) |
6357 | 0 | tok->deleteNext(); |
6358 | 0 | else if (!nextIsUppercase && afterNextIsUppercase) |
6359 | 0 | tok->next()->deleteNext(); |
6360 | 0 | } |
6361 | 1.36k | } |
6362 | | |
6363 | | //--------------------------------------------------------------------------- |
6364 | | |
6365 | | void Tokenizer::addSemicolonAfterUnknownMacro() |
6366 | 1.36k | { |
6367 | 1.36k | if (!isCPP()) |
6368 | 0 | return; |
6369 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6370 | 79.1k | if (tok->str() != ")") |
6371 | 75.0k | continue; |
6372 | 4.05k | const Token *macro = tok->link() ? tok->link()->previous() : nullptr; |
6373 | 4.05k | if (!macro || !macro->isName()) |
6374 | 848 | continue; |
6375 | 3.20k | if (Token::simpleMatch(tok, ") try") && !Token::Match(macro, "if|for|while")) |
6376 | 0 | tok->insertToken(";"); |
6377 | 3.20k | else if (Token::simpleMatch(tok, ") using")) |
6378 | 0 | tok->insertToken(";"); |
6379 | 3.20k | } |
6380 | 1.36k | } |
6381 | | //--------------------------------------------------------------------------- |
6382 | | |
6383 | | void Tokenizer::simplifyEmptyNamespaces() |
6384 | 1.36k | { |
6385 | 1.36k | if (isC()) |
6386 | 0 | return; |
6387 | | |
6388 | 1.36k | bool goback = false; |
6389 | 55.9k | for (Token *tok = list.front(); tok; tok = tok ? tok->next() : nullptr) { |
6390 | 54.6k | if (goback) { |
6391 | 0 | tok = tok->previous(); |
6392 | 0 | goback = false; |
6393 | 0 | } |
6394 | 54.6k | if (Token::Match(tok, "(|[|{")) { |
6395 | 3.48k | tok = tok->link(); |
6396 | 3.48k | continue; |
6397 | 3.48k | } |
6398 | 51.1k | if (!Token::Match(tok, "namespace %name%| {")) |
6399 | 51.1k | continue; |
6400 | 0 | const bool isAnonymousNS = tok->strAt(1) == "{"; |
6401 | 0 | if (tok->strAt(3 - isAnonymousNS) == "}") { |
6402 | 0 | tok->deleteNext(3 - isAnonymousNS); // remove '%name%| { }' |
6403 | 0 | if (!tok->previous()) { |
6404 | | // remove 'namespace' or replace it with ';' if isolated |
6405 | 0 | tok->deleteThis(); |
6406 | 0 | goback = true; |
6407 | 0 | } else { // '%any% namespace %any%' |
6408 | 0 | tok = tok->previous(); // goto previous token |
6409 | 0 | tok->deleteNext(); // remove next token: 'namespace' |
6410 | 0 | if (tok->str() == "{") { |
6411 | | // Go back in case we were within a namespace that's empty now |
6412 | 0 | tok = tok->tokAt(-2) ? tok->tokAt(-2) : tok->previous(); |
6413 | 0 | goback = true; |
6414 | 0 | } |
6415 | 0 | } |
6416 | 0 | } else { |
6417 | 0 | tok = tok->tokAt(2 - isAnonymousNS); |
6418 | 0 | } |
6419 | 0 | } |
6420 | 1.36k | } |
6421 | | |
6422 | | void Tokenizer::removeRedundantSemicolons() |
6423 | 1.36k | { |
6424 | 80.0k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6425 | 78.7k | if (tok->link() && tok->str() == "(") { |
6426 | 3.40k | tok = tok->link(); |
6427 | 3.40k | continue; |
6428 | 3.40k | } |
6429 | 75.3k | for (;;) { |
6430 | 75.3k | if (Token::simpleMatch(tok, "; ;")) { |
6431 | 0 | tok->deleteNext(); |
6432 | 75.3k | } else if (Token::simpleMatch(tok, "; { ; }")) { |
6433 | 0 | tok->deleteNext(3); |
6434 | 75.3k | } else { |
6435 | 75.3k | break; |
6436 | 75.3k | } |
6437 | 75.3k | } |
6438 | 75.3k | } |
6439 | 1.36k | } |
6440 | | |
6441 | | |
6442 | | bool Tokenizer::simplifyAddBraces() |
6443 | 1.36k | { |
6444 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6445 | 79.1k | Token const * tokRet=simplifyAddBracesToCommand(tok); |
6446 | 79.1k | if (!tokRet) |
6447 | 0 | return false; |
6448 | 79.1k | } |
6449 | 1.36k | return true; |
6450 | 1.36k | } |
6451 | | |
6452 | | Token *Tokenizer::simplifyAddBracesToCommand(Token *tok) |
6453 | 79.1k | { |
6454 | 79.1k | Token * tokEnd=tok; |
6455 | 79.1k | if (Token::Match(tok,"for|switch|BOOST_FOREACH")) { |
6456 | 0 | tokEnd=simplifyAddBracesPair(tok,true); |
6457 | 79.1k | } else if (tok->str()=="while") { |
6458 | 468 | Token *tokPossibleDo=tok->previous(); |
6459 | 468 | if (Token::simpleMatch(tok->previous(), "{")) |
6460 | 326 | tokPossibleDo = nullptr; |
6461 | 142 | else if (Token::simpleMatch(tokPossibleDo,"}")) |
6462 | 9 | tokPossibleDo = tokPossibleDo->link(); |
6463 | 468 | if (!tokPossibleDo || tokPossibleDo->strAt(-1) != "do") |
6464 | 468 | tokEnd=simplifyAddBracesPair(tok,true); |
6465 | 78.6k | } else if (tok->str()=="do") { |
6466 | 0 | tokEnd=simplifyAddBracesPair(tok,false); |
6467 | 0 | if (tokEnd!=tok) { |
6468 | | // walk on to next token, i.e. "while" |
6469 | | // such that simplifyAddBracesPair does not close other braces |
6470 | | // before the "while" |
6471 | 0 | if (tokEnd) { |
6472 | 0 | tokEnd=tokEnd->next(); |
6473 | 0 | if (!tokEnd || tokEnd->str()!="while") // no while |
6474 | 0 | syntaxError(tok); |
6475 | 0 | } |
6476 | 0 | } |
6477 | 78.6k | } else if (tok->str()=="if" && !Token::simpleMatch(tok->tokAt(-2), "operator \"\"")) { |
6478 | 920 | tokEnd=simplifyAddBracesPair(tok,true); |
6479 | 920 | if (!tokEnd) |
6480 | 0 | return nullptr; |
6481 | 920 | if (tokEnd->strAt(1) == "else") { |
6482 | 458 | Token * tokEndNextNext= tokEnd->tokAt(2); |
6483 | 458 | if (!tokEndNextNext || tokEndNextNext->str() == "}") |
6484 | 0 | syntaxError(tokEndNextNext); |
6485 | 458 | if (tokEndNextNext->str() == "if") |
6486 | | // do not change "else if ..." to "else { if ... }" |
6487 | 0 | tokEnd=simplifyAddBracesToCommand(tokEndNextNext); |
6488 | 458 | else |
6489 | 458 | tokEnd=simplifyAddBracesPair(tokEnd->next(),false); |
6490 | 458 | } |
6491 | 920 | } |
6492 | | |
6493 | 79.1k | return tokEnd; |
6494 | 79.1k | } |
6495 | | |
6496 | | Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition) |
6497 | 1.84k | { |
6498 | 1.84k | Token * tokCondition=tok->next(); |
6499 | 1.84k | if (!tokCondition) // Missing condition |
6500 | 0 | return tok; |
6501 | | |
6502 | 1.84k | Token *tokAfterCondition=tokCondition; |
6503 | 1.84k | if (commandWithCondition) { |
6504 | 1.38k | if (tokCondition->str()=="(") |
6505 | 1.38k | tokAfterCondition=tokCondition->link(); |
6506 | 0 | else |
6507 | 0 | syntaxError(tok); // Bad condition |
6508 | | |
6509 | 1.38k | if (!tokAfterCondition || tokAfterCondition->strAt(1) == "]") |
6510 | 0 | syntaxError(tok); // Bad condition |
6511 | | |
6512 | 1.38k | tokAfterCondition=tokAfterCondition->next(); |
6513 | 1.38k | if (!tokAfterCondition || Token::Match(tokAfterCondition, ")|}|,")) { |
6514 | | // No tokens left where to add braces around |
6515 | 0 | return tok; |
6516 | 0 | } |
6517 | 1.38k | } |
6518 | | // Skip labels |
6519 | 1.84k | Token * tokStatement = tokAfterCondition; |
6520 | 1.84k | while (true) { |
6521 | 1.84k | if (Token::Match(tokStatement, "%name% :")) |
6522 | 0 | tokStatement = tokStatement->tokAt(2); |
6523 | 1.84k | else if (tokStatement->str() == "case") { |
6524 | 0 | tokStatement = skipCaseLabel(tokStatement); |
6525 | 0 | if (!tokStatement) |
6526 | 0 | return tok; |
6527 | 0 | if (tokStatement->str() != ":") |
6528 | 0 | syntaxError(tokStatement); |
6529 | 0 | tokStatement = tokStatement->next(); |
6530 | 0 | } else |
6531 | 1.84k | break; |
6532 | 0 | if (!tokStatement) |
6533 | 0 | return tok; |
6534 | 0 | } |
6535 | 1.84k | Token * tokBracesEnd=nullptr; |
6536 | 1.84k | if (tokStatement->str() == "{") { |
6537 | | // already surrounded by braces |
6538 | 1.84k | if (tokStatement != tokAfterCondition) { |
6539 | | // Move the opening brace before labels |
6540 | 0 | Token::move(tokStatement, tokStatement, tokAfterCondition->previous()); |
6541 | 0 | } |
6542 | 1.84k | tokBracesEnd = tokStatement->link(); |
6543 | 1.84k | } else if (Token::simpleMatch(tokStatement, "try {") && |
6544 | 0 | Token::simpleMatch(tokStatement->linkAt(1), "} catch (")) { |
6545 | 0 | tokAfterCondition->previous()->insertToken("{"); |
6546 | 0 | Token * tokOpenBrace = tokAfterCondition->previous(); |
6547 | 0 | Token * tokEnd = tokStatement->linkAt(1)->linkAt(2)->linkAt(1); |
6548 | 0 | if (!tokEnd) { |
6549 | 0 | syntaxError(tokStatement); |
6550 | 0 | } |
6551 | 0 | tokEnd->insertToken("}"); |
6552 | 0 | Token * tokCloseBrace = tokEnd->next(); |
6553 | |
|
6554 | 0 | Token::createMutualLinks(tokOpenBrace, tokCloseBrace); |
6555 | 0 | tokBracesEnd = tokCloseBrace; |
6556 | 0 | } else { |
6557 | 0 | Token * tokEnd = simplifyAddBracesToCommand(tokStatement); |
6558 | 0 | if (!tokEnd) // Ticket #4887 |
6559 | 0 | return tok; |
6560 | 0 | if (tokEnd->str()!="}") { |
6561 | | // Token does not end with brace |
6562 | | // Look for ; to add own closing brace after it |
6563 | 0 | while (tokEnd && !Token::Match(tokEnd, ";|)|}")) { |
6564 | 0 | if (tokEnd->tokType()==Token::eBracket || tokEnd->str() == "(") { |
6565 | 0 | tokEnd = tokEnd->link(); |
6566 | 0 | if (!tokEnd) { |
6567 | | // Inner bracket does not close |
6568 | 0 | return tok; |
6569 | 0 | } |
6570 | 0 | } |
6571 | 0 | tokEnd=tokEnd->next(); |
6572 | 0 | } |
6573 | 0 | if (!tokEnd || tokEnd->str() != ";") { |
6574 | | // No trailing ; |
6575 | 0 | return tok; |
6576 | 0 | } |
6577 | 0 | } |
6578 | | |
6579 | 0 | tokAfterCondition->previous()->insertToken("{"); |
6580 | 0 | Token * tokOpenBrace=tokAfterCondition->previous(); |
6581 | |
|
6582 | 0 | tokEnd->insertToken("}"); |
6583 | 0 | Token * tokCloseBrace=tokEnd->next(); |
6584 | |
|
6585 | 0 | Token::createMutualLinks(tokOpenBrace,tokCloseBrace); |
6586 | 0 | tokBracesEnd=tokCloseBrace; |
6587 | 0 | } |
6588 | | |
6589 | 1.84k | return tokBracesEnd; |
6590 | 1.84k | } |
6591 | | |
6592 | | void Tokenizer::simplifyFunctionParameters() |
6593 | 1.36k | { |
6594 | 42.3k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6595 | 41.0k | if (tok->link() && Token::Match(tok, "{|[|(")) { |
6596 | 3.48k | tok = tok->link(); |
6597 | 3.48k | } |
6598 | | |
6599 | | // Find the function e.g. foo( x ) or foo( x, y ) |
6600 | 37.5k | else if (Token::Match(tok, "%name% ( %name% [,)]") && |
6601 | 37.5k | !(tok->strAt(-1) == ":" || tok->strAt(-1) == "," || tok->strAt(-1) == "::")) { |
6602 | | // We have found old style function, now we need to change it |
6603 | | |
6604 | | // First step: Get list of argument names in parentheses |
6605 | 0 | std::map<std::string, Token *> argumentNames; |
6606 | 0 | bool bailOut = false; |
6607 | 0 | Token * tokparam = nullptr; |
6608 | | |
6609 | | //take count of the function name.. |
6610 | 0 | const std::string& funcName(tok->str()); |
6611 | | |
6612 | | //floating token used to check for parameters |
6613 | 0 | Token *tok1 = tok; |
6614 | |
|
6615 | 0 | while (nullptr != (tok1 = tok1->tokAt(2))) { |
6616 | 0 | if (!Token::Match(tok1, "%name% [,)]")) { |
6617 | 0 | bailOut = true; |
6618 | 0 | break; |
6619 | 0 | } |
6620 | | |
6621 | | //same parameters: take note of the parameter |
6622 | 0 | if (argumentNames.find(tok1->str()) != argumentNames.end()) |
6623 | 0 | tokparam = tok1; |
6624 | 0 | else if (tok1->str() != funcName) |
6625 | 0 | argumentNames[tok1->str()] = tok1; |
6626 | 0 | else { |
6627 | 0 | if (tok1->next()->str() == ")") { |
6628 | 0 | if (tok1->previous()->str() == ",") { |
6629 | 0 | tok1 = tok1->tokAt(-2); |
6630 | 0 | tok1->deleteNext(2); |
6631 | 0 | } else { |
6632 | 0 | tok1 = tok1->previous(); |
6633 | 0 | tok1->deleteNext(); |
6634 | 0 | bailOut = true; |
6635 | 0 | break; |
6636 | 0 | } |
6637 | 0 | } else { |
6638 | 0 | tok1 = tok1->tokAt(-2); |
6639 | 0 | tok1->next()->deleteNext(2); |
6640 | 0 | } |
6641 | 0 | } |
6642 | | |
6643 | 0 | if (tok1->next()->str() == ")") { |
6644 | 0 | tok1 = tok1->tokAt(2); |
6645 | | //expect at least a type name after round brace.. |
6646 | 0 | if (!tok1 || !tok1->isName()) |
6647 | 0 | bailOut = true; |
6648 | 0 | break; |
6649 | 0 | } |
6650 | 0 | } |
6651 | | |
6652 | | //goto '(' |
6653 | 0 | tok = tok->next(); |
6654 | |
|
6655 | 0 | if (bailOut) { |
6656 | 0 | tok = tok->link(); |
6657 | 0 | continue; |
6658 | 0 | } |
6659 | | |
6660 | 0 | tok1 = tok->link()->next(); |
6661 | | |
6662 | | // there should be the sequence '; {' after the round parentheses |
6663 | 0 | for (const Token* tok2 = tok1; tok2; tok2 = tok2->next()) { |
6664 | 0 | if (Token::simpleMatch(tok2, "; {")) |
6665 | 0 | break; |
6666 | 0 | if (tok2->str() == "{") { |
6667 | 0 | bailOut = true; |
6668 | 0 | break; |
6669 | 0 | } |
6670 | 0 | } |
6671 | |
|
6672 | 0 | if (bailOut) { |
6673 | 0 | tok = tok->link(); |
6674 | 0 | continue; |
6675 | 0 | } |
6676 | | |
6677 | | // Last step: check out if the declarations between ')' and '{' match the parameters list |
6678 | 0 | std::map<std::string, Token *> argumentNames2; |
6679 | |
|
6680 | 0 | while (tok1 && tok1->str() != "{") { |
6681 | 0 | if (Token::Match(tok1, "(|)")) { |
6682 | 0 | bailOut = true; |
6683 | 0 | break; |
6684 | 0 | } |
6685 | 0 | if (tok1->str() == ";") { |
6686 | 0 | if (tokparam) { |
6687 | 0 | syntaxError(tokparam); |
6688 | 0 | } |
6689 | 0 | Token *tok2 = tok1->previous(); |
6690 | 0 | while (tok2->str() == "]") |
6691 | 0 | tok2 = tok2->link()->previous(); |
6692 | | |
6693 | | //it should be a name.. |
6694 | 0 | if (!tok2->isName()) { |
6695 | 0 | bailOut = true; |
6696 | 0 | break; |
6697 | 0 | } |
6698 | | |
6699 | 0 | if (argumentNames2.find(tok2->str()) != argumentNames2.end()) { |
6700 | | //same parameter names... |
6701 | 0 | syntaxError(tok1); |
6702 | 0 | } else |
6703 | 0 | argumentNames2[tok2->str()] = tok2; |
6704 | |
|
6705 | 0 | if (argumentNames.find(tok2->str()) == argumentNames.end()) { |
6706 | | //non-matching parameter... bailout |
6707 | 0 | bailOut = true; |
6708 | 0 | break; |
6709 | 0 | } |
6710 | 0 | } |
6711 | 0 | tok1 = tok1->next(); |
6712 | 0 | } |
6713 | |
|
6714 | 0 | if (bailOut || !tok1) { |
6715 | 0 | tok = tok->link(); |
6716 | 0 | continue; |
6717 | 0 | } |
6718 | | |
6719 | | //the two containers may not hold the same size... |
6720 | | //in that case, the missing parameters are defined as 'int' |
6721 | 0 | if (argumentNames.size() != argumentNames2.size()) { |
6722 | | //move back 'tok1' to the last ';' |
6723 | 0 | tok1 = tok1->previous(); |
6724 | 0 | for (const std::pair<const std::string, Token *>& argumentName : argumentNames) { |
6725 | 0 | if (argumentNames2.find(argumentName.first) == argumentNames2.end()) { |
6726 | | //add the missing parameter argument declaration |
6727 | 0 | tok1->insertToken(";"); |
6728 | 0 | tok1->insertToken(argumentName.first); |
6729 | | //register the change inside argumentNames2 |
6730 | 0 | argumentNames2[argumentName.first] = tok1->next(); |
6731 | 0 | tok1->insertToken("int"); |
6732 | 0 | } |
6733 | 0 | } |
6734 | 0 | } |
6735 | |
|
6736 | 0 | while (tok->str() != ")") { |
6737 | | //initialize start and end tokens to be moved |
6738 | 0 | Token *declStart = argumentNames2[tok->next()->str()]; |
6739 | 0 | Token *declEnd = declStart; |
6740 | 0 | while (declStart->previous()->str() != ";" && declStart->previous()->str() != ")") |
6741 | 0 | declStart = declStart->previous(); |
6742 | 0 | while (declEnd->next()->str() != ";" && declEnd->next()->str() != "{") |
6743 | 0 | declEnd = declEnd->next(); |
6744 | | |
6745 | | //remove ';' after declaration |
6746 | 0 | declEnd->deleteNext(); |
6747 | | |
6748 | | //replace the parameter name in the parentheses with all the declaration |
6749 | 0 | Token::replace(tok->next(), declStart, declEnd); |
6750 | | |
6751 | | //since there are changes to tokens, put tok where tok1 is |
6752 | 0 | tok = declEnd->next(); |
6753 | | |
6754 | | //fix up line number |
6755 | 0 | if (tok->str() == ",") |
6756 | 0 | tok->linenr(tok->previous()->linenr()); |
6757 | 0 | } |
6758 | | //goto forward and continue |
6759 | 0 | tok = tok->next()->link(); |
6760 | 0 | } |
6761 | 41.0k | } |
6762 | 1.36k | } |
6763 | | |
6764 | | void Tokenizer::simplifyPointerToStandardType() |
6765 | 1.36k | { |
6766 | 1.36k | if (!isC()) |
6767 | 1.36k | return; |
6768 | | |
6769 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6770 | 0 | if (!Token::Match(tok, "& %name% [ 0 ] !![")) |
6771 | 0 | continue; |
6772 | | |
6773 | 0 | if (!Token::Match(tok->previous(), "[,(=]")) |
6774 | 0 | continue; |
6775 | | |
6776 | | // Remove '[ 0 ]' suffix |
6777 | 0 | Token::eraseTokens(tok->next(), tok->tokAt(5)); |
6778 | | // Remove '&' prefix |
6779 | 0 | tok = tok->previous(); |
6780 | 0 | if (!tok) |
6781 | 0 | break; |
6782 | 0 | tok->deleteNext(); |
6783 | 0 | } |
6784 | 0 | } |
6785 | | |
6786 | | void Tokenizer::simplifyFunctionPointers() |
6787 | 1.36k | { |
6788 | 85.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
6789 | | // #2873 - do not simplify function pointer usage here: |
6790 | | // (void)(xy(*p)(0)); |
6791 | 83.7k | if (Token::simpleMatch(tok, ") (")) { |
6792 | 0 | tok = tok->next()->link(); |
6793 | 0 | continue; |
6794 | 0 | } |
6795 | | |
6796 | | // check for function pointer cast |
6797 | 83.7k | if (Token::Match(tok, "( %type% %type%| *| *| ( * ) (") || |
6798 | 83.7k | Token::Match(tok, "static_cast < %type% %type%| *| *| ( * ) (")) { |
6799 | 0 | Token *tok1 = tok; |
6800 | |
|
6801 | 0 | if (isCPP() && tok1->str() == "static_cast") |
6802 | 0 | tok1 = tok1->next(); |
6803 | |
|
6804 | 0 | tok1 = tok1->next(); |
6805 | |
|
6806 | 0 | if (Token::Match(tok1->next(), "%type%")) |
6807 | 0 | tok1 = tok1->next(); |
6808 | |
|
6809 | 0 | while (tok1->next()->str() == "*") |
6810 | 0 | tok1 = tok1->next(); |
6811 | | |
6812 | | // check that the cast ends |
6813 | 0 | if (!Token::Match(tok1->linkAt(4), ") )|>")) |
6814 | 0 | continue; |
6815 | | |
6816 | | // ok simplify this function pointer cast to an ordinary pointer cast |
6817 | 0 | tok1->deleteNext(); |
6818 | 0 | tok1->next()->deleteNext(); |
6819 | 0 | Token::eraseTokens(tok1->next(), tok1->linkAt(2)->next()); |
6820 | 0 | continue; |
6821 | 0 | } |
6822 | | |
6823 | | // check for start of statement |
6824 | 83.7k | if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|,|(|public:|protected:|private:")) |
6825 | 55.6k | continue; |
6826 | | |
6827 | 28.0k | if (Token::Match(tok, "delete|else|return|throw|typedef")) |
6828 | 2.37k | continue; |
6829 | | |
6830 | 34.2k | while (Token::Match(tok, "%type%|:: %type%|::")) |
6831 | 8.55k | tok = tok->next(); |
6832 | | |
6833 | 25.6k | Token *tok2 = (tok && tok->isName()) ? tok->next() : nullptr; |
6834 | 25.7k | while (Token::Match(tok2, "*|&")) |
6835 | 55 | tok2 = tok2->next(); |
6836 | 25.6k | if (!tok2 || tok2->str() != "(") |
6837 | 22.5k | continue; |
6838 | 3.78k | while (Token::Match(tok2, "(|:: %type%")) |
6839 | 654 | tok2 = tok2->tokAt(2); |
6840 | 3.13k | if (!Token::Match(tok2, "(|:: * *| %name%")) |
6841 | 3.13k | continue; |
6842 | 0 | tok2 = tok2->tokAt(2); |
6843 | 0 | if (tok2->str() == "*") |
6844 | 0 | tok2 = tok2->next(); |
6845 | 0 | while (Token::Match(tok2, "%type%|:: %type%|::")) |
6846 | 0 | tok2 = tok2->next(); |
6847 | |
|
6848 | 0 | if (!Token::Match(tok2, "%name% ) (") && |
6849 | 0 | !Token::Match(tok2, "%name% [ ] ) (") && |
6850 | 0 | !(Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) |
6851 | 0 | continue; |
6852 | | |
6853 | 0 | while (tok && tok->str() != "(") |
6854 | 0 | tok = tok->next(); |
6855 | | |
6856 | | // check that the declaration ends |
6857 | 0 | if (!tok || !tok->link() || !tok->link()->next()) { |
6858 | 0 | syntaxError(nullptr); |
6859 | 0 | } |
6860 | 0 | Token *endTok = tok->link()->next()->link(); |
6861 | 0 | if (Token::simpleMatch(endTok, ") throw (")) |
6862 | 0 | endTok = endTok->linkAt(2); |
6863 | 0 | if (!Token::Match(endTok, ") const|volatile| const|volatile| ;|,|)|=|[|{")) |
6864 | 0 | continue; |
6865 | | |
6866 | 0 | while (Token::Match(endTok->next(), "const|volatile")) |
6867 | 0 | endTok->deleteNext(); |
6868 | | |
6869 | | // ok simplify this function pointer to an ordinary pointer |
6870 | 0 | if (Token::simpleMatch(tok->link()->previous(), ") )")) { |
6871 | | // Function returning function pointer |
6872 | | // void (*dostuff(void))(void) {} |
6873 | 0 | Token::eraseTokens(tok->link(), endTok->next()); |
6874 | 0 | tok->link()->deleteThis(); |
6875 | 0 | tok->deleteThis(); |
6876 | 0 | } else { |
6877 | 0 | Token::eraseTokens(tok->link()->linkAt(1), endTok->next()); |
6878 | | |
6879 | | // remove variable names |
6880 | 0 | int indent = 0; |
6881 | 0 | for (Token* tok3 = tok->link()->tokAt(2); Token::Match(tok3, "%name%|*|&|[|(|)|::|,|<"); tok3 = tok3->next()) { |
6882 | 0 | if (tok3->str() == ")" && --indent < 0) |
6883 | 0 | break; |
6884 | 0 | if (tok3->str() == "<" && tok3->link()) |
6885 | 0 | tok3 = tok3->link(); |
6886 | 0 | else if (Token::Match(tok3, "[")) |
6887 | 0 | tok3 = tok3->link(); |
6888 | 0 | else if (tok3->str() == "(") { |
6889 | 0 | tok3 = tok3->link(); |
6890 | 0 | if (Token::simpleMatch(tok3, ") (")) { |
6891 | 0 | tok3 = tok3->next(); |
6892 | 0 | ++indent; |
6893 | 0 | } else |
6894 | 0 | break; |
6895 | 0 | } |
6896 | 0 | if (Token::Match(tok3, "%type%|*|&|> %name% [,)[]")) |
6897 | 0 | tok3->deleteNext(); |
6898 | 0 | } |
6899 | | |
6900 | | // TODO Keep this info |
6901 | 0 | while (Token::Match(tok, "( %type% ::")) |
6902 | 0 | tok->deleteNext(2); |
6903 | 0 | } |
6904 | 0 | } |
6905 | 1.36k | } |
6906 | | |
6907 | | void Tokenizer::simplifyVarDecl(const bool only_k_r_fpar) |
6908 | 4.08k | { |
6909 | 4.08k | simplifyVarDecl(list.front(), nullptr, only_k_r_fpar); |
6910 | 4.08k | } |
6911 | | |
6912 | | void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar) |
6913 | 4.08k | { |
6914 | 4.08k | const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; |
6915 | | |
6916 | | // Split up variable declarations.. |
6917 | | // "int a=4;" => "int a; a=4;" |
6918 | 4.08k | bool finishedwithkr = true; |
6919 | 4.08k | bool scopeDecl = false; |
6920 | 202k | for (Token *tok = tokBegin; tok != tokEnd; tok = tok->next()) { |
6921 | 198k | if (Token::Match(tok, "{|;")) |
6922 | 49.8k | scopeDecl = false; |
6923 | 198k | if (isCPP()) { |
6924 | 198k | if (Token::Match(tok, "class|struct|namespace|union")) |
6925 | 0 | scopeDecl = true; |
6926 | 198k | if (Token::Match(tok, "decltype|noexcept (")) { |
6927 | 0 | tok = tok->next()->link(); |
6928 | | // skip decltype(...){...} |
6929 | 0 | if (tok && Token::simpleMatch(tok->previous(), ") {")) |
6930 | 0 | tok = tok->link(); |
6931 | 198k | } else if (Token::simpleMatch(tok, "= {") || |
6932 | 198k | (!scopeDecl && Token::Match(tok, "%name%|> {") && |
6933 | 198k | !Token::Match(tok, "else|try|do|const|constexpr|override|volatile|noexcept"))) { |
6934 | 0 | if (!tok->next()->link()) |
6935 | 0 | syntaxError(tokBegin); |
6936 | | // Check for lambdas before skipping |
6937 | 0 | if (Token::Match(tok->tokAt(-2), ") . %name%")) { // trailing return type |
6938 | | // TODO: support lambda without parameter clause? |
6939 | 0 | Token* lambdaStart = tok->linkAt(-2)->previous(); |
6940 | 0 | if (Token::simpleMatch(lambdaStart, "]")) |
6941 | 0 | lambdaStart = lambdaStart->link(); |
6942 | 0 | Token* lambdaEnd = findLambdaEndScope(lambdaStart); |
6943 | 0 | if (lambdaEnd) |
6944 | 0 | simplifyVarDecl(lambdaEnd->link()->next(), lambdaEnd, only_k_r_fpar); |
6945 | 0 | } else { |
6946 | 0 | for (Token* tok2 = tok->next(); tok2 != tok->next()->link(); tok2 = tok2->next()) { |
6947 | 0 | Token* lambdaEnd = findLambdaEndScope(tok2); |
6948 | 0 | if (!lambdaEnd) |
6949 | 0 | continue; |
6950 | 0 | simplifyVarDecl(lambdaEnd->link()->next(), lambdaEnd, only_k_r_fpar); |
6951 | 0 | } |
6952 | 0 | } |
6953 | 0 | tok = tok->next()->link(); |
6954 | 0 | } |
6955 | | |
6956 | 198k | } else if (Token::simpleMatch(tok, "= {")) { |
6957 | 0 | tok = tok->next()->link(); |
6958 | 0 | } |
6959 | 198k | if (!tok) { |
6960 | 0 | syntaxError(tokBegin); |
6961 | 0 | } |
6962 | 198k | if (only_k_r_fpar && finishedwithkr) { |
6963 | 41.0k | if (Token::Match(tok, "(|[|{")) { |
6964 | 3.48k | tok = tok->link(); |
6965 | 3.48k | if (tok->next() && Token::Match(tok, ") !!{")) |
6966 | 0 | tok = tok->next(); |
6967 | 3.48k | else |
6968 | 3.48k | continue; |
6969 | 3.48k | } else |
6970 | 37.5k | continue; |
6971 | 157k | } else if (tok->str() == "(") { |
6972 | 6.91k | if (isCPP()) { |
6973 | 34.6k | for (Token * tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { |
6974 | 27.7k | if (Token::Match(tok2, "[(,] [")) { |
6975 | | // lambda function at tok2->next() |
6976 | | // find start of lambda body |
6977 | 0 | Token * lambdaBody = tok2; |
6978 | 0 | while (lambdaBody && lambdaBody != tok2->link() && lambdaBody->str() != "{") |
6979 | 0 | lambdaBody = lambdaBody->next(); |
6980 | 0 | if (lambdaBody && lambdaBody != tok2->link() && lambdaBody->link()) |
6981 | 0 | simplifyVarDecl(lambdaBody, lambdaBody->link()->next(), only_k_r_fpar); |
6982 | 0 | } |
6983 | 27.7k | } |
6984 | 6.91k | } |
6985 | 6.91k | tok = tok->link(); |
6986 | 6.91k | } |
6987 | | |
6988 | 157k | if (!tok) |
6989 | 0 | syntaxError(nullptr); // #7043 invalid code |
6990 | 157k | if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:")) |
6991 | 101k | continue; |
6992 | 55.7k | if (Token::simpleMatch(tok, "template <")) |
6993 | 0 | continue; |
6994 | | |
6995 | 55.7k | Token *type0 = tok; |
6996 | 55.7k | if (!Token::Match(type0, "::|extern| %type%")) |
6997 | 14.3k | continue; |
6998 | 41.3k | if (Token::Match(type0, "else|return|public:|protected:|private:")) |
6999 | 4.74k | continue; |
7000 | 36.5k | if (isCPP11 && type0->str() == "using") |
7001 | 0 | continue; |
7002 | 36.5k | if (isCPP() && type0->str() == "namespace") |
7003 | 0 | continue; |
7004 | | |
7005 | 36.5k | bool isconst = false; |
7006 | 36.5k | bool isstatic = false; |
7007 | 36.5k | Token *tok2 = type0; |
7008 | 36.5k | int typelen = 1; |
7009 | | |
7010 | 36.5k | if (Token::Match(tok2, "::|extern")) { |
7011 | 0 | tok2 = tok2->next(); |
7012 | 0 | typelen++; |
7013 | 0 | } |
7014 | | |
7015 | | //check if variable is declared 'const' or 'static' or both |
7016 | 36.5k | while (tok2) { |
7017 | 36.5k | if (!Token::Match(tok2, "const|static|constexpr") && Token::Match(tok2, "%type% const|static")) { |
7018 | 0 | tok2 = tok2->next(); |
7019 | 0 | ++typelen; |
7020 | 0 | } |
7021 | | |
7022 | 36.5k | if (Token::Match(tok2, "const|constexpr")) |
7023 | 0 | isconst = true; |
7024 | | |
7025 | 36.5k | else if (Token::Match(tok2, "static|constexpr")) |
7026 | 0 | isstatic = true; |
7027 | | |
7028 | 36.5k | else if (Token::Match(tok2, "%type% :: %type%")) { |
7029 | 0 | tok2 = tok2->next(); |
7030 | 0 | ++typelen; |
7031 | 0 | } |
7032 | | |
7033 | 36.5k | else |
7034 | 36.5k | break; |
7035 | | |
7036 | 0 | if (tok2->strAt(1) == "*") |
7037 | 0 | break; |
7038 | | |
7039 | 0 | if (Token::Match(tok2->next(), "& %name% ,")) |
7040 | 0 | break; |
7041 | | |
7042 | 0 | tok2 = tok2->next(); |
7043 | 0 | ++typelen; |
7044 | 0 | } |
7045 | | |
7046 | | // strange looking variable declaration => don't split up. |
7047 | 36.5k | if (Token::Match(tok2, "%type% *|&| %name% , %type% *|&| %name%")) |
7048 | 0 | continue; |
7049 | | |
7050 | 36.5k | if (Token::Match(tok2, "struct|union|class %type%")) { |
7051 | 0 | tok2 = tok2->next(); |
7052 | 0 | ++typelen; |
7053 | 0 | } |
7054 | | |
7055 | | // check for qualification.. |
7056 | 36.5k | if (Token::Match(tok2, ":: %type%")) { |
7057 | 0 | ++typelen; |
7058 | 0 | tok2 = tok2->next(); |
7059 | 0 | } |
7060 | | |
7061 | | //skip combinations of templates and namespaces |
7062 | 36.5k | while (!isC() && (Token::Match(tok2, "%type% <") || Token::Match(tok2, "%type% ::"))) { |
7063 | 0 | if (tok2->next()->str() == "<" && !TemplateSimplifier::templateParameters(tok2->next())) { |
7064 | 0 | tok2 = nullptr; |
7065 | 0 | break; |
7066 | 0 | } |
7067 | 0 | typelen += 2; |
7068 | 0 | tok2 = tok2->tokAt(2); |
7069 | 0 | if (tok2 && tok2->previous()->str() == "::") |
7070 | 0 | continue; |
7071 | 0 | int indentlevel = 0; |
7072 | 0 | int parens = 0; |
7073 | |
|
7074 | 0 | for (Token *tok3 = tok2; tok3; tok3 = tok3->next()) { |
7075 | 0 | ++typelen; |
7076 | |
|
7077 | 0 | if (!parens && tok3->str() == "<") { |
7078 | 0 | ++indentlevel; |
7079 | 0 | } else if (!parens && tok3->str() == ">") { |
7080 | 0 | if (indentlevel == 0) { |
7081 | 0 | tok2 = tok3->next(); |
7082 | 0 | break; |
7083 | 0 | } |
7084 | 0 | --indentlevel; |
7085 | 0 | } else if (!parens && tok3->str() == ">>") { |
7086 | 0 | if (indentlevel <= 1) { |
7087 | 0 | tok2 = tok3->next(); |
7088 | 0 | break; |
7089 | 0 | } |
7090 | 0 | indentlevel -= 2; |
7091 | 0 | } else if (tok3->str() == "(") { |
7092 | 0 | ++parens; |
7093 | 0 | } else if (tok3->str() == ")") { |
7094 | 0 | if (!parens) { |
7095 | 0 | tok2 = nullptr; |
7096 | 0 | break; |
7097 | 0 | } |
7098 | 0 | --parens; |
7099 | 0 | } else if (tok3->str() == ";") { |
7100 | 0 | break; |
7101 | 0 | } |
7102 | 0 | } |
7103 | |
|
7104 | 0 | if (Token::Match(tok2, ":: %type%")) { |
7105 | 0 | ++typelen; |
7106 | 0 | tok2 = tok2->next(); |
7107 | 0 | } |
7108 | | |
7109 | | // east const |
7110 | 0 | if (Token::simpleMatch(tok2, "const")) |
7111 | 0 | isconst = true; |
7112 | 0 | } |
7113 | | |
7114 | | //pattern: "%type% *| ... *| const| %name% ,|=" |
7115 | 36.5k | if (Token::Match(tok2, "%type%") || |
7116 | 36.5k | (tok2 && tok2->previous() && tok2->previous()->str() == ">")) { |
7117 | 36.5k | Token *varName = tok2; |
7118 | 36.5k | if (!tok2->previous() || tok2->previous()->str() != ">") |
7119 | 36.5k | varName = varName->next(); |
7120 | 0 | else |
7121 | 0 | --typelen; |
7122 | 36.5k | if (isCPP() && Token::Match(varName, "public:|private:|protected:|using")) |
7123 | 0 | continue; |
7124 | | //skip all the pointer part |
7125 | 36.5k | bool isPointerOrRef = false; |
7126 | 36.5k | while (Token::simpleMatch(varName, "*") || Token::Match(varName, "& %name% ,")) { |
7127 | 0 | isPointerOrRef = true; |
7128 | 0 | varName = varName->next(); |
7129 | 0 | } |
7130 | | |
7131 | 36.5k | while (Token::Match(varName, "%type% %type%")) { |
7132 | 0 | if (varName->str() != "const" && varName->str() != "volatile") { |
7133 | 0 | ++typelen; |
7134 | 0 | } |
7135 | 0 | varName = varName->next(); |
7136 | 0 | } |
7137 | | // Function pointer |
7138 | 36.5k | if (Token::simpleMatch(varName, "( *") && |
7139 | 36.5k | Token::Match(varName->link()->previous(), "%name% ) (") && |
7140 | 36.5k | Token::simpleMatch(varName->link()->linkAt(1), ") =")) { |
7141 | 0 | Token *endDecl = varName->link()->linkAt(1); |
7142 | 0 | varName = varName->link()->previous(); |
7143 | 0 | endDecl->insertToken(";"); |
7144 | 0 | endDecl = endDecl->next(); |
7145 | 0 | endDecl->next()->isSplittedVarDeclEq(true); |
7146 | 0 | endDecl->insertToken(varName->str()); |
7147 | 0 | endDecl->next()->isExpandedMacro(varName->isExpandedMacro()); |
7148 | 0 | continue; |
7149 | 0 | } |
7150 | | //non-VLA case |
7151 | 36.5k | if (Token::Match(varName, "%name% ,|=")) { |
7152 | 6.81k | if (varName->str() != "operator") { |
7153 | 6.81k | tok2 = varName->next(); // The ',' or '=' token |
7154 | | |
7155 | 6.81k | if (tok2->str() == "=" && (isstatic || (isconst && !isPointerOrRef))) { |
7156 | | //do not split const non-pointer variables.. |
7157 | 0 | while (tok2 && tok2->str() != "," && tok2->str() != ";") { |
7158 | 0 | if (Token::Match(tok2, "{|(|[")) |
7159 | 0 | tok2 = tok2->link(); |
7160 | 0 | const Token *tok3 = tok2; |
7161 | 0 | if (!isC() && tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) { |
7162 | 0 | tok2 = tok2->findClosingBracket(); |
7163 | 0 | } |
7164 | 0 | if (!tok2) |
7165 | 0 | syntaxError(tok3); // #6881 invalid code |
7166 | 0 | tok2 = tok2->next(); |
7167 | 0 | } |
7168 | 0 | if (tok2 && tok2->str() == ";") |
7169 | 0 | tok2 = nullptr; |
7170 | 0 | } |
7171 | 6.81k | } else |
7172 | 0 | tok2 = nullptr; |
7173 | 6.81k | } |
7174 | | |
7175 | | //VLA case |
7176 | 29.7k | else if (Token::Match(varName, "%name% [")) { |
7177 | 0 | tok2 = varName->next(); |
7178 | |
|
7179 | 0 | while (Token::Match(tok2->link(), "] ,|=|[")) |
7180 | 0 | tok2 = tok2->link()->next(); |
7181 | 0 | if (!Token::Match(tok2, "=|,")) |
7182 | 0 | tok2 = nullptr; |
7183 | 0 | if (tok2 && tok2->str() == "=") { |
7184 | 0 | while (tok2 && tok2->str() != "," && tok2->str() != ";") { |
7185 | 0 | if (Token::Match(tok2, "{|(|[")) |
7186 | 0 | tok2 = tok2->link(); |
7187 | 0 | tok2 = tok2->next(); |
7188 | 0 | } |
7189 | 0 | if (tok2 && tok2->str() == ";") |
7190 | 0 | tok2 = nullptr; |
7191 | 0 | } |
7192 | 0 | } |
7193 | | |
7194 | | // brace initialization |
7195 | 29.7k | else if (Token::Match(varName, "%name% {")) { |
7196 | 0 | tok2 = varName->next(); |
7197 | 0 | tok2 = tok2->link(); |
7198 | 0 | if (tok2) |
7199 | 0 | tok2 = tok2->next(); |
7200 | 0 | if (tok2 && tok2->str() != ",") |
7201 | 0 | tok2 = nullptr; |
7202 | 0 | } |
7203 | | |
7204 | | // function declaration |
7205 | 29.7k | else if (Token::Match(varName, "%name% (")) { |
7206 | 3.48k | Token* commaTok = varName->linkAt(1)->next(); |
7207 | 3.48k | while (Token::Match(commaTok, "const|noexcept|override|final")) { |
7208 | 0 | commaTok = commaTok->next(); |
7209 | 0 | if (Token::Match(commaTok, "( true|false )")) |
7210 | 0 | commaTok = commaTok->link()->next(); |
7211 | 0 | } |
7212 | 3.48k | tok2 = Token::simpleMatch(commaTok, ",") ? commaTok : nullptr; |
7213 | 3.48k | } |
7214 | | |
7215 | 26.2k | else |
7216 | 26.2k | tok2 = nullptr; |
7217 | 36.5k | } else { |
7218 | 0 | tok2 = nullptr; |
7219 | 0 | } |
7220 | | |
7221 | 36.5k | if (!tok2) { |
7222 | 29.7k | if (only_k_r_fpar) |
7223 | 0 | finishedwithkr = false; |
7224 | 29.7k | continue; |
7225 | 29.7k | } |
7226 | | |
7227 | 6.81k | if (tok2->str() == ",") { |
7228 | 0 | tok2->str(";"); |
7229 | 0 | tok2->isSplittedVarDeclComma(true); |
7230 | | //TODO: should we have to add also template '<>' links? |
7231 | 0 | TokenList::insertTokens(tok2, type0, typelen); |
7232 | 0 | } |
7233 | | |
7234 | 6.81k | else { |
7235 | 6.81k | Token *eq = tok2; |
7236 | | |
7237 | 20.4k | while (tok2) { |
7238 | 20.4k | if (Token::Match(tok2, "{|(|[")) |
7239 | 0 | tok2 = tok2->link(); |
7240 | | |
7241 | 20.4k | else if (!isC() && tok2->str() == "<" && ((tok2->previous()->isName() && !tok2->previous()->varId()) || tok2->strAt(-1) == "]")) |
7242 | 0 | tok2 = tok2->findClosingBracket(); |
7243 | | |
7244 | 20.4k | else if (std::strchr(";,", tok2->str()[0])) { |
7245 | | // "type var =" => "type var; var =" |
7246 | 6.81k | const Token *varTok = type0->tokAt(typelen); |
7247 | 6.81k | while (Token::Match(varTok, "%name%|*|& %name%|*|&")) |
7248 | 0 | varTok = varTok->next(); |
7249 | 6.81k | if (!varTok) |
7250 | 0 | syntaxError(tok2); // invalid code |
7251 | 6.81k | TokenList::insertTokens(eq, varTok, 2); |
7252 | 6.81k | eq->str(";"); |
7253 | 6.81k | eq->isSplittedVarDeclEq(true); |
7254 | | |
7255 | | // "= x, " => "= x; type " |
7256 | 6.81k | if (tok2->str() == ",") { |
7257 | 0 | tok2->str(";"); |
7258 | 0 | tok2->isSplittedVarDeclComma(true); |
7259 | 0 | TokenList::insertTokens(tok2, type0, typelen); |
7260 | 0 | } |
7261 | 6.81k | break; |
7262 | 6.81k | } |
7263 | 13.6k | if (tok2) |
7264 | 13.6k | tok2 = tok2->next(); |
7265 | 13.6k | } |
7266 | 6.81k | } |
7267 | 6.81k | finishedwithkr = (only_k_r_fpar && tok2 && tok2->strAt(1) == "{"); |
7268 | 6.81k | } |
7269 | 4.08k | } |
7270 | | |
7271 | | void Tokenizer::simplifyStaticConst() |
7272 | 1.36k | { |
7273 | | // This function will simplify the token list so that the qualifiers "extern", "static" |
7274 | | // and "const" appear in the same order as in the array below. |
7275 | 1.36k | const std::string qualifiers[] = {"extern", "static", "const"}; |
7276 | | |
7277 | | // Move 'const' before all other qualifiers and types and then |
7278 | | // move 'static' before all other qualifiers and types, ... |
7279 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7280 | 92.7k | bool continue2 = false; |
7281 | 370k | for (int i = 0; i < sizeof(qualifiers)/sizeof(qualifiers[0]); i++) { |
7282 | | |
7283 | | // Keep searching for a qualifier |
7284 | 278k | if (!tok->next() || tok->next()->str() != qualifiers[i]) |
7285 | 278k | continue; |
7286 | | |
7287 | | // Look backwards to find the beginning of the declaration |
7288 | 0 | Token* leftTok = tok; |
7289 | 0 | bool behindOther = false; |
7290 | 0 | for (; leftTok; leftTok = leftTok->previous()) { |
7291 | 0 | for (int j = 0; j <= i; j++) { |
7292 | 0 | if (leftTok->str() == qualifiers[j]) { |
7293 | 0 | behindOther = true; |
7294 | 0 | break; |
7295 | 0 | } |
7296 | 0 | } |
7297 | 0 | if (behindOther) |
7298 | 0 | break; |
7299 | 0 | if (isCPP() && Token::simpleMatch(leftTok, ">")) { |
7300 | 0 | Token* opening = leftTok->findOpeningBracket(); |
7301 | 0 | if (opening) { |
7302 | 0 | leftTok = opening; |
7303 | 0 | continue; |
7304 | 0 | } |
7305 | 0 | } |
7306 | 0 | if (!Token::Match(leftTok, "%type%|struct|::") || |
7307 | 0 | (isCPP() && Token::Match(leftTok, "private:|protected:|public:|operator|template"))) { |
7308 | 0 | break; |
7309 | 0 | } |
7310 | 0 | } |
7311 | | |
7312 | | // The token preceding the declaration should indicate the start of a declaration |
7313 | 0 | if (leftTok == tok) |
7314 | 0 | continue; |
7315 | | |
7316 | 0 | if (leftTok && !behindOther && !Token::Match(leftTok, ";|{|}|(|,|private:|protected:|public:")) { |
7317 | 0 | continue2 = true; |
7318 | 0 | break; |
7319 | 0 | } |
7320 | | |
7321 | | // Move the qualifier to the left-most position in the declaration |
7322 | 0 | tok->deleteNext(); |
7323 | 0 | if (!leftTok) { |
7324 | 0 | list.front()->insertToken(qualifiers[i], emptyString, false); |
7325 | 0 | list.front()->swapWithNext(); |
7326 | 0 | tok = list.front(); |
7327 | 0 | } else if (leftTok->next()) { |
7328 | 0 | leftTok->next()->insertToken(qualifiers[i], emptyString, true); |
7329 | 0 | tok = leftTok->next(); |
7330 | 0 | } else { |
7331 | 0 | leftTok->insertToken(qualifiers[i]); |
7332 | 0 | tok = leftTok; |
7333 | 0 | } |
7334 | 0 | } |
7335 | 92.7k | if (continue2) |
7336 | 0 | continue; |
7337 | 92.7k | } |
7338 | 1.36k | } |
7339 | | |
7340 | | void Tokenizer::simplifyVariableMultipleAssign() |
7341 | 1.36k | { |
7342 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7343 | 92.7k | if (Token::Match(tok, "%name% = %name% = %num%|%name% ;")) { |
7344 | | // skip intermediate assignments |
7345 | 22 | Token *tok2 = tok->previous(); |
7346 | 22 | while (tok2 && |
7347 | 22 | tok2->str() == "=" && |
7348 | 22 | Token::Match(tok2->previous(), "%name%")) { |
7349 | 0 | tok2 = tok2->tokAt(-2); |
7350 | 0 | } |
7351 | | |
7352 | 22 | if (!tok2 || tok2->str() != ";") { |
7353 | 12 | continue; |
7354 | 12 | } |
7355 | | |
7356 | 10 | Token *stopAt = tok->tokAt(2); |
7357 | 10 | const Token *valueTok = stopAt->tokAt(2); |
7358 | 10 | const std::string& value(valueTok->str()); |
7359 | 10 | tok2 = tok2->next(); |
7360 | | |
7361 | 20 | while (tok2 != stopAt) { |
7362 | 10 | tok2->next()->insertToken(";"); |
7363 | 10 | tok2->next()->insertToken(value); |
7364 | 10 | tok2 = tok2->tokAt(4); |
7365 | 10 | } |
7366 | 10 | } |
7367 | 92.7k | } |
7368 | 1.36k | } |
7369 | | |
7370 | | // Binary operators simplification map |
7371 | | static const std::unordered_map<std::string, std::string> cAlternativeTokens = { |
7372 | | std::make_pair("and", "&&") |
7373 | | , std::make_pair("and_eq", "&=") |
7374 | | , std::make_pair("bitand", "&") |
7375 | | , std::make_pair("bitor", "|") |
7376 | | , std::make_pair("not_eq", "!=") |
7377 | | , std::make_pair("or", "||") |
7378 | | , std::make_pair("or_eq", "|=") |
7379 | | , std::make_pair("xor", "^") |
7380 | | , std::make_pair("xor_eq", "^=") |
7381 | | }; |
7382 | | |
7383 | | // Simplify the C alternative tokens: |
7384 | | // and => && |
7385 | | // and_eq => &= |
7386 | | // bitand => & |
7387 | | // bitor => | |
7388 | | // compl => ~ |
7389 | | // not => ! |
7390 | | // not_eq => != |
7391 | | // or => || |
7392 | | // or_eq => |= |
7393 | | // xor => ^ |
7394 | | // xor_eq => ^= |
7395 | | bool Tokenizer::simplifyCAlternativeTokens() |
7396 | 1.36k | { |
7397 | | /* executable scope level */ |
7398 | 1.36k | int executableScopeLevel = 0; |
7399 | | |
7400 | 1.36k | std::vector<Token *> alt; |
7401 | 1.36k | bool replaceAll = false; // replace all or none |
7402 | | |
7403 | 78.5k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7404 | 77.2k | if (tok->str() == ")") { |
7405 | 3.91k | if (const Token *end = isFunctionHead(tok, "{")) { |
7406 | 3.12k | ++executableScopeLevel; |
7407 | 3.12k | tok = const_cast<Token *>(end); |
7408 | 3.12k | continue; |
7409 | 3.12k | } |
7410 | 3.91k | } |
7411 | | |
7412 | 74.1k | if (tok->str() == "{") { |
7413 | 458 | if (executableScopeLevel > 0) |
7414 | 458 | ++executableScopeLevel; |
7415 | 458 | continue; |
7416 | 458 | } |
7417 | | |
7418 | 73.6k | if (tok->str() == "}") { |
7419 | 3.58k | if (executableScopeLevel > 0) |
7420 | 3.58k | --executableScopeLevel; |
7421 | 3.58k | continue; |
7422 | 3.58k | } |
7423 | | |
7424 | 70.0k | if (!tok->isName()) |
7425 | 41.1k | continue; |
7426 | | |
7427 | 28.9k | const std::unordered_map<std::string, std::string>::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); |
7428 | 28.9k | if (cOpIt != cAlternativeTokens.end()) { |
7429 | 0 | alt.push_back(tok); |
7430 | | |
7431 | | // Is this a variable declaration.. |
7432 | 0 | if (isC() && Token::Match(tok->previous(), "%type%|* %name% [;,=]")) |
7433 | 0 | return false; |
7434 | | |
7435 | 0 | if (!Token::Match(tok->previous(), "%name%|%num%|%char%|)|]|> %name% %name%|%num%|%char%|%op%|(")) |
7436 | 0 | continue; |
7437 | 0 | if (Token::Match(tok->next(), "%assign%|%or%|%oror%|&&|*|/|%|^") && !Token::Match(tok->previous(), "%num%|%char%|) %name% *")) |
7438 | 0 | continue; |
7439 | 0 | if (executableScopeLevel == 0 && Token::Match(tok, "%name% (")) { |
7440 | 0 | const Token *start = tok; |
7441 | 0 | while (Token::Match(start, "%name%|*")) |
7442 | 0 | start = start->previous(); |
7443 | 0 | if (!start || Token::Match(start, "[;}]")) |
7444 | 0 | continue; |
7445 | 0 | } |
7446 | 0 | replaceAll = true; |
7447 | 28.9k | } else if (Token::Match(tok, "not|compl")) { |
7448 | 0 | alt.push_back(tok); |
7449 | |
|
7450 | 0 | if (Token::Match(tok->previous(), "%assign%") || Token::Match(tok->next(), "%num%")) { |
7451 | 0 | replaceAll = true; |
7452 | 0 | continue; |
7453 | 0 | } |
7454 | | |
7455 | | // Don't simplify 'not p;' (in case 'not' is a type) |
7456 | 0 | if (!Token::Match(tok->next(), "%name%|(") || |
7457 | 0 | Token::Match(tok->previous(), "[;{}]") || |
7458 | 0 | (executableScopeLevel == 0U && tok->strAt(-1) == "(")) |
7459 | 0 | continue; |
7460 | | |
7461 | 0 | replaceAll = true; |
7462 | 0 | } |
7463 | 28.9k | } |
7464 | | |
7465 | 1.36k | if (!replaceAll) |
7466 | 1.36k | return false; |
7467 | | |
7468 | 0 | for (Token *tok: alt) { |
7469 | 0 | const std::unordered_map<std::string, std::string>::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); |
7470 | 0 | if (cOpIt != cAlternativeTokens.end()) |
7471 | 0 | tok->str(cOpIt->second); |
7472 | 0 | else if (tok->str() == "not") |
7473 | 0 | tok->str("!"); |
7474 | 0 | else |
7475 | 0 | tok->str("~"); |
7476 | 0 | } |
7477 | |
|
7478 | 0 | return !alt.empty(); |
7479 | 1.36k | } |
7480 | | |
7481 | | // int i(0); => int i; i = 0; |
7482 | | // int i(0), j; => int i; i = 0; int j; |
7483 | | void Tokenizer::simplifyInitVar() |
7484 | 4.08k | { |
7485 | 4.08k | if (isC()) |
7486 | 0 | return; |
7487 | | |
7488 | 267k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7489 | 263k | if (!tok->isName() || (tok->previous() && !Token::Match(tok->previous(), "[;{}]"))) |
7490 | 208k | continue; |
7491 | | |
7492 | 55.1k | if (tok->str() == "return") |
7493 | 5.74k | continue; |
7494 | | |
7495 | 49.4k | if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ;")) { |
7496 | 0 | tok = initVar(tok); |
7497 | 49.4k | } else if (Token::Match(tok, "%type% *| %name% ( %type% (")) { |
7498 | 0 | const Token* tok2 = tok->tokAt(2); |
7499 | 0 | if (!tok2->link()) |
7500 | 0 | tok2 = tok2->next(); |
7501 | 0 | if (!tok2->link() || (tok2->link()->strAt(1) == ";" && !Token::simpleMatch(tok2->linkAt(2), ") ("))) |
7502 | 0 | tok = initVar(tok); |
7503 | 49.4k | } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,") && tok->str() != "new") { |
7504 | 0 | Token *tok1 = tok->tokAt(5); |
7505 | 0 | while (tok1->str() != ",") |
7506 | 0 | tok1 = tok1->next(); |
7507 | 0 | tok1->str(";"); |
7508 | |
|
7509 | 0 | const int numTokens = (Token::Match(tok, "class|struct|union")) ? 2U : 1U; |
7510 | 0 | TokenList::insertTokens(tok1, tok, numTokens); |
7511 | 0 | tok = initVar(tok); |
7512 | 0 | } |
7513 | 49.4k | } |
7514 | 4.08k | } |
7515 | | |
7516 | | Token * Tokenizer::initVar(Token * tok) |
7517 | 0 | { |
7518 | | // call constructor of class => no simplification |
7519 | 0 | if (Token::Match(tok, "class|struct|union")) { |
7520 | 0 | if (tok->strAt(2) != "*") |
7521 | 0 | return tok; |
7522 | | |
7523 | 0 | tok = tok->next(); |
7524 | 0 | } else if (!tok->isStandardType() && tok->str() != "auto" && tok->next()->str() != "*") |
7525 | 0 | return tok; |
7526 | | |
7527 | | // goto variable name.. |
7528 | 0 | tok = tok->next(); |
7529 | 0 | if (tok->str() == "*") |
7530 | 0 | tok = tok->next(); |
7531 | | |
7532 | | // sizeof is not a variable name.. |
7533 | 0 | if (tok->str() == "sizeof") |
7534 | 0 | return tok; |
7535 | | |
7536 | | // check initializer.. |
7537 | 0 | if (tok->tokAt(2)->isStandardType() || tok->strAt(2) == "void") |
7538 | 0 | return tok; |
7539 | 0 | if (!tok->tokAt(2)->isNumber() && !Token::Match(tok->tokAt(2), "%type% (") && tok->strAt(2) != "&" && tok->tokAt(2)->varId() == 0) |
7540 | 0 | return tok; |
7541 | | |
7542 | | // insert '; var =' |
7543 | 0 | tok->insertToken(";"); |
7544 | 0 | tok->next()->insertToken(tok->str()); |
7545 | 0 | tok->tokAt(2)->varId(tok->varId()); |
7546 | 0 | tok = tok->tokAt(2); |
7547 | 0 | tok->insertToken("="); |
7548 | | |
7549 | | // goto '('.. |
7550 | 0 | tok = tok->tokAt(2); |
7551 | | |
7552 | | // delete ')' |
7553 | 0 | tok->link()->deleteThis(); |
7554 | | |
7555 | | // delete this |
7556 | 0 | tok->deleteThis(); |
7557 | |
|
7558 | 0 | return tok; |
7559 | 0 | } |
7560 | | |
7561 | | void Tokenizer::elseif() |
7562 | 1.36k | { |
7563 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7564 | 92.3k | if (tok->str() != "else") |
7565 | 91.8k | continue; |
7566 | | |
7567 | 458 | if (!Token::Match(tok->previous(), ";|}")) |
7568 | 0 | syntaxError(tok->previous()); |
7569 | | |
7570 | 458 | if (!Token::Match(tok->next(), "%name%")) |
7571 | 458 | continue; |
7572 | | |
7573 | 0 | if (tok->strAt(1) != "if") |
7574 | 0 | unknownMacroError(tok->next()); |
7575 | |
|
7576 | 0 | for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { |
7577 | 0 | if (Token::Match(tok2, "(|{|[")) |
7578 | 0 | tok2 = tok2->link(); |
7579 | |
|
7580 | 0 | if (Token::Match(tok2, "}|;")) { |
7581 | 0 | if (tok2->next() && tok2->next()->str() != "else") { |
7582 | 0 | tok->insertToken("{"); |
7583 | 0 | tok2->insertToken("}"); |
7584 | 0 | Token::createMutualLinks(tok->next(), tok2->next()); |
7585 | 0 | break; |
7586 | 0 | } |
7587 | 0 | } |
7588 | 0 | } |
7589 | 0 | } |
7590 | 1.36k | } |
7591 | | |
7592 | | |
7593 | | void Tokenizer::simplifyIfSwitchForInit() |
7594 | 1.36k | { |
7595 | 1.36k | if (!isCPP() || mSettings->standards.cpp < Standards::CPP17) |
7596 | 0 | return; |
7597 | | |
7598 | 1.36k | const bool forInit = (mSettings->standards.cpp >= Standards::CPP20); |
7599 | | |
7600 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7601 | 92.3k | if (!Token::Match(tok, "if|switch|for (")) |
7602 | 91.3k | continue; |
7603 | | |
7604 | 920 | Token *semicolon = tok->tokAt(2); |
7605 | 6.23k | while (!Token::Match(semicolon, "[;)]")) { |
7606 | 5.31k | if (Token::Match(semicolon, "(|{|[") && semicolon->link()) |
7607 | 265 | semicolon = semicolon->link(); |
7608 | 5.31k | semicolon = semicolon->next(); |
7609 | 5.31k | } |
7610 | 920 | if (semicolon->str() != ";") |
7611 | 920 | continue; |
7612 | | |
7613 | 0 | if (tok->str() == "for") { |
7614 | 0 | if (!forInit) |
7615 | 0 | continue; |
7616 | | |
7617 | | // Is it a for range.. |
7618 | 0 | const Token *tok2 = semicolon->next(); |
7619 | 0 | bool rangeFor = false; |
7620 | 0 | while (!Token::Match(tok2, "[;)]")) { |
7621 | 0 | if (tok2->str() == "(") |
7622 | 0 | tok2 = tok2->link(); |
7623 | 0 | else if (!rangeFor && tok2->str() == "?") |
7624 | 0 | break; |
7625 | 0 | else if (tok2->str() == ":") |
7626 | 0 | rangeFor = true; |
7627 | 0 | tok2 = tok2->next(); |
7628 | 0 | } |
7629 | 0 | if (!rangeFor || tok2->str() != ")") |
7630 | 0 | continue; |
7631 | 0 | } |
7632 | | |
7633 | 0 | Token *endpar = tok->linkAt(1); |
7634 | 0 | if (!Token::simpleMatch(endpar, ") {")) |
7635 | 0 | continue; |
7636 | | |
7637 | 0 | Token *endscope = endpar->linkAt(1); |
7638 | 0 | if (Token::simpleMatch(endscope, "} else {")) |
7639 | 0 | endscope = endscope->linkAt(2); |
7640 | | |
7641 | | // Simplify, the initialization expression is broken out.. |
7642 | 0 | semicolon->insertToken(tok->str()); |
7643 | 0 | semicolon->next()->insertToken("("); |
7644 | 0 | Token::createMutualLinks(semicolon->next()->next(), endpar); |
7645 | 0 | tok->deleteNext(); |
7646 | 0 | tok->str("{"); |
7647 | 0 | endscope->insertToken("}"); |
7648 | 0 | Token::createMutualLinks(tok, endscope->next()); |
7649 | 0 | tok->isSimplifiedScope(true); |
7650 | 0 | } |
7651 | 1.36k | } |
7652 | | |
7653 | | |
7654 | | bool Tokenizer::simplifyRedundantParentheses() |
7655 | 1.36k | { |
7656 | 1.36k | bool ret = false; |
7657 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7658 | 92.3k | if (tok->str() != "(") |
7659 | 88.3k | continue; |
7660 | | |
7661 | 3.98k | if (isCPP() && Token::simpleMatch(tok->previous(), "} (")) { |
7662 | 0 | const Token* plp = tok->previous()->link()->previous(); |
7663 | 0 | if (Token::Match(plp, "%name%|>|] {") || (Token::simpleMatch(plp, ")") && Token::simpleMatch(plp->link()->previous(), "]"))) |
7664 | 0 | continue; |
7665 | 0 | } |
7666 | | |
7667 | 3.98k | if (Token::simpleMatch(tok, "( {")) |
7668 | 0 | continue; |
7669 | | |
7670 | 3.98k | if (Token::Match(tok->link(), ") %num%")) { |
7671 | 0 | tok = tok->link(); |
7672 | 0 | continue; |
7673 | 0 | } |
7674 | | |
7675 | | // Do not simplify if there is comma inside parentheses.. |
7676 | 3.98k | if (Token::Match(tok->previous(), "%op% (") || Token::Match(tok->link(), ") %op%")) { |
7677 | 800 | bool innerComma = false; |
7678 | 2.52k | for (const Token *inner = tok->link()->previous(); inner != tok; inner = inner->previous()) { |
7679 | 1.72k | if (inner->str() == ")") |
7680 | 91 | inner = inner->link(); |
7681 | 1.72k | if (inner->str() == ",") { |
7682 | 0 | innerComma = true; |
7683 | 0 | break; |
7684 | 0 | } |
7685 | 1.72k | } |
7686 | 800 | if (innerComma) |
7687 | 0 | continue; |
7688 | 800 | } |
7689 | | |
7690 | | // !!operator = ( x ) ; |
7691 | 3.98k | if (tok->strAt(-2) != "operator" && |
7692 | 3.98k | tok->previous() && tok->previous()->str() == "=" && |
7693 | 3.98k | tok->next() && tok->next()->str() != "{" && |
7694 | 3.98k | Token::simpleMatch(tok->link(), ") ;")) { |
7695 | 106 | tok->link()->deleteThis(); |
7696 | 106 | tok->deleteThis(); |
7697 | 106 | continue; |
7698 | 106 | } |
7699 | | |
7700 | 3.92k | while (Token::simpleMatch(tok, "( (") && |
7701 | 3.92k | tok->link() && tok->link()->previous() == tok->next()->link()) { |
7702 | | // We have "(( *something* ))", remove the inner |
7703 | | // parentheses |
7704 | 46 | tok->deleteNext(); |
7705 | 46 | tok->link()->tokAt(-2)->deleteNext(); |
7706 | 46 | ret = true; |
7707 | 46 | } |
7708 | | |
7709 | 3.87k | if (isCPP() && Token::Match(tok->tokAt(-2), "[;{}=(] new (") && Token::Match(tok->link(), ") [;,{}[]")) { |
7710 | | // Remove the parentheses in "new (type)" constructs |
7711 | 0 | tok->link()->deleteThis(); |
7712 | 0 | tok->deleteThis(); |
7713 | 0 | ret = true; |
7714 | 0 | } |
7715 | | |
7716 | 3.87k | if (Token::Match(tok->previous(), "! ( %name% )")) { |
7717 | | // Remove the parentheses |
7718 | 0 | tok->deleteThis(); |
7719 | 0 | tok->deleteNext(); |
7720 | 0 | ret = true; |
7721 | 0 | } |
7722 | | |
7723 | 3.87k | if (Token::Match(tok->previous(), "[(,;{}] ( %name% ) .")) { |
7724 | | // Remove the parentheses |
7725 | 0 | tok->deleteThis(); |
7726 | 0 | tok->deleteNext(); |
7727 | 0 | ret = true; |
7728 | 0 | } |
7729 | | |
7730 | 3.87k | if (Token::Match(tok->previous(), "[(,;{}] ( %name% (") && !tok->next()->isKeyword() && |
7731 | 3.87k | tok->link()->previous() == tok->linkAt(2)) { |
7732 | | // We have "( func ( *something* ))", remove the outer |
7733 | | // parentheses |
7734 | 0 | tok->link()->deleteThis(); |
7735 | 0 | tok->deleteThis(); |
7736 | 0 | ret = true; |
7737 | 0 | } |
7738 | | |
7739 | 3.87k | if (Token::Match(tok->previous(), "[,;{}] ( delete [| ]| %name% ) ;")) { |
7740 | | // We have "( delete [| ]| var )", remove the outer |
7741 | | // parentheses |
7742 | 0 | tok->link()->deleteThis(); |
7743 | 0 | tok->deleteThis(); |
7744 | 0 | ret = true; |
7745 | 0 | } |
7746 | | |
7747 | 3.87k | if (!Token::simpleMatch(tok->tokAt(-2), "operator delete") && |
7748 | 3.87k | Token::Match(tok->previous(), "delete|; (") && |
7749 | 3.87k | (tok->previous()->str() != "delete" || tok->next()->varId() > 0) && |
7750 | 3.87k | Token::Match(tok->link(), ") ;|,")) { |
7751 | 0 | tok->link()->deleteThis(); |
7752 | 0 | tok->deleteThis(); |
7753 | 0 | ret = true; |
7754 | 0 | } |
7755 | | |
7756 | 3.87k | if (Token::Match(tok->previous(), "[(!*;{}] ( %name% )") && |
7757 | 3.87k | (tok->next()->varId() != 0 || Token::Match(tok->tokAt(3), "[+-/=]")) && !tok->next()->isStandardType()) { |
7758 | | // We have "( var )", remove the parentheses |
7759 | 5 | tok->deleteThis(); |
7760 | 5 | tok->deleteNext(); |
7761 | 5 | ret = true; |
7762 | 5 | } |
7763 | | |
7764 | 3.87k | while (Token::Match(tok->previous(), "[;{}[(,!*] ( %name% .")) { |
7765 | 0 | Token *tok2 = tok->tokAt(2); |
7766 | 0 | while (Token::Match(tok2, ". %name%")) { |
7767 | 0 | tok2 = tok2->tokAt(2); |
7768 | 0 | } |
7769 | 0 | if (tok2 != tok->link()) |
7770 | 0 | break; |
7771 | | // We have "( var . var . ... . var )", remove the parentheses |
7772 | 0 | tok = tok->previous(); |
7773 | 0 | tok->deleteNext(); |
7774 | 0 | tok2->deleteThis(); |
7775 | 0 | ret = true; |
7776 | 0 | } |
7777 | | |
7778 | 3.87k | if (Token::simpleMatch(tok->previous(), "? (") && Token::simpleMatch(tok->link(), ") :")) { |
7779 | 0 | const Token *tok2 = tok->next(); |
7780 | 0 | while (tok2 && (Token::Match(tok2,"%bool%|%num%|%name%") || tok2->isArithmeticalOp())) |
7781 | 0 | tok2 = tok2->next(); |
7782 | 0 | if (tok2 && tok2->str() == ")") { |
7783 | 0 | tok->link()->deleteThis(); |
7784 | 0 | tok->deleteThis(); |
7785 | 0 | ret = true; |
7786 | 0 | continue; |
7787 | 0 | } |
7788 | 0 | } |
7789 | | |
7790 | 3.87k | while (Token::Match(tok->previous(), "[{([,] ( !!{") && |
7791 | 3.87k | Token::Match(tok->link(), ") [;,])]") && |
7792 | 3.87k | !Token::simpleMatch(tok->tokAt(-2), "operator ,") && // Ticket #5709 |
7793 | 3.87k | !Token::findsimplematch(tok, ",", tok->link())) { |
7794 | | // We have "( ... )", remove the parentheses |
7795 | 4 | tok->link()->deleteThis(); |
7796 | 4 | tok->deleteThis(); |
7797 | 4 | ret = true; |
7798 | 4 | } |
7799 | | |
7800 | 3.87k | if (Token::simpleMatch(tok->previous(), ", (") && |
7801 | 3.87k | Token::simpleMatch(tok->link(), ") =")) { |
7802 | 0 | tok->link()->deleteThis(); |
7803 | 0 | tok->deleteThis(); |
7804 | 0 | ret = true; |
7805 | 0 | } |
7806 | | |
7807 | | // Simplify "!!operator !!%name%|)|]|>|>> ( %num%|%bool% ) %op%|;|,|)" |
7808 | 3.87k | if (Token::Match(tok, "( %bool%|%num% ) %cop%|;|,|)") && |
7809 | 3.87k | tok->strAt(-2) != "operator" && |
7810 | 3.87k | tok->previous() && |
7811 | 3.87k | !Token::Match(tok->previous(), "%name%|)|]") && |
7812 | 3.87k | (!(isCPP() && Token::Match(tok->previous(),">|>>")))) { |
7813 | 64 | tok->link()->deleteThis(); |
7814 | 64 | tok->deleteThis(); |
7815 | 64 | ret = true; |
7816 | 64 | } |
7817 | | |
7818 | 3.87k | if (Token::Match(tok->previous(), "*|& ( %name% )")) { |
7819 | | // We may have a variable declaration looking like "type_name *(var_name)" |
7820 | 16 | Token *tok2 = tok->tokAt(-2); |
7821 | 30 | while (Token::Match(tok2, "%type%|static|const|extern") && tok2->str() != "operator") { |
7822 | 14 | tok2 = tok2->previous(); |
7823 | 14 | } |
7824 | 16 | if (tok2 && !Token::Match(tok2, "[;,{]")) { |
7825 | | // Not a variable declaration |
7826 | 14 | } else { |
7827 | 2 | tok->deleteThis(); |
7828 | 2 | tok->deleteNext(); |
7829 | 2 | } |
7830 | 16 | } |
7831 | 3.87k | } |
7832 | 1.36k | return ret; |
7833 | 1.36k | } |
7834 | | |
7835 | | void Tokenizer::simplifyTypeIntrinsics() |
7836 | 1.36k | { |
7837 | 1.36k | static const std::unordered_map<std::string, std::string> intrinsics = { |
7838 | 1.36k | { "__has_nothrow_assign", "has_nothrow_assign" }, |
7839 | 1.36k | { "__has_nothrow_constructor", "has_nothrow_constructor" }, |
7840 | 1.36k | { "__has_nothrow_copy", "has_nothrow_copy" }, |
7841 | 1.36k | { "__has_trivial_assign", "has_trivial_assign" }, |
7842 | 1.36k | { "__has_trivial_constructor", "has_trivial_constructor" }, |
7843 | 1.36k | { "__has_trivial_copy", "has_trivial_copy" }, |
7844 | 1.36k | { "__has_trivial_destructor", "has_trivial_destructor" }, |
7845 | 1.36k | { "__has_virtual_destructor", "has_virtual_destructor" }, |
7846 | 1.36k | { "__is_abstract", "is_abstract" }, |
7847 | 1.36k | { "__is_aggregate", "is_aggregate" }, |
7848 | 1.36k | { "__is_assignable", "is_assignable" }, |
7849 | 1.36k | { "__is_base_of", "is_base_of" }, |
7850 | 1.36k | { "__is_class", "is_class" }, |
7851 | 1.36k | { "__is_constructible", "is_constructible" }, |
7852 | 1.36k | { "__is_convertible_to", "is_convertible_to" }, |
7853 | 1.36k | { "__is_destructible", "is_destructible" }, |
7854 | 1.36k | { "__is_empty", "is_empty" }, |
7855 | 1.36k | { "__is_enum", "is_enum" }, |
7856 | 1.36k | { "__is_final", "is_final" }, |
7857 | 1.36k | { "__is_nothrow_assignable", "is_nothrow_assignable" }, |
7858 | 1.36k | { "__is_nothrow_constructible", "is_nothrow_constructible" }, |
7859 | 1.36k | { "__is_nothrow_destructible", "is_nothrow_destructible" }, |
7860 | 1.36k | { "__is_pod", "is_pod" }, |
7861 | 1.36k | { "__is_polymorphic", "is_polymorphic" }, |
7862 | 1.36k | { "__is_trivially_assignable", "is_trivially_assignable" }, |
7863 | 1.36k | { "__is_trivially_constructible", "is_trivially_constructible" }, |
7864 | 1.36k | { "__is_union", "is_union" }, |
7865 | 1.36k | }; |
7866 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
7867 | 92.3k | if (!Token::Match(tok, "%name% (")) |
7868 | 89.1k | continue; |
7869 | 3.20k | auto p = intrinsics.find(tok->str()); |
7870 | 3.20k | if (p == intrinsics.end()) |
7871 | 3.20k | continue; |
7872 | 0 | Token * end = tok->next()->link(); |
7873 | 0 | Token * prev = tok->previous(); |
7874 | 0 | tok->str(p->second); |
7875 | 0 | prev->insertToken("::"); |
7876 | 0 | prev->insertToken("std"); |
7877 | 0 | tok->next()->str("<"); |
7878 | 0 | end->str(">"); |
7879 | 0 | end->insertToken("}"); |
7880 | 0 | end->insertToken("{"); |
7881 | 0 | Token::createMutualLinks(end->tokAt(1), end->tokAt(2)); |
7882 | 0 | } |
7883 | 1.36k | } |
7884 | | |
7885 | | //--------------------------------------------------------------------------- |
7886 | | // Helper functions for handling the tokens list |
7887 | | //--------------------------------------------------------------------------- |
7888 | | |
7889 | | //--------------------------------------------------------------------------- |
7890 | | |
7891 | | bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const |
7892 | 0 | { |
7893 | 0 | std::string unknownFunc; |
7894 | 0 | const bool ret = mSettings->library.isScopeNoReturn(endScopeToken,&unknownFunc); |
7895 | 0 | if (!unknownFunc.empty() && mSettings->summaryReturn.find(unknownFunc) != mSettings->summaryReturn.end()) { |
7896 | 0 | return false; |
7897 | 0 | } |
7898 | 0 | if (unknown) |
7899 | 0 | *unknown = !unknownFunc.empty(); |
7900 | 0 | if (!unknownFunc.empty() && mSettings->checkLibrary) { |
7901 | 0 | bool warn = true; |
7902 | 0 | if (Token::simpleMatch(endScopeToken->tokAt(-2), ") ; }")) { |
7903 | 0 | const Token * const ftok = endScopeToken->linkAt(-2)->previous(); |
7904 | 0 | if (ftok && (ftok->type() || ftok->function() || ftok->variable())) // constructor call |
7905 | 0 | warn = false; |
7906 | 0 | } |
7907 | |
|
7908 | 0 | if (warn) { |
7909 | 0 | reportError(endScopeToken->previous(), |
7910 | 0 | Severity::information, |
7911 | 0 | "checkLibraryNoReturn", |
7912 | 0 | "--check-library: Function " + unknownFunc + "() should have <noreturn> configuration"); |
7913 | 0 | } |
7914 | 0 | } |
7915 | 0 | return ret; |
7916 | 0 | } |
7917 | | |
7918 | | //--------------------------------------------------------------------------- |
7919 | | |
7920 | | void Tokenizer::syntaxError(const Token *tok, const std::string &code) const |
7921 | 0 | { |
7922 | 0 | printDebugOutput(0); |
7923 | 0 | throw InternalError(tok, code.empty() ? "syntax error" : "syntax error: " + code, InternalError::SYNTAX); |
7924 | 0 | } |
7925 | | |
7926 | | void Tokenizer::unmatchedToken(const Token *tok) const |
7927 | 0 | { |
7928 | 0 | printDebugOutput(0); |
7929 | 0 | throw InternalError(tok, |
7930 | 0 | "Unmatched '" + tok->str() + "'. Configuration: '" + mConfiguration + "'.", |
7931 | 0 | InternalError::SYNTAX); |
7932 | 0 | } |
7933 | | |
7934 | | void Tokenizer::syntaxErrorC(const Token *tok, const std::string &what) const |
7935 | 0 | { |
7936 | 0 | printDebugOutput(0); |
7937 | 0 | throw InternalError(tok, "Code '"+what+"' is invalid C code. Use --std or --language to configure the language.", InternalError::SYNTAX); |
7938 | 0 | } |
7939 | | |
7940 | | void Tokenizer::unknownMacroError(const Token *tok1) const |
7941 | 0 | { |
7942 | 0 | printDebugOutput(0); |
7943 | 0 | throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO); |
7944 | 0 | } |
7945 | | |
7946 | | void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const |
7947 | 0 | { |
7948 | 0 | reportError(tok, |
7949 | 0 | Severity::information, |
7950 | 0 | "class_X_Y", |
7951 | 0 | "The code '" + |
7952 | 0 | tok->str() + " " + |
7953 | 0 | tok->strAt(1) + " " + |
7954 | 0 | tok->strAt(2) + " " + |
7955 | 0 | tok->strAt(3) + "' is not handled. You can use -I or --include to add handling of this code."); |
7956 | 0 | } |
7957 | | |
7958 | | void Tokenizer::macroWithSemicolonError(const Token *tok, const std::string ¯oName) const |
7959 | 0 | { |
7960 | 0 | reportError(tok, |
7961 | 0 | Severity::information, |
7962 | 0 | "macroWithSemicolon", |
7963 | 0 | "Ensure that '" + macroName + "' is defined either using -I, --include or -D."); |
7964 | 0 | } |
7965 | | |
7966 | | void Tokenizer::cppcheckError(const Token *tok) const |
7967 | 0 | { |
7968 | 0 | printDebugOutput(0); |
7969 | 0 | throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL); |
7970 | 0 | } |
7971 | | |
7972 | | void Tokenizer::unhandledCharLiteral(const Token *tok, const std::string& msg) const |
7973 | 0 | { |
7974 | 0 | std::string s = tok ? (" " + tok->str()) : ""; |
7975 | 0 | for (int i = 0; i < s.size(); ++i) { |
7976 | 0 | if ((unsigned char)s[i] >= 0x80) |
7977 | 0 | s.clear(); |
7978 | 0 | } |
7979 | |
|
7980 | 0 | reportError(tok, |
7981 | 0 | Severity::portability, |
7982 | 0 | "nonStandardCharLiteral", |
7983 | 0 | "Non-standard character literal" + s + ". " + msg); |
7984 | 0 | } |
7985 | | |
7986 | | /** |
7987 | | * Helper function to check whether number is equal to integer constant X |
7988 | | * or floating point pattern X.0 |
7989 | | * @param s the string to check |
7990 | | * @param intConstant the integer constant to check against |
7991 | | * @param floatConstant the string with stringified float constant to check against |
7992 | | * @return true in case s is equal to X or X.0 and false otherwise. |
7993 | | */ |
7994 | | static bool isNumberOneOf(const std::string &s, MathLib::bigint intConstant, const char* floatConstant) |
7995 | 0 | { |
7996 | 0 | if (MathLib::isInt(s)) { |
7997 | 0 | if (MathLib::toLongNumber(s) == intConstant) |
7998 | 0 | return true; |
7999 | 0 | } else if (MathLib::isFloat(s)) { |
8000 | 0 | if (MathLib::toString(MathLib::toDoubleNumber(s)) == floatConstant) |
8001 | 0 | return true; |
8002 | 0 | } |
8003 | 0 | return false; |
8004 | 0 | } |
8005 | | |
8006 | | // ------------------------------------------------------------------------ |
8007 | | // Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? |
8008 | | // @param s the string to check |
8009 | | // @return true in case s is one and false otherwise. |
8010 | | // ------------------------------------------------------------------------ |
8011 | | bool Tokenizer::isOneNumber(const std::string &s) |
8012 | 0 | { |
8013 | 0 | if (!MathLib::isPositive(s)) |
8014 | 0 | return false; |
8015 | 0 | return isNumberOneOf(s, 1L, "1.0"); |
8016 | 0 | } |
8017 | | // ------------------------------------------------------------------------ |
8018 | | void Tokenizer::checkConfiguration() const |
8019 | 1.36k | { |
8020 | 1.36k | if (!mSettings->checkConfiguration) |
8021 | 1.36k | return; |
8022 | 0 | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8023 | 0 | if (!Token::Match(tok, "%name% (")) |
8024 | 0 | continue; |
8025 | 0 | if (tok->isControlFlowKeyword()) |
8026 | 0 | continue; |
8027 | 0 | for (const Token *tok2 = tok->tokAt(2); tok2 && tok2->str() != ")"; tok2 = tok2->next()) { |
8028 | 0 | if (tok2->str() == ";") { |
8029 | 0 | macroWithSemicolonError(tok, tok->str()); |
8030 | 0 | break; |
8031 | 0 | } |
8032 | 0 | if (Token::Match(tok2, "(|{")) |
8033 | 0 | tok2 = tok2->link(); |
8034 | 0 | } |
8035 | 0 | } |
8036 | 0 | } |
8037 | | |
8038 | | void Tokenizer::validateC() const |
8039 | 1.36k | { |
8040 | 1.36k | if (isCPP()) |
8041 | 1.36k | return; |
8042 | 0 | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8043 | | // That might trigger false positives, but it's much faster to have this truncated pattern |
8044 | 0 | if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) |
8045 | 0 | syntaxErrorC(tok, "C++ cast <..."); |
8046 | | // Template function.. |
8047 | 0 | if (Token::Match(tok, "%name% < %name% > (")) { |
8048 | 0 | const Token *tok2 = tok->tokAt(5); |
8049 | 0 | while (tok2 && !Token::Match(tok2, "[()]")) |
8050 | 0 | tok2 = tok2->next(); |
8051 | 0 | if (Token::simpleMatch(tok2, ") {")) |
8052 | 0 | syntaxErrorC(tok, tok->str() + '<' + tok->strAt(2) + ">() {}"); |
8053 | 0 | } |
8054 | 0 | if (tok->previous() && !Token::Match(tok->previous(), "[;{}]")) |
8055 | 0 | continue; |
8056 | 0 | if (Token::Match(tok, "using namespace %name% ;")) |
8057 | 0 | syntaxErrorC(tok, "using namespace " + tok->strAt(2)); |
8058 | 0 | if (Token::Match(tok, "template < class|typename %name% [,>]")) |
8059 | 0 | syntaxErrorC(tok, "template<..."); |
8060 | 0 | if (Token::Match(tok, "%name% :: %name%")) |
8061 | 0 | syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); |
8062 | 0 | if (Token::Match(tok, "class|namespace %name% [:{]")) |
8063 | 0 | syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); |
8064 | 0 | } |
8065 | 0 | } |
8066 | | |
8067 | | void Tokenizer::validate() const |
8068 | 9.53k | { |
8069 | 9.53k | std::stack<const Token *> linkTokens; |
8070 | 9.53k | const Token *lastTok = nullptr; |
8071 | 616k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8072 | 607k | lastTok = tok; |
8073 | 607k | if (Token::Match(tok, "[{([]") || (tok->str() == "<" && tok->link())) { |
8074 | 52.7k | if (tok->link() == nullptr) |
8075 | 0 | cppcheckError(tok); |
8076 | | |
8077 | 52.7k | linkTokens.push(tok); |
8078 | 52.7k | } |
8079 | | |
8080 | 554k | else if (Token::Match(tok, "[})]]") || (Token::Match(tok, ">|>>") && tok->link())) { |
8081 | 52.7k | if (tok->link() == nullptr) |
8082 | 0 | cppcheckError(tok); |
8083 | | |
8084 | 52.7k | if (linkTokens.empty()) |
8085 | 0 | cppcheckError(tok); |
8086 | | |
8087 | 52.7k | if (tok->link() != linkTokens.top()) |
8088 | 0 | cppcheckError(tok); |
8089 | | |
8090 | 52.7k | if (tok != tok->link()->link()) |
8091 | 0 | cppcheckError(tok); |
8092 | | |
8093 | 52.7k | linkTokens.pop(); |
8094 | 52.7k | } |
8095 | | |
8096 | 501k | else if (tok->link() != nullptr) |
8097 | 0 | cppcheckError(tok); |
8098 | 607k | } |
8099 | | |
8100 | 9.53k | if (!linkTokens.empty()) |
8101 | 0 | cppcheckError(linkTokens.top()); |
8102 | | |
8103 | | // Validate that the Tokenizer::list.back() is updated correctly during simplifications |
8104 | 9.53k | if (lastTok != list.back()) |
8105 | 0 | cppcheckError(lastTok); |
8106 | 9.53k | } |
8107 | | |
8108 | | static const Token *findUnmatchedTernaryOp(const Token * const begin, const Token * const end, int depth = 0) |
8109 | 1.36k | { |
8110 | 1.36k | std::stack<const Token *> ternaryOp; |
8111 | 6.81k | for (const Token *tok = begin; tok != end && tok->str() != ";"; tok = tok->next()) { |
8112 | 5.44k | if (tok->str() == "?") |
8113 | 0 | ternaryOp.push(tok); |
8114 | 5.44k | else if (!ternaryOp.empty() && tok->str() == ":") |
8115 | 0 | ternaryOp.pop(); |
8116 | 5.44k | else if (depth < 100 && Token::Match(tok,"(|[")) { |
8117 | 0 | const Token *inner = findUnmatchedTernaryOp(tok->next(), tok->link(), depth+1); |
8118 | 0 | if (inner) |
8119 | 0 | return inner; |
8120 | 0 | tok = tok->link(); |
8121 | 0 | } |
8122 | 5.44k | } |
8123 | 1.36k | return ternaryOp.empty() ? nullptr : ternaryOp.top(); |
8124 | 1.36k | } |
8125 | | |
8126 | | static bool isCPPAttribute(const Token * tok) |
8127 | 160k | { |
8128 | 160k | return Token::simpleMatch(tok, "[ [") && tok->link() && tok->link()->previous() == tok->linkAt(1); |
8129 | 160k | } |
8130 | | |
8131 | | static bool isAlignAttribute(const Token * tok) |
8132 | 160k | { |
8133 | 160k | return Token::simpleMatch(tok, "alignas (") && tok->next()->link(); |
8134 | 160k | } |
8135 | | |
8136 | | template<typename T> |
8137 | | static T* skipCPPOrAlignAttribute(T * tok) |
8138 | 0 | { |
8139 | 0 | if (isCPPAttribute(tok)) |
8140 | 0 | return tok->link(); |
8141 | 0 | if (isAlignAttribute(tok)) { |
8142 | 0 | return tok->next()->link(); |
8143 | 0 | } |
8144 | 0 | return tok; |
8145 | 0 | } Unexecuted instantiation: tokenize.cpp:Token const* skipCPPOrAlignAttribute<Token const>(Token const*) Unexecuted instantiation: tokenize.cpp:Token* skipCPPOrAlignAttribute<Token>(Token*) |
8146 | | |
8147 | | static bool isNonMacro(const Token* tok) |
8148 | 3.20k | { |
8149 | 3.20k | if (tok->isKeyword() || tok->isStandardType()) |
8150 | 1.46k | return true; |
8151 | 1.74k | if (cAlternativeTokens.count(tok->str()) > 0) |
8152 | 0 | return true; |
8153 | 1.74k | if (startsWith(tok->str(), "__")) // attribute/annotation |
8154 | 0 | return true; |
8155 | 1.74k | if (Token::simpleMatch(tok, "alignas (")) |
8156 | 0 | return true; |
8157 | 1.74k | return false; |
8158 | 1.74k | } |
8159 | | |
8160 | | void Tokenizer::reportUnknownMacros() const |
8161 | 1.36k | { |
8162 | | // Report unknown macros used in expressions "%name% %num%" |
8163 | 94.1k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8164 | 92.7k | if (Token::Match(tok, "%name% %num%")) { |
8165 | | // A keyword is not an unknown macro |
8166 | 1.42k | if (tok->isKeyword()) |
8167 | 1.42k | continue; |
8168 | | |
8169 | 0 | if (Token::Match(tok->previous(), "%op%|(")) |
8170 | 0 | unknownMacroError(tok); |
8171 | 0 | } |
8172 | 92.7k | } |
8173 | | |
8174 | | // Report unknown macros before } "{ .. if (x) MACRO }" |
8175 | 94.1k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8176 | 92.7k | if (Token::Match(tok, ")|; %name% } !!)")) { |
8177 | 0 | if (tok->link() && !Token::simpleMatch(tok->link()->tokAt(-1), "if")) |
8178 | 0 | continue; |
8179 | 0 | const Token* prev = tok->linkAt(2); |
8180 | 0 | while (Token::simpleMatch(prev, "{")) |
8181 | 0 | prev = prev->previous(); |
8182 | 0 | if (Token::Match(prev, ";|)")) |
8183 | 0 | unknownMacroError(tok->next()); |
8184 | 0 | } |
8185 | 92.7k | } |
8186 | | |
8187 | | // Report unknown macros that contain several statements "MACRO(a;b;c)" |
8188 | 94.1k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8189 | 92.7k | if (!Token::Match(tok, "%name% (")) |
8190 | 89.5k | continue; |
8191 | 3.20k | if (!tok->isUpperCaseName()) |
8192 | 3.20k | continue; |
8193 | 0 | const Token *endTok = tok->linkAt(1); |
8194 | 0 | for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { |
8195 | 0 | if (Token::Match(inner, "[[({]")) |
8196 | 0 | inner = inner->link(); |
8197 | 0 | else if (inner->str() == ";") |
8198 | 0 | unknownMacroError(tok); |
8199 | 0 | } |
8200 | 0 | } |
8201 | | |
8202 | | // Report unknown macros that contain struct initialization "MACRO(a, .b=3)" |
8203 | 94.1k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8204 | 92.7k | if (!Token::Match(tok, "%name% (")) |
8205 | 89.5k | continue; |
8206 | 3.20k | const Token *endTok = tok->linkAt(1); |
8207 | 11.5k | for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { |
8208 | 8.31k | if (Token::Match(inner, "[[({]")) |
8209 | 444 | inner = inner->link(); |
8210 | 7.87k | else if (Token::Match(inner->previous(), "[,(] . %name% =|{")) |
8211 | 0 | unknownMacroError(tok); |
8212 | 8.31k | } |
8213 | 3.20k | } |
8214 | | |
8215 | | // Report unknown macros in non-executable scopes.. |
8216 | 1.36k | std::set<std::string> possible; |
8217 | 57.7k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8218 | | // Skip executable scopes.. |
8219 | 56.3k | if (tok->str() == "{") { |
8220 | 1.74k | const Token *prev = tok->previous(); |
8221 | 1.74k | while (prev && prev->isName()) |
8222 | 0 | prev = prev->previous(); |
8223 | 1.74k | if (prev && prev->str() == ")") |
8224 | 1.74k | tok = tok->link(); |
8225 | 0 | else |
8226 | 0 | possible.clear(); |
8227 | 54.6k | } else if (tok->str() == "}") |
8228 | 0 | possible.clear(); |
8229 | | |
8230 | 56.3k | if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") (") && Token::simpleMatch(tok->linkAt(1)->linkAt(1), ") {")) { |
8231 | | // A keyword is not an unknown macro |
8232 | 0 | if (tok->isKeyword()) |
8233 | 0 | continue; |
8234 | | |
8235 | 0 | const Token *bodyStart = tok->linkAt(1)->linkAt(1)->tokAt(2); |
8236 | 0 | const Token *bodyEnd = tok->link(); |
8237 | 0 | for (const Token *tok2 = bodyStart; tok2 && tok2 != bodyEnd; tok2 = tok2->next()) { |
8238 | 0 | if (Token::Match(tok2, "if|switch|for|while|return")) |
8239 | 0 | unknownMacroError(tok); |
8240 | 0 | } |
8241 | 56.3k | } else if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::Match(tok->linkAt(1), ") %name% (") && Token::Match(tok->linkAt(1)->linkAt(2), ") [;{]")) { |
8242 | 0 | if (!(tok->linkAt(1)->next() && tok->linkAt(1)->next()->isKeyword())) { // e.g. noexcept(true) |
8243 | 0 | if (possible.count(tok->str()) == 0) |
8244 | 0 | possible.insert(tok->str()); |
8245 | 0 | else |
8246 | 0 | unknownMacroError(tok); |
8247 | 0 | } |
8248 | 56.3k | } else if (isCPP() && Token::Match(tok, "public|private|protected %name% :")) { |
8249 | 0 | unknownMacroError(tok->next()); |
8250 | 0 | } |
8251 | 56.3k | } |
8252 | | |
8253 | | // String concatenation with unknown macros |
8254 | 94.1k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8255 | 92.7k | if (Token::Match(tok, "%str% %name% (") && Token::Match(tok->linkAt(2), ") %str%")) { |
8256 | 0 | if (tok->next()->isKeyword()) |
8257 | 0 | continue; |
8258 | 0 | unknownMacroError(tok->next()); |
8259 | 0 | } |
8260 | 92.7k | if (Token::Match(tok, "[(,] %name% (") && Token::Match(tok->linkAt(2), ") %name% %name%|,|)")) { |
8261 | 0 | if (tok->next()->isKeyword() || tok->linkAt(2)->next()->isKeyword()) |
8262 | 0 | continue; |
8263 | 0 | if (cAlternativeTokens.count(tok->linkAt(2)->next()->str()) > 0) |
8264 | 0 | continue; |
8265 | 0 | if (startsWith(tok->next()->str(), "__")) // attribute/annotation |
8266 | 0 | continue; |
8267 | 0 | unknownMacroError(tok->next()); |
8268 | 0 | } |
8269 | 92.7k | } |
8270 | | |
8271 | | // Report unknown macros without commas or operators inbetween statements: MACRO1() MACRO2() |
8272 | 94.1k | for (const Token* tok = tokens(); tok; tok = tok->next()) { |
8273 | 92.7k | if (!Token::Match(tok, "%name% (")) |
8274 | 89.5k | continue; |
8275 | 3.20k | if (isNonMacro(tok) && !tok->isStandardType()) |
8276 | 1.46k | continue; |
8277 | | |
8278 | 1.74k | const Token* endTok = tok->linkAt(1); |
8279 | 1.74k | if (!Token::Match(endTok, ") %name% (|.")) |
8280 | 1.74k | continue; |
8281 | | |
8282 | 0 | const Token* tok2 = endTok->next(); |
8283 | 0 | if (isNonMacro(tok2)) |
8284 | 0 | continue; |
8285 | | |
8286 | 0 | if (tok2->next()->str() == "(") { |
8287 | 0 | if (Token::Match(tok->previous(), "%name%|::|>")) |
8288 | 0 | continue; |
8289 | 0 | } |
8290 | | |
8291 | 0 | unknownMacroError(tok->isStandardType() ? tok2 : tok); |
8292 | 0 | } |
8293 | 1.36k | } |
8294 | | |
8295 | | void Tokenizer::findGarbageCode() const |
8296 | 1.36k | { |
8297 | 1.36k | const bool isCPP11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; |
8298 | | |
8299 | 1.36k | static const std::unordered_set<std::string> nonConsecutiveKeywords{ "break", |
8300 | 1.36k | "continue", |
8301 | 1.36k | "for", |
8302 | 1.36k | "goto", |
8303 | 1.36k | "if", |
8304 | 1.36k | "return", |
8305 | 1.36k | "switch", |
8306 | 1.36k | "throw", |
8307 | 1.36k | "typedef", |
8308 | 1.36k | "while" }; |
8309 | | |
8310 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8311 | | // initialization: = { |
8312 | 80.4k | if (Token::simpleMatch(tok, "= {") && Token::simpleMatch(tok->linkAt(1), "} (")) |
8313 | 0 | syntaxError(tok->linkAt(1)); |
8314 | | |
8315 | | // Inside [] there can't be ; or various keywords |
8316 | 80.4k | else if (tok->str() == "[") { |
8317 | 0 | for (const Token* inner = tok->next(); inner != tok->link(); inner = inner->next()) { |
8318 | 0 | if (Token::Match(inner, "(|[|{")) |
8319 | 0 | inner = inner->link(); |
8320 | 0 | else if (Token::Match(inner, ";|goto|return|typedef")) |
8321 | 0 | syntaxError(inner); |
8322 | 0 | } |
8323 | 0 | } |
8324 | | |
8325 | | // array assignment |
8326 | 80.4k | else if (Token::Match(tok, "%assign% [") && Token::simpleMatch(tok->linkAt(1), "] ;")) |
8327 | 0 | syntaxError(tok, tok->str() + "[...];"); |
8328 | | |
8329 | | // UNKNOWN_MACRO(return) |
8330 | 80.4k | if (tok->isKeyword() && Token::Match(tok, "throw|return )") && Token::Match(tok->linkAt(1)->previous(), "%name% (")) |
8331 | 0 | unknownMacroError(tok->linkAt(1)->previous()); |
8332 | | |
8333 | | // UNKNOWN_MACRO(return) |
8334 | 80.4k | else if (Token::Match(tok, "%name% throw|return") && std::isupper(tok->str()[0])) |
8335 | 0 | unknownMacroError(tok); |
8336 | | |
8337 | | // Assign/increment/decrement literal |
8338 | 80.4k | else if (Token::Match(tok, "!!) %num%|%str%|%char% %assign%|++|--")) { |
8339 | 0 | if (!isCPP() || mSettings->standards.cpp < Standards::CPP20 || !Token::Match(tok->previous(), "%name% : %num% =")) |
8340 | 0 | syntaxError(tok, tok->next()->str() + " " + tok->strAt(2)); |
8341 | 0 | } |
8342 | 80.4k | else if (Token::simpleMatch(tok, ") return") && !Token::Match(tok->link()->previous(), "if|while|for (")) { |
8343 | 0 | if (tok->link()->previous() && tok->link()->previous()->isUpperCaseName()) |
8344 | 0 | unknownMacroError(tok->link()->previous()); |
8345 | 0 | else |
8346 | 0 | syntaxError(tok); |
8347 | 0 | } |
8348 | | |
8349 | 80.4k | if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for|switch")) { // if|while|for|switch (EXPR) { ... } |
8350 | 1.38k | if (tok->previous() && !Token::Match(tok->previous(), "%name%|:|;|{|}|)")) { |
8351 | 0 | if (Token::Match(tok->previous(), "[,(]")) { |
8352 | 0 | const Token *prev = tok->previous(); |
8353 | 0 | while (prev && prev->str() != "(") { |
8354 | 0 | if (prev->str() == ")") |
8355 | 0 | prev = prev->link(); |
8356 | 0 | prev = prev->previous(); |
8357 | 0 | } |
8358 | 0 | if (prev && Token::Match(prev->previous(), "%name% (")) |
8359 | 0 | unknownMacroError(prev->previous()); |
8360 | 0 | } |
8361 | 0 | if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if")) |
8362 | 0 | syntaxError(tok); |
8363 | 0 | } |
8364 | 1.38k | if (!Token::Match(tok->next(), "( !!)")) |
8365 | 0 | syntaxError(tok); |
8366 | 1.38k | if (tok->str() != "for") { |
8367 | 1.38k | if (isGarbageExpr(tok->next(), tok->linkAt(1), mSettings->standards.cpp>=Standards::cppstd_t::CPP17)) |
8368 | 0 | syntaxError(tok); |
8369 | 1.38k | } |
8370 | 1.38k | } |
8371 | | |
8372 | | // keyword keyword |
8373 | 80.4k | if (tok->isKeyword() && nonConsecutiveKeywords.count(tok->str()) != 0) { |
8374 | 3.30k | if (Token::Match(tok, "%name% %name%") && nonConsecutiveKeywords.count(tok->next()->str()) == 1) |
8375 | 0 | syntaxError(tok); |
8376 | 3.30k | const Token* prev = tok; |
8377 | 6.60k | while (prev && prev->isName()) |
8378 | 3.30k | prev = prev->previous(); |
8379 | 3.30k | if (Token::Match(prev, "%op%|%num%|%str%|%char%")) { |
8380 | 0 | if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if") && |
8381 | 0 | !Token::simpleMatch(tok->tokAt(-2), "extern \"C\"") && |
8382 | 0 | !Token::simpleMatch(prev, "> typedef")) |
8383 | 0 | syntaxError(tok, prev == tok->previous() ? (prev->str() + " " + tok->str()) : (prev->str() + " .. " + tok->str())); |
8384 | 0 | } |
8385 | 3.30k | } |
8386 | 80.4k | } |
8387 | | |
8388 | | // invalid struct declaration |
8389 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8390 | 80.4k | if (Token::Match(tok, "struct|class|enum %name%| {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { |
8391 | 0 | const Token *tok2 = tok->linkAt(tok->next()->isName() ? 2 : 1); |
8392 | 0 | if (Token::Match(tok2, "} %op%")) { |
8393 | 0 | tok2 = tok2->next(); |
8394 | 0 | if (!Token::Match(tok2, "*|&|&&")) |
8395 | 0 | syntaxError(tok2, "Unexpected token '" + tok2->str() + "'"); |
8396 | 0 | while (Token::Match(tok2, "*|&|&&")) |
8397 | 0 | tok2 = tok2->next(); |
8398 | 0 | if (!Token::Match(tok2, "%name%")) |
8399 | 0 | syntaxError(tok2, "Unexpected token '" + (tok2 ? tok2->str() : "") + "'"); |
8400 | 0 | } |
8401 | 0 | } |
8402 | 80.4k | if (Token::Match(tok, "enum : %num%| {")) |
8403 | 0 | syntaxError(tok->tokAt(2), "Unexpected token '" + tok->strAt(2) + "'"); |
8404 | 80.4k | } |
8405 | | |
8406 | | // Keywords in global scope |
8407 | 1.36k | static const std::unordered_set<std::string> nonGlobalKeywords{"break", |
8408 | 1.36k | "continue", |
8409 | 1.36k | "for", |
8410 | 1.36k | "goto", |
8411 | 1.36k | "if", |
8412 | 1.36k | "return", |
8413 | 1.36k | "switch", |
8414 | 1.36k | "while", |
8415 | 1.36k | "try", |
8416 | 1.36k | "catch"}; |
8417 | 45.4k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8418 | 44.1k | if (tok->str() == "{") |
8419 | 1.74k | tok = tok->link(); |
8420 | 42.3k | else if (tok->isKeyword() && nonGlobalKeywords.count(tok->str()) && !Token::Match(tok->tokAt(-2), "operator %str%")) |
8421 | 0 | syntaxError(tok, "keyword '" + tok->str() + "' is not allowed in global scope"); |
8422 | 44.1k | } |
8423 | | |
8424 | | // case keyword must be inside switch |
8425 | 67.6k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8426 | 66.3k | if (Token::simpleMatch(tok, "switch (")) { |
8427 | 0 | if (Token::simpleMatch(tok->linkAt(1), ") {")) { |
8428 | 0 | tok = tok->linkAt(1)->linkAt(1); |
8429 | 0 | continue; |
8430 | 0 | } |
8431 | 0 | const Token *switchToken = tok; |
8432 | 0 | tok = tok->linkAt(1); |
8433 | 0 | if (!tok) |
8434 | 0 | syntaxError(switchToken); |
8435 | | // Look for the end of the switch statement, i.e. the first semi-colon or '}' |
8436 | 0 | for (; tok; tok = tok->next()) { |
8437 | 0 | if (tok->str() == "{") { |
8438 | 0 | tok = tok->link(); |
8439 | 0 | } |
8440 | 0 | if (Token::Match(tok, ";|}")) { |
8441 | | // We're at the end of the switch block |
8442 | 0 | if (tok->str() == "}" && tok->strAt(-1) == ":") // Invalid case |
8443 | 0 | syntaxError(switchToken); |
8444 | 0 | break; |
8445 | 0 | } |
8446 | 0 | } |
8447 | 0 | if (!tok) |
8448 | 0 | break; |
8449 | 66.3k | } else if (tok->str() == "(") { |
8450 | 3.50k | tok = tok->link(); |
8451 | 62.8k | } else if (tok->str() == "case") { |
8452 | 0 | syntaxError(tok); |
8453 | 0 | } |
8454 | 66.3k | } |
8455 | | |
8456 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8457 | 80.4k | if (!Token::simpleMatch(tok, "for (")) // find for loops |
8458 | 80.4k | continue; |
8459 | | // count number of semicolons |
8460 | 0 | int semicolons = 0; |
8461 | 0 | const Token* const startTok = tok; |
8462 | 0 | tok = tok->next()->link()->previous(); // find ")" of the for-loop |
8463 | | // walk backwards until we find the beginning (startTok) of the for() again |
8464 | 0 | for (; tok != startTok; tok = tok->previous()) { |
8465 | 0 | if (tok->str() == ";") { // do the counting |
8466 | 0 | semicolons++; |
8467 | 0 | } else if (tok->str() == ")") { // skip pairs of ( ) |
8468 | 0 | tok = tok->link(); |
8469 | 0 | } |
8470 | 0 | } |
8471 | | // if we have an invalid number of semicolons inside for( ), assume syntax error |
8472 | 0 | if (semicolons > 2) |
8473 | 0 | syntaxError(tok); |
8474 | 0 | if (semicolons == 1 && !(isCPP() && mSettings->standards.cpp >= Standards::CPP20)) |
8475 | 0 | syntaxError(tok); |
8476 | 0 | } |
8477 | | |
8478 | | // Operators without operands.. |
8479 | 1.36k | const Token *templateEndToken = nullptr; |
8480 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8481 | 80.4k | if (!templateEndToken) { |
8482 | 80.3k | if (tok->str() == "<" && isCPP()) |
8483 | 522 | templateEndToken = tok->findClosingBracket(); |
8484 | 80.3k | } else { |
8485 | 156 | if (templateEndToken == tok) |
8486 | 45 | templateEndToken = nullptr; |
8487 | 156 | if (Token::Match(tok, "> %cop%")) |
8488 | 2 | continue; |
8489 | 156 | } |
8490 | | // skip C++ attributes [[...]] |
8491 | 80.4k | if (isCPP11 && (isCPPAttribute(tok) || isAlignAttribute(tok))) { |
8492 | 0 | tok = skipCPPOrAlignAttribute(tok); |
8493 | 0 | continue; |
8494 | 0 | } |
8495 | 80.4k | { |
8496 | 80.4k | bool match1 = Token::Match(tok, "%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|++|--|::|sizeof"); |
8497 | 80.4k | bool match2 = Token::Match(tok->next(), "{|if|else|while|do|for|return|switch|break"); |
8498 | 80.4k | if (isCPP()) { |
8499 | 80.4k | match1 = match1 || Token::Match(tok, "::|throw|decltype|typeof"); |
8500 | 80.4k | match2 = match2 || Token::Match(tok->next(), "try|catch|namespace"); |
8501 | 80.4k | } |
8502 | 80.4k | if (match1 && match2) |
8503 | 0 | syntaxError(tok); |
8504 | 80.4k | } |
8505 | 80.4k | if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|%")) { |
8506 | 5.75k | std::string code; |
8507 | 5.75k | if (Token::Match(tok->next(), ")|]|}")) |
8508 | 0 | code = tok->str() + tok->next()->str(); |
8509 | 5.75k | if (Token::simpleMatch(tok->next(), "( )")) |
8510 | 0 | code = tok->str() + "()"; |
8511 | 5.75k | if (!code.empty()) { |
8512 | 0 | if (isC() || (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator"))) |
8513 | 0 | syntaxError(tok, code); |
8514 | 0 | } |
8515 | 5.75k | } |
8516 | 80.4k | if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%")) |
8517 | 0 | syntaxError(tok); |
8518 | 80.4k | if (Token::Match(tok, "%assign% typename|class %assign%")) |
8519 | 0 | syntaxError(tok); |
8520 | 80.4k | if (Token::Match(tok, "%cop%|=|,|[ %or%|%oror%|/|%")) |
8521 | 0 | syntaxError(tok); |
8522 | 80.4k | if (Token::Match(tok, ";|(|[ %comp%")) |
8523 | 0 | syntaxError(tok); |
8524 | 80.4k | if (Token::Match(tok, "%cop%|= ]") && !(isCPP() && Token::Match(tok->previous(), "%type%|[|,|%num% &|=|> ]"))) |
8525 | 0 | syntaxError(tok); |
8526 | 80.4k | if (Token::Match(tok, "[+-] [;,)]}]") && !(isCPP() && Token::Match(tok->previous(), "operator [+-] ;"))) |
8527 | 0 | syntaxError(tok); |
8528 | 80.4k | if (Token::simpleMatch(tok, ",") && |
8529 | 80.4k | !Token::Match(tok->tokAt(-2), "[ = , &|%name%")) { |
8530 | 0 | if (Token::Match(tok->previous(), "(|[|{|<|%assign%|%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|::|sizeof")) |
8531 | 0 | syntaxError(tok); |
8532 | 0 | if (isCPP() && Token::Match(tok->previous(), "throw|decltype|typeof")) |
8533 | 0 | syntaxError(tok); |
8534 | 0 | if (Token::Match(tok->next(), ")|]|>|%assign%|%or%|%oror%|==|!=|/|>=|<=|&&")) |
8535 | 0 | syntaxError(tok); |
8536 | 0 | } |
8537 | 80.4k | if (Token::simpleMatch(tok, ".") && |
8538 | 80.4k | !Token::simpleMatch(tok->previous(), ".") && |
8539 | 80.4k | !Token::simpleMatch(tok->next(), ".") && |
8540 | 80.4k | !Token::Match(tok->previous(), "{|, . %name% =|.|[|{") && |
8541 | 80.4k | !Token::Match(tok->previous(), ", . %name%")) { |
8542 | 0 | if (!Token::Match(tok->previous(), "%name%|)|]|>|}")) |
8543 | 0 | syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); |
8544 | 0 | if (!Token::Match(tok->next(), "%name%|*|~")) |
8545 | 0 | syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); |
8546 | 0 | } |
8547 | 80.4k | if (Token::Match(tok, "[!|+-/%^~] )|]")) |
8548 | 0 | syntaxError(tok); |
8549 | 80.4k | if (Token::Match(tok, "==|!=|<=|>= %comp%") && tok->strAt(-1) != "operator") |
8550 | 0 | syntaxError(tok, tok->str() + " " + tok->strAt(1)); |
8551 | 80.4k | } |
8552 | | |
8553 | | // ternary operator without : |
8554 | 1.36k | if (const Token *ternaryOp = findUnmatchedTernaryOp(tokens(), nullptr)) |
8555 | 0 | syntaxError(ternaryOp); |
8556 | | |
8557 | | // Code must not start with an arithmetical operand |
8558 | 1.36k | if (Token::Match(list.front(), "%cop%")) |
8559 | 0 | syntaxError(list.front()); |
8560 | | |
8561 | | // Code must end with } ; ) NAME |
8562 | 1.36k | if (!Token::Match(list.back(), "%name%|;|}|)")) |
8563 | 0 | syntaxError(list.back()); |
8564 | 1.36k | if (list.back()->str() == ")" && !Token::Match(list.back()->link()->previous(), "%name%|> (")) |
8565 | 0 | syntaxError(list.back()); |
8566 | 1.36k | for (const Token *end = list.back(); end && end->isName(); end = end->previous()) { |
8567 | 0 | if (Token::Match(end, "void|char|short|int|long|float|double|const|volatile|static|inline|struct|class|enum|union|template|sizeof|case|break|continue|typedef")) |
8568 | 0 | syntaxError(list.back()); |
8569 | 0 | } |
8570 | 1.36k | if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword()) |
8571 | 0 | syntaxError(list.back()->previous()); |
8572 | | |
8573 | | // Garbage templates.. |
8574 | 1.36k | if (isCPP()) { |
8575 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8576 | 80.4k | if (!Token::simpleMatch(tok, "template <")) |
8577 | 80.4k | continue; |
8578 | 0 | if (tok->previous() && !Token::Match(tok->previous(), ":|;|{|}|)|>|\"C++\"")) { |
8579 | 0 | if (tok->previous()->isUpperCaseName()) |
8580 | 0 | unknownMacroError(tok->previous()); |
8581 | 0 | else |
8582 | 0 | syntaxError(tok); |
8583 | 0 | } |
8584 | 0 | const Token * const tok1 = tok; |
8585 | 0 | tok = tok->next()->findClosingBracket(); |
8586 | 0 | if (!tok) |
8587 | 0 | syntaxError(tok1); |
8588 | 0 | if (!Token::Match(tok, ">|>> ::|...| %name%") && |
8589 | 0 | !Token::Match(tok, ">|>> [ [ %name%") && |
8590 | 0 | !Token::Match(tok, "> >|*")) |
8591 | 0 | syntaxError(tok->next() ? tok->next() : tok1); |
8592 | 0 | } |
8593 | 1.36k | } |
8594 | | |
8595 | | // Objective C/C++ |
8596 | 81.8k | for (const Token *tok = tokens(); tok; tok = tok->next()) { |
8597 | 80.4k | if (Token::Match(tok, "[;{}] [ %name% %name% ] ;")) |
8598 | 0 | syntaxError(tok->next()); |
8599 | 80.4k | } |
8600 | 1.36k | } |
8601 | | |
8602 | | |
8603 | | bool Tokenizer::isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon) |
8604 | 1.38k | { |
8605 | 12.4k | for (const Token *tok = start; tok != end; tok = tok->next()) { |
8606 | 11.1k | if (tok->isControlFlowKeyword()) |
8607 | 0 | return true; |
8608 | 11.1k | if (!allowSemicolon && tok->str() == ";") |
8609 | 0 | return true; |
8610 | 11.1k | if (tok->str() == "{") |
8611 | 0 | tok = tok->link(); |
8612 | 11.1k | } |
8613 | 1.38k | return false; |
8614 | 1.38k | } |
8615 | | |
8616 | | std::string Tokenizer::simplifyString(const std::string &source) |
8617 | 0 | { |
8618 | 0 | std::string str = source; |
8619 | |
|
8620 | 0 | for (std::string::size_type i = 0; i + 1U < str.size(); ++i) { |
8621 | 0 | if (str[i] != '\\') |
8622 | 0 | continue; |
8623 | | |
8624 | 0 | int c = 'a'; // char |
8625 | 0 | int sz = 0; // size of stringdata |
8626 | 0 | if (str[i+1] == 'x') { |
8627 | 0 | sz = 2; |
8628 | 0 | while (sz < 4 && std::isxdigit((unsigned char)str[i+sz])) |
8629 | 0 | sz++; |
8630 | 0 | if (sz > 2) { |
8631 | 0 | std::istringstream istr(str.substr(i+2, sz-2)); |
8632 | 0 | istr >> std::hex >> c; |
8633 | 0 | } |
8634 | 0 | } else if (MathLib::isOctalDigit(str[i+1])) { |
8635 | 0 | sz = 2; |
8636 | 0 | while (sz < 4 && MathLib::isOctalDigit(str[i+sz])) |
8637 | 0 | sz++; |
8638 | 0 | std::istringstream istr(str.substr(i+1, sz-1)); |
8639 | 0 | istr >> std::oct >> c; |
8640 | 0 | str = str.replace(i, sz, std::string(1U, (char)c)); |
8641 | 0 | continue; |
8642 | 0 | } |
8643 | | |
8644 | 0 | if (sz <= 2) |
8645 | 0 | i++; |
8646 | 0 | else if (i+sz < str.size()) |
8647 | 0 | str.replace(i, sz, std::string(1U, (char)c)); |
8648 | 0 | else |
8649 | 0 | str.replace(i, str.size() - i - 1U, "a"); |
8650 | 0 | } |
8651 | |
|
8652 | 0 | return str; |
8653 | 0 | } |
8654 | | |
8655 | | void Tokenizer::simplifyFunctionTryCatch() |
8656 | 1.36k | { |
8657 | 1.36k | if (!isCPP()) |
8658 | 0 | return; |
8659 | | |
8660 | 81.8k | for (Token * tok = list.front(); tok; tok = tok->next()) { |
8661 | 80.4k | if (!Token::Match(tok, "try {|:")) |
8662 | 80.4k | continue; |
8663 | 0 | if (!isFunctionHead(tok->previous(), "try")) |
8664 | 0 | continue; |
8665 | | |
8666 | 0 | Token* tryStartToken = skipInitializerList(tok->next()); |
8667 | |
|
8668 | 0 | if (!Token::simpleMatch(tryStartToken, "{")) |
8669 | 0 | syntaxError(tryStartToken, "Invalid function-try-catch block code. Did not find '{' for try body."); |
8670 | | |
8671 | | // find the end of the last catch block |
8672 | 0 | Token * const tryEndToken = tryStartToken->link(); |
8673 | 0 | Token * endToken = tryEndToken; |
8674 | 0 | while (Token::simpleMatch(endToken, "} catch (")) { |
8675 | 0 | endToken = endToken->linkAt(2)->next(); |
8676 | 0 | if (!endToken) |
8677 | 0 | break; |
8678 | 0 | if (endToken->str() != "{") { |
8679 | 0 | endToken = nullptr; |
8680 | 0 | break; |
8681 | 0 | } |
8682 | 0 | endToken = endToken->link(); |
8683 | 0 | } |
8684 | 0 | if (!endToken || endToken == tryEndToken) |
8685 | 0 | continue; |
8686 | | |
8687 | 0 | tok->previous()->insertToken("{"); |
8688 | 0 | endToken->insertToken("}"); |
8689 | 0 | Token::createMutualLinks(tok->previous(), endToken->next()); |
8690 | 0 | } |
8691 | 1.36k | } |
8692 | | |
8693 | | |
8694 | | void Tokenizer::simplifyStructDecl() |
8695 | 1.36k | { |
8696 | 1.36k | const bool cpp = isCPP(); |
8697 | | |
8698 | | // A counter that is used when giving unique names for anonymous structs. |
8699 | 1.36k | int count = 0; |
8700 | | |
8701 | | // Add names for anonymous structs |
8702 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
8703 | 92.7k | if (!tok->isName()) |
8704 | 56.9k | continue; |
8705 | | // check for anonymous struct/union |
8706 | 35.7k | if (Token::Match(tok, "struct|union {")) { |
8707 | 0 | if (Token::Match(tok->next()->link(), "} const| *|&| const| %type% ,|;|[|(|{|=")) { |
8708 | 0 | tok->insertToken("Anonymous" + std::to_string(count++)); |
8709 | 0 | } |
8710 | 0 | } |
8711 | | // check for derived anonymous class/struct |
8712 | 35.7k | else if (cpp && Token::Match(tok, "class|struct :")) { |
8713 | 0 | const Token *tok1 = Token::findsimplematch(tok, "{"); |
8714 | 0 | if (tok1 && Token::Match(tok1->link(), "} const| *|&| const| %type% ,|;|[|(|{")) { |
8715 | 0 | tok->insertToken("Anonymous" + std::to_string(count++)); |
8716 | 0 | } |
8717 | 0 | } |
8718 | | // check for anonymous enum |
8719 | 35.7k | else if ((Token::simpleMatch(tok, "enum {") && |
8720 | 35.7k | !Token::Match(tok->tokAt(-3), "using %name% =") && |
8721 | 35.7k | Token::Match(tok->next()->link(), "} (| %type%| )| ,|;|[|(|{")) || |
8722 | 35.7k | (Token::Match(tok, "enum : %type% {") && Token::Match(tok->linkAt(3), "} (| %type%| )| ,|;|[|(|{"))) { |
8723 | 0 | Token *start = tok->strAt(1) == ":" ? tok->linkAt(3) : tok->linkAt(1); |
8724 | 0 | if (start && Token::Match(start->next(), "( %type% )")) { |
8725 | 0 | start->next()->link()->deleteThis(); |
8726 | 0 | start->next()->deleteThis(); |
8727 | 0 | } |
8728 | 0 | tok->insertToken("Anonymous" + std::to_string(count++)); |
8729 | 0 | } |
8730 | 35.7k | } |
8731 | | |
8732 | | // "{" token for current scope |
8733 | 1.36k | std::stack<const Token*> scopeStart; |
8734 | 1.36k | const Token* functionEnd = nullptr; |
8735 | | |
8736 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
8737 | | |
8738 | | // check for start of scope and determine if it is in a function |
8739 | 92.7k | if (tok->str() == "{") { |
8740 | 3.58k | scopeStart.push(tok); |
8741 | 3.58k | if (!functionEnd && Token::Match(tok->previous(), "const|)")) |
8742 | 1.74k | functionEnd = tok->link(); |
8743 | 3.58k | } |
8744 | | |
8745 | | // end of scope |
8746 | 89.1k | else if (tok->str() == "}") { |
8747 | 3.58k | if (!scopeStart.empty()) |
8748 | 3.58k | scopeStart.pop(); |
8749 | 3.58k | if (tok == functionEnd) |
8750 | 1.74k | functionEnd = nullptr; |
8751 | 3.58k | } |
8752 | | |
8753 | | // check for named struct/union |
8754 | 85.5k | else if (Token::Match(tok, "class|struct|union|enum %type% :|{")) { |
8755 | 0 | Token *start = tok; |
8756 | 0 | while (Token::Match(start->previous(), "%type%")) |
8757 | 0 | start = start->previous(); |
8758 | 0 | const Token * const type = tok->next(); |
8759 | 0 | Token *next = tok->tokAt(2); |
8760 | |
|
8761 | 0 | while (next && next->str() != "{") |
8762 | 0 | next = next->next(); |
8763 | 0 | if (!next) |
8764 | 0 | continue; |
8765 | 0 | Token* after = next->link(); |
8766 | 0 | if (!after) |
8767 | 0 | break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) |
8768 | | |
8769 | | // check for named type |
8770 | 0 | if (Token::Match(after->next(), "const|static|volatile| *|&| const| (| %type% )| ,|;|[|=|(|{")) { |
8771 | 0 | after->insertToken(";"); |
8772 | 0 | after = after->next(); |
8773 | 0 | while (!Token::Match(start, "struct|class|union|enum")) { |
8774 | 0 | after->insertToken(start->str()); |
8775 | 0 | after = after->next(); |
8776 | 0 | start->deleteThis(); |
8777 | 0 | } |
8778 | 0 | tok = start; |
8779 | 0 | if (!after) |
8780 | 0 | break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) |
8781 | 0 | after->insertToken(type->str()); |
8782 | 0 | if (start->str() != "class") { |
8783 | 0 | after->insertToken(start->str()); |
8784 | 0 | after = after->next(); |
8785 | 0 | } |
8786 | |
|
8787 | 0 | after = after->tokAt(2); |
8788 | |
|
8789 | 0 | if (Token::Match(after, "( %type% )")) { |
8790 | 0 | after->link()->deleteThis(); |
8791 | 0 | after->deleteThis(); |
8792 | 0 | } |
8793 | | |
8794 | | // check for initialization |
8795 | 0 | if (Token::Match(after, "%any% (|{")) { |
8796 | 0 | after->insertToken("="); |
8797 | 0 | after = after->next(); |
8798 | 0 | const bool isEnum = start->str() == "enum"; |
8799 | 0 | if (!isEnum && cpp) { |
8800 | 0 | after->insertToken(type->str()); |
8801 | 0 | after = after->next(); |
8802 | 0 | } |
8803 | |
|
8804 | 0 | if (isEnum) { |
8805 | 0 | if (Token::Match(after->next(), "{ !!}")) { |
8806 | 0 | after->next()->str("("); |
8807 | 0 | after->linkAt(1)->str(")"); |
8808 | 0 | } |
8809 | 0 | } |
8810 | 0 | } |
8811 | 0 | } |
8812 | 0 | } |
8813 | | |
8814 | | // check for anonymous struct/union |
8815 | 85.5k | else { |
8816 | | // unnamed anonymous struct/union so possibly remove it |
8817 | 85.5k | bool done = false; |
8818 | 85.5k | while (!done && Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->linkAt(1), "} ;")) { |
8819 | 0 | done = true; |
8820 | | |
8821 | | // is this a class/struct/union scope? |
8822 | 0 | bool isClassStructUnionScope = false; |
8823 | 0 | if (!scopeStart.empty()) { |
8824 | 0 | for (const Token* tok2 = scopeStart.top()->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) { |
8825 | 0 | if (Token::Match(tok2, "class|struct|union")) { |
8826 | 0 | isClassStructUnionScope = true; |
8827 | 0 | break; |
8828 | 0 | } |
8829 | 0 | } |
8830 | 0 | } |
8831 | | |
8832 | | // remove unnamed anonymous struct/union |
8833 | | // * not in class/struct/union scopes |
8834 | 0 | if (Token::simpleMatch(tok->linkAt(1), "} ;") && !isClassStructUnionScope && tok->str() != "union") { |
8835 | 0 | tok->linkAt(1)->previous()->deleteNext(2); |
8836 | 0 | tok->deleteNext(); |
8837 | 0 | tok->deleteThis(); |
8838 | 0 | done = false; |
8839 | 0 | } |
8840 | 0 | } |
8841 | 85.5k | } |
8842 | 92.7k | } |
8843 | 1.36k | } |
8844 | | |
8845 | | void Tokenizer::simplifyCallingConvention() |
8846 | 1.36k | { |
8847 | 1.36k | const bool windows = mSettings->platform.isWindows(); |
8848 | | |
8849 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
8850 | 79.1k | while (Token::Match(tok, "__cdecl|__stdcall|__fastcall|__thiscall|__clrcall|__syscall|__pascal|__fortran|__far|__near") || (windows && Token::Match(tok, "WINAPI|APIENTRY|CALLBACK"))) { |
8851 | 0 | tok->deleteThis(); |
8852 | 0 | } |
8853 | 79.1k | } |
8854 | 1.36k | } |
8855 | | |
8856 | 159k | static bool isAttribute(const Token* tok, bool gcc) { |
8857 | 159k | return gcc ? Token::Match(tok, "__attribute__|__attribute (") : Token::Match(tok, "__declspec|_declspec ("); |
8858 | 159k | } |
8859 | | |
8860 | 0 | static Token* getTokenAfterAttributes(Token* tok, bool gccattr) { |
8861 | 0 | Token* after = tok; |
8862 | 0 | while (isAttribute(after, gccattr)) |
8863 | 0 | after = after->linkAt(1)->next(); |
8864 | 0 | return after; |
8865 | 0 | } |
8866 | | |
8867 | 0 | Token* Tokenizer::getAttributeFuncTok(Token* tok, bool gccattr) const { |
8868 | 0 | if (!Token::Match(tok, "%name% (")) |
8869 | 0 | return nullptr; |
8870 | 0 | Token* const after = getTokenAfterAttributes(tok, gccattr); |
8871 | 0 | if (!after) |
8872 | 0 | syntaxError(tok); |
8873 | |
|
8874 | 0 | if (Token::Match(after, "%name%|*|&|(")) { |
8875 | 0 | Token *ftok = after; |
8876 | 0 | while (Token::Match(ftok, "%name%|::|<|*|& !!(")) { |
8877 | 0 | if (ftok->str() == "<") { |
8878 | 0 | ftok = ftok->findClosingBracket(); |
8879 | 0 | if (!ftok) |
8880 | 0 | break; |
8881 | 0 | } |
8882 | 0 | ftok = ftok->next(); |
8883 | 0 | } |
8884 | 0 | if (Token::simpleMatch(ftok, "( *")) |
8885 | 0 | ftok = ftok->tokAt(2); |
8886 | 0 | if (Token::Match(ftok, "%name% (|)")) |
8887 | 0 | return ftok; |
8888 | 0 | } else if (Token::Match(after, "[;{=:]")) { |
8889 | 0 | Token *prev = tok->previous(); |
8890 | 0 | while (Token::Match(prev, "%name%")) |
8891 | 0 | prev = prev->previous(); |
8892 | 0 | if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->previous(), "%name% (")) |
8893 | 0 | return prev->link()->previous(); |
8894 | 0 | if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->tokAt(-2), "operator %op% (") && isCPP()) |
8895 | 0 | return prev->link()->tokAt(-2); |
8896 | 0 | if ((!prev || Token::Match(prev, "[;{}*]")) && Token::Match(tok->previous(), "%name%")) |
8897 | 0 | return tok->previous(); |
8898 | 0 | } |
8899 | 0 | return nullptr; |
8900 | 0 | } |
8901 | | |
8902 | | void Tokenizer::simplifyDeclspec() |
8903 | 1.36k | { |
8904 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
8905 | 79.1k | while (isAttribute(tok, false)) { |
8906 | 0 | Token *functok = getAttributeFuncTok(tok, false); |
8907 | 0 | if (Token::Match(tok->tokAt(2), "noreturn|nothrow|dllexport")) { |
8908 | 0 | if (functok) { |
8909 | 0 | if (tok->strAt(2) == "noreturn") |
8910 | 0 | functok->isAttributeNoreturn(true); |
8911 | 0 | else if (tok->strAt(2) == "nothrow") |
8912 | 0 | functok->isAttributeNothrow(true); |
8913 | 0 | else |
8914 | 0 | functok->isAttributeExport(true); |
8915 | 0 | } |
8916 | 0 | } else if (tok->strAt(2) == "property") |
8917 | 0 | tok->next()->link()->insertToken("__property"); |
8918 | |
|
8919 | 0 | Token::eraseTokens(tok, tok->next()->link()->next()); |
8920 | 0 | tok->deleteThis(); |
8921 | 0 | } |
8922 | 79.1k | } |
8923 | 1.36k | } |
8924 | | |
8925 | | void Tokenizer::simplifyAttribute() |
8926 | 1.36k | { |
8927 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
8928 | 80.4k | if (!tok->isKeyword() && Token::Match(tok, "%type% (") && !mSettings->library.isNotLibraryFunction(tok)) { |
8929 | 0 | if (mSettings->library.isFunctionConst(tok->str(), true)) |
8930 | 0 | tok->isAttributePure(true); |
8931 | 0 | if (mSettings->library.isFunctionConst(tok->str(), false)) |
8932 | 0 | tok->isAttributeConst(true); |
8933 | 0 | } |
8934 | 80.4k | while (isAttribute(tok, true)) { |
8935 | 0 | Token *functok = getAttributeFuncTok(tok, true); |
8936 | |
|
8937 | 0 | for (Token *attr = tok->tokAt(2); attr->str() != ")"; attr = attr->next()) { |
8938 | 0 | if (Token::Match(attr, "%name% (")) |
8939 | 0 | attr = attr->linkAt(1); |
8940 | |
|
8941 | 0 | if (Token::Match(attr, "[(,] constructor|__constructor__ [,()]")) { |
8942 | 0 | if (!functok) |
8943 | 0 | syntaxError(tok); |
8944 | 0 | functok->isAttributeConstructor(true); |
8945 | 0 | } |
8946 | | |
8947 | 0 | else if (Token::Match(attr, "[(,] destructor|__destructor__ [,()]")) { |
8948 | 0 | if (!functok) |
8949 | 0 | syntaxError(tok); |
8950 | 0 | functok->isAttributeDestructor(true); |
8951 | 0 | } |
8952 | | |
8953 | 0 | else if (Token::Match(attr, "[(,] unused|__unused__|used|__used__ [,)]")) { |
8954 | 0 | Token *vartok = nullptr; |
8955 | 0 | Token *after = getTokenAfterAttributes(tok, true); |
8956 | | |
8957 | | // check if after variable name |
8958 | 0 | if (Token::Match(after, ";|=")) { |
8959 | 0 | Token *prev = tok->previous(); |
8960 | 0 | while (Token::simpleMatch(prev, "]")) |
8961 | 0 | prev = prev->link()->previous(); |
8962 | 0 | if (Token::Match(prev, "%type%")) |
8963 | 0 | vartok = prev; |
8964 | 0 | } |
8965 | | |
8966 | | // check if before variable name |
8967 | 0 | else if (Token::Match(after, "%type%")) |
8968 | 0 | vartok = after; |
8969 | |
|
8970 | 0 | if (vartok) { |
8971 | 0 | const std::string &attribute(attr->next()->str()); |
8972 | 0 | if (attribute.find("unused") != std::string::npos) |
8973 | 0 | vartok->isAttributeUnused(true); |
8974 | 0 | else |
8975 | 0 | vartok->isAttributeUsed(true); |
8976 | 0 | } |
8977 | 0 | } |
8978 | | |
8979 | 0 | else if (Token::Match(attr, "[(,] pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__|warn_unused_result [,)]")) { |
8980 | 0 | if (!functok) |
8981 | 0 | syntaxError(tok); |
8982 | |
|
8983 | 0 | const std::string &attribute(attr->next()->str()); |
8984 | 0 | if (attribute.find("pure") != std::string::npos) |
8985 | 0 | functok->isAttributePure(true); |
8986 | 0 | else if (attribute.find("const") != std::string::npos) |
8987 | 0 | functok->isAttributeConst(true); |
8988 | 0 | else if (attribute.find("noreturn") != std::string::npos) |
8989 | 0 | functok->isAttributeNoreturn(true); |
8990 | 0 | else if (attribute.find("nothrow") != std::string::npos) |
8991 | 0 | functok->isAttributeNothrow(true); |
8992 | 0 | else if (attribute.find("warn_unused_result") != std::string::npos) |
8993 | 0 | functok->isAttributeNodiscard(true); |
8994 | 0 | } |
8995 | | |
8996 | 0 | else if (Token::Match(attr, "[(,] packed [,)]") && Token::simpleMatch(tok->previous(), "}")) |
8997 | 0 | tok->previous()->isAttributePacked(true); |
8998 | | |
8999 | 0 | else if (functok && Token::simpleMatch(attr, "( __visibility__ ( \"default\" ) )")) |
9000 | 0 | functok->isAttributeExport(true); |
9001 | 0 | } |
9002 | |
|
9003 | 0 | Token::eraseTokens(tok, tok->linkAt(1)->next()); |
9004 | 0 | tok->deleteThis(); |
9005 | 0 | } |
9006 | 80.4k | } |
9007 | 1.36k | } |
9008 | | |
9009 | | void Tokenizer::simplifyCppcheckAttribute() |
9010 | 1.36k | { |
9011 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9012 | 80.4k | if (tok->str() != "(") |
9013 | 76.4k | continue; |
9014 | 4.05k | if (!tok->previous()) |
9015 | 0 | continue; |
9016 | 4.05k | const std::string &attr = tok->previous()->str(); |
9017 | 4.05k | if (!startsWith(attr, "__cppcheck_")) |
9018 | 4.05k | continue; |
9019 | 0 | if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") |
9020 | 0 | continue; |
9021 | | |
9022 | 0 | Token *vartok = tok->link(); |
9023 | 0 | while (Token::Match(vartok->next(), "%name%|*|&|::")) { |
9024 | 0 | vartok = vartok->next(); |
9025 | 0 | if (Token::Match(vartok, "%name% (") && startsWith(vartok->str(),"__cppcheck_")) |
9026 | 0 | vartok = vartok->linkAt(1); |
9027 | 0 | } |
9028 | |
|
9029 | 0 | if (vartok->isName()) { |
9030 | 0 | if (Token::Match(tok->previous(), "__cppcheck_low__ ( %num% )")) |
9031 | 0 | vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(tok->next()->str())); |
9032 | 0 | else if (Token::Match(tok->previous(), "__cppcheck_high__ ( %num% )")) |
9033 | 0 | vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(tok->next()->str())); |
9034 | 0 | } |
9035 | | |
9036 | | // Delete cppcheck attribute.. |
9037 | 0 | if (tok->tokAt(-2)) { |
9038 | 0 | tok = tok->tokAt(-2); |
9039 | 0 | Token::eraseTokens(tok, tok->linkAt(2)->next()); |
9040 | 0 | } else { |
9041 | 0 | tok = tok->previous(); |
9042 | 0 | Token::eraseTokens(tok, tok->linkAt(1)->next()); |
9043 | 0 | tok->str(";"); |
9044 | 0 | } |
9045 | 0 | } |
9046 | 1.36k | } |
9047 | | |
9048 | | void Tokenizer::simplifyCPPAttribute() |
9049 | 1.36k | { |
9050 | 1.36k | if (mSettings->standards.cpp < Standards::CPP11 || isC()) |
9051 | 0 | return; |
9052 | | |
9053 | 81.8k | for (Token *tok = list.front(); tok;) { |
9054 | 80.4k | if (!isCPPAttribute(tok) && !isAlignAttribute(tok)) { |
9055 | 80.4k | tok = tok->next(); |
9056 | 80.4k | continue; |
9057 | 80.4k | } |
9058 | 0 | if (isCPPAttribute(tok)) { |
9059 | 0 | if (Token::findsimplematch(tok->tokAt(2), "noreturn", tok->link())) { |
9060 | 0 | Token * head = skipCPPOrAlignAttribute(tok)->next(); |
9061 | 0 | while (isCPPAttribute(head) || isAlignAttribute(head)) |
9062 | 0 | head = skipCPPOrAlignAttribute(head)->next(); |
9063 | 0 | while (Token::Match(head, "%name%|::|*|&|<|>|,")) // skip return type |
9064 | 0 | head = head->next(); |
9065 | 0 | if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { |
9066 | 0 | head->previous()->isAttributeNoreturn(true); |
9067 | 0 | } |
9068 | 0 | } else if (Token::findsimplematch(tok->tokAt(2), "nodiscard", tok->link())) { |
9069 | 0 | Token * head = skipCPPOrAlignAttribute(tok)->next(); |
9070 | 0 | while (isCPPAttribute(head) || isAlignAttribute(head)) |
9071 | 0 | head = skipCPPOrAlignAttribute(head)->next(); |
9072 | 0 | while (Token::Match(head, "%name%|::|*|&|<|>|,")) |
9073 | 0 | head = head->next(); |
9074 | 0 | if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { |
9075 | 0 | head->previous()->isAttributeNodiscard(true); |
9076 | 0 | } |
9077 | 0 | } else if (Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link())) { |
9078 | 0 | Token* head = skipCPPOrAlignAttribute(tok)->next(); |
9079 | 0 | while (isCPPAttribute(head) || isAlignAttribute(head)) |
9080 | 0 | head = skipCPPOrAlignAttribute(head)->next(); |
9081 | 0 | head->isAttributeMaybeUnused(true); |
9082 | 0 | } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { |
9083 | 0 | const Token *vartok = tok->tokAt(4); |
9084 | 0 | if (vartok->str() == ":") |
9085 | 0 | vartok = vartok->next(); |
9086 | 0 | Token *argtok = tok->tokAt(-2); |
9087 | 0 | while (argtok && argtok->str() != "(") { |
9088 | 0 | if (argtok->str() == vartok->str()) |
9089 | 0 | break; |
9090 | 0 | if (argtok->str() == ")") |
9091 | 0 | argtok = argtok->link(); |
9092 | 0 | argtok = argtok->previous(); |
9093 | 0 | } |
9094 | 0 | if (argtok && argtok->str() == vartok->str()) { |
9095 | 0 | if (vartok->next()->str() == ">=") |
9096 | 0 | argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))); |
9097 | 0 | else if (vartok->next()->str() == ">") |
9098 | 0 | argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))+1); |
9099 | 0 | else if (vartok->next()->str() == "<=") |
9100 | 0 | argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))); |
9101 | 0 | else if (vartok->next()->str() == "<") |
9102 | 0 | argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))-1); |
9103 | 0 | } |
9104 | 0 | } |
9105 | 0 | } else { |
9106 | 0 | if (Token::simpleMatch(tok, "alignas (")) { |
9107 | | // alignment requirements could be checked here |
9108 | 0 | } |
9109 | 0 | } |
9110 | 0 | Token::eraseTokens(tok, skipCPPOrAlignAttribute(tok)->next()); |
9111 | 0 | tok->deleteThis(); |
9112 | 0 | } |
9113 | 1.36k | } |
9114 | | |
9115 | | void Tokenizer::simplifySpaceshipOperator() |
9116 | 1.36k | { |
9117 | 1.36k | if (isCPP() && mSettings->standards.cpp >= Standards::CPP20) { |
9118 | 80.4k | for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { |
9119 | 79.1k | if (Token::simpleMatch(tok, "<= >")) { |
9120 | 0 | tok->str("<=>"); |
9121 | 0 | tok->deleteNext(); |
9122 | 0 | } |
9123 | 79.1k | } |
9124 | 1.36k | } |
9125 | 1.36k | } |
9126 | | |
9127 | | static const std::unordered_set<std::string> keywords = { |
9128 | | "inline" |
9129 | | , "_inline" |
9130 | | , "__inline" |
9131 | | , "__forceinline" |
9132 | | , "register" |
9133 | | , "__restrict" |
9134 | | , "__restrict__" |
9135 | | , "__thread" |
9136 | | }; |
9137 | | // Remove "inline", "register", "restrict", "override", "static" and "constexpr" |
9138 | | // "restrict" keyword |
9139 | | // - New to 1999 ANSI/ISO C standard |
9140 | | // - Not in C++ standard yet |
9141 | | void Tokenizer::simplifyKeyword() |
9142 | 1.36k | { |
9143 | | // FIXME: There is a risk that "keywords" are removed by mistake. This |
9144 | | // code should be fixed so it doesn't remove variables etc. Nonstandard |
9145 | | // keywords should be defined with a library instead. For instance the |
9146 | | // linux kernel code at least uses "_inline" as struct member name at some |
9147 | | // places. |
9148 | | |
9149 | 1.36k | const bool c99 = isC() && mSettings->standards.c >= Standards::C99; |
9150 | 1.36k | const bool cpp11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; |
9151 | 1.36k | const bool cpp20 = isCPP() && mSettings->standards.cpp >= Standards::CPP20; |
9152 | | |
9153 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9154 | 79.1k | if (keywords.find(tok->str()) != keywords.end()) { |
9155 | | // Don't remove struct members |
9156 | 0 | if (!Token::simpleMatch(tok->previous(), ".")) { |
9157 | 0 | const bool isinline = (tok->str().find("inline") != std::string::npos); |
9158 | 0 | const bool isrestrict = (tok->str().find("restrict") != std::string::npos); |
9159 | 0 | if (isinline || isrestrict) { |
9160 | 0 | for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { |
9161 | 0 | if (isinline) |
9162 | 0 | temp->isInline(true); |
9163 | 0 | if (isrestrict) |
9164 | 0 | temp->isRestrict(true); |
9165 | 0 | } |
9166 | 0 | } |
9167 | 0 | tok->deleteThis(); // Simplify.. |
9168 | 0 | } |
9169 | 0 | } |
9170 | | |
9171 | 79.1k | if (isC() || mSettings->standards.cpp == Standards::CPP03) { |
9172 | 0 | if (tok->str() == "auto") |
9173 | 0 | tok->deleteThis(); |
9174 | 0 | } |
9175 | | |
9176 | | // simplify static keyword: |
9177 | | // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); |
9178 | 79.1k | if (Token::Match(tok, "[ static %num%")) |
9179 | 0 | tok->deleteNext(); |
9180 | | |
9181 | 79.1k | if (c99) { |
9182 | 0 | auto getTypeTokens = [tok]() { |
9183 | 0 | std::vector<Token*> ret; |
9184 | 0 | for (Token *temp = tok; Token::Match(temp, "%name%"); temp = temp->previous()) { |
9185 | 0 | if (!temp->isKeyword()) |
9186 | 0 | ret.emplace_back(temp); |
9187 | 0 | } |
9188 | 0 | for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { |
9189 | 0 | if (!temp->isKeyword()) |
9190 | 0 | ret.emplace_back(temp); |
9191 | 0 | } |
9192 | 0 | return ret; |
9193 | 0 | }; |
9194 | |
|
9195 | 0 | if (tok->str() == "restrict") { |
9196 | 0 | for (Token* temp: getTypeTokens()) |
9197 | 0 | temp->isRestrict(true); |
9198 | 0 | tok->deleteThis(); |
9199 | 0 | } |
9200 | |
|
9201 | 0 | if (mSettings->standards.c >= Standards::C11) { |
9202 | 0 | while (tok->str() == "_Atomic") { |
9203 | 0 | for (Token* temp: getTypeTokens()) |
9204 | 0 | temp->isAtomic(true); |
9205 | 0 | tok->deleteThis(); |
9206 | 0 | } |
9207 | 0 | } |
9208 | 0 | } |
9209 | | |
9210 | 79.1k | else if (cpp11) { |
9211 | 79.1k | if (cpp20 && tok->str() == "consteval") { |
9212 | 0 | tok->originalName(tok->str()); |
9213 | 0 | tok->str("constexpr"); |
9214 | 79.1k | } else if (cpp20 && tok->str() == "constinit") { |
9215 | 0 | tok->deleteThis(); |
9216 | 0 | } |
9217 | | |
9218 | | // final: |
9219 | | // 1) struct name final { }; <- struct is final |
9220 | 79.1k | if (Token::Match(tok->previous(), "struct|class|union %type%")) { |
9221 | 0 | Token* finalTok = tok->next(); |
9222 | 0 | if (tok->isUpperCaseName() && Token::Match(finalTok, "%type%") && finalTok->str() != "final") { |
9223 | 0 | tok = finalTok; |
9224 | 0 | finalTok = finalTok->next(); |
9225 | 0 | } |
9226 | 0 | if (Token::simpleMatch(finalTok, "<")) { // specialization |
9227 | 0 | finalTok = finalTok->findClosingBracket(); |
9228 | 0 | if (finalTok) |
9229 | 0 | finalTok = finalTok->next(); |
9230 | 0 | } |
9231 | 0 | if (Token::Match(finalTok, "final [:{]")) { |
9232 | 0 | finalTok->deleteThis(); |
9233 | 0 | tok->previous()->isFinalType(true); |
9234 | 0 | } |
9235 | 0 | } |
9236 | | |
9237 | | // noexcept -> noexcept(true) |
9238 | | // 2) void f() noexcept; -> void f() noexcept(true); |
9239 | 79.1k | else if (Token::Match(tok, ") const|override|final| noexcept :|{|;|,|const|override|final")) { |
9240 | | // Insertion is done in inverse order |
9241 | | // The brackets are linked together accordingly afterwards |
9242 | 0 | Token* tokNoExcept = tok->next(); |
9243 | 0 | while (tokNoExcept->str() != "noexcept") |
9244 | 0 | tokNoExcept = tokNoExcept->next(); |
9245 | 0 | tokNoExcept->insertToken(")"); |
9246 | 0 | Token * braceEnd = tokNoExcept->next(); |
9247 | 0 | tokNoExcept->insertToken("true"); |
9248 | 0 | tokNoExcept->insertToken("("); |
9249 | 0 | Token * braceStart = tokNoExcept->next(); |
9250 | 0 | tok = tok->tokAt(3); |
9251 | 0 | Token::createMutualLinks(braceStart, braceEnd); |
9252 | 0 | } |
9253 | | |
9254 | | // 3) thread_local -> static |
9255 | | // on single thread thread_local has the effect of static |
9256 | 79.1k | else if (tok->str() == "thread_local") { |
9257 | 0 | tok->originalName(tok->str()); |
9258 | 0 | tok->str("static"); |
9259 | 0 | } |
9260 | 79.1k | } |
9261 | 79.1k | } |
9262 | 1.36k | } |
9263 | | |
9264 | | static Token* setTokenDebug(Token* start, TokenDebug td) |
9265 | 0 | { |
9266 | 0 | if (!start->link()) |
9267 | 0 | return nullptr; |
9268 | 0 | Token* end = start->link(); |
9269 | 0 | start->deleteThis(); |
9270 | 0 | for (Token* tok = start; tok != end; tok = tok->next()) { |
9271 | 0 | tok->setTokenDebug(td); |
9272 | 0 | } |
9273 | 0 | end->deleteThis(); |
9274 | 0 | return end; |
9275 | 0 | } |
9276 | | |
9277 | | void Tokenizer::simplifyDebug() |
9278 | 1.36k | { |
9279 | 1.36k | if (!mSettings->debugnormal && !mSettings->debugwarnings) |
9280 | 1.36k | return; |
9281 | 0 | static const std::unordered_map<std::string, TokenDebug> m = {{"debug_valueflow", TokenDebug::ValueFlow}, |
9282 | 0 | {"debug_valuetype", TokenDebug::ValueType}}; |
9283 | 0 | for (Token* tok = list.front(); tok; tok = tok->next()) { |
9284 | 0 | if (!Token::Match(tok, "%name% (")) |
9285 | 0 | continue; |
9286 | 0 | auto it = m.find(tok->str()); |
9287 | 0 | if (it != m.end()) { |
9288 | 0 | tok->deleteThis(); |
9289 | 0 | tok = setTokenDebug(tok, it->second); |
9290 | 0 | } |
9291 | 0 | } |
9292 | 0 | } |
9293 | | |
9294 | | void Tokenizer::simplifyAssignmentBlock() |
9295 | 1.36k | { |
9296 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9297 | 92.7k | if (Token::Match(tok, "[;{}] %name% = ( {")) { |
9298 | 0 | const std::string &varname = tok->next()->str(); |
9299 | | |
9300 | | // goto the "} )" |
9301 | 0 | int indentlevel = 0; |
9302 | 0 | Token *tok2 = tok; |
9303 | 0 | while (nullptr != (tok2 = tok2->next())) { |
9304 | 0 | if (Token::Match(tok2, "(|{")) |
9305 | 0 | ++indentlevel; |
9306 | 0 | else if (Token::Match(tok2, ")|}")) { |
9307 | 0 | if (indentlevel <= 2) |
9308 | 0 | break; |
9309 | 0 | --indentlevel; |
9310 | 0 | } else if (indentlevel == 2 && tok2->str() == varname && Token::Match(tok2->previous(), "%type%|*")) |
9311 | | // declaring variable in inner scope with same name as lhs variable |
9312 | 0 | break; |
9313 | 0 | } |
9314 | 0 | if (indentlevel == 2 && Token::simpleMatch(tok2, "} )")) { |
9315 | 0 | tok2 = tok2->tokAt(-3); |
9316 | 0 | if (Token::Match(tok2, "[;{}] %num%|%name% ;")) { |
9317 | 0 | tok2->insertToken("="); |
9318 | 0 | tok2->insertToken(tok->next()->str()); |
9319 | 0 | tok2->next()->varId(tok->next()->varId()); |
9320 | 0 | tok->deleteNext(3); |
9321 | 0 | tok2->tokAt(5)->deleteNext(); |
9322 | 0 | } |
9323 | 0 | } |
9324 | 0 | } |
9325 | 92.7k | } |
9326 | 1.36k | } |
9327 | | |
9328 | | // Remove __asm.. |
9329 | | void Tokenizer::simplifyAsm() |
9330 | 1.36k | { |
9331 | 1.36k | std::string instruction; |
9332 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9333 | 80.4k | if (Token::Match(tok, "__asm|_asm|asm {") && |
9334 | 80.4k | tok->next()->link()->next()) { |
9335 | 0 | instruction = tok->tokAt(2)->stringifyList(tok->next()->link()); |
9336 | 0 | Token::eraseTokens(tok, tok->next()->link()->next()); |
9337 | 0 | } |
9338 | | |
9339 | 80.4k | else if (Token::Match(tok, "asm|__asm|__asm__ volatile|__volatile|__volatile__| (")) { |
9340 | | // Goto "(" |
9341 | 0 | Token *partok = tok->next(); |
9342 | 0 | if (partok->str() != "(") |
9343 | 0 | partok = partok->next(); |
9344 | 0 | instruction = partok->next()->stringifyList(partok->link()); |
9345 | 0 | Token::eraseTokens(tok, partok->link()->next()); |
9346 | 0 | } |
9347 | | |
9348 | 80.4k | else if (Token::Match(tok, "_asm|__asm")) { |
9349 | 0 | Token *endasm = tok->next(); |
9350 | 0 | const Token *firstSemiColon = nullptr; |
9351 | 0 | int comment = 0; |
9352 | 0 | while (Token::Match(endasm, "%num%|%name%|,|:|;") || (endasm && endasm->linenr() == comment)) { |
9353 | 0 | if (Token::Match(endasm, "_asm|__asm|__endasm")) |
9354 | 0 | break; |
9355 | 0 | if (endasm->str() == ";") { |
9356 | 0 | comment = endasm->linenr(); |
9357 | 0 | if (!firstSemiColon) |
9358 | 0 | firstSemiColon = endasm; |
9359 | 0 | } |
9360 | 0 | endasm = endasm->next(); |
9361 | 0 | } |
9362 | 0 | if (Token::simpleMatch(endasm, "__endasm")) { |
9363 | 0 | instruction = tok->next()->stringifyList(endasm); |
9364 | 0 | Token::eraseTokens(tok, endasm->next()); |
9365 | 0 | if (!Token::simpleMatch(tok->next(), ";")) |
9366 | 0 | tok->insertToken(";"); |
9367 | 0 | } else if (firstSemiColon) { |
9368 | 0 | instruction = tok->next()->stringifyList(firstSemiColon); |
9369 | 0 | Token::eraseTokens(tok, firstSemiColon); |
9370 | 0 | } else if (!endasm) { |
9371 | 0 | instruction = tok->next()->stringifyList(endasm); |
9372 | 0 | Token::eraseTokens(tok, endasm); |
9373 | 0 | tok->insertToken(";"); |
9374 | 0 | } else |
9375 | 0 | continue; |
9376 | 0 | } |
9377 | | |
9378 | 80.4k | else |
9379 | 80.4k | continue; |
9380 | | |
9381 | 0 | if (Token::Match(tok->previous(), ") %name% %name% (")) { |
9382 | 0 | tok->deleteThis(); |
9383 | 0 | continue; |
9384 | 0 | } |
9385 | | |
9386 | | // insert "asm ( "instruction" )" |
9387 | 0 | tok->str("asm"); |
9388 | 0 | if (tok->strAt(1) != ";" && tok->strAt(1) != "{") |
9389 | 0 | tok->insertToken(";"); |
9390 | 0 | tok->insertToken(")"); |
9391 | 0 | tok->insertToken("\"" + instruction + "\""); |
9392 | 0 | tok->insertToken("("); |
9393 | |
|
9394 | 0 | tok = tok->next(); |
9395 | 0 | Token::createMutualLinks(tok, tok->tokAt(2)); |
9396 | | |
9397 | | //move the new tokens in the same line as ";" if available |
9398 | 0 | tok = tok->tokAt(2); |
9399 | 0 | if (tok->next() && tok->next()->str() == ";" && |
9400 | 0 | tok->next()->linenr() != tok->linenr()) { |
9401 | 0 | const int endposition = tok->next()->linenr(); |
9402 | 0 | tok = tok->tokAt(-3); |
9403 | 0 | for (int i = 0; i < 4; ++i) { |
9404 | 0 | tok = tok->next(); |
9405 | 0 | tok->linenr(endposition); |
9406 | 0 | } |
9407 | 0 | } |
9408 | 0 | } |
9409 | 1.36k | } |
9410 | | |
9411 | | void Tokenizer::simplifyAsm2() |
9412 | 1.36k | { |
9413 | | // Block declarations: ^{} |
9414 | | // A C extension used to create lambda like closures. |
9415 | | |
9416 | | // Put ^{} statements in asm() |
9417 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9418 | 92.7k | if (tok->str() != "^") |
9419 | 92.4k | continue; |
9420 | | |
9421 | 266 | if (Token::simpleMatch(tok, "^ {") || (Token::simpleMatch(tok->linkAt(1), ") {") && tok->strAt(-1) != "operator")) { |
9422 | 0 | Token * start = tok; |
9423 | 0 | while (start && !Token::Match(start, "[,(;{}=]")) { |
9424 | 0 | if (start->link() && Token::Match(start, ")|]|>")) |
9425 | 0 | start = start->link(); |
9426 | 0 | start = start->previous(); |
9427 | 0 | } |
9428 | |
|
9429 | 0 | const Token *last = tok->next()->link(); |
9430 | 0 | if (Token::simpleMatch(last, ") {")) |
9431 | 0 | last = last->linkAt(1); |
9432 | 0 | last = last->next(); |
9433 | 0 | while (last && !Token::Match(last, "%cop%|,|;|{|}|)")) { |
9434 | 0 | if (Token::Match(last, "(|[")) |
9435 | 0 | last = last->link(); |
9436 | 0 | last = last->next(); |
9437 | 0 | } |
9438 | |
|
9439 | 0 | if (start && last) { |
9440 | 0 | std::string asmcode; |
9441 | 0 | while (start->next() != last) { |
9442 | 0 | asmcode += start->next()->str(); |
9443 | 0 | start->deleteNext(); |
9444 | 0 | } |
9445 | 0 | if (last->str() == "}") |
9446 | 0 | start->insertToken(";"); |
9447 | 0 | start->insertToken(")"); |
9448 | 0 | start->insertToken("\"" + asmcode + "\""); |
9449 | 0 | start->insertToken("("); |
9450 | 0 | start->insertToken("asm"); |
9451 | 0 | start->tokAt(2)->link(start->tokAt(4)); |
9452 | 0 | start->tokAt(4)->link(start->tokAt(2)); |
9453 | 0 | tok = start->tokAt(4); |
9454 | 0 | } |
9455 | 0 | } |
9456 | 266 | } |
9457 | 1.36k | } |
9458 | | |
9459 | | void Tokenizer::simplifyAt() |
9460 | 1.36k | { |
9461 | 1.36k | std::set<std::string> var; |
9462 | | |
9463 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9464 | 92.7k | if (Token::Match(tok, "%name%|] @ %num%|%name%|(")) { |
9465 | 0 | const Token *end = tok->tokAt(2); |
9466 | 0 | if (end->isNumber()) |
9467 | 0 | end = end->next(); |
9468 | 0 | else if (end->str() == "(") { |
9469 | 0 | int par = 0; |
9470 | 0 | while ((end = end->next()) != nullptr) { |
9471 | 0 | if (end->str() == "(") |
9472 | 0 | par++; |
9473 | 0 | else if (end->str() == ")") { |
9474 | 0 | if (--par < 0) |
9475 | 0 | break; |
9476 | 0 | } |
9477 | 0 | } |
9478 | 0 | end = end ? end->next() : nullptr; |
9479 | 0 | } else if (var.find(end->str()) != var.end()) |
9480 | 0 | end = end->next(); |
9481 | 0 | else |
9482 | 0 | continue; |
9483 | | |
9484 | 0 | if (Token::Match(end, ": %num% ;")) |
9485 | 0 | end = end->tokAt(2); |
9486 | |
|
9487 | 0 | if (end && end->str() == ";") { |
9488 | 0 | if (tok->isName()) |
9489 | 0 | var.insert(tok->str()); |
9490 | 0 | tok->isAtAddress(true); |
9491 | 0 | Token::eraseTokens(tok, end); |
9492 | 0 | } |
9493 | 0 | } |
9494 | | |
9495 | | // keywords in compiler from cosmic software for STM8 |
9496 | | // TODO: Should use platform configuration. |
9497 | 92.7k | if (Token::Match(tok, "@ builtin|eeprom|far|inline|interrupt|near|noprd|nostack|nosvf|packed|stack|svlreg|tiny|vector")) { |
9498 | 0 | tok->str(tok->next()->str() + "@"); |
9499 | 0 | tok->deleteNext(); |
9500 | 0 | } |
9501 | 92.7k | } |
9502 | 1.36k | } |
9503 | | |
9504 | | // Simplify bitfields |
9505 | | void Tokenizer::simplifyBitfields() |
9506 | 1.36k | { |
9507 | 1.36k | bool goback = false; |
9508 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9509 | 92.7k | if (goback) { |
9510 | 0 | goback = false; |
9511 | 0 | tok = tok->previous(); |
9512 | 0 | } |
9513 | 92.7k | Token *last = nullptr; |
9514 | | |
9515 | 92.7k | if (Token::simpleMatch(tok, "for (")) |
9516 | 0 | tok = tok->linkAt(1); |
9517 | | |
9518 | 92.7k | if (!Token::Match(tok, ";|{|}|public:|protected:|private:")) |
9519 | 68.5k | continue; |
9520 | | |
9521 | 24.2k | bool isEnum = false; |
9522 | 24.2k | if (tok->str() == "}") { |
9523 | 3.58k | const Token *type = tok->link()->previous(); |
9524 | 4.04k | while (type && type->isName()) { |
9525 | 458 | if (type->str() == "enum") { |
9526 | 0 | isEnum = true; |
9527 | 0 | break; |
9528 | 0 | } |
9529 | 458 | type = type->previous(); |
9530 | 458 | } |
9531 | 3.58k | } |
9532 | | |
9533 | 24.2k | if (Token::Match(tok->next(), "const| %type% %name% :") && |
9534 | 24.2k | !Token::Match(tok->next(), "case|public|protected|private|class|struct") && |
9535 | 24.2k | !Token::simpleMatch(tok->tokAt(2), "default :")) { |
9536 | 0 | Token *tok1 = (tok->next()->str() == "const") ? tok->tokAt(3) : tok->tokAt(2); |
9537 | 0 | if (Token::Match(tok1, "%name% : %num% [;=]")) |
9538 | 0 | tok1->setBits(MathLib::toLongNumber(tok1->strAt(2))); |
9539 | 0 | if (tok1 && tok1->tokAt(2) && |
9540 | 0 | (Token::Match(tok1->tokAt(2), "%bool%|%num%") || |
9541 | 0 | !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { |
9542 | 0 | while (tok1->next() && !Token::Match(tok1->next(), "[;,)]{}=]")) { |
9543 | 0 | if (Token::Match(tok1->next(), "[([]")) |
9544 | 0 | Token::eraseTokens(tok1, tok1->next()->link()); |
9545 | 0 | tok1->deleteNext(); |
9546 | 0 | } |
9547 | |
|
9548 | 0 | last = tok1->next(); |
9549 | 0 | } |
9550 | 24.2k | } else if (isEnum && Token::Match(tok, "} %name%| : %num% ;")) { |
9551 | 0 | if (tok->next()->str() == ":") { |
9552 | 0 | tok->deleteNext(2); |
9553 | 0 | tok->insertToken("Anonymous"); |
9554 | 0 | } else { |
9555 | 0 | tok->next()->deleteNext(2); |
9556 | 0 | } |
9557 | 24.2k | } else if (Token::Match(tok->next(), "const| %type% : %num%|%bool% ;") && |
9558 | 24.2k | tok->next()->str() != "default") { |
9559 | 0 | const int offset = (tok->next()->str() == "const") ? 1 : 0; |
9560 | 0 | if (!Token::Match(tok->tokAt(3 + offset), "[{};()]")) { |
9561 | 0 | tok->deleteNext(4 + offset); |
9562 | 0 | goback = true; |
9563 | 0 | } |
9564 | 0 | } |
9565 | | |
9566 | 24.2k | if (last && last->str() == ",") { |
9567 | 0 | Token * tok1 = last; |
9568 | 0 | tok1->str(";"); |
9569 | |
|
9570 | 0 | const Token *const tok2 = tok->next(); |
9571 | 0 | tok1->insertToken(tok2->str()); |
9572 | 0 | tok1 = tok1->next(); |
9573 | 0 | tok1->isSigned(tok2->isSigned()); |
9574 | 0 | tok1->isUnsigned(tok2->isUnsigned()); |
9575 | 0 | tok1->isLong(tok2->isLong()); |
9576 | 0 | } |
9577 | 24.2k | } |
9578 | 1.36k | } |
9579 | | |
9580 | | // Add std:: in front of std classes, when using namespace std; was given |
9581 | | void Tokenizer::simplifyNamespaceStd() |
9582 | 1.36k | { |
9583 | 1.36k | if (!isCPP()) |
9584 | 0 | return; |
9585 | | |
9586 | 1.36k | std::set<std::string> userFunctions; |
9587 | | |
9588 | 1.36k | for (Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) { |
9589 | 0 | bool insert = false; |
9590 | 0 | if (Token::Match(tok, "enum class|struct| %name%| :|{")) { // Don't replace within enum definitions |
9591 | 0 | skipEnumBody(&tok); |
9592 | 0 | } |
9593 | 0 | if (!tok->isName() || tok->isKeyword() || tok->isStandardType() || tok->varId()) |
9594 | 0 | continue; |
9595 | 0 | if (Token::Match(tok->previous(), ".|::|namespace")) |
9596 | 0 | continue; |
9597 | 0 | if (Token::simpleMatch(tok->next(), "(")) { |
9598 | 0 | if (isFunctionHead(tok->next(), "{")) |
9599 | 0 | userFunctions.insert(tok->str()); |
9600 | 0 | else if (isFunctionHead(tok->next(), ";")) { |
9601 | 0 | const Token *start = tok; |
9602 | 0 | while (Token::Match(start->previous(), "%type%|*|&")) |
9603 | 0 | start = start->previous(); |
9604 | 0 | if (start != tok && start->isName() && !start->isKeyword() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) |
9605 | 0 | userFunctions.insert(tok->str()); |
9606 | 0 | } |
9607 | 0 | if (userFunctions.find(tok->str()) == userFunctions.end() && mSettings->library.matchArguments(tok, "std::" + tok->str())) |
9608 | 0 | insert = true; |
9609 | 0 | } else if (Token::simpleMatch(tok->next(), "<") && |
9610 | 0 | (mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true) || mSettings->library.detectSmartPointer(tok, /*withoutStd*/ true))) |
9611 | 0 | insert = true; |
9612 | 0 | else if (mSettings->library.hasAnyTypeCheck("std::" + tok->str()) || |
9613 | 0 | mSettings->library.podtype("std::" + tok->str()) || |
9614 | 0 | mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true)) |
9615 | 0 | insert = true; |
9616 | |
|
9617 | 0 | if (insert) { |
9618 | 0 | tok->previous()->insertToken("std"); |
9619 | 0 | tok->previous()->linenr(tok->linenr()); // For stylistic reasons we put the std:: in the same line as the following token |
9620 | 0 | tok->previous()->fileIndex(tok->fileIndex()); |
9621 | 0 | tok->previous()->insertToken("::"); |
9622 | 0 | } |
9623 | 0 | } |
9624 | | |
9625 | 93.6k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
9626 | 92.3k | if (Token::simpleMatch(tok, "using namespace std ;")) { |
9627 | 0 | Token::eraseTokens(tok, tok->tokAt(4)); |
9628 | 0 | tok->deleteThis(); |
9629 | 0 | } |
9630 | 92.3k | } |
9631 | 1.36k | } |
9632 | | |
9633 | | |
9634 | | void Tokenizer::simplifyMicrosoftMemoryFunctions() |
9635 | 1.36k | { |
9636 | | // skip if not Windows |
9637 | 1.36k | if (!mSettings->platform.isWindows()) |
9638 | 1.36k | return; |
9639 | | |
9640 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9641 | 0 | if (tok->strAt(1) != "(") |
9642 | 0 | continue; |
9643 | | |
9644 | 0 | if (Token::Match(tok, "CopyMemory|RtlCopyMemory|RtlCopyBytes")) { |
9645 | 0 | tok->str("memcpy"); |
9646 | 0 | } else if (Token::Match(tok, "MoveMemory|RtlMoveMemory")) { |
9647 | 0 | tok->str("memmove"); |
9648 | 0 | } else if (Token::Match(tok, "FillMemory|RtlFillMemory|RtlFillBytes")) { |
9649 | | // FillMemory(dst, len, val) -> memset(dst, val, len) |
9650 | 0 | tok->str("memset"); |
9651 | |
|
9652 | 0 | Token *tok1 = tok->tokAt(2); |
9653 | 0 | if (tok1) |
9654 | 0 | tok1 = tok1->nextArgument(); // Second argument |
9655 | 0 | if (tok1) { |
9656 | 0 | Token *tok2 = tok1->nextArgument(); // Third argument |
9657 | |
|
9658 | 0 | if (tok2) |
9659 | 0 | Token::move(tok1->previous(), tok2->tokAt(-2), tok->next()->link()->previous()); // Swap third with second argument |
9660 | 0 | } |
9661 | 0 | } else if (Token::Match(tok, "ZeroMemory|RtlZeroMemory|RtlZeroBytes|RtlSecureZeroMemory")) { |
9662 | | // ZeroMemory(dst, len) -> memset(dst, 0, len) |
9663 | 0 | tok->str("memset"); |
9664 | |
|
9665 | 0 | Token *tok1 = tok->tokAt(2); |
9666 | 0 | if (tok1) |
9667 | 0 | tok1 = tok1->nextArgument(); // Second argument |
9668 | |
|
9669 | 0 | if (tok1) { |
9670 | 0 | tok1 = tok1->previous(); |
9671 | 0 | tok1->insertToken("0"); |
9672 | 0 | tok1 = tok1->next(); |
9673 | 0 | tok1->insertToken(","); |
9674 | 0 | } |
9675 | 0 | } else if (Token::simpleMatch(tok, "RtlCompareMemory")) { |
9676 | | // RtlCompareMemory(src1, src2, len) -> memcmp(src1, src2, len) |
9677 | 0 | tok->str("memcmp"); |
9678 | | // For the record, when memcmp returns 0, both strings are equal. |
9679 | | // When RtlCompareMemory returns len, both strings are equal. |
9680 | | // It might be needed to improve this replacement by something |
9681 | | // like ((len - memcmp(src1, src2, len)) % (len + 1)) to |
9682 | | // respect execution path (if required) |
9683 | 0 | } |
9684 | 0 | } |
9685 | 0 | } |
9686 | | |
9687 | | namespace { |
9688 | | struct triplet { |
9689 | 64 | triplet(const char* m, const char* u) : mbcs(m), unicode(u) {} |
9690 | | std::string mbcs, unicode; |
9691 | | }; |
9692 | | |
9693 | | const std::map<std::string, triplet> apis = { |
9694 | | std::make_pair("_topen", triplet("open", "_wopen")), |
9695 | | std::make_pair("_tsopen_s", triplet("_sopen_s", "_wsopen_s")), |
9696 | | std::make_pair("_tfopen", triplet("fopen", "_wfopen")), |
9697 | | std::make_pair("_tfopen_s", triplet("fopen_s", "_wfopen_s")), |
9698 | | std::make_pair("_tfreopen", triplet("freopen", "_wfreopen")), |
9699 | | std::make_pair("_tfreopen_s", triplet("freopen_s", "_wfreopen_s")), |
9700 | | std::make_pair("_tcscat", triplet("strcat", "wcscat")), |
9701 | | std::make_pair("_tcschr", triplet("strchr", "wcschr")), |
9702 | | std::make_pair("_tcscmp", triplet("strcmp", "wcscmp")), |
9703 | | std::make_pair("_tcsdup", triplet("strdup", "wcsdup")), |
9704 | | std::make_pair("_tcscpy", triplet("strcpy", "wcscpy")), |
9705 | | std::make_pair("_tcslen", triplet("strlen", "wcslen")), |
9706 | | std::make_pair("_tcsncat", triplet("strncat", "wcsncat")), |
9707 | | std::make_pair("_tcsncpy", triplet("strncpy", "wcsncpy")), |
9708 | | std::make_pair("_tcsnlen", triplet("strnlen", "wcsnlen")), |
9709 | | std::make_pair("_tcsrchr", triplet("strrchr", "wcsrchr")), |
9710 | | std::make_pair("_tcsstr", triplet("strstr", "wcsstr")), |
9711 | | std::make_pair("_tcstok", triplet("strtok", "wcstok")), |
9712 | | std::make_pair("_ftprintf", triplet("fprintf", "fwprintf")), |
9713 | | std::make_pair("_tprintf", triplet("printf", "wprintf")), |
9714 | | std::make_pair("_stprintf", triplet("sprintf", "swprintf")), |
9715 | | std::make_pair("_sntprintf", triplet("_snprintf", "_snwprintf")), |
9716 | | std::make_pair("_ftscanf", triplet("fscanf", "fwscanf")), |
9717 | | std::make_pair("_tscanf", triplet("scanf", "wscanf")), |
9718 | | std::make_pair("_stscanf", triplet("sscanf", "swscanf")), |
9719 | | std::make_pair("_ftprintf_s", triplet("fprintf_s", "fwprintf_s")), |
9720 | | std::make_pair("_tprintf_s", triplet("printf_s", "wprintf_s")), |
9721 | | std::make_pair("_stprintf_s", triplet("sprintf_s", "swprintf_s")), |
9722 | | std::make_pair("_sntprintf_s", triplet("_snprintf_s", "_snwprintf_s")), |
9723 | | std::make_pair("_ftscanf_s", triplet("fscanf_s", "fwscanf_s")), |
9724 | | std::make_pair("_tscanf_s", triplet("scanf_s", "wscanf_s")), |
9725 | | std::make_pair("_stscanf_s", triplet("sscanf_s", "swscanf_s")) |
9726 | | }; |
9727 | | } |
9728 | | |
9729 | | void Tokenizer::simplifyMicrosoftStringFunctions() |
9730 | 1.36k | { |
9731 | | // skip if not Windows |
9732 | 1.36k | if (!mSettings->platform.isWindows()) |
9733 | 1.36k | return; |
9734 | | |
9735 | 0 | const bool ansi = mSettings->platform.type == cppcheck::Platform::Type::Win32A; |
9736 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9737 | 0 | if (tok->strAt(1) != "(") |
9738 | 0 | continue; |
9739 | | |
9740 | 0 | const std::map<std::string, triplet>::const_iterator match = apis.find(tok->str()); |
9741 | 0 | if (match!=apis.end()) { |
9742 | 0 | tok->str(ansi ? match->second.mbcs : match->second.unicode); |
9743 | 0 | tok->originalName(match->first); |
9744 | 0 | } else if (Token::Match(tok, "_T|_TEXT|TEXT ( %char%|%str% )")) { |
9745 | 0 | tok->deleteNext(); |
9746 | 0 | tok->deleteThis(); |
9747 | 0 | tok->deleteNext(); |
9748 | 0 | if (!ansi) { |
9749 | 0 | tok->isLong(true); |
9750 | 0 | if (tok->str()[0] != 'L') |
9751 | 0 | tok->str("L" + tok->str()); |
9752 | 0 | } |
9753 | 0 | while (Token::Match(tok->next(), "_T|_TEXT|TEXT ( %char%|%str% )")) { |
9754 | 0 | tok->next()->deleteNext(); |
9755 | 0 | tok->next()->deleteThis(); |
9756 | 0 | tok->next()->deleteNext(); |
9757 | 0 | tok->concatStr(tok->next()->str()); |
9758 | 0 | tok->deleteNext(); |
9759 | 0 | } |
9760 | 0 | } |
9761 | 0 | } |
9762 | 0 | } |
9763 | | |
9764 | | // Remove Borland code |
9765 | | void Tokenizer::simplifyBorland() |
9766 | 1.36k | { |
9767 | | // skip if not Windows |
9768 | 1.36k | if (!mSettings->platform.isWindows()) |
9769 | 1.36k | return; |
9770 | 0 | if (isC()) |
9771 | 0 | return; |
9772 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9773 | 0 | if (Token::Match(tok, "( __closure * %name% )")) { |
9774 | 0 | tok->deleteNext(); |
9775 | 0 | } |
9776 | 0 | } |
9777 | | |
9778 | | // I think that these classes are always declared at the outer scope |
9779 | | // I save some time by ignoring inner classes. |
9780 | 0 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9781 | 0 | if (tok->str() == "{" && !Token::Match(tok->tokAt(-2), "namespace %type%")) { |
9782 | 0 | tok = tok->link(); |
9783 | 0 | if (!tok) |
9784 | 0 | break; |
9785 | 0 | } else if (Token::Match(tok, "class %name% :|{")) { |
9786 | 0 | while (tok && tok->str() != "{" && tok->str() != ";") |
9787 | 0 | tok = tok->next(); |
9788 | 0 | if (!tok) |
9789 | 0 | break; |
9790 | 0 | if (tok->str() == ";") |
9791 | 0 | continue; |
9792 | | |
9793 | 0 | const Token* end = tok->link()->next(); |
9794 | 0 | for (Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { |
9795 | 0 | if (tok2->str() == "__property" && |
9796 | 0 | Token::Match(tok2->previous(), ";|{|}|protected:|public:|__published:")) { |
9797 | 0 | while (tok2->next() && !Token::Match(tok2->next(), "{|;")) |
9798 | 0 | tok2->deleteNext(); |
9799 | 0 | tok2->deleteThis(); |
9800 | 0 | if (tok2->str() == "{") { |
9801 | 0 | Token::eraseTokens(tok2, tok2->link()); |
9802 | 0 | tok2->deleteNext(); |
9803 | 0 | tok2->deleteThis(); |
9804 | | |
9805 | | // insert "; __property ;" |
9806 | 0 | tok2->previous()->insertToken(";"); |
9807 | 0 | tok2->previous()->insertToken("__property"); |
9808 | 0 | tok2->previous()->insertToken(";"); |
9809 | 0 | } |
9810 | 0 | } |
9811 | 0 | } |
9812 | 0 | } |
9813 | 0 | } |
9814 | 0 | } |
9815 | | |
9816 | | void Tokenizer::createSymbolDatabase() |
9817 | 1.36k | { |
9818 | 1.36k | if (!mSymbolDatabase) |
9819 | 1.36k | mSymbolDatabase = new SymbolDatabase(*this, *mSettings, mErrorLogger); |
9820 | 1.36k | mSymbolDatabase->validate(); |
9821 | 1.36k | } |
9822 | | |
9823 | | bool Tokenizer::operatorEnd(const Token * tok) const |
9824 | 0 | { |
9825 | 0 | if (tok && tok->str() == ")") { |
9826 | 0 | if (isFunctionHead(tok, "{|;|?|:|[")) |
9827 | 0 | return true; |
9828 | | |
9829 | 0 | tok = tok->next(); |
9830 | 0 | while (tok && !Token::Match(tok, "[=;{),]")) { |
9831 | 0 | if (Token::Match(tok, "const|volatile|override")) { |
9832 | 0 | tok = tok->next(); |
9833 | 0 | } else if (tok->str() == "noexcept") { |
9834 | 0 | tok = tok->next(); |
9835 | 0 | if (tok && tok->str() == "(") { |
9836 | 0 | tok = tok->link()->next(); |
9837 | 0 | } |
9838 | 0 | } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { |
9839 | 0 | tok = tok->next()->link()->next(); |
9840 | 0 | } |
9841 | | // unknown macros ") MACRO {" and ") MACRO(...) {" |
9842 | 0 | else if (tok->isUpperCaseName()) { |
9843 | 0 | tok = tok->next(); |
9844 | 0 | if (tok && tok->str() == "(") { |
9845 | 0 | tok = tok->link()->next(); |
9846 | 0 | } |
9847 | 0 | } else if (Token::Match(tok, "%op% !!(") || |
9848 | 0 | (Token::Match(tok, "%op% (") && !isFunctionHead(tok->next(), "{"))) |
9849 | 0 | break; |
9850 | 0 | else |
9851 | 0 | return false; |
9852 | 0 | } |
9853 | | |
9854 | 0 | return true; |
9855 | 0 | } |
9856 | | |
9857 | 0 | return false; |
9858 | 0 | } |
9859 | | |
9860 | | void Tokenizer::simplifyOperatorName() |
9861 | 1.36k | { |
9862 | 1.36k | if (isC()) |
9863 | 0 | return; |
9864 | | |
9865 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9866 | 92.7k | if (Token::Match(tok, "using|:: operator %op%|%name% ;")) { |
9867 | 0 | tok->next()->str("operator" + tok->strAt(2)); |
9868 | 0 | tok->next()->deleteNext(); |
9869 | 0 | continue; |
9870 | 0 | } |
9871 | | |
9872 | 92.7k | if (tok->str() != "operator") |
9873 | 92.7k | continue; |
9874 | | // operator op |
9875 | 0 | if (Token::Match(tok, "operator %op% (") && !operatorEnd(tok->linkAt(2))) { |
9876 | 0 | tok->str(tok->str() + tok->next()->str()); |
9877 | 0 | tok->deleteNext(); |
9878 | 0 | continue; |
9879 | 0 | } |
9880 | 0 | std::string op; |
9881 | 0 | Token *par = tok->next(); |
9882 | 0 | bool done = false; |
9883 | 0 | while (!done && par) { |
9884 | 0 | done = true; |
9885 | 0 | if (par->isName()) { |
9886 | 0 | op += par->str(); |
9887 | 0 | par = par->next(); |
9888 | | // merge namespaces eg. 'operator std :: string () const {' |
9889 | 0 | if (Token::Match(par, ":: %name%|%op%|.")) { |
9890 | 0 | op += par->str(); |
9891 | 0 | par = par->next(); |
9892 | 0 | } |
9893 | 0 | done = false; |
9894 | 0 | } else if (Token::Match(par, ".|%op%|,")) { |
9895 | | // check for operator in template |
9896 | 0 | if (par->str() == "," && !op.empty()) |
9897 | 0 | break; |
9898 | 0 | if (!(Token::Match(par, "<|>") && !op.empty())) { |
9899 | 0 | op += par->str() == "." ? par->originalName() : par->str(); |
9900 | 0 | par = par->next(); |
9901 | 0 | done = false; |
9902 | 0 | } |
9903 | 0 | } else if (Token::simpleMatch(par, "[ ]")) { |
9904 | 0 | op += "[]"; |
9905 | 0 | par = par->tokAt(2); |
9906 | 0 | done = false; |
9907 | 0 | } else if (Token::Match(par, "( *| )")) { |
9908 | | // break out and simplify.. |
9909 | 0 | if (operatorEnd(par->next())) |
9910 | 0 | break; |
9911 | | |
9912 | 0 | while (par->str() != ")") { |
9913 | 0 | op += par->str(); |
9914 | 0 | par = par->next(); |
9915 | 0 | } |
9916 | 0 | op += ")"; |
9917 | 0 | par = par->next(); |
9918 | 0 | if (Token::simpleMatch(par, "...")) { |
9919 | 0 | op.clear(); |
9920 | 0 | par = nullptr; |
9921 | 0 | break; |
9922 | 0 | } |
9923 | 0 | done = false; |
9924 | 0 | } else if (Token::Match(par, "\"\" %name% )| (|;|<")) { |
9925 | 0 | op += "\"\""; |
9926 | 0 | op += par->strAt(1); |
9927 | 0 | par = par->tokAt(2); |
9928 | 0 | if (par->str() == ")") { |
9929 | 0 | par->link()->deleteThis(); |
9930 | 0 | par = par->next(); |
9931 | 0 | par->deletePrevious(); |
9932 | 0 | tok = par->tokAt(-3); |
9933 | 0 | } |
9934 | 0 | done = true; |
9935 | 0 | } else if (par->str() == "::") { |
9936 | 0 | op += par->str(); |
9937 | 0 | par = par->next(); |
9938 | 0 | done = false; |
9939 | 0 | } else if (par->str() == ";" || par->str() == ")") { |
9940 | 0 | done = true; |
9941 | 0 | } else if (par->str() != "(") { |
9942 | 0 | syntaxError(par, "operator"); |
9943 | 0 | } |
9944 | 0 | } |
9945 | |
|
9946 | 0 | if (par && !op.empty()) { |
9947 | 0 | tok->str("operator" + op); |
9948 | 0 | Token::eraseTokens(tok, par); |
9949 | 0 | } |
9950 | |
|
9951 | 0 | if (!op.empty()) |
9952 | 0 | tok->isOperatorKeyword(true); |
9953 | 0 | } |
9954 | | |
9955 | 94.1k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9956 | 92.7k | if (Token::Match(tok, "%op% %str% %name%")) { |
9957 | 0 | const std::string name = tok->strAt(2); |
9958 | 0 | Token * const str = tok->next(); |
9959 | 0 | str->deleteNext(); |
9960 | 0 | tok->insertToken("operator\"\"" + name); |
9961 | 0 | tok = tok->next(); |
9962 | 0 | tok->isOperatorKeyword(true); |
9963 | 0 | tok->insertToken("("); |
9964 | 0 | str->insertToken(")"); |
9965 | 0 | Token::createMutualLinks(tok->next(), str->next()); |
9966 | 0 | str->insertToken(std::to_string(Token::getStrLength(str))); |
9967 | 0 | str->insertToken(","); |
9968 | 0 | } |
9969 | 92.7k | } |
9970 | | |
9971 | 1.36k | if (mSettings->debugwarnings) { |
9972 | 0 | const Token *tok = list.front(); |
9973 | |
|
9974 | 0 | while ((tok = Token::findsimplematch(tok, "operator")) != nullptr) { |
9975 | 0 | reportError(tok, Severity::debug, "debug", |
9976 | 0 | "simplifyOperatorName: found unsimplified operator name"); |
9977 | 0 | tok = tok->next(); |
9978 | 0 | } |
9979 | 0 | } |
9980 | 1.36k | } |
9981 | | |
9982 | | void Tokenizer::simplifyOverloadedOperators() |
9983 | 1.36k | { |
9984 | 1.36k | if (isC()) |
9985 | 0 | return; |
9986 | 1.36k | std::set<std::string> classNames; |
9987 | 1.36k | std::set<nonneg int> classVars; |
9988 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
9989 | 92.3k | if (!tok->isName()) |
9990 | 56.5k | continue; |
9991 | | |
9992 | 35.7k | if (Token::simpleMatch(tok, "this ) (") && Token::simpleMatch(tok->tokAt(-2), "( *")) { |
9993 | 0 | tok = tok->next(); |
9994 | 0 | tok->insertToken("operator()"); |
9995 | 0 | tok->insertToken("."); |
9996 | 0 | continue; |
9997 | 0 | } |
9998 | | |
9999 | | // Get classes that have operator() member |
10000 | 35.7k | if (Token::Match(tok, "class|struct %name% [:{]")) { |
10001 | 0 | int indent = 0; |
10002 | 0 | for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { |
10003 | 0 | if (tok2->str() == "}") |
10004 | 0 | break; |
10005 | 0 | if (indent == 0 && tok2->str() == ";") |
10006 | 0 | break; |
10007 | 0 | if (tok2->str() == "{") { |
10008 | 0 | if (indent == 0) |
10009 | 0 | ++indent; |
10010 | 0 | else |
10011 | 0 | tok2 = tok2->link(); |
10012 | 0 | } else if (indent == 1 && Token::simpleMatch(tok2, "operator() (") && isFunctionHead(tok2->next(), ";{")) { |
10013 | 0 | classNames.insert(tok->strAt(1)); |
10014 | 0 | break; |
10015 | 0 | } |
10016 | 0 | } |
10017 | 0 | } |
10018 | | |
10019 | | // Get variables that have operator() member |
10020 | 35.7k | if (Token::Match(tok, "%type% &| %var%") && classNames.find(tok->str()) != classNames.end()) { |
10021 | 0 | tok = tok->next(); |
10022 | 0 | while (!tok->isName()) |
10023 | 0 | tok = tok->next(); |
10024 | 0 | classVars.insert(tok->varId()); |
10025 | 0 | } |
10026 | | |
10027 | | // Simplify operator() calls |
10028 | 35.7k | if (Token::Match(tok, "%var% (") && classVars.find(tok->varId()) != classVars.end()) { |
10029 | | // constructor init list.. |
10030 | 0 | if (Token::Match(tok->previous(), "[:,]")) { |
10031 | 0 | const Token *start = tok->previous(); |
10032 | 0 | while (Token::simpleMatch(start, ",")) { |
10033 | 0 | if (Token::simpleMatch(start->previous(), ")")) |
10034 | 0 | start = start->linkAt(-1); |
10035 | 0 | else |
10036 | 0 | break; |
10037 | 0 | if (Token::Match(start->previous(), "%name%")) |
10038 | 0 | start = start->tokAt(-2); |
10039 | 0 | else |
10040 | 0 | break; |
10041 | 0 | } |
10042 | 0 | const Token *after = tok->linkAt(1); |
10043 | 0 | while (Token::Match(after, ")|} , %name% (|{")) |
10044 | 0 | after = after->linkAt(3); |
10045 | | |
10046 | | // Do not simplify initlist |
10047 | 0 | if (Token::simpleMatch(start, ":") && Token::simpleMatch(after, ") {")) |
10048 | 0 | continue; |
10049 | 0 | } |
10050 | | |
10051 | 0 | tok->insertToken("operator()"); |
10052 | 0 | tok->insertToken("."); |
10053 | 0 | } |
10054 | 35.7k | } |
10055 | 1.36k | } |
10056 | | |
10057 | | // remove unnecessary member qualification.. |
10058 | | void Tokenizer::removeUnnecessaryQualification() |
10059 | 1.36k | { |
10060 | 1.36k | if (isC()) |
10061 | 0 | return; |
10062 | | |
10063 | 1.36k | std::vector<Space> classInfo; |
10064 | 80.4k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
10065 | 79.1k | if (Token::Match(tok, "class|struct|namespace %type% :|{") && |
10066 | 79.1k | (!tok->previous() || tok->previous()->str() != "enum")) { |
10067 | 0 | Space info; |
10068 | 0 | info.isNamespace = tok->str() == "namespace"; |
10069 | 0 | tok = tok->next(); |
10070 | 0 | info.className = tok->str(); |
10071 | 0 | tok = tok->next(); |
10072 | 0 | while (tok && tok->str() != "{") |
10073 | 0 | tok = tok->next(); |
10074 | 0 | if (!tok) |
10075 | 0 | return; |
10076 | 0 | info.bodyEnd = tok->link(); |
10077 | 0 | classInfo.push_back(std::move(info)); |
10078 | 79.1k | } else if (!classInfo.empty()) { |
10079 | 0 | if (tok == classInfo.back().bodyEnd) |
10080 | 0 | classInfo.pop_back(); |
10081 | 0 | else if (tok->str() == classInfo.back().className && |
10082 | 0 | !classInfo.back().isNamespace && tok->previous()->str() != ":" && |
10083 | 0 | (Token::Match(tok, "%type% :: ~| %type% (") || |
10084 | 0 | Token::Match(tok, "%type% :: operator"))) { |
10085 | 0 | const Token *tok1 = tok->tokAt(3); |
10086 | 0 | if (tok->strAt(2) == "operator") { |
10087 | | // check for operator () |
10088 | 0 | if (tok1->str() == "(") |
10089 | 0 | tok1 = tok1->next(); |
10090 | |
|
10091 | 0 | while (tok1 && tok1->str() != "(") { |
10092 | 0 | if (tok1->str() == ";") |
10093 | 0 | break; |
10094 | 0 | tok1 = tok1->next(); |
10095 | 0 | } |
10096 | 0 | if (!tok1 || tok1->str() != "(") |
10097 | 0 | continue; |
10098 | 0 | } else if (tok->strAt(2) == "~") |
10099 | 0 | tok1 = tok1->next(); |
10100 | | |
10101 | 0 | if (!tok1 || !Token::Match(tok1->link(), ") const| {|;|:")) { |
10102 | 0 | continue; |
10103 | 0 | } |
10104 | | |
10105 | 0 | const bool isConstructorOrDestructor = |
10106 | 0 | Token::Match(tok, "%type% :: ~| %type%") && (tok->strAt(2) == tok->str() || (tok->strAt(2) == "~" && tok->strAt(3) == tok->str())); |
10107 | 0 | if (!isConstructorOrDestructor) { |
10108 | 0 | bool isPrependedByType = Token::Match(tok->previous(), "%type%"); |
10109 | 0 | if (!isPrependedByType) { |
10110 | 0 | const Token* tok2 = tok->tokAt(-2); |
10111 | 0 | isPrependedByType = Token::Match(tok2, "%type% *|&"); |
10112 | 0 | } |
10113 | 0 | if (!isPrependedByType) { |
10114 | 0 | const Token* tok3 = tok->tokAt(-3); |
10115 | 0 | isPrependedByType = Token::Match(tok3, "%type% * *|&"); |
10116 | 0 | } |
10117 | 0 | if (!isPrependedByType) { |
10118 | | // It's not a constructor declaration and it's not a function declaration so |
10119 | | // this is a function call which can have all the qualifiers just fine - skip. |
10120 | 0 | continue; |
10121 | 0 | } |
10122 | 0 | } |
10123 | 0 | } |
10124 | 0 | } |
10125 | 79.1k | } |
10126 | 1.36k | } |
10127 | | |
10128 | | void Tokenizer::printUnknownTypes() const |
10129 | 0 | { |
10130 | 0 | if (!mSymbolDatabase) |
10131 | 0 | return; |
10132 | | |
10133 | 0 | std::vector<std::pair<std::string, const Token *>> unknowns; |
10134 | |
|
10135 | 0 | for (int i = 1; i <= mVarId; ++i) { |
10136 | 0 | const Variable *var = mSymbolDatabase->getVariableFromVarId(i); |
10137 | 0 | if (!var) |
10138 | 0 | continue; |
10139 | | // is unknown type? |
10140 | 0 | if (var->type() || var->typeStartToken()->isStandardType()) |
10141 | 0 | continue; |
10142 | | |
10143 | 0 | std::string name; |
10144 | 0 | const Token * nameTok; |
10145 | | |
10146 | | // single token type? |
10147 | 0 | if (var->typeStartToken() == var->typeEndToken()) { |
10148 | 0 | nameTok = var->typeStartToken(); |
10149 | 0 | name = nameTok->str(); |
10150 | 0 | } |
10151 | | |
10152 | | // complicated type |
10153 | 0 | else { |
10154 | 0 | const Token *tok = var->typeStartToken(); |
10155 | 0 | int level = 0; |
10156 | |
|
10157 | 0 | nameTok = tok; |
10158 | |
|
10159 | 0 | while (tok) { |
10160 | | // skip pointer and reference part of type |
10161 | 0 | if (level == 0 && Token::Match(tok, "*|&")) |
10162 | 0 | break; |
10163 | | |
10164 | 0 | name += tok->str(); |
10165 | |
|
10166 | 0 | if (Token::Match(tok, "struct|union|enum")) |
10167 | 0 | name += " "; |
10168 | | |
10169 | | // pointers and references are OK in template |
10170 | 0 | else if (tok->str() == "<") |
10171 | 0 | ++level; |
10172 | 0 | else if (tok->str() == ">") |
10173 | 0 | --level; |
10174 | |
|
10175 | 0 | if (tok == var->typeEndToken()) |
10176 | 0 | break; |
10177 | | |
10178 | 0 | tok = tok->next(); |
10179 | 0 | } |
10180 | 0 | } |
10181 | |
|
10182 | 0 | unknowns.emplace_back(std::move(name), nameTok); |
10183 | 0 | } |
10184 | |
|
10185 | 0 | if (!unknowns.empty()) { |
10186 | 0 | std::string last; |
10187 | 0 | int count = 0; |
10188 | |
|
10189 | 0 | for (auto it = unknowns.cbegin(); it != unknowns.cend(); ++it) { |
10190 | | // skip types is std namespace because they are not interesting |
10191 | 0 | if (it->first.find("std::") != 0) { |
10192 | 0 | if (it->first != last) { |
10193 | 0 | last = it->first; |
10194 | 0 | count = 1; |
10195 | 0 | reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); |
10196 | 0 | } else { |
10197 | 0 | if (count < 3) // limit same type to 3 |
10198 | 0 | reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); |
10199 | 0 | count++; |
10200 | 0 | } |
10201 | 0 | } |
10202 | 0 | } |
10203 | 0 | } |
10204 | 0 | } |
10205 | | |
10206 | | void Tokenizer::prepareTernaryOpForAST() |
10207 | 2.72k | { |
10208 | | // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: |
10209 | | // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." |
10210 | | // The AST parser relies on this function to add such parentheses where necessary. |
10211 | 174k | for (Token* tok = list.front(); tok; tok = tok->next()) { |
10212 | 171k | if (tok->str() == "?") { |
10213 | 0 | bool parenthesesNeeded = false; |
10214 | 0 | int depth = 0; |
10215 | 0 | Token* tok2 = tok->next(); |
10216 | 0 | for (; tok2; tok2 = tok2->next()) { |
10217 | 0 | if (tok2->link() && Token::Match(tok2, "[|(|<")) |
10218 | 0 | tok2 = tok2->link(); |
10219 | 0 | else if (tok2->str() == ":") { |
10220 | 0 | if (depth == 0) |
10221 | 0 | break; |
10222 | 0 | depth--; |
10223 | 0 | } else if (tok2->str() == ";" || (tok2->link() && tok2->str() != "{" && tok2->str() != "}")) |
10224 | 0 | break; |
10225 | 0 | else if (tok2->str() == ",") |
10226 | 0 | parenthesesNeeded = true; |
10227 | 0 | else if (tok2->str() == "<") |
10228 | 0 | parenthesesNeeded = true; |
10229 | 0 | else if (tok2->str() == "?") { |
10230 | 0 | depth++; |
10231 | 0 | parenthesesNeeded = true; |
10232 | 0 | } |
10233 | 0 | } |
10234 | 0 | if (parenthesesNeeded && tok2 && tok2->str() == ":") { |
10235 | 0 | tok->insertToken("("); |
10236 | 0 | tok2->insertToken(")", emptyString, true); |
10237 | 0 | Token::createMutualLinks(tok->next(), tok2->previous()); |
10238 | 0 | } |
10239 | 0 | } |
10240 | 171k | } |
10241 | 2.72k | } |
10242 | | |
10243 | | void Tokenizer::reportError(const Token* tok, const Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const |
10244 | 0 | { |
10245 | 0 | const std::list<const Token*> callstack(1, tok); |
10246 | 0 | reportError(callstack, severity, id, msg, inconclusive); |
10247 | 0 | } |
10248 | | |
10249 | | void Tokenizer::reportError(const std::list<const Token*>& callstack, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const |
10250 | 0 | { |
10251 | 0 | const ErrorMessage errmsg(callstack, &list, severity, id, msg, inconclusive ? Certainty::inconclusive : Certainty::normal); |
10252 | 0 | if (mErrorLogger) |
10253 | 0 | mErrorLogger->reportErr(errmsg); |
10254 | 0 | else |
10255 | 0 | Check::writeToErrorList(errmsg); |
10256 | 0 | } |
10257 | | |
10258 | | void Tokenizer::setPodTypes() |
10259 | 1.36k | { |
10260 | 1.36k | if (!mSettings) |
10261 | 0 | return; |
10262 | 93.6k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
10263 | 92.3k | if (!tok->isName() || tok->varId()) |
10264 | 76.3k | continue; |
10265 | | |
10266 | | // pod type |
10267 | 15.9k | const struct Library::PodType *podType = mSettings->library.podtype(tok->str()); |
10268 | 15.9k | if (podType) { |
10269 | 0 | const Token *prev = tok->previous(); |
10270 | 0 | while (prev && prev->isName()) |
10271 | 0 | prev = prev->previous(); |
10272 | 0 | if (prev && !Token::Match(prev, ";|{|}|,|(")) |
10273 | 0 | continue; |
10274 | 0 | tok->isStandardType(true); |
10275 | 0 | } |
10276 | 15.9k | } |
10277 | 1.36k | } |
10278 | | |
10279 | | const Token *Tokenizer::findSQLBlockEnd(const Token *tokSQLStart) |
10280 | 0 | { |
10281 | 0 | const Token *tokLastEnd = nullptr; |
10282 | 0 | for (const Token *tok = tokSQLStart->tokAt(2); tok != nullptr; tok = tok->next()) { |
10283 | 0 | if (tokLastEnd == nullptr && tok->str() == ";") |
10284 | 0 | tokLastEnd = tok; |
10285 | 0 | else if (tok->str() == "__CPPCHECK_EMBEDDED_SQL_EXEC__") { |
10286 | 0 | if (Token::simpleMatch(tok->tokAt(-2), "END - __CPPCHECK_EMBEDDED_SQL_EXEC__ ;")) |
10287 | 0 | return tok->next(); |
10288 | 0 | return tokLastEnd; |
10289 | 0 | } else if (Token::Match(tok, "{|}|==|&&|!|^|<<|>>|++|+=|-=|/=|*=|>>=|<<=|~")) |
10290 | 0 | break; // We are obviously outside the SQL block |
10291 | 0 | } |
10292 | | |
10293 | 0 | return tokLastEnd; |
10294 | 0 | } |
10295 | | |
10296 | | void Tokenizer::simplifyNestedNamespace() |
10297 | 1.36k | { |
10298 | 1.36k | if (!isCPP()) |
10299 | 0 | return; |
10300 | | |
10301 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
10302 | 80.4k | if (Token::Match(tok, "namespace %name% ::") && tok->strAt(-1) != "using") { |
10303 | 0 | Token * tok2 = tok->tokAt(2); |
10304 | | |
10305 | | // validate syntax |
10306 | 0 | while (Token::Match(tok2, ":: %name%")) |
10307 | 0 | tok2 = tok2->tokAt(2); |
10308 | |
|
10309 | 0 | if (!tok2 || tok2->str() != "{") |
10310 | 0 | return; // syntax error |
10311 | | |
10312 | 0 | std::stack<Token *> links; |
10313 | 0 | tok2 = tok->tokAt(2); |
10314 | |
|
10315 | 0 | while (tok2->str() == "::") { |
10316 | 0 | links.push(tok2); |
10317 | 0 | tok2->str("{"); |
10318 | 0 | tok2->insertToken("namespace"); |
10319 | 0 | tok2 = tok2->tokAt(3); |
10320 | 0 | } |
10321 | |
|
10322 | 0 | tok = tok2; |
10323 | |
|
10324 | 0 | if (!links.empty() && tok2->str() == "{") { |
10325 | 0 | tok2 = tok2->link(); |
10326 | 0 | while (!links.empty()) { |
10327 | 0 | tok2->insertToken("}"); |
10328 | 0 | tok2 = tok2->next(); |
10329 | 0 | Token::createMutualLinks(links.top(), tok2); |
10330 | 0 | links.pop(); |
10331 | 0 | } |
10332 | 0 | } |
10333 | 0 | } |
10334 | 80.4k | } |
10335 | 1.36k | } |
10336 | | |
10337 | | void Tokenizer::simplifyCoroutines() |
10338 | 1.36k | { |
10339 | 1.36k | if (!isCPP() || mSettings->standards.cpp < Standards::CPP20) |
10340 | 0 | return; |
10341 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
10342 | 80.4k | if (!tok->isName() || !Token::Match(tok, "co_return|co_yield|co_await")) |
10343 | 80.4k | continue; |
10344 | 0 | Token *end = tok->next(); |
10345 | 0 | while (end && end->str() != ";") { |
10346 | 0 | if (Token::Match(end, "[({[]")) |
10347 | 0 | end = end->link(); |
10348 | 0 | else if (Token::Match(end, "[)]}]")) |
10349 | 0 | break; |
10350 | 0 | end = end->next(); |
10351 | 0 | } |
10352 | 0 | if (Token::simpleMatch(end, ";")) { |
10353 | 0 | tok->insertToken("("); |
10354 | 0 | end->previous()->insertToken(")"); |
10355 | 0 | Token::createMutualLinks(tok->next(), end->previous()); |
10356 | 0 | } |
10357 | 0 | } |
10358 | 1.36k | } |
10359 | | |
10360 | | static bool sameTokens(const Token *first, const Token *last, const Token *other) |
10361 | 0 | { |
10362 | 0 | while (other && first->str() == other->str()) { |
10363 | 0 | if (first == last) |
10364 | 0 | return true; |
10365 | 0 | first = first->next(); |
10366 | 0 | other = other->next(); |
10367 | 0 | } |
10368 | | |
10369 | 0 | return false; |
10370 | 0 | } |
10371 | | |
10372 | | static bool alreadyHasNamespace(const Token *first, const Token *last, const Token *end) |
10373 | 0 | { |
10374 | 0 | while (end && last->str() == end->str()) { |
10375 | 0 | if (first == last) |
10376 | 0 | return true; |
10377 | 0 | last = last->previous(); |
10378 | 0 | end = end->previous(); |
10379 | 0 | } |
10380 | | |
10381 | 0 | return false; |
10382 | 0 | } |
10383 | | |
10384 | | static Token * deleteAlias(Token * tok) |
10385 | 0 | { |
10386 | 0 | Token::eraseTokens(tok, Token::findsimplematch(tok, ";")); |
10387 | | |
10388 | | // delete first token |
10389 | 0 | tok->deleteThis(); |
10390 | | |
10391 | | // delete ';' if not last token |
10392 | 0 | tok->deleteThis(); |
10393 | |
|
10394 | 0 | return tok; |
10395 | 0 | } |
10396 | | |
10397 | | void Tokenizer::simplifyNamespaceAliases() |
10398 | 1.36k | { |
10399 | 1.36k | if (!isCPP()) |
10400 | 0 | return; |
10401 | | |
10402 | 1.36k | int scope = 0; |
10403 | | |
10404 | 81.8k | for (Token *tok = list.front(); tok; tok = tok->next()) { |
10405 | 80.4k | bool isPrev{}; |
10406 | 80.4k | if (tok->str() == "{") |
10407 | 3.58k | scope++; |
10408 | 76.9k | else if (tok->str() == "}") |
10409 | 3.58k | scope--; |
10410 | 73.3k | else if (Token::Match(tok, "namespace %name% =") || (isPrev = Token::Match(tok->previous(), "namespace %name% ="))) { |
10411 | 0 | if (isPrev) |
10412 | 0 | tok = tok->previous(); |
10413 | 0 | const std::string name(tok->next()->str()); |
10414 | 0 | Token * tokNameStart = tok->tokAt(3); |
10415 | 0 | Token * tokNameEnd = tokNameStart; |
10416 | |
|
10417 | 0 | while (tokNameEnd && tokNameEnd->next() && tokNameEnd->next()->str() != ";") { |
10418 | 0 | if (tokNameEnd->str() == "(") { |
10419 | 0 | if (tokNameEnd->previous()->isName()) |
10420 | 0 | unknownMacroError(tokNameEnd->previous()); |
10421 | 0 | else |
10422 | 0 | syntaxError(tokNameEnd); |
10423 | 0 | } |
10424 | 0 | tokNameEnd = tokNameEnd->next(); |
10425 | 0 | } |
10426 | |
|
10427 | 0 | if (!tokNameEnd) |
10428 | 0 | return; // syntax error |
10429 | | |
10430 | 0 | int endScope = scope; |
10431 | 0 | Token * tokLast = tokNameEnd->next(); |
10432 | 0 | Token * tokNext = tokLast->next(); |
10433 | 0 | Token * tok2 = tokNext; |
10434 | |
|
10435 | 0 | while (tok2 && endScope >= scope) { |
10436 | 0 | if (Token::simpleMatch(tok2, "{")) |
10437 | 0 | endScope++; |
10438 | 0 | else if (Token::simpleMatch(tok2, "}")) |
10439 | 0 | endScope--; |
10440 | 0 | else if (tok2->str() == name) { |
10441 | 0 | if (Token::Match(tok2->previous(), "namespace %name% =")) { |
10442 | | // check for possible duplicate aliases |
10443 | 0 | if (sameTokens(tokNameStart, tokNameEnd, tok2->tokAt(2))) { |
10444 | | // delete duplicate |
10445 | 0 | tok2 = deleteAlias(tok2->previous()); |
10446 | 0 | continue; |
10447 | 0 | } |
10448 | | // conflicting declaration (syntax error) |
10449 | | // cppcheck-suppress duplicateBranch - remove when TODO below is addressed |
10450 | 0 | if (endScope == scope) { |
10451 | | // delete conflicting declaration |
10452 | 0 | tok2 = deleteAlias(tok2->previous()); |
10453 | 0 | } |
10454 | | |
10455 | | // new declaration |
10456 | 0 | else { |
10457 | | // TODO: use the new alias in this scope |
10458 | 0 | tok2 = deleteAlias(tok2->previous()); |
10459 | 0 | } |
10460 | 0 | continue; |
10461 | 0 | } |
10462 | | |
10463 | 0 | if (tok2->strAt(1) == "::" && !alreadyHasNamespace(tokNameStart, tokNameEnd, tok2)) { |
10464 | 0 | tok2->str(tokNameStart->str()); |
10465 | 0 | Token * tok3 = tokNameStart; |
10466 | 0 | while (tok3 != tokNameEnd) { |
10467 | 0 | tok2->insertToken(tok3->next()->str()); |
10468 | 0 | tok2 = tok2->next(); |
10469 | 0 | tok3 = tok3->next(); |
10470 | 0 | } |
10471 | 0 | } |
10472 | 0 | } |
10473 | 0 | tok2 = tok2->next(); |
10474 | 0 | } |
10475 | |
|
10476 | 0 | if (tok->previous() && tokNext) { |
10477 | 0 | Token::eraseTokens(tok->previous(), tokNext); |
10478 | 0 | tok = tokNext->previous(); |
10479 | 0 | } else if (tok->previous()) { |
10480 | 0 | Token::eraseTokens(tok->previous(), tokLast); |
10481 | 0 | tok = tokLast; |
10482 | 0 | } else if (tokNext) { |
10483 | 0 | Token::eraseTokens(tok, tokNext); |
10484 | 0 | tok->deleteThis(); |
10485 | 0 | } else { |
10486 | 0 | Token::eraseTokens(tok, tokLast); |
10487 | 0 | tok->deleteThis(); |
10488 | 0 | } |
10489 | 0 | } |
10490 | 80.4k | } |
10491 | 1.36k | } |
10492 | | |
10493 | | bool Tokenizer::hasIfdef(const Token *start, const Token *end) const |
10494 | 2.00k | { |
10495 | 2.00k | assert(mPreprocessor); |
10496 | | |
10497 | 0 | return std::any_of(mPreprocessor->getDirectives().cbegin(), mPreprocessor->getDirectives().cend(), [&](const Directive& d) { |
10498 | 0 | return startsWith(d.str, "#if") && |
10499 | 0 | d.linenr >= start->linenr() && |
10500 | 0 | d.linenr <= end->linenr() && |
10501 | 0 | start->fileIndex() < list.getFiles().size() && |
10502 | 0 | d.file == list.getFiles()[start->fileIndex()]; |
10503 | 0 | }); |
10504 | 2.00k | } |