/src/cppcheck/lib/tokenlist.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 "tokenlist.h" |
21 | | |
22 | | #include "astutils.h" |
23 | | #include "errorlogger.h" |
24 | | #include "errortypes.h" |
25 | | #include "keywords.h" |
26 | | #include "library.h" |
27 | | #include "mathlib.h" |
28 | | #include "path.h" |
29 | | #include "platform.h" |
30 | | #include "settings.h" |
31 | | #include "standards.h" |
32 | | #include "token.h" |
33 | | |
34 | | #include <algorithm> |
35 | | #include <cctype> |
36 | | #include <exception> |
37 | | #include <functional> |
38 | | #include <utility> |
39 | | #include <set> |
40 | | #include <stack> |
41 | | #include <unordered_set> |
42 | | |
43 | | #include <simplecpp.h> |
44 | | |
45 | | // How many compileExpression recursions are allowed? |
46 | | // For practical code this could be endless. But in some special torture test |
47 | | // there needs to be a limit. |
48 | | static const int AST_MAX_DEPTH = 150; |
49 | | |
50 | | |
51 | | TokenList::TokenList(const Settings* settings) : |
52 | | mSettings(settings) |
53 | 1.36k | { |
54 | 1.36k | mTokensFrontBack.list = this; |
55 | 1.36k | } |
56 | | |
57 | | TokenList::~TokenList() |
58 | 1.36k | { |
59 | 1.36k | deallocateTokens(); |
60 | 1.36k | } |
61 | | |
62 | | //--------------------------------------------------------------------------- |
63 | | |
64 | | const std::string& TokenList::getSourceFilePath() const |
65 | 75.5k | { |
66 | 75.5k | if (getFiles().empty()) { |
67 | 0 | return emptyString; |
68 | 0 | } |
69 | 75.5k | return getFiles()[0]; |
70 | 75.5k | } |
71 | | |
72 | | //--------------------------------------------------------------------------- |
73 | | |
74 | | // Deallocate lists.. |
75 | | void TokenList::deallocateTokens() |
76 | 1.36k | { |
77 | 1.36k | deleteTokens(mTokensFrontBack.front); |
78 | 1.36k | mTokensFrontBack.front = nullptr; |
79 | 1.36k | mTokensFrontBack.back = nullptr; |
80 | 1.36k | mFiles.clear(); |
81 | 1.36k | } |
82 | | |
83 | | void TokenList::determineCppC() |
84 | 1.36k | { |
85 | 1.36k | if (!mSettings) { |
86 | 0 | mIsC = Path::isC(getSourceFilePath()); |
87 | 0 | mIsCpp = Path::isCPP(getSourceFilePath()); |
88 | 1.36k | } else { |
89 | 1.36k | mIsC = mSettings->enforcedLang == Settings::Language::C || (mSettings->enforcedLang == Settings::Language::None && Path::isC(getSourceFilePath())); |
90 | 1.36k | mIsCpp = mSettings->enforcedLang == Settings::Language::CPP || (mSettings->enforcedLang == Settings::Language::None && Path::isCPP(getSourceFilePath())); |
91 | 1.36k | } |
92 | 1.36k | } |
93 | | |
94 | | int TokenList::appendFileIfNew(std::string fileName) |
95 | 0 | { |
96 | | // Has this file been tokenized already? |
97 | 0 | for (int i = 0; i < mFiles.size(); ++i) |
98 | 0 | if (Path::sameFileName(mFiles[i], fileName)) |
99 | 0 | return i; |
100 | | |
101 | | // The "mFiles" vector remembers what files have been tokenized.. |
102 | 0 | mFiles.push_back(std::move(fileName)); |
103 | | |
104 | | // Update mIsC and mIsCpp properties |
105 | 0 | if (mFiles.size() == 1) { // Update only useful if first file added to _files |
106 | 0 | determineCppC(); |
107 | 0 | } |
108 | 0 | return mFiles.size() - 1; |
109 | 0 | } |
110 | | |
111 | | void TokenList::clangSetOrigFiles() |
112 | 0 | { |
113 | 0 | mOrigFiles = mFiles; |
114 | 0 | } |
115 | | |
116 | | void TokenList::deleteTokens(Token *tok) |
117 | 1.36k | { |
118 | 93.6k | while (tok) { |
119 | 92.3k | Token *next = tok->next(); |
120 | 92.3k | delete tok; |
121 | 92.3k | tok = next; |
122 | 92.3k | } |
123 | 1.36k | } |
124 | | |
125 | | //--------------------------------------------------------------------------- |
126 | | // add a token. |
127 | | //--------------------------------------------------------------------------- |
128 | | |
129 | | void TokenList::addtoken(const std::string& str, const nonneg int lineno, const nonneg int column, const nonneg int fileno, bool split) |
130 | 0 | { |
131 | 0 | if (str.empty()) |
132 | 0 | return; |
133 | | |
134 | | // If token contains # characters, split it up |
135 | 0 | if (split) { |
136 | 0 | size_t begin = 0; |
137 | 0 | size_t end = 0; |
138 | 0 | while ((end = str.find("##", begin)) != std::string::npos) { |
139 | 0 | addtoken(str.substr(begin, end - begin), lineno, fileno, false); |
140 | 0 | addtoken("##", lineno, column, fileno, false); |
141 | 0 | begin = end+2; |
142 | 0 | } |
143 | 0 | if (begin != 0) { |
144 | 0 | addtoken(str.substr(begin), lineno, column, fileno, false); |
145 | 0 | return; |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | 0 | if (mTokensFrontBack.back) { |
150 | 0 | mTokensFrontBack.back->insertToken(str); |
151 | 0 | } else { |
152 | 0 | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
153 | 0 | mTokensFrontBack.back = mTokensFrontBack.front; |
154 | 0 | mTokensFrontBack.back->str(str); |
155 | 0 | } |
156 | |
|
157 | 0 | mTokensFrontBack.back->linenr(lineno); |
158 | 0 | mTokensFrontBack.back->column(column); |
159 | 0 | mTokensFrontBack.back->fileIndex(fileno); |
160 | 0 | } |
161 | | |
162 | | void TokenList::addtoken(const std::string& str, const Token *locationTok) |
163 | 0 | { |
164 | 0 | if (str.empty()) |
165 | 0 | return; |
166 | | |
167 | 0 | if (mTokensFrontBack.back) { |
168 | 0 | mTokensFrontBack.back->insertToken(str); |
169 | 0 | } else { |
170 | 0 | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
171 | 0 | mTokensFrontBack.back = mTokensFrontBack.front; |
172 | 0 | mTokensFrontBack.back->str(str); |
173 | 0 | } |
174 | |
|
175 | 0 | mTokensFrontBack.back->linenr(locationTok->linenr()); |
176 | 0 | mTokensFrontBack.back->column(locationTok->column()); |
177 | 0 | mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); |
178 | 0 | } |
179 | | |
180 | | void TokenList::addtoken(const Token * tok, const nonneg int lineno, const nonneg int column, const nonneg int fileno) |
181 | 0 | { |
182 | 0 | if (tok == nullptr) |
183 | 0 | return; |
184 | | |
185 | 0 | if (mTokensFrontBack.back) { |
186 | 0 | mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); |
187 | 0 | } else { |
188 | 0 | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
189 | 0 | mTokensFrontBack.back = mTokensFrontBack.front; |
190 | 0 | mTokensFrontBack.back->str(tok->str()); |
191 | 0 | if (!tok->originalName().empty()) |
192 | 0 | mTokensFrontBack.back->originalName(tok->originalName()); |
193 | 0 | } |
194 | |
|
195 | 0 | mTokensFrontBack.back->linenr(lineno); |
196 | 0 | mTokensFrontBack.back->column(column); |
197 | 0 | mTokensFrontBack.back->fileIndex(fileno); |
198 | 0 | mTokensFrontBack.back->flags(tok->flags()); |
199 | 0 | } |
200 | | |
201 | | void TokenList::addtoken(const Token *tok, const Token *locationTok) |
202 | 0 | { |
203 | 0 | if (tok == nullptr || locationTok == nullptr) |
204 | 0 | return; |
205 | | |
206 | 0 | if (mTokensFrontBack.back) { |
207 | 0 | mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); |
208 | 0 | } else { |
209 | 0 | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
210 | 0 | mTokensFrontBack.back = mTokensFrontBack.front; |
211 | 0 | mTokensFrontBack.back->str(tok->str()); |
212 | 0 | if (!tok->originalName().empty()) |
213 | 0 | mTokensFrontBack.back->originalName(tok->originalName()); |
214 | 0 | } |
215 | |
|
216 | 0 | mTokensFrontBack.back->flags(tok->flags()); |
217 | 0 | mTokensFrontBack.back->linenr(locationTok->linenr()); |
218 | 0 | mTokensFrontBack.back->column(locationTok->column()); |
219 | 0 | mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); |
220 | 0 | } |
221 | | |
222 | | void TokenList::addtoken(const Token *tok) |
223 | 0 | { |
224 | 0 | if (tok == nullptr) |
225 | 0 | return; |
226 | | |
227 | 0 | if (mTokensFrontBack.back) { |
228 | 0 | mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); |
229 | 0 | } else { |
230 | 0 | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
231 | 0 | mTokensFrontBack.back = mTokensFrontBack.front; |
232 | 0 | mTokensFrontBack.back->str(tok->str()); |
233 | 0 | if (!tok->originalName().empty()) |
234 | 0 | mTokensFrontBack.back->originalName(tok->originalName()); |
235 | 0 | } |
236 | |
|
237 | 0 | mTokensFrontBack.back->flags(tok->flags()); |
238 | 0 | mTokensFrontBack.back->linenr(tok->linenr()); |
239 | 0 | mTokensFrontBack.back->column(tok->column()); |
240 | 0 | mTokensFrontBack.back->fileIndex(tok->fileIndex()); |
241 | 0 | } |
242 | | |
243 | | |
244 | | //--------------------------------------------------------------------------- |
245 | | // copyTokens - Copy and insert tokens |
246 | | //--------------------------------------------------------------------------- |
247 | | |
248 | | Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last, bool one_line) |
249 | 0 | { |
250 | 0 | std::stack<Token *> links; |
251 | 0 | Token *tok2 = dest; |
252 | 0 | int linenr = dest->linenr(); |
253 | 0 | const int commonFileIndex = dest->fileIndex(); |
254 | 0 | for (const Token *tok = first; tok != last->next(); tok = tok->next()) { |
255 | 0 | tok2->insertToken(tok->str()); |
256 | 0 | tok2 = tok2->next(); |
257 | 0 | tok2->fileIndex(commonFileIndex); |
258 | 0 | tok2->linenr(linenr); |
259 | 0 | tok2->tokType(tok->tokType()); |
260 | 0 | tok2->flags(tok->flags()); |
261 | 0 | tok2->varId(tok->varId()); |
262 | 0 | tok2->setTokenDebug(tok->getTokenDebug()); |
263 | | |
264 | | // Check for links and fix them up |
265 | 0 | if (Token::Match(tok2, "(|[|{")) |
266 | 0 | links.push(tok2); |
267 | 0 | else if (Token::Match(tok2, ")|]|}")) { |
268 | 0 | if (links.empty()) |
269 | 0 | return tok2; |
270 | | |
271 | 0 | Token * link = links.top(); |
272 | |
|
273 | 0 | tok2->link(link); |
274 | 0 | link->link(tok2); |
275 | |
|
276 | 0 | links.pop(); |
277 | 0 | } |
278 | 0 | if (!one_line && tok->next()) |
279 | 0 | linenr += tok->next()->linenr() - tok->linenr(); |
280 | 0 | } |
281 | 0 | return tok2; |
282 | 0 | } |
283 | | |
284 | | //--------------------------------------------------------------------------- |
285 | | // InsertTokens - Copy and insert tokens |
286 | | //--------------------------------------------------------------------------- |
287 | | |
288 | | void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n) |
289 | 6.81k | { |
290 | 6.81k | std::stack<Token *> link; |
291 | | |
292 | 20.4k | while (n > 0) { |
293 | 13.6k | dest->insertToken(src->str(), src->originalName()); |
294 | 13.6k | dest = dest->next(); |
295 | | |
296 | | // Set links |
297 | 13.6k | if (Token::Match(dest, "(|[|{")) |
298 | 0 | link.push(dest); |
299 | 13.6k | else if (!link.empty() && Token::Match(dest, ")|]|}")) { |
300 | 0 | Token::createMutualLinks(dest, link.top()); |
301 | 0 | link.pop(); |
302 | 0 | } |
303 | | |
304 | 13.6k | dest->fileIndex(src->fileIndex()); |
305 | 13.6k | dest->linenr(src->linenr()); |
306 | 13.6k | dest->column(src->column()); |
307 | 13.6k | dest->varId(src->varId()); |
308 | 13.6k | dest->tokType(src->tokType()); |
309 | 13.6k | dest->flags(src->flags()); |
310 | 13.6k | src = src->next(); |
311 | 13.6k | --n; |
312 | 13.6k | } |
313 | 6.81k | } |
314 | | |
315 | | //--------------------------------------------------------------------------- |
316 | | // Tokenize - tokenizes a given file. |
317 | | //--------------------------------------------------------------------------- |
318 | | |
319 | | bool TokenList::createTokens(std::istream &code, const std::string& file0) |
320 | 0 | { |
321 | 0 | appendFileIfNew(file0); |
322 | |
|
323 | 0 | simplecpp::OutputList outputList; |
324 | 0 | simplecpp::TokenList tokens(code, mFiles, file0, &outputList); |
325 | |
|
326 | 0 | createTokens(std::move(tokens)); |
327 | |
|
328 | 0 | return outputList.empty(); |
329 | 0 | } |
330 | | |
331 | | //--------------------------------------------------------------------------- |
332 | | |
333 | | // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) |
334 | | void TokenList::createTokens(simplecpp::TokenList&& tokenList) |
335 | 1.36k | { |
336 | 1.36k | if (tokenList.cfront()) { |
337 | | // this is a copy |
338 | | // TODO: the same as TokenList.files - move that instead |
339 | | // TODO: this points to mFiles when called from createTokens(std::istream &, const std::string&) |
340 | 1.36k | mOrigFiles = mFiles = tokenList.cfront()->location.files; |
341 | 1.36k | } |
342 | 0 | else |
343 | 0 | mFiles.clear(); |
344 | | |
345 | 1.36k | determineCppC(); |
346 | | |
347 | 81.8k | for (const simplecpp::Token *tok = tokenList.cfront(); tok;) { |
348 | | |
349 | | // TODO: move from TokenList |
350 | 80.4k | std::string str = tok->str(); |
351 | | |
352 | | // Float literal |
353 | 80.4k | if (str.size() > 1 && str[0] == '.' && std::isdigit(str[1])) |
354 | 0 | str = '0' + str; |
355 | | |
356 | 80.4k | if (mTokensFrontBack.back) { |
357 | 79.1k | mTokensFrontBack.back->insertToken(str); |
358 | 79.1k | } else { |
359 | 1.36k | mTokensFrontBack.front = new Token(&mTokensFrontBack); |
360 | 1.36k | mTokensFrontBack.back = mTokensFrontBack.front; |
361 | 1.36k | mTokensFrontBack.back->str(str); |
362 | 1.36k | } |
363 | | |
364 | 80.4k | mTokensFrontBack.back->fileIndex(tok->location.fileIndex); |
365 | 80.4k | mTokensFrontBack.back->linenr(tok->location.line); |
366 | 80.4k | mTokensFrontBack.back->column(tok->location.col); |
367 | 80.4k | mTokensFrontBack.back->isExpandedMacro(!tok->macro.empty()); |
368 | | |
369 | 80.4k | tok = tok->next; |
370 | 80.4k | if (tok) |
371 | 79.1k | tokenList.deleteToken(tok->previous); |
372 | 80.4k | } |
373 | | |
374 | 1.36k | if (mSettings && mSettings->relativePaths) { |
375 | 0 | for (std::string & mFile : mFiles) |
376 | 0 | mFile = Path::getRelativePath(mFile, mSettings->basePaths); |
377 | 0 | } |
378 | | |
379 | 1.36k | Token::assignProgressValues(mTokensFrontBack.front); |
380 | 1.36k | } |
381 | | |
382 | | //--------------------------------------------------------------------------- |
383 | | |
384 | | std::size_t TokenList::calculateHash() const |
385 | 1.36k | { |
386 | 1.36k | std::string hashData; |
387 | 93.5k | for (const Token* tok = front(); tok; tok = tok->next()) { |
388 | 92.2k | hashData += std::to_string(tok->flags()); |
389 | 92.2k | hashData += std::to_string(tok->varId()); |
390 | 92.2k | hashData += std::to_string(tok->tokType()); |
391 | 92.2k | hashData += tok->str(); |
392 | 92.2k | hashData += tok->originalName(); |
393 | 92.2k | } |
394 | 1.36k | return (std::hash<std::string>{})(hashData); |
395 | 1.36k | } |
396 | | |
397 | | |
398 | | //--------------------------------------------------------------------------- |
399 | | |
400 | | struct AST_state { |
401 | | std::stack<Token*> op; |
402 | | int depth{}; |
403 | | int inArrayAssignment{}; |
404 | | bool cpp; |
405 | | int assign{}; |
406 | | bool inCase{}; // true from case to : |
407 | | bool stopAtColon{}; // help to properly parse ternary operators |
408 | | const Token* functionCallEndPar{}; |
409 | 18.4k | explicit AST_state(bool cpp) : cpp(cpp) {} |
410 | | }; |
411 | | |
412 | | static Token* skipDecl(Token* tok, std::vector<Token*>* inner = nullptr) |
413 | 23.1k | { |
414 | 23.1k | auto isDecltypeFuncParam = [](const Token* tok) -> bool { |
415 | 0 | if (!Token::simpleMatch(tok, ")")) |
416 | 0 | return false; |
417 | 0 | tok = tok->next(); |
418 | 0 | while (Token::Match(tok, "*|&|&&|const")) |
419 | 0 | tok = tok->next(); |
420 | 0 | if (Token::simpleMatch(tok, "(")) |
421 | 0 | tok = tok->link()->next(); |
422 | 0 | return Token::Match(tok, "%name%| ,|)"); |
423 | 0 | }; |
424 | | |
425 | 23.1k | if (!Token::Match(tok->previous(), "( %name%")) |
426 | 21.9k | return tok; |
427 | 1.15k | Token *vartok = tok; |
428 | 2.15k | while (Token::Match(vartok, "%name%|*|&|::|<")) { |
429 | 1.36k | if (vartok->str() == "<") { |
430 | 112 | if (vartok->link()) |
431 | 2 | vartok = vartok->link(); |
432 | 110 | else |
433 | 110 | return tok; |
434 | 1.25k | } else if (Token::Match(vartok, "%var% [:=(]")) { |
435 | 255 | return vartok; |
436 | 997 | } else if (Token::Match(vartok, "decltype|typeof (") && !isDecltypeFuncParam(tok->linkAt(1))) { |
437 | 0 | if (inner) |
438 | 0 | inner->push_back(vartok->tokAt(2)); |
439 | 0 | return vartok->linkAt(1)->next(); |
440 | 0 | } |
441 | 999 | vartok = vartok->next(); |
442 | 999 | } |
443 | 794 | return tok; |
444 | 1.15k | } |
445 | | |
446 | | static bool iscast(const Token *tok, bool cpp) |
447 | 39.6k | { |
448 | 39.6k | if (!Token::Match(tok, "( ::| %name%")) |
449 | 38.4k | return false; |
450 | | |
451 | 1.26k | if (Token::simpleMatch(tok->link(), ") ( )")) |
452 | 0 | return false; |
453 | | |
454 | 1.26k | if (tok->previous() && tok->previous()->isName() && tok->previous()->str() != "return" && |
455 | 1.26k | (!cpp || !Token::Match(tok->previous(), "delete|throw"))) |
456 | 651 | return false; |
457 | | |
458 | 616 | if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) |
459 | 0 | return false; |
460 | | |
461 | 616 | if (Token::Match(tok, "( (| typeof (") && Token::Match(tok->link(), ") %num%")) |
462 | 0 | return true; |
463 | | |
464 | 616 | if (Token::Match(tok->link(), ") }|)|]|;")) |
465 | 183 | return false; |
466 | | |
467 | 433 | if (Token::Match(tok->link(), ") %cop%") && !Token::Match(tok->link(), ") [&*+-~!]")) |
468 | 230 | return false; |
469 | | |
470 | 203 | if (Token::Match(tok->previous(), "= ( %name% ) {") && tok->next()->varId() == 0) |
471 | 0 | return true; |
472 | | |
473 | 203 | bool type = false; |
474 | 255 | for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { |
475 | 255 | if (tok2->varId() != 0) |
476 | 155 | return false; |
477 | 100 | if (cpp && !type && tok2->str() == "new") |
478 | 0 | return false; |
479 | | |
480 | 100 | while (tok2->link() && Token::Match(tok2, "(|[|<")) |
481 | 0 | tok2 = tok2->link()->next(); |
482 | | |
483 | 100 | if (tok2->str() == ")") { |
484 | 40 | if (Token::simpleMatch(tok2, ") (") && Token::simpleMatch(tok2->linkAt(1), ") .")) |
485 | 0 | return true; |
486 | 40 | if (Token::simpleMatch(tok2, ") {") && !type) { |
487 | 0 | const Token *tok3 = tok2->linkAt(1); |
488 | 0 | while (tok3 != tok2 && Token::Match(tok3, "[{}]")) |
489 | 0 | tok3 = tok3->previous(); |
490 | 0 | return tok3->str() != ";"; |
491 | 0 | } |
492 | 40 | const bool res = type || tok2->strAt(-1) == "*" || Token::simpleMatch(tok2, ") ~") || |
493 | 40 | (Token::Match(tok2, ") %any%") && |
494 | 40 | (!tok2->next()->isOp() || Token::Match(tok2->next(), "!|~|++|--")) && |
495 | 40 | !Token::Match(tok2->next(), "[[]);,?:.]")); |
496 | 40 | return res; |
497 | 40 | } |
498 | | |
499 | 60 | if (Token::Match(tok2, "&|&& )")) |
500 | 0 | return true; |
501 | | |
502 | 60 | if (!Token::Match(tok2, "%name%|*|::")) |
503 | 8 | return false; |
504 | | |
505 | 52 | if (tok2->isStandardType() && (tok2->next()->str() != "(" || Token::Match(tok2->next(), "( * *| )"))) |
506 | 0 | type = true; |
507 | 52 | } |
508 | | |
509 | 0 | return false; |
510 | 203 | } |
511 | | |
512 | | // int(1), int*(2), .. |
513 | | static Token * findCppTypeInitPar(Token *tok) |
514 | 23.1k | { |
515 | 23.1k | if (!tok || !Token::Match(tok->previous(), "[,()] %name%")) |
516 | 21.9k | return nullptr; |
517 | 1.15k | bool istype = false; |
518 | 2.32k | while (Token::Match(tok, "%name%|::|<")) { |
519 | 1.26k | if (tok->str() == "<") { |
520 | 108 | tok = tok->link(); |
521 | 108 | if (!tok) |
522 | 106 | return nullptr; |
523 | 108 | } |
524 | 1.16k | istype |= tok->isStandardType(); |
525 | 1.16k | tok = tok->next(); |
526 | 1.16k | } |
527 | 1.05k | if (!istype) |
528 | 1.05k | return nullptr; |
529 | 0 | if (!Token::Match(tok, "[*&]")) |
530 | 0 | return nullptr; |
531 | 0 | while (Token::Match(tok, "[*&]")) |
532 | 0 | tok = tok->next(); |
533 | 0 | return (tok && tok->str() == "(") ? tok : nullptr; |
534 | 0 | } |
535 | | |
536 | | // X{} X<Y>{} etc |
537 | | static bool iscpp11init_impl(const Token * const tok); |
538 | | static bool iscpp11init(const Token * const tok) |
539 | 28.5k | { |
540 | 28.5k | if (tok->isCpp11init() == TokenImpl::Cpp11init::UNKNOWN) |
541 | 26.6k | tok->setCpp11init(iscpp11init_impl(tok)); |
542 | 28.5k | return tok->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; |
543 | 28.5k | } |
544 | | |
545 | | static bool iscpp11init_impl(const Token * const tok) |
546 | 26.6k | { |
547 | 26.6k | if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->link()->previous(), "; }")) |
548 | 2.10k | return false; |
549 | 24.5k | const Token *nameToken = tok; |
550 | 26.0k | while (nameToken && nameToken->str() == "{") { |
551 | 1.47k | if (nameToken->isCpp11init() != TokenImpl::Cpp11init::UNKNOWN) |
552 | 0 | return nameToken->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; |
553 | 1.47k | nameToken = nameToken->previous(); |
554 | 1.47k | if (nameToken && nameToken->str() == "," && Token::simpleMatch(nameToken->previous(), "} ,")) |
555 | 0 | nameToken = nameToken->linkAt(-1); |
556 | 1.47k | } |
557 | 24.5k | if (!nameToken) |
558 | 0 | return false; |
559 | 24.5k | if (nameToken->str() == ")" && Token::simpleMatch(nameToken->link()->previous(), "decltype (") && |
560 | 24.5k | !Token::simpleMatch(nameToken->link()->tokAt(-2), ".")) |
561 | 0 | nameToken = nameToken->link()->previous(); |
562 | 24.5k | if (Token::simpleMatch(nameToken, ", {")) |
563 | 0 | return true; |
564 | 24.5k | if (nameToken->str() == ">" && nameToken->link()) |
565 | 0 | nameToken = nameToken->link()->previous(); |
566 | 24.5k | if (Token::Match(nameToken, "]|*")) { |
567 | 0 | const Token* newTok = nameToken->link() ? nameToken->link()->previous() : nameToken->previous(); |
568 | 0 | while (Token::Match(newTok, "%type%|::|*") && !newTok->isKeyword()) |
569 | 0 | newTok = newTok->previous(); |
570 | 0 | if (Token::simpleMatch(newTok, "new")) |
571 | 0 | return true; |
572 | 0 | } |
573 | | |
574 | 24.5k | auto isCaseStmt = [](const Token* colonTok) { |
575 | 437 | if (!Token::Match(colonTok->tokAt(-1), "%name%|%num%|%char% :")) |
576 | 437 | return false; |
577 | 0 | const Token* caseTok = colonTok->tokAt(-2); |
578 | 0 | while (caseTok && Token::Match(caseTok->tokAt(-1), "::|%name%")) |
579 | 0 | caseTok = caseTok->tokAt(-1); |
580 | 0 | return Token::simpleMatch(caseTok, "case"); |
581 | 437 | }; |
582 | | |
583 | 24.5k | const Token *endtok = nullptr; |
584 | 24.5k | if (Token::Match(nameToken, "%name%|return|: {") && !isCaseStmt(nameToken) && |
585 | 24.5k | (!Token::simpleMatch(nameToken->tokAt(2), "[") || findLambdaEndScope(nameToken->tokAt(2)))) |
586 | 437 | endtok = nameToken->linkAt(1); |
587 | 24.1k | else if (Token::Match(nameToken,"%name% <") && Token::simpleMatch(nameToken->linkAt(1),"> {")) |
588 | 0 | endtok = nameToken->linkAt(1)->linkAt(1); |
589 | 24.1k | else if (Token::Match(nameToken->previous(), "%name%|> ( {")) |
590 | 0 | endtok = nameToken->linkAt(1); |
591 | 24.1k | else if (Token::simpleMatch(nameToken, "decltype") && nameToken->linkAt(1)) |
592 | 0 | endtok = nameToken->linkAt(1)->linkAt(1); |
593 | 24.1k | else |
594 | 24.1k | return false; |
595 | 437 | if (Token::Match(nameToken, "else|try|do|const|constexpr|override|volatile|&|&&")) |
596 | 437 | return false; |
597 | 0 | if (Token::simpleMatch(nameToken->previous(), ". void {") && nameToken->previous()->originalName() == "->") |
598 | 0 | return false; // trailing return type. The only function body that can contain no semicolon is a void function. |
599 | 0 | if (Token::simpleMatch(nameToken->previous(), "namespace") || Token::simpleMatch(nameToken, "namespace") /*anonymous namespace*/) |
600 | 0 | return false; |
601 | 0 | if (precedes(nameToken->next(), endtok) && !Token::Match(nameToken, "return|:")) { |
602 | | // If there is semicolon between {..} this is not a initlist |
603 | 0 | for (const Token *tok2 = nameToken->next(); tok2 != endtok; tok2 = tok2->next()) { |
604 | 0 | if (tok2->str() == ";") |
605 | 0 | return false; |
606 | 0 | const Token * lambdaEnd = findLambdaEndScope(tok2); |
607 | 0 | if (lambdaEnd) |
608 | 0 | tok2 = lambdaEnd; |
609 | 0 | } |
610 | 0 | } |
611 | | // There is no initialisation for example here: 'class Fred {};' |
612 | 0 | if (!Token::simpleMatch(endtok, "} ;")) |
613 | 0 | return true; |
614 | 0 | const Token *prev = nameToken; |
615 | 0 | while (Token::Match(prev, "%name%|::|:|<|>|(|)|,|%num%|%cop%|...")) { |
616 | 0 | if (Token::Match(prev, "class|struct|union|enum")) |
617 | 0 | return false; |
618 | | |
619 | 0 | prev = prev->previous(); |
620 | 0 | } |
621 | 0 | return true; |
622 | 0 | } |
623 | | |
624 | | static bool isQualifier(const Token* tok) |
625 | 508 | { |
626 | 1.01k | while (Token::Match(tok, "&|&&|*")) |
627 | 508 | tok = tok->next(); |
628 | 508 | return Token::Match(tok, "{|;"); |
629 | 508 | } |
630 | | |
631 | | static void compileUnaryOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, AST_state& state)) |
632 | 4.13k | { |
633 | 4.13k | Token *unaryop = tok; |
634 | 4.13k | if (f) { |
635 | 4.13k | tok = tok->next(); |
636 | 4.13k | state.depth++; |
637 | 4.13k | if (state.depth > AST_MAX_DEPTH) |
638 | 0 | throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); |
639 | 4.13k | if (tok) |
640 | 4.13k | f(tok, state); |
641 | 4.13k | state.depth--; |
642 | 4.13k | } |
643 | | |
644 | 4.13k | if (!state.op.empty()) { |
645 | 4.13k | unaryop->astOperand1(state.op.top()); |
646 | 4.13k | state.op.pop(); |
647 | 4.13k | } |
648 | 4.13k | state.op.push(unaryop); |
649 | 4.13k | } |
650 | | |
651 | | static void compileBinOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, AST_state& state)) |
652 | 14.2k | { |
653 | 14.2k | Token *binop = tok; |
654 | 14.2k | if (f) { |
655 | 12.8k | tok = tok->next(); |
656 | 12.8k | if (Token::Match(binop, "::|. ~")) |
657 | 0 | tok = tok->next(); |
658 | 12.8k | state.depth++; |
659 | 12.8k | if (tok && state.depth <= AST_MAX_DEPTH) |
660 | 12.8k | f(tok, state); |
661 | 12.8k | if (state.depth > AST_MAX_DEPTH) |
662 | 0 | throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); |
663 | 12.8k | state.depth--; |
664 | 12.8k | } |
665 | | |
666 | | // TODO: Should we check if op is empty. |
667 | | // * Is it better to add assertion that it isn't? |
668 | | // * Write debug warning if it's empty? |
669 | 14.2k | if (!state.op.empty()) { |
670 | 14.2k | binop->astOperand2(state.op.top()); |
671 | 14.2k | state.op.pop(); |
672 | 14.2k | } |
673 | 14.2k | if (!state.op.empty()) { |
674 | 14.2k | binop->astOperand1(state.op.top()); |
675 | 14.2k | state.op.pop(); |
676 | 14.2k | } |
677 | 14.2k | state.op.push(binop); |
678 | 14.2k | } |
679 | | |
680 | | static void compileExpression(Token *&tok, AST_state& state); |
681 | | |
682 | | static void compileTerm(Token *&tok, AST_state& state) |
683 | 37.5k | { |
684 | 37.5k | if (!tok) |
685 | 0 | return; |
686 | 37.5k | if (Token::Match(tok, "L %str%|%char%")) |
687 | 0 | tok = tok->next(); |
688 | 37.5k | if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] . %name%")) { // Jump over . in C style struct initialization |
689 | 0 | state.op.push(tok); |
690 | 0 | tok->astOperand1(tok->next()); |
691 | 0 | tok = tok->tokAt(2); |
692 | 0 | } |
693 | 37.5k | if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] [ %num%|%name% ]")) { |
694 | 0 | state.op.push(tok); |
695 | 0 | tok->astOperand1(tok->next()); |
696 | 0 | tok = tok->tokAt(3); |
697 | 0 | } |
698 | 37.5k | if (tok->isLiteral()) { |
699 | 9.58k | state.op.push(tok); |
700 | 9.58k | do { |
701 | 9.58k | tok = tok->next(); |
702 | 9.58k | } while (Token::Match(tok, "%name%|%str%")); |
703 | 27.9k | } else if (tok->isName()) { |
704 | 25.0k | if (Token::Match(tok, "return|case") || (state.cpp && tok->str() == "throw")) { |
705 | 1.91k | if (tok->str() == "case") |
706 | 0 | state.inCase = true; |
707 | 1.91k | const bool tokIsReturn = tok->str() == "return"; |
708 | 1.91k | const bool stopAtColon = state.stopAtColon; |
709 | 1.91k | state.stopAtColon=true; |
710 | 1.91k | compileUnaryOp(tok, state, compileExpression); |
711 | 1.91k | state.stopAtColon=stopAtColon; |
712 | 1.91k | if (tokIsReturn) |
713 | 1.91k | state.op.pop(); |
714 | 1.91k | if (state.inCase && Token::simpleMatch(tok, ": ;")) { |
715 | 0 | state.inCase = false; |
716 | 0 | tok = tok->next(); |
717 | 0 | } |
718 | 23.1k | } else if (Token::Match(tok, "sizeof !!(")) { |
719 | 0 | compileUnaryOp(tok, state, compileExpression); |
720 | 0 | state.op.pop(); |
721 | 23.1k | } else if (state.cpp && findCppTypeInitPar(tok)) { // int(0), int*(123), .. |
722 | 0 | tok = findCppTypeInitPar(tok); |
723 | 0 | state.op.push(tok); |
724 | 0 | tok = tok->tokAt(2); |
725 | 23.1k | } else if (state.cpp && iscpp11init(tok)) { // X{} X<Y>{} etc |
726 | 0 | state.op.push(tok); |
727 | 0 | tok = tok->next(); |
728 | 0 | if (tok->str() == "<") |
729 | 0 | tok = tok->link()->next(); |
730 | |
|
731 | 0 | if (Token::Match(tok, "{ . %name% =|{")) { |
732 | 0 | const Token* end = tok->link(); |
733 | 0 | const int inArrayAssignment = state.inArrayAssignment; |
734 | 0 | state.inArrayAssignment = 1; |
735 | 0 | compileBinOp(tok, state, compileExpression); |
736 | 0 | state.inArrayAssignment = inArrayAssignment; |
737 | 0 | if (tok == end) |
738 | 0 | tok = tok->next(); |
739 | 0 | else |
740 | 0 | throw InternalError(tok, "Syntax error. Unexpected tokens in designated initializer.", InternalError::AST); |
741 | 0 | } |
742 | 23.1k | } else if (!state.cpp || !Token::Match(tok, "new|delete %name%|*|&|::|(|[")) { |
743 | 23.1k | std::vector<Token*> inner; |
744 | 23.1k | tok = skipDecl(tok, &inner); |
745 | 23.1k | for (Token* tok3 : inner) { |
746 | 0 | AST_state state1(state.cpp); |
747 | 0 | compileExpression(tok3, state1); |
748 | 0 | } |
749 | 23.1k | bool repeat = true; |
750 | 47.5k | while (repeat) { |
751 | 24.4k | repeat = false; |
752 | 24.4k | if (Token::Match(tok->next(), "%name%")) { |
753 | 1.36k | tok = tok->next(); |
754 | 1.36k | repeat = true; |
755 | 1.36k | } |
756 | 24.4k | if (Token::simpleMatch(tok->next(), "<") && Token::Match(tok->linkAt(1), "> %name%")) { |
757 | 3 | tok = tok->next()->link()->next(); |
758 | 3 | repeat = true; |
759 | 3 | } |
760 | 24.4k | } |
761 | 23.1k | state.op.push(tok); |
762 | 23.1k | if (Token::Match(tok, "%name% <") && tok->linkAt(1)) |
763 | 1 | tok = tok->linkAt(1); |
764 | 23.0k | else if (Token::Match(tok, "%name% ...") || (state.op.size() == 1 && state.depth == 0 && Token::Match(tok->tokAt(-3), "!!& ) ( %name% ) ="))) |
765 | 0 | tok = tok->next(); |
766 | 23.1k | tok = tok->next(); |
767 | 23.1k | if (Token::Match(tok, "%str%")) { |
768 | 0 | while (Token::Match(tok, "%name%|%str%")) |
769 | 0 | tok = tok->next(); |
770 | 0 | } |
771 | 23.1k | if (Token::Match(tok, "%name% %assign%")) |
772 | 0 | tok = tok->next(); |
773 | 23.1k | } |
774 | 25.0k | } else if (tok->str() == "{") { |
775 | 0 | const Token *prev = tok->previous(); |
776 | 0 | if (Token::simpleMatch(prev, ") {") && iscast(prev->link(), state.cpp)) |
777 | 0 | prev = prev->link()->previous(); |
778 | 0 | if (Token::simpleMatch(tok->link(),"} [")) { |
779 | 0 | tok = tok->next(); |
780 | 0 | } else if ((state.cpp && iscpp11init(tok)) || Token::simpleMatch(tok->previous(), "] {")) { |
781 | 0 | Token *const end = tok->link(); |
782 | 0 | if (state.op.empty() || Token::Match(tok->previous(), "[{,]") || Token::Match(tok->tokAt(-2), "%name% (")) { |
783 | 0 | if (Token::Match(tok, "{ . %name% =|{")) { |
784 | 0 | const int inArrayAssignment = state.inArrayAssignment; |
785 | 0 | state.inArrayAssignment = 1; |
786 | 0 | compileBinOp(tok, state, compileExpression); |
787 | 0 | state.inArrayAssignment = inArrayAssignment; |
788 | 0 | } else if (Token::simpleMatch(tok, "{ }")) { |
789 | 0 | state.op.push(tok); |
790 | 0 | tok = tok->next(); |
791 | 0 | } else { |
792 | 0 | compileUnaryOp(tok, state, compileExpression); |
793 | 0 | if (precedes(tok,end)) // typically for something like `MACRO(x, { if (c) { ... } })`, where end is the last curly, and tok is the open curly for the if |
794 | 0 | tok = end; |
795 | 0 | } |
796 | 0 | } else |
797 | 0 | compileBinOp(tok, state, compileExpression); |
798 | 0 | if (tok != end) |
799 | 0 | throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST); |
800 | 0 | if (tok->next()) |
801 | 0 | tok = tok->next(); |
802 | 0 | } else if (state.cpp && Token::Match(tok->tokAt(-2), "%name% ( {") && !Token::findsimplematch(tok, ";", tok->link())) { |
803 | 0 | if (Token::simpleMatch(tok, "{ }")) |
804 | 0 | tok = tok->tokAt(2); |
805 | 0 | else { |
806 | 0 | Token *tok1 = tok; |
807 | 0 | state.inArrayAssignment++; |
808 | 0 | compileUnaryOp(tok, state, compileExpression); |
809 | 0 | state.inArrayAssignment--; |
810 | 0 | tok = tok1->link()->next(); |
811 | 0 | } |
812 | 0 | } else if (!state.inArrayAssignment && !Token::simpleMatch(prev, "=")) { |
813 | 0 | state.op.push(tok); |
814 | 0 | tok = tok->link()->next(); |
815 | 0 | } else { |
816 | 0 | if (tok->link() != tok->next()) { |
817 | 0 | state.inArrayAssignment++; |
818 | 0 | compileUnaryOp(tok, state, compileExpression); |
819 | 0 | if (Token::Match(tok, "} [,};]") && state.inArrayAssignment > 0) { |
820 | 0 | tok = tok->next(); |
821 | 0 | state.inArrayAssignment--; |
822 | 0 | } |
823 | 0 | } else { |
824 | 0 | state.op.push(tok); |
825 | 0 | tok = tok->tokAt(2); |
826 | 0 | } |
827 | 0 | } |
828 | 0 | } |
829 | 37.5k | } |
830 | | |
831 | | static void compileScope(Token *&tok, AST_state& state) |
832 | 37.5k | { |
833 | 37.5k | compileTerm(tok, state); |
834 | 37.5k | while (tok) { |
835 | 37.5k | if (tok->str() == "::") { |
836 | 0 | const Token *lastOp = state.op.empty() ? nullptr : state.op.top(); |
837 | 0 | if (Token::Match(lastOp, ":: %name%")) |
838 | 0 | lastOp = lastOp->next(); |
839 | 0 | if (Token::Match(lastOp, "%name%") && |
840 | 0 | (lastOp->next() == tok || (Token::Match(lastOp, "%name% <") && lastOp->linkAt(1) && tok == lastOp->linkAt(1)->next()))) |
841 | 0 | compileBinOp(tok, state, compileTerm); |
842 | 0 | else |
843 | 0 | compileUnaryOp(tok, state, compileTerm); |
844 | 37.5k | } else break; |
845 | 37.5k | } |
846 | 37.5k | } |
847 | | |
848 | | static bool isPrefixUnary(const Token* tok, bool cpp) |
849 | 4.94k | { |
850 | 4.94k | if (cpp && Token::simpleMatch(tok->previous(), "* [") && Token::simpleMatch(tok->link(), "] {")) { |
851 | 0 | for (const Token* prev = tok->previous(); Token::Match(prev, "%name%|::|*|&|>|>>"); prev = prev->previous()) { |
852 | 0 | if (Token::Match(prev, ">|>>")) { |
853 | 0 | if (!prev->link()) |
854 | 0 | break; |
855 | 0 | prev = prev->link(); |
856 | 0 | } |
857 | 0 | if (prev->str() == "new") |
858 | 0 | return false; |
859 | 0 | } |
860 | 0 | } |
861 | 4.94k | if (!tok->previous() |
862 | 4.94k | || ((Token::Match(tok->previous(), "(|[|{|%op%|;|?|:|,|.|return|::") || (cpp && tok->strAt(-1) == "throw")) |
863 | 4.94k | && (tok->previous()->tokType() != Token::eIncDecOp || tok->tokType() == Token::eIncDecOp))) |
864 | 3.58k | return true; |
865 | | |
866 | 1.35k | if (tok->previous()->str() == "}") { |
867 | 0 | const Token* parent = tok->linkAt(-1)->tokAt(-1); |
868 | 0 | return !Token::Match(parent, "%type%") || parent->isKeyword(); |
869 | 0 | } |
870 | | |
871 | 1.35k | if (tok->str() == "*" && tok->previous()->tokType() == Token::eIncDecOp && isPrefixUnary(tok->previous(), cpp)) |
872 | 0 | return true; |
873 | | |
874 | 1.35k | return tok->strAt(-1) == ")" && iscast(tok->linkAt(-1), cpp); |
875 | 1.35k | } |
876 | | |
877 | | static void compilePrecedence2(Token *&tok, AST_state& state) |
878 | 37.4k | { |
879 | 37.4k | auto doCompileScope = [&](const Token* tok) -> bool { |
880 | 37.4k | const bool isStartOfCpp11Init = state.cpp && tok && tok->str() == "{" && iscpp11init(tok); |
881 | 37.4k | if (isStartOfCpp11Init || Token::simpleMatch(tok, "(")) { |
882 | 694 | tok = tok->previous(); |
883 | 720 | while (Token::simpleMatch(tok, "*")) |
884 | 26 | tok = tok->previous(); |
885 | 694 | while (tok && Token::Match(tok->previous(), ":: %type%")) |
886 | 0 | tok = tok->tokAt(-2); |
887 | 694 | if (tok && !tok->isKeyword()) |
888 | 620 | tok = tok->previous(); |
889 | 694 | return !Token::Match(tok, "new ::| %type%"); |
890 | 694 | } |
891 | 36.7k | return !findLambdaEndTokenWithoutAST(tok); |
892 | 37.4k | }; |
893 | | |
894 | 37.4k | bool isNew = true; |
895 | 37.4k | if (doCompileScope(tok)) { |
896 | 37.4k | compileScope(tok, state); |
897 | 37.4k | isNew = false; |
898 | 37.4k | } |
899 | 39.5k | while (tok) { |
900 | 39.5k | if (tok->tokType() == Token::eIncDecOp && !isPrefixUnary(tok, state.cpp)) { |
901 | 35 | compileUnaryOp(tok, state, compileScope); |
902 | 39.5k | } else if (tok->str() == "...") { |
903 | 0 | if (!Token::simpleMatch(tok->previous(), ")")) |
904 | 0 | state.op.push(tok); |
905 | 0 | tok = tok->next(); |
906 | 0 | break; |
907 | 39.5k | } else if (tok->str() == "." && tok->strAt(1) != "*") { |
908 | 0 | if (tok->strAt(1) == ".") { |
909 | 0 | state.op.push(tok); |
910 | 0 | tok = tok->tokAt(3); |
911 | 0 | break; |
912 | 0 | } |
913 | 0 | compileBinOp(tok, state, compileScope); |
914 | 39.5k | } else if (tok->str() == "[") { |
915 | 0 | if (state.cpp && isPrefixUnary(tok, /*cpp*/ true) && Token::Match(tok->link(), "] (|{|<")) { // Lambda |
916 | | // What we do here: |
917 | | // - Nest the round bracket under the square bracket. |
918 | | // - Nest what follows the lambda (if anything) with the lambda opening [ |
919 | | // - Compile the content of the lambda function as separate tree (this is done later) |
920 | | // this must be consistent with isLambdaCaptureList |
921 | 0 | Token* const squareBracket = tok; |
922 | | // Parse arguments in the capture list |
923 | 0 | if (tok->strAt(1) != "]") { |
924 | 0 | Token* tok2 = tok->next(); |
925 | 0 | AST_state state2(state.cpp); |
926 | 0 | compileExpression(tok2, state2); |
927 | 0 | if (!state2.op.empty()) { |
928 | 0 | squareBracket->astOperand2(state2.op.top()); |
929 | 0 | } |
930 | 0 | } |
931 | |
|
932 | 0 | const bool hasTemplateArg = Token::simpleMatch(squareBracket->link(), "] <") && |
933 | 0 | Token::simpleMatch(squareBracket->link()->next()->link(), "> ("); |
934 | 0 | if (Token::simpleMatch(squareBracket->link(), "] (") || hasTemplateArg) { |
935 | 0 | Token* const roundBracket = hasTemplateArg ? squareBracket->link()->next()->link()->next() : squareBracket->link()->next(); |
936 | 0 | Token* curlyBracket = roundBracket->link()->next(); |
937 | 0 | while (Token::Match(curlyBracket, "mutable|const|constexpr|consteval")) |
938 | 0 | curlyBracket = curlyBracket->next(); |
939 | 0 | if (Token::simpleMatch(curlyBracket, "noexcept")) { |
940 | 0 | if (Token::simpleMatch(curlyBracket->next(), "(")) |
941 | 0 | curlyBracket = curlyBracket->linkAt(1)->next(); |
942 | 0 | else |
943 | 0 | curlyBracket = curlyBracket->next(); |
944 | 0 | } |
945 | 0 | if (curlyBracket && curlyBracket->originalName() == "->") |
946 | 0 | curlyBracket = findTypeEnd(curlyBracket->next()); |
947 | 0 | if (curlyBracket && curlyBracket->str() == "{") { |
948 | 0 | squareBracket->astOperand1(roundBracket); |
949 | 0 | roundBracket->astOperand1(curlyBracket); |
950 | 0 | state.op.push(squareBracket); |
951 | 0 | tok = curlyBracket->link()->next(); |
952 | 0 | continue; |
953 | 0 | } |
954 | 0 | } else { |
955 | 0 | Token* const curlyBracket = squareBracket->link()->next(); |
956 | 0 | squareBracket->astOperand1(curlyBracket); |
957 | 0 | state.op.push(squareBracket); |
958 | 0 | tok = curlyBracket->link()->next(); |
959 | 0 | continue; |
960 | 0 | } |
961 | 0 | } |
962 | | |
963 | 0 | Token* const tok2 = tok; |
964 | 0 | if (tok->strAt(1) != "]") { |
965 | 0 | compileBinOp(tok, state, compileExpression); |
966 | 0 | if (Token::Match(tok2->previous(), "%type%|* [") && Token::Match(tok, "] { !!}")) { |
967 | 0 | tok = tok->next(); |
968 | 0 | Token* const tok3 = tok; |
969 | 0 | compileBinOp(tok, state, compileExpression); |
970 | 0 | if (tok != tok3->link()) |
971 | 0 | throw InternalError(tok, "Syntax error in {..}", InternalError::AST); |
972 | 0 | tok = tok->next(); |
973 | 0 | continue; |
974 | 0 | } |
975 | 0 | } |
976 | 0 | else |
977 | 0 | compileUnaryOp(tok, state, compileExpression); |
978 | 0 | tok = tok2->link()->next(); |
979 | 39.5k | } else if (Token::simpleMatch(tok, "( {") && Token::simpleMatch(tok->linkAt(1)->previous(), "; } )") && !Token::Match(tok->previous(), "%name% (")) { |
980 | 0 | state.op.push(tok->next()); |
981 | 0 | tok = tok->link()->next(); |
982 | 0 | continue; |
983 | 39.5k | } else if (tok->str() == "(" && (!iscast(tok, state.cpp) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) { |
984 | 2.08k | Token* tok2 = tok; |
985 | 2.08k | tok = tok->next(); |
986 | 2.08k | const bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; |
987 | 2.08k | const std::size_t oldOpSize = state.op.size(); |
988 | 2.08k | compileExpression(tok, state); |
989 | 2.08k | tok = tok2; |
990 | 2.08k | if ((oldOpSize > 0 && (isNew || Token::simpleMatch(tok->previous(), "} ("))) |
991 | 2.08k | || (tok->previous() && tok->previous()->isName() && !Token::Match(tok->previous(), "return|case") && (!state.cpp || !Token::Match(tok->previous(), "throw|delete"))) |
992 | 2.08k | || (tok->strAt(-1) == "]" && (!state.cpp || !Token::Match(tok->linkAt(-1)->previous(), "new|delete"))) |
993 | 2.08k | || (tok->strAt(-1) == ">" && tok->linkAt(-1)) |
994 | 2.08k | || (tok->strAt(-1) == ")" && !iscast(tok->linkAt(-1), state.cpp)) // Don't treat brackets to clarify precedence as function calls |
995 | 2.08k | || (tok->strAt(-1) == "}" && opPrevTopSquare)) { |
996 | 1.38k | const bool operandInside = oldOpSize < state.op.size(); |
997 | 1.38k | if (operandInside) |
998 | 1.38k | compileBinOp(tok, state, nullptr); |
999 | 0 | else |
1000 | 0 | compileUnaryOp(tok, state, nullptr); |
1001 | 1.38k | } |
1002 | 2.08k | tok = tok->link()->next(); |
1003 | 37.4k | } else if (iscast(tok, state.cpp) && Token::simpleMatch(tok->link(), ") {") && Token::simpleMatch(tok->link()->linkAt(1), "} [")) { |
1004 | 0 | Token *cast = tok; |
1005 | 0 | tok = tok->link()->next(); |
1006 | 0 | Token *tok1 = tok; |
1007 | 0 | compileUnaryOp(tok, state, compileExpression); |
1008 | 0 | cast->astOperand1(tok1); |
1009 | 0 | tok = tok1->link()->next(); |
1010 | 37.4k | } else if (state.cpp && tok->str() == "{" && iscpp11init(tok)) { |
1011 | 0 | Token* end = tok->link(); |
1012 | 0 | if (Token::simpleMatch(tok, "{ }")) |
1013 | 0 | { |
1014 | 0 | compileUnaryOp(tok, state, nullptr); |
1015 | 0 | tok = tok->next(); |
1016 | 0 | } |
1017 | 0 | else |
1018 | 0 | { |
1019 | 0 | compileBinOp(tok, state, compileExpression); |
1020 | 0 | } |
1021 | 0 | if (tok == end) |
1022 | 0 | tok = end->next(); |
1023 | 0 | else |
1024 | 0 | throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST); |
1025 | 37.4k | } else break; |
1026 | 39.5k | } |
1027 | 37.4k | } |
1028 | | |
1029 | | static void compilePrecedence3(Token *&tok, AST_state& state) |
1030 | 37.4k | { |
1031 | 37.4k | compilePrecedence2(tok, state); |
1032 | 39.6k | while (tok) { |
1033 | 39.6k | if ((Token::Match(tok, "[+-!~*&]") || tok->tokType() == Token::eIncDecOp) && |
1034 | 39.6k | isPrefixUnary(tok, state.cpp)) { |
1035 | 2.18k | if (Token::Match(tok, "* [*,)]")) { |
1036 | 0 | Token* tok2 = tok->next(); |
1037 | 0 | while (tok2->next() && tok2->str() == "*") |
1038 | 0 | tok2 = tok2->next(); |
1039 | 0 | if (Token::Match(tok2, "[>),]")) { |
1040 | 0 | tok = tok2; |
1041 | 0 | continue; |
1042 | 0 | } |
1043 | 0 | } |
1044 | 2.18k | compileUnaryOp(tok, state, compilePrecedence3); |
1045 | 37.4k | } else if (tok->str() == "(" && iscast(tok, state.cpp)) { |
1046 | 1 | Token* castTok = tok; |
1047 | 1 | castTok->isCast(true); |
1048 | 1 | tok = tok->link()->next(); |
1049 | 1 | const int inArrayAssignment = state.inArrayAssignment; |
1050 | 1 | if (tok && tok->str() == "{") |
1051 | 0 | state.inArrayAssignment = 1; |
1052 | 1 | compilePrecedence3(tok, state); |
1053 | 1 | state.inArrayAssignment = inArrayAssignment; |
1054 | 1 | compileUnaryOp(castTok, state, nullptr); |
1055 | 37.4k | } else if (state.cpp && Token::Match(tok, "new %name%|::|(")) { |
1056 | 0 | Token* newtok = tok; |
1057 | 0 | tok = tok->next(); |
1058 | 0 | bool innertype = false; |
1059 | 0 | if (tok->str() == "(") { |
1060 | 0 | if (Token::Match(tok, "( &| %name%") && Token::Match(tok->link(), ") ( %type%") && Token::simpleMatch(tok->link()->linkAt(1), ") (")) |
1061 | 0 | tok = tok->link()->next(); |
1062 | 0 | if (Token::Match(tok->link(), ") ::| %type%")) { |
1063 | 0 | if (Token::Match(tok, "( !!)")) { |
1064 | 0 | Token *innerTok = tok->next(); |
1065 | 0 | AST_state innerState(true); |
1066 | 0 | compileExpression(innerTok, innerState); |
1067 | 0 | } |
1068 | 0 | tok = tok->link()->next(); |
1069 | 0 | } else if (Token::Match(tok, "( %type%") && Token::Match(tok->link(), ") [();,[]")) { |
1070 | 0 | tok = tok->next(); |
1071 | 0 | innertype = true; |
1072 | 0 | } else if (Token::Match(tok, "( &| %name%") && Token::simpleMatch(tok->link(), ") (")) { |
1073 | 0 | tok = tok->next(); |
1074 | 0 | innertype = true; |
1075 | 0 | } else { |
1076 | | /* bad code */ |
1077 | 0 | continue; |
1078 | 0 | } |
1079 | 0 | } |
1080 | | |
1081 | 0 | Token* leftToken = tok; |
1082 | 0 | if (Token::simpleMatch(tok, "::")) { |
1083 | 0 | tok->astOperand1(tok->next()); |
1084 | 0 | tok = tok->next(); |
1085 | 0 | } |
1086 | 0 | else { |
1087 | 0 | while (Token::Match(tok->next(), ":: %name%")) { |
1088 | 0 | Token* scopeToken = tok->next(); //The :: |
1089 | 0 | scopeToken->astOperand1(leftToken); |
1090 | 0 | scopeToken->astOperand2(scopeToken->next()); |
1091 | 0 | leftToken = scopeToken; |
1092 | 0 | tok = scopeToken->next(); |
1093 | 0 | } |
1094 | 0 | } |
1095 | |
|
1096 | 0 | state.op.push(tok); |
1097 | 0 | while (Token::Match(tok, "%name%|*|&|<|::")) { |
1098 | 0 | if (tok->link()) |
1099 | 0 | tok = tok->link(); |
1100 | 0 | tok = tok->next(); |
1101 | 0 | } |
1102 | 0 | if (Token::Match(tok, "( const| %type% ) (")) { |
1103 | 0 | state.op.push(tok->next()); |
1104 | 0 | tok = tok->link()->next(); |
1105 | 0 | compileBinOp(tok, state, compilePrecedence2); |
1106 | 0 | } else if (Token::Match(tok, "(|{|[")) |
1107 | 0 | compilePrecedence2(tok, state); |
1108 | 0 | else if (innertype && Token::simpleMatch(tok, ") [")) { |
1109 | 0 | tok = tok->next(); |
1110 | 0 | compilePrecedence2(tok, state); |
1111 | 0 | } |
1112 | 0 | compileUnaryOp(newtok, state, nullptr); |
1113 | 0 | if (innertype && Token::simpleMatch(tok, ") ,")) |
1114 | 0 | tok = tok->next(); |
1115 | 37.4k | } else if (state.cpp && Token::Match(tok, "delete %name%|*|&|::|(|[")) { |
1116 | 0 | Token* tok2 = tok; |
1117 | 0 | tok = tok->next(); |
1118 | 0 | if (tok && tok->str() == "[") |
1119 | 0 | tok = tok->link()->next(); |
1120 | 0 | compilePrecedence3(tok, state); |
1121 | 0 | compileUnaryOp(tok2, state, nullptr); |
1122 | 0 | } |
1123 | | // TODO: Handle sizeof |
1124 | 37.4k | else break; |
1125 | 39.6k | } |
1126 | 37.4k | } |
1127 | | |
1128 | | static void compilePointerToElem(Token *&tok, AST_state& state) |
1129 | 35.2k | { |
1130 | 35.2k | compilePrecedence3(tok, state); |
1131 | 35.2k | while (tok) { |
1132 | 35.2k | if (Token::simpleMatch(tok, ". *")) { |
1133 | 0 | compileBinOp(tok, state, compilePrecedence3); |
1134 | 35.2k | } else break; |
1135 | 35.2k | } |
1136 | 35.2k | } |
1137 | | |
1138 | | static void compileMulDiv(Token *&tok, AST_state& state) |
1139 | 34.4k | { |
1140 | 34.4k | compilePointerToElem(tok, state); |
1141 | 35.2k | while (tok) { |
1142 | 35.2k | if (Token::Match(tok, "[/%]") || (tok->str() == "*" && !tok->astOperand1() && !isQualifier(tok))) { |
1143 | 814 | if (Token::Match(tok, "* [*,)]")) { |
1144 | 0 | Token* tok2 = tok->next(); |
1145 | 0 | while (tok2->next() && tok2->str() == "*") |
1146 | 0 | tok2 = tok2->next(); |
1147 | 0 | if (Token::Match(tok2, "[>),]")) { |
1148 | 0 | tok = tok2; |
1149 | 0 | break; |
1150 | 0 | } |
1151 | 0 | } |
1152 | 814 | compileBinOp(tok, state, compilePointerToElem); |
1153 | 34.4k | } else break; |
1154 | 35.2k | } |
1155 | 34.4k | } |
1156 | | |
1157 | | static void compileAddSub(Token *&tok, AST_state& state) |
1158 | 33.9k | { |
1159 | 33.9k | compileMulDiv(tok, state); |
1160 | 34.4k | while (tok) { |
1161 | 34.4k | if (Token::Match(tok, "+|-") && !tok->astOperand1()) { |
1162 | 529 | compileBinOp(tok, state, compileMulDiv); |
1163 | 33.9k | } else break; |
1164 | 34.4k | } |
1165 | 33.9k | } |
1166 | | |
1167 | | static void compileShift(Token *&tok, AST_state& state) |
1168 | 33.9k | { |
1169 | 33.9k | compileAddSub(tok, state); |
1170 | 33.9k | while (tok) { |
1171 | 33.9k | if (Token::Match(tok, "<<|>>")) { |
1172 | 0 | compileBinOp(tok, state, compileAddSub); |
1173 | 33.9k | } else break; |
1174 | 33.9k | } |
1175 | 33.9k | } |
1176 | | |
1177 | | static void compileThreewayComp(Token *&tok, AST_state& state) |
1178 | 33.9k | { |
1179 | 33.9k | compileShift(tok, state); |
1180 | 33.9k | while (tok) { |
1181 | 33.9k | if (tok->str() == "<=>") { |
1182 | 0 | compileBinOp(tok, state, compileShift); |
1183 | 33.9k | } else break; |
1184 | 33.9k | } |
1185 | 33.9k | } |
1186 | | |
1187 | | static void compileRelComp(Token *&tok, AST_state& state) |
1188 | 32.4k | { |
1189 | 32.4k | compileThreewayComp(tok, state); |
1190 | 33.9k | while (tok) { |
1191 | 33.9k | if (Token::Match(tok, "<|<=|>=|>") && !tok->link()) { |
1192 | 1.50k | compileBinOp(tok, state, compileThreewayComp); |
1193 | 32.4k | } else break; |
1194 | 33.9k | } |
1195 | 32.4k | } |
1196 | | |
1197 | | static void compileEqComp(Token *&tok, AST_state& state) |
1198 | 31.9k | { |
1199 | 31.9k | compileRelComp(tok, state); |
1200 | 32.4k | while (tok) { |
1201 | 32.4k | if (Token::Match(tok, "==|!=")) { |
1202 | 486 | compileBinOp(tok, state, compileRelComp); |
1203 | 31.9k | } else break; |
1204 | 32.4k | } |
1205 | 31.9k | } |
1206 | | |
1207 | | static void compileAnd(Token *&tok, AST_state& state) |
1208 | 31.7k | { |
1209 | 31.7k | compileEqComp(tok, state); |
1210 | 31.9k | while (tok) { |
1211 | 31.9k | if (tok->str() == "&" && !tok->astOperand1() && !isQualifier(tok)) { |
1212 | 252 | Token* tok2 = tok->next(); |
1213 | 252 | if (!tok2) |
1214 | 0 | break; |
1215 | 252 | if (tok2->str() == "&") |
1216 | 0 | tok2 = tok2->next(); |
1217 | 252 | if (state.cpp && Token::Match(tok2, ",|)")) { |
1218 | 0 | tok = tok2; |
1219 | 0 | break; // rValue reference |
1220 | 0 | } |
1221 | 252 | compileBinOp(tok, state, compileEqComp); |
1222 | 31.7k | } else break; |
1223 | 31.9k | } |
1224 | 31.7k | } |
1225 | | |
1226 | | static void compileXor(Token *&tok, AST_state& state) |
1227 | 31.4k | { |
1228 | 31.4k | compileAnd(tok, state); |
1229 | 31.7k | while (tok) { |
1230 | 31.7k | if (tok->str() == "^") { |
1231 | 266 | compileBinOp(tok, state, compileAnd); |
1232 | 31.4k | } else break; |
1233 | 31.7k | } |
1234 | 31.4k | } |
1235 | | |
1236 | | static void compileOr(Token *&tok, AST_state& state) |
1237 | 31.1k | { |
1238 | 31.1k | compileXor(tok, state); |
1239 | 31.4k | while (tok) { |
1240 | 31.4k | if (tok->str() == "|") { |
1241 | 264 | compileBinOp(tok, state, compileXor); |
1242 | 31.1k | } else break; |
1243 | 31.4k | } |
1244 | 31.1k | } |
1245 | | |
1246 | | static void compileLogicAnd(Token *&tok, AST_state& state) |
1247 | 31.1k | { |
1248 | 31.1k | compileOr(tok, state); |
1249 | 31.1k | while (tok) { |
1250 | 31.1k | if (tok->str() == "&&" && !isQualifier(tok)) { |
1251 | 0 | if (!tok->astOperand1()) { |
1252 | 0 | Token* tok2 = tok->next(); |
1253 | 0 | if (!tok2) |
1254 | 0 | break; |
1255 | 0 | if (state.cpp && Token::Match(tok2, ",|)")) { |
1256 | 0 | tok = tok2; |
1257 | 0 | break; // rValue reference |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | compileBinOp(tok, state, compileOr); |
1261 | 31.1k | } else break; |
1262 | 31.1k | } |
1263 | 31.1k | } |
1264 | | |
1265 | | static void compileLogicOr(Token *&tok, AST_state& state) |
1266 | 31.1k | { |
1267 | 31.1k | compileLogicAnd(tok, state); |
1268 | 31.1k | while (tok) { |
1269 | 31.1k | if (tok->str() == "||") { |
1270 | 0 | compileBinOp(tok, state, compileLogicAnd); |
1271 | 31.1k | } else break; |
1272 | 31.1k | } |
1273 | 31.1k | } |
1274 | | |
1275 | | static void compileAssignTernary(Token *&tok, AST_state& state) |
1276 | 31.1k | { |
1277 | 31.1k | compileLogicOr(tok, state); |
1278 | 39.8k | while (tok) { |
1279 | 39.8k | if (tok->isAssignmentOp()) { |
1280 | 8.71k | state.assign++; |
1281 | 8.71k | const Token *tok1 = tok->next(); |
1282 | 8.71k | compileBinOp(tok, state, compileAssignTernary); |
1283 | 8.71k | if (Token::simpleMatch(tok1, "{") && tok == tok1->link() && tok->next()) |
1284 | 0 | tok = tok->next(); |
1285 | 8.71k | if (state.assign > 0) |
1286 | 8.71k | state.assign--; |
1287 | 31.1k | } else if (tok->str() == "?") { |
1288 | | // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: |
1289 | | // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." |
1290 | | // Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary. |
1291 | 0 | const bool stopAtColon = state.stopAtColon; |
1292 | 0 | state.stopAtColon = false; |
1293 | 0 | if (tok->strAt(1) == ":") { |
1294 | 0 | state.op.push(nullptr); |
1295 | 0 | } |
1296 | 0 | const int assign = state.assign; |
1297 | 0 | state.assign = 0; |
1298 | 0 | compileBinOp(tok, state, compileAssignTernary); |
1299 | 0 | state.assign = assign; |
1300 | 0 | state.stopAtColon = stopAtColon; |
1301 | 31.1k | } else if (tok->str() == ":") { |
1302 | 0 | if (state.depth == 1U && state.inCase) { |
1303 | 0 | state.inCase = false; |
1304 | 0 | tok = tok->next(); |
1305 | 0 | break; |
1306 | 0 | } |
1307 | 0 | if (state.stopAtColon) |
1308 | 0 | break; |
1309 | 0 | if (state.assign > 0) |
1310 | 0 | break; |
1311 | 0 | compileBinOp(tok, state, compileAssignTernary); |
1312 | 31.1k | } else break; |
1313 | 39.8k | } |
1314 | 31.1k | } |
1315 | | |
1316 | | static void compileComma(Token *&tok, AST_state& state) |
1317 | 22.4k | { |
1318 | 22.4k | compileAssignTernary(tok, state); |
1319 | 22.4k | while (tok) { |
1320 | 22.4k | if (tok->str() == ",") { |
1321 | 0 | if (Token::simpleMatch(tok, ", }")) |
1322 | 0 | tok = tok->next(); |
1323 | 0 | else |
1324 | 0 | compileBinOp(tok, state, compileAssignTernary); |
1325 | 22.4k | } else if (tok->str() == ";" && state.functionCallEndPar && tok->index() < state.functionCallEndPar->index()) { |
1326 | 0 | compileBinOp(tok, state, compileAssignTernary); |
1327 | 22.4k | } else break; |
1328 | 22.4k | } |
1329 | 22.4k | } |
1330 | | |
1331 | | static void compileExpression(Token *&tok, AST_state& state) |
1332 | 22.4k | { |
1333 | 22.4k | if (state.depth > AST_MAX_DEPTH) |
1334 | 0 | throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); // ticket #5592 |
1335 | 22.4k | if (tok) |
1336 | 22.4k | compileComma(tok, state); |
1337 | 22.4k | } |
1338 | | |
1339 | | const Token* isLambdaCaptureList(const Token * tok) |
1340 | 7.69k | { |
1341 | | // a lambda expression '[x](y){}' is compiled as: |
1342 | | // [ |
1343 | | // `-( <<-- optional |
1344 | | // `-{ |
1345 | | // see compilePrecedence2 |
1346 | 7.69k | if (!Token::simpleMatch(tok, "[")) |
1347 | 7.69k | return nullptr; |
1348 | 0 | if (!Token::Match(tok->link(), "] (|{")) |
1349 | 0 | return nullptr; |
1350 | 0 | if (Token::simpleMatch(tok->astOperand1(), "{") && tok->astOperand1() == tok->link()->next()) |
1351 | 0 | return tok->astOperand1(); |
1352 | 0 | if (!tok->astOperand1() || tok->astOperand1()->str() != "(") |
1353 | 0 | return nullptr; |
1354 | 0 | const Token * params = tok->astOperand1(); |
1355 | 0 | if (!Token::simpleMatch(params->astOperand1(), "{")) |
1356 | 0 | return nullptr; |
1357 | 0 | return params->astOperand1(); |
1358 | 0 | } |
1359 | | |
1360 | 36.7k | const Token* findLambdaEndTokenWithoutAST(const Token* tok) { |
1361 | 36.7k | if (!(Token::simpleMatch(tok, "[") && tok->link())) |
1362 | 36.7k | return nullptr; |
1363 | 0 | tok = tok->link()->next(); |
1364 | 0 | if (Token::simpleMatch(tok, "(") && tok->link()) |
1365 | 0 | tok = tok->link()->next(); |
1366 | 0 | if (!(Token::simpleMatch(tok, "{") && tok->link())) |
1367 | 0 | return nullptr; |
1368 | 0 | return tok->link()->next(); |
1369 | 0 | } |
1370 | | |
1371 | | static Token * createAstAtToken(Token *tok, bool cpp); |
1372 | | |
1373 | | // Compile inner expressions inside inner ({..}) and lambda bodies |
1374 | | static void createAstAtTokenInner(Token * const tok1, const Token *endToken, bool cpp) |
1375 | 18.4k | { |
1376 | 55.2k | for (Token* tok = tok1; precedes(tok, endToken); tok = tok ? tok->next() : nullptr) { |
1377 | 36.7k | if (tok->str() == "{" && !iscpp11init(tok)) { |
1378 | 0 | const Token * const endToken2 = tok->link(); |
1379 | 0 | bool hasAst = false; |
1380 | 0 | for (const Token *inner = tok->next(); inner != endToken2; inner = inner->next()) { |
1381 | 0 | if (inner->astOperand1()) { |
1382 | 0 | hasAst = true; |
1383 | 0 | break; |
1384 | 0 | } |
1385 | 0 | if (tok->isConstOp()) |
1386 | 0 | break; |
1387 | 0 | if (inner->str() == "{") |
1388 | 0 | inner = inner->link(); |
1389 | 0 | } |
1390 | 0 | if (!hasAst) { |
1391 | 0 | for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) |
1392 | 0 | tok = createAstAtToken(tok, cpp); |
1393 | 0 | } |
1394 | 36.7k | } else if (cpp && tok->str() == "[") { |
1395 | 0 | if (isLambdaCaptureList(tok)) { |
1396 | 0 | tok = tok->astOperand1(); |
1397 | 0 | if (tok->str() == "(") |
1398 | 0 | tok = tok->astOperand1(); |
1399 | 0 | const Token * const endToken2 = tok->link(); |
1400 | 0 | tok = tok->next(); |
1401 | 0 | for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) |
1402 | 0 | tok = createAstAtToken(tok, cpp); |
1403 | 0 | } |
1404 | 0 | } |
1405 | 36.7k | else if (Token::simpleMatch(tok, "( * ) [")) { |
1406 | 0 | bool hasAst = false; |
1407 | 0 | for (const Token* tok2 = tok->linkAt(3); tok2 != tok; tok2 = tok2->previous()) { |
1408 | 0 | if (tok2->astParent() || tok2->astOperand1() || tok2->astOperand2()) { |
1409 | 0 | hasAst = true; |
1410 | 0 | break; |
1411 | 0 | } |
1412 | 0 | } |
1413 | 0 | if (!hasAst) { |
1414 | 0 | Token *const startTok = tok = tok->tokAt(4); |
1415 | 0 | const Token* const endtok = startTok->linkAt(-1); |
1416 | 0 | AST_state state(cpp); |
1417 | 0 | compileExpression(tok, state); |
1418 | 0 | createAstAtTokenInner(startTok, endtok, cpp); |
1419 | 0 | } |
1420 | 0 | } |
1421 | 36.7k | } |
1422 | 18.4k | } |
1423 | | |
1424 | | static Token * findAstTop(Token *tok1, const Token *tok2) |
1425 | 0 | { |
1426 | 0 | for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { |
1427 | 0 | if (tok->astParent() || tok->astOperand1() || tok->astOperand2()) { |
1428 | 0 | while (tok->astParent() && tok->astParent()->index() >= tok1->index() && tok->astParent()->index() <= tok2->index()) |
1429 | 0 | tok = tok->astParent(); |
1430 | 0 | return tok; |
1431 | 0 | } |
1432 | 0 | if (Token::simpleMatch(tok, "( {")) |
1433 | 0 | tok = tok->link(); |
1434 | 0 | } |
1435 | 0 | for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { |
1436 | 0 | if (tok->isName() || tok->isNumber()) |
1437 | 0 | return tok; |
1438 | 0 | if (Token::simpleMatch(tok, "( {")) |
1439 | 0 | tok = tok->link(); |
1440 | 0 | } |
1441 | 0 | return nullptr; |
1442 | 0 | } |
1443 | | |
1444 | | static Token * createAstAtToken(Token *tok, bool cpp) |
1445 | 52.0k | { |
1446 | | // skip function pointer declaration |
1447 | 52.0k | if (Token::Match(tok, "%type%") && !Token::Match(tok, "return|throw|if|while|new|delete")) { |
1448 | 9.39k | Token* type = tok; |
1449 | 20.5k | while (Token::Match(type, "%type%|*|&|<")) { |
1450 | 11.1k | if (type->str() == "<") { |
1451 | 0 | if (type->link()) |
1452 | 0 | type = type->link(); |
1453 | 0 | else |
1454 | 0 | break; |
1455 | 0 | } |
1456 | 11.1k | type = type->next(); |
1457 | 11.1k | } |
1458 | 9.39k | if (Token::Match(type, "( * *| %var%") && |
1459 | 9.39k | Token::Match(type->link()->previous(), "%var%|] ) (") && |
1460 | 9.39k | Token::Match(type->link()->linkAt(1), ") [;,)]")) |
1461 | 0 | return type->link()->linkAt(1)->next(); |
1462 | 9.39k | } |
1463 | | |
1464 | 52.0k | if (Token::simpleMatch(tok, "for (")) { |
1465 | 0 | if (cpp && Token::Match(tok, "for ( const| auto &|&&| [")) { |
1466 | 0 | Token *decl = Token::findsimplematch(tok, "["); |
1467 | 0 | if (Token::simpleMatch(decl->link(), "] :")) { |
1468 | 0 | AST_state state1(cpp); |
1469 | 0 | while (decl->str() != "]") { |
1470 | 0 | if (Token::Match(decl, "%name% ,|]")) { |
1471 | 0 | state1.op.push(decl); |
1472 | 0 | } else if (decl->str() == ",") { |
1473 | 0 | if (!state1.op.empty()) { |
1474 | 0 | decl->astOperand1(state1.op.top()); |
1475 | 0 | state1.op.pop(); |
1476 | 0 | } |
1477 | 0 | if (!state1.op.empty()) { |
1478 | 0 | state1.op.top()->astOperand2(decl); |
1479 | 0 | state1.op.pop(); |
1480 | 0 | } |
1481 | 0 | state1.op.push(decl); |
1482 | 0 | } |
1483 | 0 | decl = decl->next(); |
1484 | 0 | } |
1485 | 0 | if (state1.op.size() > 1) { |
1486 | 0 | Token *lastName = state1.op.top(); |
1487 | 0 | state1.op.pop(); |
1488 | 0 | state1.op.top()->astOperand2(lastName); |
1489 | 0 | } |
1490 | 0 | decl = decl->next(); |
1491 | |
|
1492 | 0 | Token *colon = decl; |
1493 | 0 | compileExpression(decl, state1); |
1494 | |
|
1495 | 0 | tok->next()->astOperand1(tok); |
1496 | 0 | tok->next()->astOperand2(colon); |
1497 | |
|
1498 | 0 | return decl; |
1499 | 0 | } |
1500 | 0 | } |
1501 | | |
1502 | 0 | std::vector<Token*> inner; |
1503 | 0 | Token* tok2 = skipDecl(tok->tokAt(2), &inner); |
1504 | 0 | for (Token* tok3 : inner) { |
1505 | 0 | AST_state state1(cpp); |
1506 | 0 | compileExpression(tok3, state1); |
1507 | 0 | } |
1508 | 0 | Token *init1 = nullptr; |
1509 | 0 | Token * const endPar = tok->next()->link(); |
1510 | 0 | if (tok2 == tok->tokAt(2) && Token::Match(tok2, "%op%|(")) { |
1511 | 0 | init1 = tok2; |
1512 | 0 | AST_state state1(cpp); |
1513 | 0 | compileExpression(tok2, state1); |
1514 | 0 | if (Token::Match(init1, "( !!{")) { |
1515 | 0 | for (Token *tok3 = init1; tok3 != tok3->link(); tok3 = tok3->next()) { |
1516 | 0 | if (tok3->astParent()) { |
1517 | 0 | while (tok3->astParent()) |
1518 | 0 | tok3 = tok3->astParent(); |
1519 | 0 | init1 = tok3; |
1520 | 0 | break; |
1521 | 0 | } |
1522 | 0 | if (!Token::Match(tok3, "%op%|(|[")) |
1523 | 0 | init1 = tok3; |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 | } else { |
1527 | 0 | while (tok2 && tok2 != endPar && tok2->str() != ";") { |
1528 | 0 | if (tok2->str() == "<" && tok2->link()) { |
1529 | 0 | tok2 = tok2->link(); |
1530 | 0 | } else if (Token::Match(tok2, "%name% )| %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { |
1531 | 0 | init1 = tok2; |
1532 | 0 | AST_state state1(cpp); |
1533 | 0 | compileExpression(tok2, state1); |
1534 | 0 | if (Token::Match(tok2, ";|)")) |
1535 | 0 | break; |
1536 | 0 | init1 = nullptr; |
1537 | 0 | } |
1538 | 0 | if (!tok2) // #7109 invalid code |
1539 | 0 | return nullptr; |
1540 | 0 | tok2 = tok2->next(); |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 | if (!tok2 || tok2->str() != ";") { |
1544 | 0 | if (tok2 == endPar && init1) { |
1545 | 0 | createAstAtTokenInner(init1->next(), endPar, cpp); |
1546 | 0 | tok->next()->astOperand2(init1); |
1547 | 0 | tok->next()->astOperand1(tok); |
1548 | 0 | } |
1549 | 0 | return tok2; |
1550 | 0 | } |
1551 | | |
1552 | 0 | Token * const init = init1 ? init1 : tok2; |
1553 | |
|
1554 | 0 | Token * const semicolon1 = tok2; |
1555 | 0 | tok2 = tok2->next(); |
1556 | 0 | AST_state state2(cpp); |
1557 | 0 | compileExpression(tok2, state2); |
1558 | |
|
1559 | 0 | Token * const semicolon2 = tok2; |
1560 | 0 | if (!semicolon2) |
1561 | 0 | return nullptr; // invalid code #7235 |
1562 | | |
1563 | 0 | if (semicolon2->str() == ";") { |
1564 | 0 | tok2 = tok2->next(); |
1565 | 0 | AST_state state3(cpp); |
1566 | 0 | if (Token::simpleMatch(tok2, "( {")) { |
1567 | 0 | state3.op.push(tok2->next()); |
1568 | 0 | tok2 = tok2->link()->next(); |
1569 | 0 | } |
1570 | 0 | compileExpression(tok2, state3); |
1571 | |
|
1572 | 0 | tok2 = findAstTop(semicolon1->next(), semicolon2); |
1573 | 0 | if (tok2) |
1574 | 0 | semicolon2->astOperand1(tok2); |
1575 | 0 | tok2 = findAstTop(semicolon2->next(), endPar); |
1576 | 0 | if (tok2) |
1577 | 0 | semicolon2->astOperand2(tok2); |
1578 | 0 | else if (!state3.op.empty()) |
1579 | 0 | semicolon2->astOperand2(state3.op.top()); |
1580 | 0 | semicolon1->astOperand2(semicolon2); |
1581 | 0 | } else { |
1582 | 0 | if (!cpp || state2.op.empty() || !Token::simpleMatch(state2.op.top(), ":")) |
1583 | 0 | throw InternalError(tok, "syntax error", InternalError::SYNTAX); |
1584 | | |
1585 | 0 | semicolon1->astOperand2(state2.op.top()); |
1586 | 0 | } |
1587 | | |
1588 | 0 | if (init != semicolon1) |
1589 | 0 | semicolon1->astOperand1(init->astTop()); |
1590 | 0 | tok->next()->astOperand1(tok); |
1591 | 0 | tok->next()->astOperand2(semicolon1); |
1592 | |
|
1593 | 0 | createAstAtTokenInner(endPar->link(), endPar, cpp); |
1594 | |
|
1595 | 0 | return endPar; |
1596 | 0 | } |
1597 | | |
1598 | 52.0k | if (Token::simpleMatch(tok, "( {")) |
1599 | 0 | return tok; |
1600 | | |
1601 | 52.0k | if (Token::Match(tok, "%type% <") && tok->linkAt(1) && !Token::Match(tok->linkAt(1), "> [({]")) |
1602 | 0 | return tok->linkAt(1); |
1603 | | |
1604 | 52.0k | if (cpp && !tok->isKeyword() && Token::Match(tok, "%type% ::|<|%name%")) { |
1605 | 8.55k | Token *tok2 = tok; |
1606 | 8.55k | while (true) { |
1607 | 8.55k | if (Token::Match(tok2, "%name%|> :: %name%")) |
1608 | 0 | tok2 = tok2->tokAt(2); |
1609 | 8.55k | else if (Token::Match(tok2, "%name% <") && tok2->linkAt(1)) |
1610 | 0 | tok2 = tok2->linkAt(1); |
1611 | 8.55k | else |
1612 | 8.55k | break; |
1613 | 8.55k | } |
1614 | 8.55k | if (Token::Match(tok2, "%name%|> %name% {") && tok2->next()->varId() && iscpp11init(tok2->tokAt(2))) { |
1615 | 0 | Token *const tok1 = tok = tok2->next(); |
1616 | 0 | AST_state state(cpp); |
1617 | 0 | compileExpression(tok, state); |
1618 | 0 | createAstAtTokenInner(tok1->next(), tok1->linkAt(1), cpp); |
1619 | 0 | return tok; |
1620 | 0 | } |
1621 | 8.55k | } |
1622 | | |
1623 | 52.0k | if (Token::Match(tok, "%type% %name%|*|&|::") && !Token::Match(tok, "return|new")) { |
1624 | 8.55k | int typecount = 0; |
1625 | 8.55k | Token *typetok = tok; |
1626 | 18.8k | while (Token::Match(typetok, "%type%|::|*|&")) { |
1627 | 10.2k | if (typetok->isName() && !Token::simpleMatch(typetok->previous(), "::")) |
1628 | 10.2k | typecount++; |
1629 | 10.2k | typetok = typetok->next(); |
1630 | 10.2k | } |
1631 | 8.55k | if (Token::Match(typetok, "%var% =") && typetok->varId()) |
1632 | 0 | tok = typetok; |
1633 | | |
1634 | | // Do not create AST for function declaration |
1635 | 8.55k | if (typetok && |
1636 | 8.55k | typecount >= 2 && |
1637 | 8.55k | !Token::Match(tok, "return|throw") && |
1638 | 8.55k | Token::Match(typetok->previous(), "%name% ( !!*") && |
1639 | 8.55k | typetok->previous()->varId() == 0 && |
1640 | 8.55k | !typetok->previous()->isKeyword() && |
1641 | 8.55k | (Token::Match(typetok->link(), ") const|;|{") || Token::Match(typetok->link(), ") const| = delete ;"))) |
1642 | 1.74k | return typetok; |
1643 | 8.55k | } |
1644 | | |
1645 | 50.3k | if (Token::Match(tok, "return|case") || |
1646 | 50.3k | (cpp && tok->str() == "throw") || |
1647 | 50.3k | !tok->previous() || |
1648 | 50.3k | Token::Match(tok, "%name% %op%|(|[|.|::|<|?|;") || |
1649 | 50.3k | (cpp && Token::Match(tok, "%name% {") && iscpp11init(tok->next())) || |
1650 | 50.3k | Token::Match(tok->previous(), "[;{}] %cop%|++|--|( !!{") || |
1651 | 50.3k | Token::Match(tok->previous(), "[;{}] %num%|%str%|%char%") || |
1652 | 50.3k | Token::Match(tok->previous(), "[;{}] delete new")) { |
1653 | 18.4k | if (cpp && (Token::Match(tok->tokAt(-2), "[;{}] new|delete %name%") || Token::Match(tok->tokAt(-3), "[;{}] :: new|delete %name%"))) |
1654 | 0 | tok = tok->previous(); |
1655 | | |
1656 | 18.4k | Token * const tok1 = tok; |
1657 | 18.4k | AST_state state(cpp); |
1658 | 18.4k | if (Token::Match(tok, "%name% (")) |
1659 | 1.46k | state.functionCallEndPar = tok->linkAt(1); |
1660 | 18.4k | compileExpression(tok, state); |
1661 | 18.4k | Token * const endToken = tok; |
1662 | 18.4k | if (endToken == tok1 || !endToken) |
1663 | 0 | return tok1; |
1664 | | |
1665 | 18.4k | createAstAtTokenInner(tok1->next(), endToken, cpp); |
1666 | | |
1667 | 18.4k | return endToken->previous(); |
1668 | 18.4k | } |
1669 | | |
1670 | 31.8k | if (cpp && tok->str() == "{" && iscpp11init(tok)) { |
1671 | 0 | Token * const tok1 = tok; |
1672 | 0 | AST_state state(cpp); |
1673 | 0 | compileExpression(tok, state); |
1674 | 0 | Token* const endToken = tok; |
1675 | 0 | if (endToken == tok1 || !endToken) |
1676 | 0 | return tok1; |
1677 | | |
1678 | 0 | createAstAtTokenInner(tok1->next(), endToken, cpp); |
1679 | 0 | return endToken->previous(); |
1680 | 0 | } |
1681 | | |
1682 | 31.8k | return tok; |
1683 | 31.8k | } |
1684 | | |
1685 | | void TokenList::createAst() const |
1686 | 1.36k | { |
1687 | 53.4k | for (Token *tok = mTokensFrontBack.front; tok; tok = tok ? tok->next() : nullptr) { |
1688 | 52.0k | tok = createAstAtToken(tok, isCPP()); |
1689 | 52.0k | } |
1690 | 1.36k | } |
1691 | | |
1692 | | struct OnException { |
1693 | | std::function<void()> f; |
1694 | | |
1695 | 1.36k | ~OnException() { |
1696 | 1.36k | #ifndef _MSC_VER |
1697 | 1.36k | if (std::uncaught_exception()) |
1698 | 1 | f(); |
1699 | 1.36k | #endif |
1700 | 1.36k | } |
1701 | | }; |
1702 | | |
1703 | | void TokenList::validateAst() const |
1704 | 1.36k | { |
1705 | 1.36k | OnException oe{[&] { |
1706 | 1 | if (mSettings && mSettings->debugnormal) |
1707 | 0 | mTokensFrontBack.front->printOut(); |
1708 | 1 | }}; |
1709 | | // Check for some known issues in AST to avoid crash/hang later on |
1710 | 1.36k | std::set<const Token*> safeAstTokens; // list of "safe" AST tokens without endless recursion |
1711 | 93.6k | for (const Token *tok = mTokensFrontBack.front; tok; tok = tok->next()) { |
1712 | | // Syntax error if binary operator only has 1 operand |
1713 | 92.2k | if ((tok->isAssignmentOp() || tok->isComparisonOp() || Token::Match(tok,"[|^/%]")) && tok->astOperand1() && !tok->astOperand2()) |
1714 | 0 | throw InternalError(tok, "Syntax Error: AST broken, binary operator has only one operand.", InternalError::AST); |
1715 | | |
1716 | | // Syntax error if we encounter "?" with operand2 that is not ":" |
1717 | 92.2k | if (tok->str() == "?") { |
1718 | 0 | if (!tok->astOperand1() || !tok->astOperand2()) |
1719 | 0 | throw InternalError(tok, "AST broken, ternary operator missing operand(s)", InternalError::AST); |
1720 | 0 | if (tok->astOperand2()->str() != ":") |
1721 | 0 | throw InternalError(tok, "Syntax Error: AST broken, ternary operator lacks ':'.", InternalError::AST); |
1722 | 0 | } |
1723 | | |
1724 | | // Check for endless recursion |
1725 | 92.2k | const Token* parent = tok->astParent(); |
1726 | 92.2k | if (parent) { |
1727 | 32.5k | std::set<const Token*> astTokens; // list of ancestors |
1728 | 32.5k | astTokens.insert(tok); |
1729 | 37.0k | do { |
1730 | 37.0k | if (safeAstTokens.find(parent) != safeAstTokens.end()) |
1731 | 22.8k | break; |
1732 | 14.2k | if (astTokens.find(parent) != astTokens.end()) |
1733 | 0 | throw InternalError(tok, "AST broken: endless recursion from '" + tok->str() + "'", InternalError::AST); |
1734 | 14.2k | astTokens.insert(parent); |
1735 | 14.2k | } while ((parent = parent->astParent()) != nullptr); |
1736 | 32.5k | safeAstTokens.insert(astTokens.cbegin(), astTokens.cend()); |
1737 | 59.7k | } else if (tok->str() == ";") { |
1738 | 17.0k | safeAstTokens.clear(); |
1739 | 42.6k | } else { |
1740 | 42.6k | safeAstTokens.insert(tok); |
1741 | 42.6k | } |
1742 | | |
1743 | | // Don't check templates |
1744 | 92.2k | if (tok->str() == "<" && tok->link()) { |
1745 | 4 | tok = tok->link(); |
1746 | 4 | continue; |
1747 | 4 | } |
1748 | 92.2k | if (tok->isCast() && tok->astOperand1() && tok->link()) { // skip casts (not part of the AST) |
1749 | 0 | tok = tok->link(); |
1750 | 0 | continue; |
1751 | 0 | } |
1752 | | |
1753 | 92.2k | if (findLambdaEndToken(tok)) { // skip lambda captures |
1754 | 0 | tok = tok->link(); |
1755 | 0 | continue; |
1756 | 0 | } |
1757 | | |
1758 | | // Check binary operators |
1759 | 92.2k | if (Token::Match(tok, "%or%|%oror%|%assign%|%comp%")) { |
1760 | | // Skip pure virtual functions |
1761 | 10.9k | if (Token::simpleMatch(tok->previous(), ") = 0")) |
1762 | 0 | continue; |
1763 | | // Skip operator definitions |
1764 | 10.9k | if (Token::simpleMatch(tok->previous(), "operator")) |
1765 | 0 | continue; |
1766 | | // Skip incomplete code |
1767 | 10.9k | if (!tok->astOperand1() && !tok->astOperand2() && !tok->astParent()) |
1768 | 0 | continue; |
1769 | | // Skip lambda assignment and/or initializer |
1770 | 10.9k | if (Token::Match(tok, "= {|^|[")) |
1771 | 0 | continue; |
1772 | | // FIXME: Workaround broken AST assignment in type aliases |
1773 | 10.9k | if (Token::Match(tok->previous(), "%name% = %name%")) |
1774 | 987 | continue; |
1775 | 9.98k | if (!tok->astOperand1() || !tok->astOperand2()) |
1776 | 1 | throw InternalError(tok, "Syntax Error: AST broken, binary operator '" + tok->str() + "' doesn't have two operands.", InternalError::AST); |
1777 | 9.98k | } |
1778 | | |
1779 | | // Check control blocks and asserts |
1780 | 91.2k | if (Token::Match(tok->previous(), "if|while|for|switch|assert|ASSERT (")) { |
1781 | 1.38k | if (!tok->astOperand1() || !tok->astOperand2()) |
1782 | 0 | throw InternalError(tok, |
1783 | 0 | "Syntax Error: AST broken, '" + tok->previous()->str() + |
1784 | 0 | "' doesn't have two operands.", |
1785 | 0 | InternalError::AST); |
1786 | 1.38k | } |
1787 | | |
1788 | | // Check member access |
1789 | 91.2k | if (Token::Match(tok, "%var% .")) { |
1790 | 0 | if (!tok->astParent()) { |
1791 | 0 | throw InternalError( |
1792 | 0 | tok, "Syntax Error: AST broken, '" + tok->str() + "' doesn't have a parent.", InternalError::AST); |
1793 | 0 | } |
1794 | 0 | if (!tok->next()->astOperand1() || !tok->next()->astOperand2()) { |
1795 | 0 | const std::string& op = |
1796 | 0 | tok->next()->originalName().empty() ? tok->next()->str() : tok->next()->originalName(); |
1797 | 0 | throw InternalError( |
1798 | 0 | tok, "Syntax Error: AST broken, '" + op + "' doesn't have two operands.", InternalError::AST); |
1799 | 0 | } |
1800 | 0 | } |
1801 | 91.2k | } |
1802 | 1.36k | } |
1803 | | |
1804 | | std::string TokenList::getOrigFile(const Token *tok) const |
1805 | 1.55k | { |
1806 | 1.55k | return mOrigFiles.at(tok->fileIndex()); |
1807 | 1.55k | } |
1808 | | |
1809 | | const std::string& TokenList::file(const Token *tok) const |
1810 | 3.28k | { |
1811 | 3.28k | return mFiles.at(tok->fileIndex()); |
1812 | 3.28k | } |
1813 | | |
1814 | | std::string TokenList::fileLine(const Token *tok) const |
1815 | 0 | { |
1816 | 0 | return ErrorMessage::FileLocation(tok, this).stringify(); |
1817 | 0 | } |
1818 | | |
1819 | | bool TokenList::validateToken(const Token* tok) const |
1820 | 0 | { |
1821 | 0 | if (!tok) |
1822 | 0 | return true; |
1823 | 0 | for (const Token *t = mTokensFrontBack.front; t; t = t->next()) { |
1824 | 0 | if (tok==t) |
1825 | 0 | return true; |
1826 | 0 | } |
1827 | 0 | return false; |
1828 | 0 | } |
1829 | | |
1830 | | void TokenList::simplifyPlatformTypes() |
1831 | 1.36k | { |
1832 | 1.36k | if (!mSettings) |
1833 | 0 | return; |
1834 | | |
1835 | 1.36k | const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; |
1836 | | |
1837 | 1.36k | enum { isLongLong, isLong, isInt } type; |
1838 | | |
1839 | | /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ |
1840 | | |
1841 | 1.36k | if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_long) |
1842 | 1.36k | type = isLong; |
1843 | 0 | else if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_long_long) |
1844 | 0 | type = isLongLong; |
1845 | 0 | else if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_int) |
1846 | 0 | type = isInt; |
1847 | 0 | else |
1848 | 0 | return; |
1849 | | |
1850 | 94.1k | for (Token *tok = front(); tok; tok = tok->next()) { |
1851 | | // pre-check to reduce unneeded match calls |
1852 | 92.7k | if (!Token::Match(tok, "std| ::| %type%")) |
1853 | 56.9k | continue; |
1854 | 35.7k | bool isUnsigned; |
1855 | 35.7k | if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) { |
1856 | 0 | if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") |
1857 | 0 | continue; |
1858 | 0 | isUnsigned = true; |
1859 | 35.7k | } else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) { |
1860 | 0 | if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") |
1861 | 0 | continue; |
1862 | 0 | isUnsigned = false; |
1863 | 0 | } else |
1864 | 35.7k | continue; |
1865 | | |
1866 | 0 | bool inStd = false; |
1867 | 0 | if (tok->str() == "::") { |
1868 | 0 | tok->deleteThis(); |
1869 | 0 | } else if (tok->str() == "std") { |
1870 | 0 | if (tok->next()->str() != "::") |
1871 | 0 | continue; |
1872 | 0 | inStd = true; |
1873 | 0 | tok->deleteNext(); |
1874 | 0 | tok->deleteThis(); |
1875 | 0 | } |
1876 | | |
1877 | 0 | if (inStd) |
1878 | 0 | tok->originalName("std::" + tok->str()); |
1879 | 0 | else |
1880 | 0 | tok->originalName(tok->str()); |
1881 | 0 | if (isUnsigned) |
1882 | 0 | tok->isUnsigned(true); |
1883 | |
|
1884 | 0 | switch (type) { |
1885 | 0 | case isLongLong: |
1886 | 0 | tok->isLong(true); |
1887 | 0 | tok->str("long"); |
1888 | 0 | break; |
1889 | 0 | case isLong: |
1890 | 0 | tok->str("long"); |
1891 | 0 | break; |
1892 | 0 | case isInt: |
1893 | 0 | tok->str("int"); |
1894 | 0 | break; |
1895 | 0 | } |
1896 | 0 | } |
1897 | | |
1898 | 1.36k | const std::string platform_type(mSettings->platform.toString()); |
1899 | | |
1900 | 94.1k | for (Token *tok = front(); tok; tok = tok->next()) { |
1901 | 92.7k | if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) |
1902 | 60.7k | continue; |
1903 | | |
1904 | 32.0k | const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type); |
1905 | | |
1906 | 32.0k | if (platformtype) { |
1907 | | // check for namespace |
1908 | 0 | if (tok->strAt(-1) == "::") { |
1909 | 0 | const Token * tok1 = tok->tokAt(-2); |
1910 | | // skip when non-global namespace defined |
1911 | 0 | if (tok1 && tok1->tokType() == Token::eName) |
1912 | 0 | continue; |
1913 | 0 | tok = tok->previous(); |
1914 | 0 | tok->deleteThis(); |
1915 | 0 | } |
1916 | 0 | Token *typeToken; |
1917 | 0 | if (platformtype->mConstPtr) { |
1918 | 0 | tok->str("const"); |
1919 | 0 | tok->insertToken("*"); |
1920 | 0 | tok->insertToken(platformtype->mType); |
1921 | 0 | typeToken = tok; |
1922 | 0 | } else if (platformtype->mPointer) { |
1923 | 0 | tok->str(platformtype->mType); |
1924 | 0 | typeToken = tok; |
1925 | 0 | tok->insertToken("*"); |
1926 | 0 | } else if (platformtype->mPtrPtr) { |
1927 | 0 | tok->str(platformtype->mType); |
1928 | 0 | typeToken = tok; |
1929 | 0 | tok->insertToken("*"); |
1930 | 0 | tok->insertToken("*"); |
1931 | 0 | } else { |
1932 | 0 | tok->originalName(tok->str()); |
1933 | 0 | tok->str(platformtype->mType); |
1934 | 0 | typeToken = tok; |
1935 | 0 | } |
1936 | 0 | if (platformtype->mSigned) |
1937 | 0 | typeToken->isSigned(true); |
1938 | 0 | if (platformtype->mUnsigned) |
1939 | 0 | typeToken->isUnsigned(true); |
1940 | 0 | if (platformtype->mLong) |
1941 | 0 | typeToken->isLong(true); |
1942 | 0 | } |
1943 | 32.0k | } |
1944 | 1.36k | } |
1945 | | |
1946 | | void TokenList::simplifyStdType() |
1947 | 1.36k | { |
1948 | 1.36k | auto isVarDeclC = [](const Token* tok) -> bool { |
1949 | 0 | if (!Token::simpleMatch(tok, "}")) |
1950 | 0 | return false; |
1951 | 0 | tok = tok->link()->previous(); |
1952 | 0 | while (Token::Match(tok, "%name%")) { |
1953 | 0 | if (Token::Match(tok, "struct|union|enum")) |
1954 | 0 | return true; |
1955 | 0 | tok = tok->previous(); |
1956 | 0 | } |
1957 | 0 | return false; |
1958 | 0 | }; |
1959 | | |
1960 | 94.1k | for (Token *tok = front(); tok; tok = tok->next()) { |
1961 | | |
1962 | 92.7k | if (isC() && Token::Match(tok, "const|extern *|&|%name%") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { |
1963 | 0 | if (Token::Match(tok->next(), "%name% !!;")) |
1964 | 0 | continue; |
1965 | 0 | if (isVarDeclC(tok->previous())) |
1966 | 0 | continue; |
1967 | | |
1968 | 0 | tok->insertToken("int"); |
1969 | 0 | tok->next()->isImplicitInt(true); |
1970 | 0 | continue; |
1971 | 0 | } |
1972 | | |
1973 | 92.7k | if (Token::Match(tok, "char|short|int|long|unsigned|signed|double|float") || (mSettings->standards.c >= Standards::C99 && Token::Match(tok, "complex|_Complex"))) { |
1974 | 8.55k | bool isFloat= false; |
1975 | 8.55k | bool isSigned = false; |
1976 | 8.55k | bool isUnsigned = false; |
1977 | 8.55k | bool isComplex = false; |
1978 | 8.55k | int countLong = 0; |
1979 | 8.55k | Token* typeSpec = nullptr; |
1980 | | |
1981 | 8.55k | Token* tok2 = tok; |
1982 | 17.1k | for (; tok2->next(); tok2 = tok2->next()) { |
1983 | 17.1k | if (tok2->str() == "long") { |
1984 | 0 | countLong++; |
1985 | 0 | if (!isFloat) |
1986 | 0 | typeSpec = tok2; |
1987 | 17.1k | } else if (tok2->str() == "short") { |
1988 | 0 | typeSpec = tok2; |
1989 | 17.1k | } else if (tok2->str() == "unsigned") |
1990 | 0 | isUnsigned = true; |
1991 | 17.1k | else if (tok2->str() == "signed") |
1992 | 0 | isSigned = true; |
1993 | 17.1k | else if (Token::Match(tok2, "float|double")) { |
1994 | 0 | isFloat = true; |
1995 | 0 | typeSpec = tok2; |
1996 | 17.1k | } else if (mSettings->standards.c >= Standards::C99 && Token::Match(tok2, "complex|_Complex")) |
1997 | 0 | isComplex = !isFloat || tok2->str() == "_Complex" || Token::Match(tok2->next(), "*|&|%name%"); // Ensure that "complex" is not the variables name |
1998 | 17.1k | else if (Token::Match(tok2, "char|int")) { |
1999 | 8.55k | if (!typeSpec) |
2000 | 8.55k | typeSpec = tok2; |
2001 | 8.55k | } else |
2002 | 8.55k | break; |
2003 | 17.1k | } |
2004 | | |
2005 | 8.55k | if (!typeSpec) { // unsigned i; or similar declaration |
2006 | 0 | if (!isComplex) { // Ensure that "complex" is not the variables name |
2007 | 0 | tok->str("int"); |
2008 | 0 | tok->isSigned(isSigned); |
2009 | 0 | tok->isUnsigned(isUnsigned); |
2010 | 0 | tok->isImplicitInt(true); |
2011 | 0 | } |
2012 | 8.55k | } else { |
2013 | 8.55k | typeSpec->isLong(typeSpec->isLong() || (isFloat && countLong == 1) || countLong > 1); |
2014 | 8.55k | typeSpec->isComplex(typeSpec->isComplex() || (isFloat && isComplex)); |
2015 | 8.55k | typeSpec->isSigned(typeSpec->isSigned() || isSigned); |
2016 | 8.55k | typeSpec->isUnsigned(typeSpec->isUnsigned() || isUnsigned); |
2017 | | |
2018 | | // Remove specifiers |
2019 | 8.55k | const Token* tok3 = tok->previous(); |
2020 | 8.55k | tok2 = tok2->previous(); |
2021 | 17.1k | while (tok3 != tok2) { |
2022 | 8.55k | if (tok2 != typeSpec && |
2023 | 8.55k | (isComplex || !Token::Match(tok2, "complex|_Complex"))) // Ensure that "complex" is not the variables name |
2024 | 0 | tok2->deleteThis(); |
2025 | 8.55k | tok2 = tok2->previous(); |
2026 | 8.55k | } |
2027 | 8.55k | } |
2028 | 8.55k | } |
2029 | 92.7k | } |
2030 | 1.36k | } |
2031 | | |
2032 | | bool TokenList::isKeyword(const std::string &str) const |
2033 | 78.3k | { |
2034 | 78.3k | if (mIsCpp) { |
2035 | | // TODO: integrate into keywords? |
2036 | | // types and literals are not handled as keywords |
2037 | 78.3k | static const std::unordered_set<std::string> cpp_types = {"bool", "false", "true"}; |
2038 | 78.3k | if (cpp_types.find(str) != cpp_types.end()) |
2039 | 0 | return false; |
2040 | | |
2041 | | // TODO: properly apply configured standard |
2042 | 78.3k | if (!mSettings || mSettings->standards.cpp >= Standards::CPP20) { |
2043 | 78.3k | static const auto& cpp20_keywords = Keywords::getAll(Standards::cppstd_t::CPP20); |
2044 | 78.3k | return cpp20_keywords.find(str) != cpp20_keywords.end(); |
2045 | 78.3k | } |
2046 | | |
2047 | 0 | static const auto& cpp_keywords = Keywords::getAll(Standards::cppstd_t::CPP11); |
2048 | 0 | return cpp_keywords.find(str) != cpp_keywords.end(); |
2049 | 78.3k | } |
2050 | | |
2051 | | // TODO: integrate into Keywords? |
2052 | | // types are not handled as keywords |
2053 | 0 | static const std::unordered_set<std::string> c_types = {"char", "double", "float", "int", "long", "short"}; |
2054 | 0 | if (c_types.find(str) != c_types.end()) |
2055 | 0 | return false; |
2056 | | |
2057 | | // TODO: use configured standard |
2058 | 0 | static const auto& c_keywords = Keywords::getAll(Standards::cstd_t::C99); |
2059 | 0 | return c_keywords.find(str) != c_keywords.end(); |
2060 | 0 | } |