/src/cppcheck/lib/symboldatabase.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 "symboldatabase.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 "templatesimplifier.h" |
33 | | #include "token.h" |
34 | | #include "tokenize.h" |
35 | | #include "tokenlist.h" |
36 | | #include "utils.h" |
37 | | #include "valueflow.h" |
38 | | |
39 | | #include <algorithm> |
40 | | #include <cassert> |
41 | | #include <cstring> |
42 | | #include <initializer_list> |
43 | | #include <iomanip> |
44 | | #include <iostream> |
45 | | #include <iterator> |
46 | | #include <limits> |
47 | | #include <sstream> // IWYU pragma: keep |
48 | | #include <stack> |
49 | | #include <string> |
50 | | #include <tuple> |
51 | | #include <type_traits> |
52 | | #include <unordered_map> |
53 | | #include <unordered_set> |
54 | | //--------------------------------------------------------------------------- |
55 | | |
56 | | SymbolDatabase::SymbolDatabase(Tokenizer& tokenizer, const Settings& settings, ErrorLogger* errorLogger) |
57 | | : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger) |
58 | 1.36k | { |
59 | 1.36k | if (!mTokenizer.tokens()) |
60 | 0 | return; |
61 | | |
62 | 1.36k | mIsCpp = isCPP(); |
63 | | |
64 | 1.36k | if (mSettings.platform.defaultSign == 's' || mSettings.platform.defaultSign == 'S') |
65 | 1.36k | mDefaultSignedness = ValueType::SIGNED; |
66 | 0 | else if (mSettings.platform.defaultSign == 'u' || mSettings.platform.defaultSign == 'U') |
67 | 0 | mDefaultSignedness = ValueType::UNSIGNED; |
68 | 0 | else |
69 | 0 | mDefaultSignedness = ValueType::UNKNOWN_SIGN; |
70 | | |
71 | 1.36k | createSymbolDatabaseFindAllScopes(); |
72 | 1.36k | createSymbolDatabaseClassInfo(); |
73 | 1.36k | createSymbolDatabaseVariableInfo(); |
74 | 1.36k | createSymbolDatabaseCopyAndMoveConstructors(); |
75 | 1.36k | createSymbolDatabaseFunctionScopes(); |
76 | 1.36k | createSymbolDatabaseClassAndStructScopes(); |
77 | 1.36k | createSymbolDatabaseFunctionReturnTypes(); |
78 | 1.36k | createSymbolDatabaseNeedInitialization(); |
79 | 1.36k | createSymbolDatabaseVariableSymbolTable(); |
80 | 1.36k | createSymbolDatabaseSetScopePointers(); |
81 | 1.36k | createSymbolDatabaseSetVariablePointers(); |
82 | 1.36k | setValueTypeInTokenList(false); |
83 | 1.36k | createSymbolDatabaseSetTypePointers(); |
84 | 1.36k | createSymbolDatabaseSetFunctionPointers(true); |
85 | 1.36k | createSymbolDatabaseSetSmartPointerType(); |
86 | 1.36k | setValueTypeInTokenList(false); |
87 | 1.36k | createSymbolDatabaseEnums(); |
88 | 1.36k | createSymbolDatabaseEscapeFunctions(); |
89 | 1.36k | createSymbolDatabaseIncompleteVars(); |
90 | 1.36k | createSymbolDatabaseExprIds(); |
91 | 1.36k | debugSymbolDatabase(); |
92 | 1.36k | } |
93 | | |
94 | | static const Token* skipScopeIdentifiers(const Token* tok) |
95 | 27.0k | { |
96 | 27.0k | if (Token::Match(tok, ":: %name%")) |
97 | 0 | tok = tok->next(); |
98 | 27.0k | while (Token::Match(tok, "%name% ::") || |
99 | 27.0k | (Token::Match(tok, "%name% <") && Token::Match(tok->linkAt(1), ">|>> ::"))) { |
100 | 0 | if (tok->strAt(1) == "::") |
101 | 0 | tok = tok->tokAt(2); |
102 | 0 | else |
103 | 0 | tok = tok->linkAt(1)->tokAt(2); |
104 | 0 | } |
105 | | |
106 | 27.0k | return tok; |
107 | 27.0k | } |
108 | | |
109 | | static bool isExecutableScope(const Token* tok) |
110 | 0 | { |
111 | 0 | if (!Token::simpleMatch(tok, "{")) |
112 | 0 | return false; |
113 | 0 | const Token * tok2 = tok->link()->previous(); |
114 | 0 | if (Token::simpleMatch(tok2, "; }")) |
115 | 0 | return true; |
116 | 0 | if (tok2 == tok) |
117 | 0 | return false; |
118 | 0 | if (Token::simpleMatch(tok2, "} }")) { // inner scope |
119 | 0 | const Token* startTok = tok2->link(); |
120 | 0 | if (Token::Match(startTok->previous(), "do|try|else {")) |
121 | 0 | return true; |
122 | 0 | if (Token::Match(startTok->previous(), ")|] {")) |
123 | 0 | return !findLambdaStartToken(tok2); |
124 | 0 | return isExecutableScope(startTok); |
125 | 0 | } |
126 | 0 | return false; |
127 | 0 | } |
128 | | |
129 | | void SymbolDatabase::createSymbolDatabaseFindAllScopes() |
130 | 1.36k | { |
131 | | // create global scope |
132 | 1.36k | scopeList.emplace_back(this, nullptr, nullptr); |
133 | | |
134 | | // pointer to current scope |
135 | 1.36k | Scope *scope = &scopeList.back(); |
136 | | |
137 | | // Store the ending of init lists |
138 | 1.36k | std::stack<std::pair<const Token*, const Scope*>> endInitList; |
139 | 69.2k | auto inInitList = [&] { |
140 | 69.2k | if (endInitList.empty()) |
141 | 69.2k | return false; |
142 | 0 | return endInitList.top().second == scope; |
143 | 69.2k | }; |
144 | | |
145 | 1.36k | auto addLambda = [this, &scope](const Token* tok, const Token* lambdaEndToken) -> const Token* { |
146 | 0 | const Token* lambdaStartToken = lambdaEndToken->link(); |
147 | 0 | const Token* argStart = lambdaStartToken->astParent(); |
148 | 0 | const Token* funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent(); |
149 | 0 | const Function* function = addGlobalFunction(scope, tok, argStart, funcStart); |
150 | 0 | if (!function) |
151 | 0 | mTokenizer.syntaxError(tok); |
152 | 0 | return lambdaStartToken; |
153 | 0 | }; |
154 | | |
155 | | // Store current access in each scope (depends on evaluation progress) |
156 | 1.36k | std::map<const Scope*, AccessControl> access; |
157 | | |
158 | | // find all scopes |
159 | 74.2k | for (const Token *tok = mTokenizer.tokens(); tok; tok = tok ? tok->next() : nullptr) { |
160 | | // #5593 suggested to add here: |
161 | 72.8k | if (mErrorLogger) |
162 | 72.8k | mErrorLogger->reportProgress(mTokenizer.list.getSourceFilePath(), |
163 | 72.8k | "SymbolDatabase", |
164 | 72.8k | tok->progressValue()); |
165 | | // Locate next class |
166 | 72.8k | if ((mTokenizer.isCPP() && tok->isKeyword() && |
167 | 72.8k | ((Token::Match(tok, "class|struct|union|namespace ::| %name% final| {|:|::|<") && |
168 | 3.75k | !Token::Match(tok->previous(), "new|friend|const|enum|typedef|mutable|volatile|using|)|(|<")) || |
169 | 3.75k | (Token::Match(tok, "enum class| %name% {") || |
170 | 3.75k | Token::Match(tok, "enum class| %name% : %name% {")))) |
171 | 72.8k | || (mTokenizer.isC() && tok->isKeyword() && Token::Match(tok, "struct|union|enum %name% {"))) { |
172 | 0 | const Token *tok2 = tok->tokAt(2); |
173 | |
|
174 | 0 | if (tok->strAt(1) == "::") |
175 | 0 | tok2 = tok2->next(); |
176 | 0 | else if (mTokenizer.isCPP() && tok->strAt(1) == "class") |
177 | 0 | tok2 = tok2->next(); |
178 | |
|
179 | 0 | while (Token::Match(tok2, ":: %name%")) |
180 | 0 | tok2 = tok2->tokAt(2); |
181 | 0 | while (Token::Match(tok2, "%name% :: %name%")) |
182 | 0 | tok2 = tok2->tokAt(2); |
183 | | |
184 | | // skip over template args |
185 | 0 | while (tok2 && tok2->str() == "<" && tok2->link()) { |
186 | 0 | tok2 = tok2->link()->next(); |
187 | 0 | while (Token::Match(tok2, ":: %name%")) |
188 | 0 | tok2 = tok2->tokAt(2); |
189 | 0 | } |
190 | | |
191 | | // skip over final |
192 | 0 | if (mTokenizer.isCPP() && Token::simpleMatch(tok2, "final")) |
193 | 0 | tok2 = tok2->next(); |
194 | | |
195 | | // make sure we have valid code |
196 | 0 | if (!Token::Match(tok2, "{|:")) { |
197 | | // check for qualified variable |
198 | 0 | if (tok2 && tok2->next()) { |
199 | 0 | if (tok2->next()->str() == ";") |
200 | 0 | tok = tok2->next(); |
201 | 0 | else if (Token::simpleMatch(tok2->next(), "= {") && |
202 | 0 | Token::simpleMatch(tok2->linkAt(2), "} ;")) |
203 | 0 | tok = tok2->linkAt(2)->next(); |
204 | 0 | else if (Token::Match(tok2->next(), "(|{") && |
205 | 0 | tok2->next()->link()->strAt(1) == ";") |
206 | 0 | tok = tok2->next()->link()->next(); |
207 | | // skip variable declaration |
208 | 0 | else if (Token::Match(tok2, "*|&|>")) |
209 | 0 | continue; |
210 | 0 | else if (Token::Match(tok2, "%name% (") && mTokenizer.isFunctionHead(tok2->next(), "{;")) |
211 | 0 | continue; |
212 | 0 | else if (Token::Match(tok2, "%name% [|=")) |
213 | 0 | continue; |
214 | | // skip template |
215 | 0 | else if (Token::simpleMatch(tok2, ";") && |
216 | 0 | Token::Match(tok->previous(), "template|> class|struct")) { |
217 | 0 | tok = tok2; |
218 | 0 | continue; |
219 | 0 | } |
220 | | // forward declaration |
221 | 0 | else if (Token::simpleMatch(tok2, ";") && |
222 | 0 | Token::Match(tok, "class|struct|union")) { |
223 | | // TODO: see if it can be used |
224 | 0 | tok = tok2; |
225 | 0 | continue; |
226 | 0 | } |
227 | | // skip constructor |
228 | 0 | else if (Token::simpleMatch(tok2, "(") && |
229 | 0 | Token::simpleMatch(tok2->link(), ") ;")) { |
230 | 0 | tok = tok2->link()->next(); |
231 | 0 | continue; |
232 | 0 | } else |
233 | 0 | throw InternalError(tok2, "SymbolDatabase bailout; unhandled code", InternalError::SYNTAX); |
234 | 0 | continue; |
235 | 0 | } |
236 | 0 | break; // bail |
237 | 0 | } |
238 | | |
239 | 0 | const Token * name = tok->next(); |
240 | |
|
241 | 0 | if (name->str() == "class" && name->strAt(-1) == "enum") |
242 | 0 | name = name->next(); |
243 | |
|
244 | 0 | Scope *new_scope = findScope(name, scope); |
245 | |
|
246 | 0 | if (new_scope) { |
247 | | // only create base list for classes and structures |
248 | 0 | if (new_scope->isClassOrStruct()) { |
249 | | // goto initial '{' |
250 | 0 | if (!new_scope->definedType) |
251 | 0 | mTokenizer.syntaxError(nullptr); // #6808 |
252 | 0 | tok2 = new_scope->definedType->initBaseInfo(tok, tok2); |
253 | | // make sure we have valid code |
254 | 0 | if (!tok2) { |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | // definition may be different than declaration |
260 | 0 | if (mTokenizer.isCPP() && tok->str() == "class") { |
261 | 0 | access[new_scope] = AccessControl::Private; |
262 | 0 | new_scope->type = Scope::eClass; |
263 | 0 | } else if (tok->str() == "struct") { |
264 | 0 | access[new_scope] = AccessControl::Public; |
265 | 0 | new_scope->type = Scope::eStruct; |
266 | 0 | } |
267 | |
|
268 | 0 | new_scope->classDef = tok; |
269 | 0 | new_scope->setBodyStartEnd(tok2); |
270 | | // make sure we have valid code |
271 | 0 | if (!new_scope->bodyEnd) { |
272 | 0 | mTokenizer.syntaxError(tok); |
273 | 0 | } |
274 | 0 | scope = new_scope; |
275 | 0 | tok = tok2; |
276 | 0 | } else { |
277 | 0 | scopeList.emplace_back(this, tok, scope); |
278 | 0 | new_scope = &scopeList.back(); |
279 | |
|
280 | 0 | if (tok->str() == "class") |
281 | 0 | access[new_scope] = AccessControl::Private; |
282 | 0 | else if (tok->str() == "struct" || tok->str() == "union") |
283 | 0 | access[new_scope] = AccessControl::Public; |
284 | | |
285 | | // fill typeList... |
286 | 0 | if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) { |
287 | 0 | Type* new_type = findType(name, scope); |
288 | 0 | if (!new_type) { |
289 | 0 | typeList.emplace_back(new_scope->classDef, new_scope, scope); |
290 | 0 | new_type = &typeList.back(); |
291 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
292 | 0 | } else |
293 | 0 | new_type->classScope = new_scope; |
294 | 0 | new_scope->definedType = new_type; |
295 | 0 | } |
296 | | |
297 | | // only create base list for classes and structures |
298 | 0 | if (new_scope->isClassOrStruct()) { |
299 | | // goto initial '{' |
300 | 0 | tok2 = new_scope->definedType->initBaseInfo(tok, tok2); |
301 | | |
302 | | // make sure we have valid code |
303 | 0 | if (!tok2) { |
304 | 0 | mTokenizer.syntaxError(tok); |
305 | 0 | } |
306 | 0 | } else if (new_scope->type == Scope::eEnum) { |
307 | 0 | if (tok2->str() == ":") |
308 | 0 | tok2 = tok2->tokAt(2); |
309 | 0 | } |
310 | |
|
311 | 0 | new_scope->setBodyStartEnd(tok2); |
312 | | |
313 | | // make sure we have valid code |
314 | 0 | if (!new_scope->bodyEnd) { |
315 | 0 | mTokenizer.syntaxError(tok); |
316 | 0 | } |
317 | |
|
318 | 0 | if (new_scope->type == Scope::eEnum) { |
319 | 0 | tok2 = new_scope->addEnum(tok, mTokenizer.isCPP()); |
320 | 0 | scope->nestedList.push_back(new_scope); |
321 | |
|
322 | 0 | if (!tok2) |
323 | 0 | mTokenizer.syntaxError(tok); |
324 | 0 | } else { |
325 | | // make the new scope the current scope |
326 | 0 | scope->nestedList.push_back(new_scope); |
327 | 0 | scope = new_scope; |
328 | 0 | } |
329 | |
|
330 | 0 | tok = tok2; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | // Namespace and unknown macro (#3854) |
335 | 72.8k | else if (mTokenizer.isCPP() && tok->isKeyword() && |
336 | 72.8k | Token::Match(tok, "namespace %name% %type% (") && |
337 | 72.8k | tok->tokAt(2)->isUpperCaseName() && |
338 | 72.8k | Token::simpleMatch(tok->linkAt(3), ") {")) { |
339 | 0 | scopeList.emplace_back(this, tok, scope); |
340 | |
|
341 | 0 | Scope *new_scope = &scopeList.back(); |
342 | 0 | access[new_scope] = AccessControl::Public; |
343 | |
|
344 | 0 | const Token *tok2 = tok->linkAt(3)->next(); |
345 | |
|
346 | 0 | new_scope->setBodyStartEnd(tok2); |
347 | | |
348 | | // make sure we have valid code |
349 | 0 | if (!new_scope->bodyEnd) { |
350 | 0 | scopeList.pop_back(); |
351 | 0 | break; |
352 | 0 | } |
353 | | |
354 | | // make the new scope the current scope |
355 | 0 | scope->nestedList.push_back(new_scope); |
356 | 0 | scope = &scopeList.back(); |
357 | |
|
358 | 0 | tok = tok2; |
359 | 0 | } |
360 | | |
361 | | // forward declaration |
362 | 72.8k | else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;") && |
363 | 72.8k | tok->strAt(-1) != "friend") { |
364 | 0 | if (!findType(tok->next(), scope)) { |
365 | | // fill typeList.. |
366 | 0 | typeList.emplace_back(tok, nullptr, scope); |
367 | 0 | Type* new_type = &typeList.back(); |
368 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
369 | 0 | } |
370 | 0 | tok = tok->tokAt(2); |
371 | 0 | } |
372 | | |
373 | | // using namespace |
374 | 72.8k | else if (mTokenizer.isCPP() && tok->isKeyword() && Token::Match(tok, "using namespace ::| %type% ;|::")) { |
375 | 0 | Scope::UsingInfo using_info; |
376 | |
|
377 | 0 | using_info.start = tok; // save location |
378 | 0 | using_info.scope = findNamespace(tok->tokAt(2), scope); |
379 | |
|
380 | 0 | scope->usingList.push_back(using_info); |
381 | | |
382 | | // check for global namespace |
383 | 0 | if (tok->strAt(2) == "::") |
384 | 0 | tok = tok->tokAt(4); |
385 | 0 | else |
386 | 0 | tok = tok->tokAt(3); |
387 | | |
388 | | // skip over qualification |
389 | 0 | while (Token::Match(tok, "%type% ::")) |
390 | 0 | tok = tok->tokAt(2); |
391 | 0 | } |
392 | | |
393 | | // using type alias |
394 | 72.8k | else if (mTokenizer.isCPP() && tok->isKeyword() && Token::Match(tok, "using %name% =")) { |
395 | 0 | if (tok->strAt(-1) != ">" && !findType(tok->next(), scope)) { |
396 | | // fill typeList.. |
397 | 0 | typeList.emplace_back(tok, nullptr, scope); |
398 | 0 | Type* new_type = &typeList.back(); |
399 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
400 | 0 | } |
401 | |
|
402 | 0 | tok = tok->tokAt(3); |
403 | |
|
404 | 0 | while (tok && tok->str() != ";") { |
405 | 0 | if (Token::simpleMatch(tok, "decltype (")) |
406 | 0 | tok = tok->linkAt(1); |
407 | 0 | else |
408 | 0 | tok = tok->next(); |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | // unnamed struct and union |
413 | 72.8k | else if (tok->isKeyword() && Token::Match(tok, "struct|union {") && |
414 | 72.8k | Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) { |
415 | 0 | scopeList.emplace_back(this, tok, scope); |
416 | |
|
417 | 0 | Scope *new_scope = &scopeList.back(); |
418 | 0 | access[new_scope] = AccessControl::Public; |
419 | |
|
420 | 0 | const Token* varNameTok = tok->next()->link()->next(); |
421 | 0 | if (varNameTok->str() == "*") { |
422 | 0 | varNameTok = varNameTok->next(); |
423 | 0 | } else if (varNameTok->str() == "&") { |
424 | 0 | varNameTok = varNameTok->next(); |
425 | 0 | } |
426 | |
|
427 | 0 | typeList.emplace_back(tok, new_scope, scope); |
428 | 0 | { |
429 | 0 | Type* new_type = &typeList.back(); |
430 | 0 | new_scope->definedType = new_type; |
431 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
432 | 0 | } |
433 | |
|
434 | 0 | scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope, &mSettings); |
435 | |
|
436 | 0 | const Token *tok2 = tok->next(); |
437 | |
|
438 | 0 | new_scope->setBodyStartEnd(tok2); |
439 | | |
440 | | // make sure we have valid code |
441 | 0 | if (!new_scope->bodyEnd) { |
442 | 0 | scopeList.pop_back(); |
443 | 0 | break; |
444 | 0 | } |
445 | | |
446 | | // make the new scope the current scope |
447 | 0 | scope->nestedList.push_back(new_scope); |
448 | 0 | scope = new_scope; |
449 | |
|
450 | 0 | tok = tok2; |
451 | 0 | } |
452 | | |
453 | | // anonymous struct, union and namespace |
454 | 72.8k | else if (tok->isKeyword() && ((Token::Match(tok, "struct|union {") && |
455 | 3.75k | Token::simpleMatch(tok->next()->link(), "} ;")) || |
456 | 3.75k | Token::simpleMatch(tok, "namespace {"))) { |
457 | 0 | scopeList.emplace_back(this, tok, scope); |
458 | |
|
459 | 0 | Scope *new_scope = &scopeList.back(); |
460 | 0 | access[new_scope] = AccessControl::Public; |
461 | |
|
462 | 0 | const Token *tok2 = tok->next(); |
463 | |
|
464 | 0 | new_scope->setBodyStartEnd(tok2); |
465 | |
|
466 | 0 | typeList.emplace_back(tok, new_scope, scope); |
467 | 0 | { |
468 | 0 | Type* new_type = &typeList.back(); |
469 | 0 | new_scope->definedType = new_type; |
470 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
471 | 0 | } |
472 | | |
473 | | // make sure we have valid code |
474 | 0 | if (!new_scope->bodyEnd) { |
475 | 0 | scopeList.pop_back(); |
476 | 0 | break; |
477 | 0 | } |
478 | | |
479 | | // make the new scope the current scope |
480 | 0 | scope->nestedList.push_back(new_scope); |
481 | 0 | scope = new_scope; |
482 | |
|
483 | 0 | tok = tok2; |
484 | 0 | } |
485 | | |
486 | | // forward declared enum |
487 | 72.8k | else if (tok->isKeyword() && (Token::Match(tok, "enum class| %name% ;") || Token::Match(tok, "enum class| %name% : %name% ;"))) { |
488 | 0 | typeList.emplace_back(tok, nullptr, scope); |
489 | 0 | Type* new_type = &typeList.back(); |
490 | 0 | scope->definedTypesMap[new_type->name()] = new_type; |
491 | 0 | tok = tok->tokAt(2); |
492 | 0 | } |
493 | | |
494 | | // check for end of scope |
495 | 72.8k | else if (tok == scope->bodyEnd) { |
496 | 3.58k | do { |
497 | 3.58k | access.erase(scope); |
498 | 3.58k | scope = const_cast<Scope*>(scope->nestedIn); |
499 | 3.58k | } while (scope->type != Scope::eGlobal && succeeds(tok, scope->bodyEnd)); |
500 | 3.58k | continue; |
501 | 3.58k | } |
502 | | // check for end of init list |
503 | 69.2k | else if (inInitList() && tok == endInitList.top().first) { |
504 | 0 | endInitList.pop(); |
505 | 0 | continue; |
506 | 0 | } |
507 | | |
508 | | // check if in class or structure or union |
509 | 69.2k | else if (scope->isClassOrStructOrUnion()) { |
510 | 0 | const Token *funcStart = nullptr; |
511 | 0 | const Token *argStart = nullptr; |
512 | 0 | const Token *declEnd = nullptr; |
513 | | |
514 | | // What section are we in.. |
515 | 0 | if (tok->str() == "private:") |
516 | 0 | access[scope] = AccessControl::Private; |
517 | 0 | else if (tok->str() == "protected:") |
518 | 0 | access[scope] = AccessControl::Protected; |
519 | 0 | else if (tok->str() == "public:" || tok->str() == "__published:") |
520 | 0 | access[scope] = AccessControl::Public; |
521 | 0 | else if (Token::Match(tok, "public|protected|private %name% :")) { |
522 | 0 | if (tok->str() == "private") |
523 | 0 | access[scope] = AccessControl::Private; |
524 | 0 | else if (tok->str() == "protected") |
525 | 0 | access[scope] = AccessControl::Protected; |
526 | 0 | else |
527 | 0 | access[scope] = AccessControl::Public; |
528 | |
|
529 | 0 | tok = tok->tokAt(2); |
530 | 0 | } |
531 | | |
532 | | // class function? |
533 | 0 | else if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { |
534 | 0 | if (tok->previous()->str() != "::" || tok->strAt(-2) == scope->className) { |
535 | 0 | Function function(&mTokenizer, tok, scope, funcStart, argStart); |
536 | | |
537 | | // save the access type |
538 | 0 | function.access = access[scope]; |
539 | |
|
540 | 0 | const Token *end = function.argDef->link(); |
541 | | |
542 | | // count the number of constructors |
543 | 0 | if (function.isConstructor()) |
544 | 0 | scope->numConstructors++; |
545 | | |
546 | | // assume implementation is inline (definition and implementation same) |
547 | 0 | function.token = function.tokenDef; |
548 | 0 | function.arg = function.argDef; |
549 | | |
550 | | // out of line function |
551 | 0 | if (const Token *endTok = mTokenizer.isFunctionHead(end, ";")) { |
552 | 0 | tok = endTok; |
553 | 0 | scope->addFunction(function); |
554 | 0 | } |
555 | | |
556 | | // inline function |
557 | 0 | else { |
558 | | // find start of function '{' |
559 | 0 | bool foundInitList = false; |
560 | 0 | while (end && end->str() != "{" && end->str() != ";") { |
561 | 0 | if (end->link() && Token::Match(end, "(|<")) { |
562 | 0 | end = end->link(); |
563 | 0 | } else if (foundInitList && |
564 | 0 | Token::Match(end, "%name%|> {") && |
565 | 0 | Token::Match(end->linkAt(1), "} ,|{")) { |
566 | 0 | end = end->linkAt(1); |
567 | 0 | } else { |
568 | 0 | if (end->str() == ":") |
569 | 0 | foundInitList = true; |
570 | 0 | end = end->next(); |
571 | 0 | } |
572 | 0 | } |
573 | |
|
574 | 0 | if (!end || end->str() == ";") |
575 | 0 | continue; |
576 | | |
577 | 0 | scope->addFunction(function); |
578 | |
|
579 | 0 | Function* funcptr = &scope->functionList.back(); |
580 | 0 | const Token *tok2 = funcStart; |
581 | |
|
582 | 0 | addNewFunction(&scope, &tok2); |
583 | 0 | if (scope) { |
584 | 0 | scope->functionOf = function.nestedIn; |
585 | 0 | scope->function = funcptr; |
586 | 0 | scope->function->functionScope = scope; |
587 | 0 | } |
588 | |
|
589 | 0 | tok = tok2; |
590 | 0 | } |
591 | 0 | } |
592 | | |
593 | | // nested class or friend function? |
594 | 0 | else { |
595 | | /** @todo check entire qualification for match */ |
596 | 0 | const Scope * const nested = scope->findInNestedListRecursive(tok->strAt(-2)); |
597 | |
|
598 | 0 | if (nested) |
599 | 0 | addClassFunction(&scope, &tok, argStart); |
600 | 0 | else { |
601 | | /** @todo handle friend functions */ |
602 | 0 | } |
603 | 0 | } |
604 | 0 | } |
605 | | |
606 | | // friend class declaration? |
607 | 0 | else if (mTokenizer.isCPP() && tok->isKeyword() && Token::Match(tok, "friend class|struct| ::| %any% ;|::")) { |
608 | 0 | Type::FriendInfo friendInfo; |
609 | | |
610 | | // save the name start |
611 | 0 | friendInfo.nameStart = tok->strAt(1) == "class" ? tok->tokAt(2) : tok->next(); |
612 | 0 | friendInfo.nameEnd = friendInfo.nameStart; |
613 | | |
614 | | // skip leading "::" |
615 | 0 | if (friendInfo.nameEnd->str() == "::") |
616 | 0 | friendInfo.nameEnd = friendInfo.nameEnd->next(); |
617 | | |
618 | | // skip qualification "name ::" |
619 | 0 | while (friendInfo.nameEnd && friendInfo.nameEnd->strAt(1) == "::") |
620 | 0 | friendInfo.nameEnd = friendInfo.nameEnd->tokAt(2); |
621 | | |
622 | | // fill this in after parsing is complete |
623 | 0 | friendInfo.type = nullptr; |
624 | |
|
625 | 0 | if (!scope->definedType) |
626 | 0 | mTokenizer.syntaxError(tok); |
627 | |
|
628 | 0 | scope->definedType->friendList.push_back(friendInfo); |
629 | 0 | } |
630 | 69.2k | } else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) { |
631 | 51.1k | const Token *funcStart = nullptr; |
632 | 51.1k | const Token *argStart = nullptr; |
633 | 51.1k | const Token *declEnd = nullptr; |
634 | | |
635 | | // function? |
636 | 51.1k | if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { |
637 | | // has body? |
638 | 1.73k | if (declEnd && declEnd->str() == "{") { |
639 | 1.73k | tok = funcStart; |
640 | | |
641 | | // class function |
642 | 1.73k | if (tok->previous() && tok->previous()->str() == "::") |
643 | 0 | addClassFunction(&scope, &tok, argStart); |
644 | | |
645 | | // class destructor |
646 | 1.73k | else if (tok->previous() && |
647 | 1.73k | tok->previous()->str() == "~" && |
648 | 1.73k | tok->strAt(-2) == "::") |
649 | 0 | addClassFunction(&scope, &tok, argStart); |
650 | | |
651 | | // regular function |
652 | 1.73k | else { |
653 | 1.73k | const Function* const function = addGlobalFunction(scope, tok, argStart, funcStart); |
654 | | |
655 | 1.73k | if (!function) |
656 | 0 | mTokenizer.syntaxError(tok); |
657 | 1.73k | } |
658 | | |
659 | | // syntax error? |
660 | 1.73k | if (!scope) |
661 | 0 | mTokenizer.syntaxError(tok); |
662 | 1.73k | } |
663 | | // function prototype? |
664 | 0 | else if (declEnd && declEnd->str() == ";") { |
665 | 0 | if (tok->astParent() && tok->astParent()->str() == "::" && |
666 | 0 | Token::Match(declEnd->previous(), "default|delete")) { |
667 | 0 | addClassFunction(&scope, &tok, argStart); |
668 | 0 | continue; |
669 | 0 | } |
670 | | |
671 | 0 | bool newFunc = true; // Is this function already in the database? |
672 | 0 | auto range = scope->functionMap.equal_range(tok->str()); |
673 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
674 | 0 | if (it->second->argsMatch(scope, it->second->argDef, argStart, emptyString, 0)) { |
675 | 0 | newFunc = false; |
676 | 0 | break; |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | // save function prototype in database |
681 | 0 | if (newFunc) { |
682 | 0 | addGlobalFunctionDecl(scope, tok, argStart, funcStart); |
683 | 0 | } |
684 | |
|
685 | 0 | tok = declEnd; |
686 | 0 | continue; |
687 | 0 | } |
688 | 49.3k | } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { |
689 | 0 | tok = addLambda(tok, lambdaEndToken); |
690 | 0 | } |
691 | 51.1k | } else if (scope->isExecutable()) { |
692 | 18.1k | if (tok->isKeyword() && Token::Match(tok, "else|try|do {")) { |
693 | 457 | const Token* tok1 = tok->next(); |
694 | 457 | if (tok->str() == "else") |
695 | 457 | scopeList.emplace_back(this, tok, scope, Scope::eElse, tok1); |
696 | 0 | else if (tok->str() == "do") |
697 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eDo, tok1); |
698 | 0 | else //if (tok->str() == "try") |
699 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eTry, tok1); |
700 | | |
701 | 457 | tok = tok1; |
702 | 457 | scope->nestedList.push_back(&scopeList.back()); |
703 | 457 | scope = &scopeList.back(); |
704 | 17.6k | } else if (tok->isKeyword() && Token::Match(tok, "if|for|while|catch|switch (") && Token::simpleMatch(tok->next()->link(), ") {")) { |
705 | 1.38k | const Token *scopeStartTok = tok->next()->link()->next(); |
706 | 1.38k | if (tok->str() == "if") |
707 | 919 | scopeList.emplace_back(this, tok, scope, Scope::eIf, scopeStartTok); |
708 | 468 | else if (tok->str() == "for") { |
709 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eFor, scopeStartTok); |
710 | 468 | } else if (tok->str() == "while") |
711 | 468 | scopeList.emplace_back(this, tok, scope, Scope::eWhile, scopeStartTok); |
712 | 0 | else if (tok->str() == "catch") { |
713 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eCatch, scopeStartTok); |
714 | 0 | } else // if (tok->str() == "switch") |
715 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eSwitch, scopeStartTok); |
716 | | |
717 | 1.38k | scope->nestedList.push_back(&scopeList.back()); |
718 | 1.38k | scope = &scopeList.back(); |
719 | 1.38k | if (scope->type == Scope::eFor) |
720 | 0 | scope->checkVariable(tok->tokAt(2), AccessControl::Local, &mSettings); // check for variable declaration and add it to new scope if found |
721 | 1.38k | else if (scope->type == Scope::eCatch) |
722 | 0 | scope->checkVariable(tok->tokAt(2), AccessControl::Throw, &mSettings); // check for variable declaration and add it to new scope if found |
723 | 1.38k | tok = scopeStartTok; |
724 | 16.3k | } else if (Token::Match(tok, "%var% {")) { |
725 | 0 | endInitList.emplace(tok->next()->link(), scope); |
726 | 0 | tok = tok->next(); |
727 | 16.3k | } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { |
728 | 0 | tok = addLambda(tok, lambdaEndToken); |
729 | 16.3k | } else if (tok->str() == "{") { |
730 | 0 | if (inInitList()) { |
731 | 0 | endInitList.emplace(tok->link(), scope); |
732 | 0 | } else if (isExecutableScope(tok)) { |
733 | 0 | scopeList.emplace_back(this, tok, scope, Scope::eUnconditional, tok); |
734 | 0 | scope->nestedList.push_back(&scopeList.back()); |
735 | 0 | scope = &scopeList.back(); |
736 | 0 | } else if (scope->isExecutable()) { |
737 | 0 | endInitList.emplace(tok->link(), scope); |
738 | 0 | } else { |
739 | 0 | tok = tok->link(); |
740 | 0 | } |
741 | 0 | } |
742 | | // syntax error? |
743 | 18.1k | if (!scope) |
744 | 0 | mTokenizer.syntaxError(tok); |
745 | | // End of scope or list should be handled above |
746 | 18.1k | if (tok->str() == "}") |
747 | 0 | mTokenizer.syntaxError(tok); |
748 | 18.1k | } |
749 | 72.8k | } |
750 | 1.36k | } |
751 | | |
752 | | void SymbolDatabase::createSymbolDatabaseClassInfo() |
753 | 1.36k | { |
754 | 1.36k | if (mTokenizer.isC()) |
755 | 0 | return; |
756 | | |
757 | | // fill in using info |
758 | 4.94k | for (Scope& scope : scopeList) { |
759 | 4.94k | for (Scope::UsingInfo& usingInfo : scope.usingList) { |
760 | | // only find if not already found |
761 | 0 | if (usingInfo.scope == nullptr) { |
762 | | // check scope for match |
763 | 0 | const Scope * const found = findScope(usingInfo.start->tokAt(2), &scope); |
764 | 0 | if (found) { |
765 | | // set found scope |
766 | 0 | usingInfo.scope = found; |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | } |
770 | 0 | } |
771 | 4.94k | } |
772 | | |
773 | | // fill in base class info |
774 | 1.36k | for (Type& type : typeList) { |
775 | | // finish filling in base class info |
776 | 0 | for (Type::BaseInfo & i : type.derivedFrom) { |
777 | 0 | const Type* found = findType(i.nameTok, type.enclosingScope, /*lookOutside*/ true); |
778 | 0 | if (found && found->findDependency(&type)) { |
779 | | // circular dependency |
780 | | //mTokenizer.syntaxError(nullptr); |
781 | 0 | } else { |
782 | 0 | i.type = found; |
783 | 0 | } |
784 | 0 | } |
785 | 0 | } |
786 | | |
787 | | // fill in friend info |
788 | 1.36k | for (Type & type : typeList) { |
789 | 0 | for (Type::FriendInfo &friendInfo : type.friendList) { |
790 | 0 | friendInfo.type = findType(friendInfo.nameStart, type.enclosingScope); |
791 | 0 | } |
792 | 0 | } |
793 | 1.36k | } |
794 | | |
795 | | |
796 | | void SymbolDatabase::createSymbolDatabaseVariableInfo() |
797 | 1.36k | { |
798 | | // fill in variable info |
799 | 4.94k | for (Scope& scope : scopeList) { |
800 | | // find variables |
801 | 4.94k | scope.getVariableList(&mSettings); |
802 | 4.94k | } |
803 | | |
804 | | // fill in function arguments |
805 | 4.94k | for (Scope& scope : scopeList) { |
806 | 4.94k | std::list<Function>::iterator func; |
807 | | |
808 | 6.68k | for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { |
809 | | // add arguments |
810 | 1.73k | func->addArguments(this, &scope); |
811 | 1.73k | } |
812 | 4.94k | } |
813 | 1.36k | } |
814 | | |
815 | | void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors() |
816 | 1.36k | { |
817 | | // fill in class and struct copy/move constructors |
818 | 4.94k | for (Scope& scope : scopeList) { |
819 | 4.94k | if (!scope.isClassOrStruct()) |
820 | 4.94k | continue; |
821 | | |
822 | 0 | std::list<Function>::iterator func; |
823 | 0 | for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { |
824 | 0 | if (!func->isConstructor() || func->minArgCount() != 1) |
825 | 0 | continue; |
826 | | |
827 | 0 | const Variable* firstArg = func->getArgumentVar(0); |
828 | 0 | if (firstArg->type() == scope.definedType) { |
829 | 0 | if (firstArg->isRValueReference()) |
830 | 0 | func->type = Function::eMoveConstructor; |
831 | 0 | else if (firstArg->isReference() && !firstArg->isPointer()) |
832 | 0 | func->type = Function::eCopyConstructor; |
833 | 0 | } |
834 | |
|
835 | 0 | if (func->type == Function::eCopyConstructor || |
836 | 0 | func->type == Function::eMoveConstructor) |
837 | 0 | scope.numCopyOrMoveConstructors++; |
838 | 0 | } |
839 | 0 | } |
840 | 1.36k | } |
841 | | |
842 | | void SymbolDatabase::createSymbolDatabaseFunctionScopes() |
843 | 1.36k | { |
844 | | // fill in function scopes |
845 | 4.94k | for (const Scope & scope : scopeList) { |
846 | 4.94k | if (scope.type == Scope::eFunction) |
847 | 1.73k | functionScopes.push_back(&scope); |
848 | 4.94k | } |
849 | 1.36k | } |
850 | | |
851 | | void SymbolDatabase::createSymbolDatabaseClassAndStructScopes() |
852 | 1.36k | { |
853 | | // fill in class and struct scopes |
854 | 4.94k | for (const Scope& scope : scopeList) { |
855 | 4.94k | if (scope.isClassOrStruct()) |
856 | 0 | classAndStructScopes.push_back(&scope); |
857 | 4.94k | } |
858 | 1.36k | } |
859 | | |
860 | | void SymbolDatabase::createSymbolDatabaseFunctionReturnTypes() |
861 | 1.36k | { |
862 | | // fill in function return types |
863 | 4.94k | for (Scope& scope : scopeList) { |
864 | 4.94k | std::list<Function>::iterator func; |
865 | | |
866 | 6.68k | for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { |
867 | | // add return types |
868 | 1.73k | if (func->retDef) { |
869 | 1.73k | const Token *type = func->retDef; |
870 | 1.73k | while (Token::Match(type, "static|const|struct|union|enum")) |
871 | 0 | type = type->next(); |
872 | 1.73k | if (type) { |
873 | 1.73k | func->retType = findVariableTypeInBase(&scope, type); |
874 | 1.73k | if (!func->retType) |
875 | 1.73k | func->retType = findTypeInNested(type, func->nestedIn); |
876 | 1.73k | } |
877 | 1.73k | } |
878 | 1.73k | } |
879 | 4.94k | } |
880 | 1.36k | } |
881 | | |
882 | | void SymbolDatabase::createSymbolDatabaseNeedInitialization() |
883 | 1.36k | { |
884 | 1.36k | if (mTokenizer.isC()) { |
885 | | // For C code it is easy, as there are no constructors and no default values |
886 | 0 | for (const Scope& scope : scopeList) { |
887 | 0 | if (scope.definedType) |
888 | 0 | scope.definedType->needInitialization = Type::NeedInitialization::True; |
889 | 0 | } |
890 | 1.36k | } else { |
891 | | // For C++, it is more difficult: Determine if user defined type needs initialization... |
892 | 1.36k | unsigned int unknowns = 0; // stop checking when there are no unknowns |
893 | 1.36k | unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason |
894 | | |
895 | 1.36k | do { |
896 | 1.36k | unknowns = 0; |
897 | | |
898 | 4.94k | for (Scope& scope : scopeList) { |
899 | 4.94k | if (!scope.isClassOrStructOrUnion()) |
900 | 4.94k | continue; |
901 | 0 | if (scope.classDef && Token::simpleMatch(scope.classDef->previous(), ">")) // skip uninstantiated template |
902 | 0 | continue; |
903 | | |
904 | 0 | if (!scope.definedType) { |
905 | 0 | mBlankTypes.emplace_back(); |
906 | 0 | scope.definedType = &mBlankTypes.back(); |
907 | 0 | } |
908 | |
|
909 | 0 | if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) { |
910 | | // check for default constructor |
911 | 0 | bool hasDefaultConstructor = false; |
912 | |
|
913 | 0 | for (const Function& func : scope.functionList) { |
914 | 0 | if (func.type == Function::eConstructor) { |
915 | | // check for no arguments: func ( ) |
916 | 0 | if (func.argCount() == 0) { |
917 | 0 | hasDefaultConstructor = true; |
918 | 0 | break; |
919 | 0 | } |
920 | | |
921 | | /** check for arguments with default values */ |
922 | 0 | if (func.argCount() == func.initializedArgCount()) { |
923 | 0 | hasDefaultConstructor = true; |
924 | 0 | break; |
925 | 0 | } |
926 | 0 | } |
927 | 0 | } |
928 | | |
929 | | // User defined types with user defined default constructor doesn't need initialization. |
930 | | // We assume the default constructor initializes everything. |
931 | | // Another check will figure out if the constructor actually initializes everything. |
932 | 0 | if (hasDefaultConstructor) |
933 | 0 | scope.definedType->needInitialization = Type::NeedInitialization::False; |
934 | | |
935 | | // check each member variable to see if it needs initialization |
936 | 0 | else { |
937 | 0 | bool needInitialization = false; |
938 | 0 | bool unknown = false; |
939 | |
|
940 | 0 | for (const Variable& var: scope.varlist) { |
941 | 0 | if (var.isClass()) { |
942 | 0 | if (var.type()) { |
943 | | // does this type need initialization? |
944 | 0 | if (var.type()->needInitialization == Type::NeedInitialization::True && !var.hasDefault() && !var.isStatic()) |
945 | 0 | needInitialization = true; |
946 | 0 | else if (var.type()->needInitialization == Type::NeedInitialization::Unknown) { |
947 | 0 | if (!(var.valueType() && var.valueType()->type == ValueType::CONTAINER)) |
948 | 0 | unknown = true; |
949 | 0 | } |
950 | 0 | } |
951 | 0 | } else if (!var.hasDefault() && !var.isStatic()) { |
952 | 0 | needInitialization = true; |
953 | 0 | break; |
954 | 0 | } |
955 | 0 | } |
956 | |
|
957 | 0 | if (needInitialization) |
958 | 0 | scope.definedType->needInitialization = Type::NeedInitialization::True; |
959 | 0 | else if (!unknown) |
960 | 0 | scope.definedType->needInitialization = Type::NeedInitialization::False; |
961 | 0 | else { |
962 | 0 | if (scope.definedType->needInitialization == Type::NeedInitialization::Unknown) |
963 | 0 | unknowns++; |
964 | 0 | } |
965 | 0 | } |
966 | 0 | } else if (scope.type == Scope::eUnion && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) |
967 | 0 | scope.definedType->needInitialization = Type::NeedInitialization::True; |
968 | 0 | } |
969 | | |
970 | 1.36k | retry++; |
971 | 1.36k | } while (unknowns && retry < 100); |
972 | | |
973 | | // this shouldn't happen so output a debug warning |
974 | 1.36k | if (retry == 100 && mSettings.debugwarnings) { |
975 | 0 | for (const Scope& scope : scopeList) { |
976 | 0 | if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) |
977 | 0 | debugMessage(scope.classDef, "debug", "SymbolDatabase couldn't resolve all user defined types."); |
978 | 0 | } |
979 | 0 | } |
980 | 1.36k | } |
981 | 1.36k | } |
982 | | |
983 | | void SymbolDatabase::createSymbolDatabaseVariableSymbolTable() |
984 | 1.36k | { |
985 | | // create variable symbol table |
986 | 1.36k | mVariableList.resize(mTokenizer.varIdCount() + 1); |
987 | 1.36k | std::fill_n(mVariableList.begin(), mVariableList.size(), nullptr); |
988 | | |
989 | | // check all scopes for variables |
990 | 4.94k | for (Scope& scope : scopeList) { |
991 | | // add all variables |
992 | 6.80k | for (Variable& var: scope.varlist) { |
993 | 6.80k | const unsigned int varId = var.declarationId(); |
994 | 6.80k | if (varId) |
995 | 6.80k | mVariableList[varId] = &var; |
996 | | // fix up variables without type |
997 | 6.80k | if (!var.type() && !var.typeStartToken()->isStandardType()) { |
998 | 0 | const Type *type = findType(var.typeStartToken(), &scope); |
999 | 0 | if (type) |
1000 | 0 | var.type(type); |
1001 | 0 | } |
1002 | 6.80k | } |
1003 | | |
1004 | | // add all function parameters |
1005 | 4.94k | for (Function& func : scope.functionList) { |
1006 | 1.73k | for (Variable& arg: func.argumentList) { |
1007 | | // check for named parameters |
1008 | 0 | if (arg.nameToken() && arg.declarationId()) { |
1009 | 0 | const unsigned int declarationId = arg.declarationId(); |
1010 | 0 | mVariableList[declarationId] = &arg; |
1011 | | // fix up parameters without type |
1012 | 0 | if (!arg.type() && !arg.typeStartToken()->isStandardType()) { |
1013 | 0 | const Type *type = findTypeInNested(arg.typeStartToken(), &scope); |
1014 | 0 | if (type) |
1015 | 0 | arg.type(type); |
1016 | 0 | } |
1017 | 0 | } |
1018 | 0 | } |
1019 | 1.73k | } |
1020 | 4.94k | } |
1021 | | |
1022 | | // fill in missing variables if possible |
1023 | 1.73k | for (const Scope *func: functionScopes) { |
1024 | 35.9k | for (const Token *tok = func->bodyStart->next(); tok && tok != func->bodyEnd; tok = tok->next()) { |
1025 | | // check for member variable |
1026 | 34.1k | if (!Token::Match(tok, "%var% .|[")) |
1027 | 34.1k | continue; |
1028 | 0 | const Token* tokDot = tok->next(); |
1029 | 0 | while (Token::simpleMatch(tokDot, "[")) |
1030 | 0 | tokDot = tokDot->link()->next(); |
1031 | 0 | if (!Token::Match(tokDot, ". %var%")) |
1032 | 0 | continue; |
1033 | 0 | const Token *member = tokDot->next(); |
1034 | 0 | if (mVariableList[member->varId()] == nullptr) { |
1035 | 0 | const Variable *var1 = mVariableList[tok->varId()]; |
1036 | 0 | if (var1 && var1->typeScope()) { |
1037 | 0 | const Variable* memberVar = var1->typeScope()->getVariable(member->str()); |
1038 | 0 | if (memberVar) { |
1039 | | // add this variable to the look up table |
1040 | 0 | mVariableList[member->varId()] = memberVar; |
1041 | 0 | } |
1042 | 0 | } |
1043 | 0 | } |
1044 | 0 | } |
1045 | 1.73k | } |
1046 | 1.36k | } |
1047 | | |
1048 | | void SymbolDatabase::createSymbolDatabaseSetScopePointers() |
1049 | 1.36k | { |
1050 | 4.94k | auto setScopePointers = [](const Scope &scope, const Token *bodyStart, const Token *bodyEnd) { |
1051 | 4.94k | assert(bodyStart); |
1052 | 0 | assert(bodyEnd); |
1053 | | |
1054 | 0 | const_cast<Token *>(bodyEnd)->scope(&scope); |
1055 | | |
1056 | 93.5k | for (Token* tok = const_cast<Token *>(bodyStart); tok != bodyEnd; tok = tok->next()) { |
1057 | 90.4k | if (bodyStart != bodyEnd && tok->str() == "{") { |
1058 | 7.16k | bool isEndOfScope = false; |
1059 | 7.16k | for (Scope* innerScope: scope.nestedList) { |
1060 | 6.54k | const auto &list = innerScope->bodyStartList; |
1061 | 6.54k | if (std::find(list.cbegin(), list.cend(), tok) != list.cend()) { // Is begin of inner scope |
1062 | 3.58k | tok = tok->link(); |
1063 | 3.58k | if (tok->next() == bodyEnd || !tok->next()) { |
1064 | 1.78k | isEndOfScope = true; |
1065 | 1.78k | break; |
1066 | 1.78k | } |
1067 | 1.80k | tok = tok->next(); |
1068 | 1.80k | break; |
1069 | 3.58k | } |
1070 | 6.54k | } |
1071 | 7.16k | if (isEndOfScope) |
1072 | 1.78k | break; |
1073 | 7.16k | } |
1074 | 88.6k | tok->scope(&scope); |
1075 | 88.6k | } |
1076 | 4.94k | }; |
1077 | | |
1078 | | // Set scope pointers |
1079 | 4.94k | for (const Scope& scope: scopeList) { |
1080 | 4.94k | if (scope.type == Scope::eGlobal) |
1081 | 1.36k | setScopePointers(scope, mTokenizer.list.front(), mTokenizer.list.back()); |
1082 | 3.58k | else { |
1083 | 3.58k | for (const Token *bodyStart: scope.bodyStartList) |
1084 | 3.58k | setScopePointers(scope, bodyStart, bodyStart->link()); |
1085 | 3.58k | } |
1086 | 4.94k | } |
1087 | 1.36k | } |
1088 | | |
1089 | | void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass) |
1090 | 6.80k | { |
1091 | 6.80k | if (firstPass) { |
1092 | | // Set function definition and declaration pointers |
1093 | 4.94k | for (const Scope& scope: scopeList) { |
1094 | 4.94k | for (const Function& func: scope.functionList) { |
1095 | 1.73k | if (func.tokenDef) |
1096 | 1.73k | const_cast<Token *>(func.tokenDef)->function(&func); |
1097 | | |
1098 | 1.73k | if (func.token) |
1099 | 1.73k | const_cast<Token *>(func.token)->function(&func); |
1100 | 1.73k | } |
1101 | 4.94k | } |
1102 | 1.36k | } |
1103 | | |
1104 | | // Set function call pointers |
1105 | 461k | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1106 | 454k | if (tok->isName() && !tok->function() && tok->varId() == 0 && Token::Match(tok, "%name% [{(,)>;]") && !isReservedName(tok->str())) { |
1107 | 5.46k | if (tok->next()->str() == ">" && !tok->next()->link()) |
1108 | 455 | continue; |
1109 | | |
1110 | 5.01k | bool isTemplateArg = false; |
1111 | 5.01k | if (!Token::Match(tok->next(), "(|{")) { |
1112 | 3.27k | const Token *start = tok; |
1113 | 3.27k | while (Token::Match(start->tokAt(-2), "%name% ::")) |
1114 | 0 | start = start->tokAt(-2); |
1115 | 3.27k | if (!Token::Match(start->previous(), "[(,<=]") && !Token::simpleMatch(start->previous(), "::") && !Token::Match(start->tokAt(-2), "[(,<=] &") && !Token::Match(start, "%name% ;")) |
1116 | 800 | continue; |
1117 | 2.47k | isTemplateArg = Token::simpleMatch(start->previous(), "<") || Token::simpleMatch(start->tokAt(-2), "<"); |
1118 | 2.47k | } |
1119 | | |
1120 | 4.21k | const Function *function = findFunction(tok); |
1121 | 4.21k | if (!function || (isTemplateArg && function->isConstructor())) |
1122 | 2.47k | continue; |
1123 | | |
1124 | 1.73k | const_cast<Token *>(tok)->function(function); |
1125 | | |
1126 | 1.73k | if (tok->next()->str() != "(") |
1127 | 0 | const_cast<Function *>(function)->functionPointerUsage = tok; |
1128 | 1.73k | } |
1129 | 454k | } |
1130 | | |
1131 | | // Set C++ 11 delegate constructor function call pointers |
1132 | 24.7k | for (const Scope& scope: scopeList) { |
1133 | 24.7k | for (const Function& func: scope.functionList) { |
1134 | | // look for initializer list |
1135 | 8.69k | if (func.isConstructor() && func.functionScope && func.functionScope->functionOf && func.arg) { |
1136 | 0 | const Token * tok = func.arg->link()->next(); |
1137 | 0 | if (tok->str() == "noexcept") { |
1138 | 0 | const Token * closingParenTok = tok->linkAt(1); |
1139 | 0 | if (!closingParenTok || !closingParenTok->next()) { |
1140 | 0 | continue; |
1141 | 0 | } |
1142 | 0 | tok = closingParenTok->next(); |
1143 | 0 | } |
1144 | 0 | if (tok->str() != ":") { |
1145 | 0 | continue; |
1146 | 0 | } |
1147 | 0 | tok = tok->next(); |
1148 | 0 | while (tok && tok != func.functionScope->bodyStart) { |
1149 | 0 | if (Token::Match(tok, "%name% {|(")) { |
1150 | 0 | if (tok->str() == func.tokenDef->str()) { |
1151 | 0 | const Function *function = func.functionScope->functionOf->findFunction(tok); |
1152 | 0 | if (function) |
1153 | 0 | const_cast<Token *>(tok)->function(function); |
1154 | 0 | break; |
1155 | 0 | } |
1156 | 0 | tok = tok->linkAt(1); |
1157 | 0 | } |
1158 | 0 | tok = tok->next(); |
1159 | 0 | } |
1160 | 0 | } |
1161 | 8.69k | } |
1162 | 24.7k | } |
1163 | 6.80k | } |
1164 | | |
1165 | | void SymbolDatabase::createSymbolDatabaseSetTypePointers() |
1166 | 1.36k | { |
1167 | 1.36k | std::unordered_set<std::string> typenames; |
1168 | 1.36k | for (const Type &t : typeList) { |
1169 | 0 | typenames.insert(t.name()); |
1170 | 0 | } |
1171 | | |
1172 | | // Set type pointers |
1173 | 92.2k | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1174 | 90.8k | if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator()) |
1175 | 76.7k | continue; |
1176 | | |
1177 | 14.1k | if (typenames.find(tok->str()) == typenames.end()) |
1178 | 14.1k | continue; |
1179 | | |
1180 | 0 | const Type *type = findVariableType(tok->scope(), tok); |
1181 | 0 | if (type) |
1182 | 0 | const_cast<Token *>(tok)->type(type); // TODO: avoid const_cast |
1183 | 0 | } |
1184 | 1.36k | } |
1185 | | |
1186 | | void SymbolDatabase::createSymbolDatabaseSetSmartPointerType() |
1187 | 1.36k | { |
1188 | 4.94k | for (Scope &scope: scopeList) { |
1189 | 6.80k | for (Variable &var: scope.varlist) { |
1190 | 6.80k | if (var.valueType() && var.valueType()->smartPointerTypeToken && !var.valueType()->smartPointerType) { |
1191 | 0 | ValueType vt(*var.valueType()); |
1192 | 0 | vt.smartPointerType = vt.smartPointerTypeToken->type(); |
1193 | 0 | var.setValueType(vt); |
1194 | 0 | } |
1195 | 6.80k | } |
1196 | 4.94k | } |
1197 | 1.36k | } |
1198 | | |
1199 | | void SymbolDatabase::fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar) |
1200 | 0 | { |
1201 | 0 | VarIdMap::iterator varId = varIds.find(vartok->varId()); |
1202 | 0 | if (varId == varIds.end()) { |
1203 | 0 | MemberIdMap memberId; |
1204 | 0 | if (membertok->varId() == 0) { |
1205 | 0 | memberId[membervar->nameToken()->varId()] = const_cast<Tokenizer &>(mTokenizer).newVarId(); |
1206 | 0 | mVariableList.push_back(membervar); |
1207 | 0 | } else |
1208 | 0 | mVariableList[membertok->varId()] = membervar; |
1209 | 0 | varIds.insert(std::make_pair(vartok->varId(), memberId)); |
1210 | 0 | varId = varIds.find(vartok->varId()); |
1211 | 0 | } |
1212 | 0 | MemberIdMap::iterator memberId = varId->second.find(membervar->nameToken()->varId()); |
1213 | 0 | if (memberId == varId->second.end()) { |
1214 | 0 | if (membertok->varId() == 0) { |
1215 | 0 | varId->second.insert(std::make_pair(membervar->nameToken()->varId(), const_cast<Tokenizer &>(mTokenizer).newVarId())); |
1216 | 0 | mVariableList.push_back(membervar); |
1217 | 0 | memberId = varId->second.find(membervar->nameToken()->varId()); |
1218 | 0 | } else |
1219 | 0 | mVariableList[membertok->varId()] = membervar; |
1220 | 0 | } |
1221 | 0 | if (membertok->varId() == 0) |
1222 | 0 | membertok->varId(memberId->second); |
1223 | 0 | } |
1224 | | |
1225 | | static bool isContainerYieldElement(Library::Container::Yield yield); |
1226 | | |
1227 | | void SymbolDatabase::createSymbolDatabaseSetVariablePointers() |
1228 | 6.80k | { |
1229 | 6.80k | VarIdMap varIds; |
1230 | | |
1231 | 6.80k | auto setMemberVar = [&](const Variable* membervar, Token* membertok, const Token* vartok) -> void { |
1232 | 0 | if (membervar) { |
1233 | 0 | membertok->variable(membervar); |
1234 | 0 | if (vartok && (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)) |
1235 | 0 | fixVarId(varIds, vartok, membertok, membervar); |
1236 | 0 | } |
1237 | 0 | }; |
1238 | | |
1239 | | // Set variable pointers |
1240 | 461k | for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1241 | 454k | if (!tok->isName() || tok->isKeyword() || tok->isStandardType()) |
1242 | 337k | continue; |
1243 | 117k | if (tok->varId()) |
1244 | 99.1k | const_cast<Token*>(tok)->variable(getVariableFromVarId(tok->varId())); |
1245 | | |
1246 | | // Set Token::variable pointer for array member variable |
1247 | | // Since it doesn't point at a fixed location it doesn't have varid |
1248 | 117k | const bool isVar = tok->variable() && (tok->variable()->typeScope() || tok->variable()->isSmartPointer() || |
1249 | 99.1k | (tok->valueType() && (tok->valueType()->type == ValueType::CONTAINER || tok->valueType()->type == ValueType::ITERATOR))); |
1250 | 117k | const bool isArrayAccess = isVar && Token::simpleMatch(tok->astParent(), "["); |
1251 | 117k | const bool isDirectAccess = isVar && !isArrayAccess && Token::simpleMatch(tok->astParent(), "."); |
1252 | 117k | const bool isDerefAccess = isVar && !isDirectAccess && Token::simpleMatch(tok->astParent(), "*") && Token::simpleMatch(tok->astParent()->astParent(), "."); |
1253 | 117k | if (isVar && (isArrayAccess || isDirectAccess || isDerefAccess)) { |
1254 | 0 | Token* membertok{}; |
1255 | 0 | if (isArrayAccess) { |
1256 | 0 | membertok = const_cast<Token*>(tok->astParent()); |
1257 | 0 | while (Token::simpleMatch(membertok, "[")) |
1258 | 0 | membertok = membertok->astParent(); |
1259 | 0 | if (membertok) |
1260 | 0 | membertok = membertok->astOperand2(); |
1261 | 0 | } |
1262 | 0 | else if (isDirectAccess) { |
1263 | 0 | membertok = const_cast<Token*>(tok->astParent()->astOperand2()); |
1264 | 0 | if (membertok == tok) { |
1265 | 0 | Token* gptok = const_cast<Token*>(tok->astParent()->astParent()); |
1266 | 0 | if (Token::simpleMatch(gptok, ".")) // chained access |
1267 | 0 | membertok = gptok->astOperand2(); |
1268 | 0 | else if (Token::simpleMatch(gptok, "[") && Token::simpleMatch(gptok->astParent(), ".")) |
1269 | 0 | membertok = gptok->astParent()->astOperand2(); |
1270 | 0 | } |
1271 | 0 | } |
1272 | 0 | else { // isDerefAccess |
1273 | 0 | membertok = const_cast<Token*>(tok->astParent()); |
1274 | 0 | while (Token::simpleMatch(membertok, "*")) |
1275 | 0 | membertok = membertok->astParent(); |
1276 | 0 | if (membertok) |
1277 | 0 | membertok = membertok->astOperand2(); |
1278 | 0 | } |
1279 | |
|
1280 | 0 | if (membertok && membertok != tok) { |
1281 | 0 | const Variable *var = tok->variable(); |
1282 | 0 | if (var->typeScope()) { |
1283 | 0 | const Variable *membervar = var->typeScope()->getVariable(membertok->str()); |
1284 | 0 | setMemberVar(membervar, membertok, tok); |
1285 | 0 | } else if (const ::Type *type = var->smartPointerType()) { |
1286 | 0 | const Scope *classScope = type->classScope; |
1287 | 0 | const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr; |
1288 | 0 | setMemberVar(membervar, membertok, tok); |
1289 | 0 | } else if (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) { |
1290 | 0 | if (const Token* ctt = tok->valueType()->containerTypeToken) { |
1291 | 0 | while (ctt && ctt->isKeyword()) |
1292 | 0 | ctt = ctt->next(); |
1293 | 0 | const Type* ct = findTypeInNested(ctt, tok->scope()); |
1294 | 0 | if (ct && ct->classScope && ct->classScope->definedType) { |
1295 | 0 | const Variable *membervar = ct->classScope->getVariable(membertok->str()); |
1296 | 0 | setMemberVar(membervar, membertok, tok); |
1297 | 0 | } |
1298 | 0 | } |
1299 | 0 | } else if (const Type* iterType = var->iteratorType()) { |
1300 | 0 | if (iterType->classScope && iterType->classScope->definedType) { |
1301 | 0 | const Variable *membervar = iterType->classScope->getVariable(membertok->str()); |
1302 | 0 | setMemberVar(membervar, membertok, tok); |
1303 | 0 | } |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | } |
1307 | | |
1308 | | // check for function returning record type |
1309 | | // func(...).var |
1310 | | // func(...)[...].var |
1311 | 117k | else if (tok->function() && tok->next()->str() == "(" && |
1312 | 117k | (Token::Match(tok->next()->link(), ") . %name% !!(") || |
1313 | 6.95k | (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { |
1314 | 0 | const Type *type = tok->function()->retType; |
1315 | 0 | Token* membertok; |
1316 | 0 | if (tok->next()->link()->next()->str() == ".") |
1317 | 0 | membertok = tok->next()->link()->next()->next(); |
1318 | 0 | else |
1319 | 0 | membertok = tok->next()->link()->next()->link()->next()->next(); |
1320 | 0 | if (type) { |
1321 | 0 | const Variable *membervar = membertok->variable(); |
1322 | 0 | if (!membervar) { |
1323 | 0 | if (type->classScope) { |
1324 | 0 | membervar = type->classScope->getVariable(membertok->str()); |
1325 | 0 | setMemberVar(membervar, membertok, tok->function()->retDef); |
1326 | 0 | } |
1327 | 0 | } |
1328 | 0 | } else if (mSettings.library.detectSmartPointer(tok->function()->retDef)) { |
1329 | 0 | if (const Token* templateArg = Token::findsimplematch(tok->function()->retDef, "<")) { |
1330 | 0 | if (const Type* spType = findTypeInNested(templateArg->next(), tok->scope())) { |
1331 | 0 | if (spType->classScope) { |
1332 | 0 | const Variable* membervar = spType->classScope->getVariable(membertok->str()); |
1333 | 0 | setMemberVar(membervar, membertok, tok->function()->retDef); |
1334 | 0 | } |
1335 | 0 | } |
1336 | 0 | } |
1337 | 0 | } |
1338 | 0 | } |
1339 | 117k | else if (Token::simpleMatch(tok->astParent(), ".") && tok->next()->str() == "(" && |
1340 | 117k | astIsContainer(tok->astParent()->astOperand1()) && Token::Match(tok->next()->link(), ") . %name% !!(")) { |
1341 | 0 | const ValueType* vt = tok->astParent()->astOperand1()->valueType(); |
1342 | 0 | const Library::Container* cont = vt->container; |
1343 | 0 | auto it = cont->functions.find(tok->str()); |
1344 | 0 | if (it != cont->functions.end() && isContainerYieldElement(it->second.yield) && vt->containerTypeToken) { |
1345 | 0 | Token* memberTok = tok->next()->link()->tokAt(2); |
1346 | 0 | const Scope* scope = vt->containerTypeToken->scope(); |
1347 | 0 | const Type* contType{}; |
1348 | 0 | const std::string& typeStr = vt->containerTypeToken->str(); // TODO: handle complex type expressions |
1349 | 0 | while (scope && !contType) { |
1350 | 0 | contType = scope->findType(typeStr); // find the type stored in the container |
1351 | 0 | scope = scope->nestedIn; |
1352 | 0 | } |
1353 | 0 | if (contType && contType->classScope) { |
1354 | 0 | const Variable* membervar = contType->classScope->getVariable(memberTok->str()); |
1355 | 0 | setMemberVar(membervar, memberTok, vt->containerTypeToken); |
1356 | 0 | } |
1357 | 0 | } |
1358 | 0 | } |
1359 | 117k | } |
1360 | 6.80k | } |
1361 | | |
1362 | | void SymbolDatabase::createSymbolDatabaseEnums() |
1363 | 1.36k | { |
1364 | | // fill in enumerators in enum |
1365 | 4.94k | for (const Scope &scope : scopeList) { |
1366 | 4.94k | if (scope.type != Scope::eEnum) |
1367 | 4.94k | continue; |
1368 | | |
1369 | | // add enumerators to enumerator tokens |
1370 | 0 | for (const Enumerator & i : scope.enumeratorList) |
1371 | 0 | const_cast<Token *>(i.name)->enumerator(&i); |
1372 | 0 | } |
1373 | | |
1374 | 1.36k | std::set<std::string> tokensThatAreNotEnumeratorValues; |
1375 | | |
1376 | 4.94k | for (const Scope &scope : scopeList) { |
1377 | 4.94k | if (scope.type != Scope::eEnum) |
1378 | 4.94k | continue; |
1379 | | |
1380 | 0 | for (const Enumerator & enumerator : scope.enumeratorList) { |
1381 | | // look for initialization tokens that can be converted to enumerators and convert them |
1382 | 0 | if (enumerator.start) { |
1383 | 0 | if (!enumerator.end) |
1384 | 0 | mTokenizer.syntaxError(enumerator.start); |
1385 | 0 | for (const Token * tok3 = enumerator.start; tok3 && tok3 != enumerator.end->next(); tok3 = tok3->next()) { |
1386 | 0 | if (tok3->tokType() == Token::eName) { |
1387 | 0 | const Enumerator * e = findEnumerator(tok3, tokensThatAreNotEnumeratorValues); |
1388 | 0 | if (e) |
1389 | 0 | const_cast<Token *>(tok3)->enumerator(e); |
1390 | 0 | } |
1391 | 0 | } |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 | } |
1395 | | |
1396 | | // find enumerators |
1397 | 92.2k | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1398 | 90.8k | const bool isVariable = (tok->tokType() == Token::eVariable && !tok->variable()); |
1399 | 90.8k | if (tok->tokType() != Token::eName && !isVariable) |
1400 | 82.2k | continue; |
1401 | 8.67k | const Enumerator * enumerator = findEnumerator(tok, tokensThatAreNotEnumeratorValues); |
1402 | 8.67k | if (enumerator) { |
1403 | 0 | if (isVariable) |
1404 | 0 | const_cast<Token*>(tok)->varId(0); |
1405 | 0 | const_cast<Token*>(tok)->enumerator(enumerator); |
1406 | 0 | } |
1407 | 8.67k | } |
1408 | 1.36k | } |
1409 | | |
1410 | | void SymbolDatabase::createSymbolDatabaseIncompleteVars() |
1411 | 1.36k | { |
1412 | | // TODO: replace with Keywords::getX() |
1413 | 1.36k | static const std::unordered_set<std::string> cpp20keywords = { |
1414 | 1.36k | "alignas", |
1415 | 1.36k | "alignof", |
1416 | 1.36k | "axiom", |
1417 | 1.36k | "co_await", |
1418 | 1.36k | "co_return", |
1419 | 1.36k | "co_yield", |
1420 | 1.36k | "concept", |
1421 | 1.36k | "synchronized", |
1422 | 1.36k | "consteval", |
1423 | 1.36k | "reflexpr", |
1424 | 1.36k | "requires", |
1425 | 1.36k | }; |
1426 | 1.36k | static const std::unordered_set<std::string> cppkeywords = { |
1427 | 1.36k | "asm", |
1428 | 1.36k | "auto", |
1429 | 1.36k | "catch", |
1430 | 1.36k | "char", |
1431 | 1.36k | "class", |
1432 | 1.36k | "const", |
1433 | 1.36k | "constexpr", |
1434 | 1.36k | "decltype", |
1435 | 1.36k | "default", |
1436 | 1.36k | "do", |
1437 | 1.36k | "enum", |
1438 | 1.36k | "explicit", |
1439 | 1.36k | "export", |
1440 | 1.36k | "extern", |
1441 | 1.36k | "final", |
1442 | 1.36k | "friend", |
1443 | 1.36k | "inline", |
1444 | 1.36k | "mutable", |
1445 | 1.36k | "namespace", |
1446 | 1.36k | "new", |
1447 | 1.36k | "noexcept", |
1448 | 1.36k | "nullptr", |
1449 | 1.36k | "override", |
1450 | 1.36k | "private", |
1451 | 1.36k | "protected", |
1452 | 1.36k | "public", |
1453 | 1.36k | "register", |
1454 | 1.36k | "sizeof", |
1455 | 1.36k | "static", |
1456 | 1.36k | "static_assert", |
1457 | 1.36k | "struct", |
1458 | 1.36k | "template", |
1459 | 1.36k | "this", |
1460 | 1.36k | "thread_local", |
1461 | 1.36k | "throw", |
1462 | 1.36k | "try", |
1463 | 1.36k | "typedef", |
1464 | 1.36k | "typeid", |
1465 | 1.36k | "typename", |
1466 | 1.36k | "union", |
1467 | 1.36k | "using", |
1468 | 1.36k | "virtual", |
1469 | 1.36k | "void", |
1470 | 1.36k | "volatile", |
1471 | 1.36k | "NULL", |
1472 | 1.36k | }; |
1473 | 92.2k | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1474 | 90.8k | const Scope * scope = tok->scope(); |
1475 | 90.8k | if (!scope) |
1476 | 0 | continue; |
1477 | 90.8k | if (!scope->isExecutable()) |
1478 | 54.5k | continue; |
1479 | 36.2k | if (tok->varId() != 0) |
1480 | 6.22k | continue; |
1481 | 30.0k | if (tok->isCast() && !isCPPCast(tok) && tok->link() && tok->str() == "(") { |
1482 | 0 | tok = tok->link(); |
1483 | 0 | continue; |
1484 | 0 | } |
1485 | 30.0k | if (Token::Match(tok, "catch|typeid (")) { |
1486 | 0 | tok = tok->linkAt(1); |
1487 | 0 | continue; |
1488 | 0 | } |
1489 | 30.0k | if (!(tok->isNameOnly() || tok->isKeyword())) |
1490 | 24.4k | continue; |
1491 | 5.62k | if (tok->type()) |
1492 | 0 | continue; |
1493 | 5.62k | if (Token::Match(tok->next(), "::|.|(|{|:|%var%")) |
1494 | 2.06k | continue; |
1495 | 3.56k | if (Token::Match(tok->next(), "&|&&|* )|,|%var%|const")) |
1496 | 35 | continue; |
1497 | | // Very likely a typelist |
1498 | 3.52k | if (Token::Match(tok->tokAt(-2), "%type% ,") || Token::Match(tok->next(), ", %type%")) |
1499 | 0 | continue; |
1500 | | // Inside template brackets |
1501 | 3.52k | if (Token::simpleMatch(tok->next(), "<") && tok->linkAt(1)) { |
1502 | 4 | tok = tok->linkAt(1); |
1503 | 4 | continue; |
1504 | 4 | } |
1505 | 3.52k | if (tok->isKeyword()) |
1506 | 1.69k | continue; |
1507 | | // Skip goto labels |
1508 | 1.82k | if (Token::simpleMatch(tok->previous(), "goto")) |
1509 | 0 | continue; |
1510 | | // TODO: handle all C/C++ standards |
1511 | 1.82k | if (cppkeywords.count(tok->str()) > 0) |
1512 | 0 | continue; |
1513 | 1.82k | if (mSettings.standards.cpp >= Standards::CPP20 && cpp20keywords.count(tok->str()) > 0) |
1514 | 0 | continue; |
1515 | 1.82k | std::string fstr = tok->str(); |
1516 | 1.82k | const Token* ftok = tok->previous(); |
1517 | 1.82k | while (Token::simpleMatch(ftok, "::")) { |
1518 | 0 | if (!Token::Match(ftok->previous(), "%name%")) |
1519 | 0 | break; |
1520 | 0 | fstr.insert(0, ftok->previous()->str() + "::"); |
1521 | 0 | ftok = ftok->tokAt(-2); |
1522 | 0 | } |
1523 | 1.82k | if (mSettings.library.functions.find(fstr) != mSettings.library.functions.end()) |
1524 | 0 | continue; |
1525 | 1.82k | const_cast<Token *>(tok)->isIncompleteVar(true); // TODO: avoid const_cast |
1526 | 1.82k | } |
1527 | 1.36k | } |
1528 | | |
1529 | | void SymbolDatabase::createSymbolDatabaseEscapeFunctions() |
1530 | 1.36k | { |
1531 | 4.94k | for (const Scope& scope : scopeList) { |
1532 | 4.94k | if (scope.type != Scope::eFunction) |
1533 | 3.20k | continue; |
1534 | 1.73k | Function * function = scope.function; |
1535 | 1.73k | if (!function) |
1536 | 0 | continue; |
1537 | 1.73k | if (Token::findsimplematch(scope.bodyStart, "return", scope.bodyEnd)) |
1538 | 1.73k | continue; |
1539 | 0 | function->isEscapeFunction(isReturnScope(scope.bodyEnd, &mSettings.library, nullptr, true)); |
1540 | 0 | } |
1541 | 1.36k | } |
1542 | | |
1543 | | static bool isExpression(const Token* tok) |
1544 | 31.5k | { |
1545 | 31.5k | if (!tok) |
1546 | 0 | return false; |
1547 | 31.5k | if (Token::simpleMatch(tok, "{") && tok->scope() && tok->scope()->bodyStart != tok && |
1548 | 31.5k | (tok->astOperand1() || tok->astOperand2())) |
1549 | 0 | return true; |
1550 | 31.5k | if (!Token::Match(tok, "(|.|[|::|?|:|++|--|%cop%|%assign%")) |
1551 | 19.4k | return false; |
1552 | 12.0k | if (Token::Match(tok, "*|&|&&")) { |
1553 | 3.18k | const Token* vartok = findAstNode(tok, [&](const Token* tok2) { |
1554 | 3.18k | const Variable* var = tok2->variable(); |
1555 | 3.18k | if (!var) |
1556 | 2.25k | return false; |
1557 | 938 | return var->nameToken() == tok2; |
1558 | 3.18k | }); |
1559 | 620 | if (vartok) |
1560 | 0 | return false; |
1561 | 620 | } |
1562 | 12.0k | return true; |
1563 | 12.0k | } |
1564 | | |
1565 | | static std::string getIncompleteNameID(const Token* tok) |
1566 | 1.76k | { |
1567 | 1.76k | std::string result = tok->str() + "@"; |
1568 | 1.76k | while (Token::Match(tok->astParent(), ".|::")) |
1569 | 0 | tok = tok->astParent(); |
1570 | 1.76k | return result + tok->expressionString(); |
1571 | 1.76k | } |
1572 | | |
1573 | | void SymbolDatabase::createSymbolDatabaseExprIds() |
1574 | 1.36k | { |
1575 | 1.36k | nonneg int base = 0; |
1576 | | // Find highest varId |
1577 | 8.16k | for (const Variable *var : mVariableList) { |
1578 | 8.16k | if (!var) |
1579 | 1.36k | continue; |
1580 | 6.80k | base = std::max<MathLib::bigint>(base, var->declarationId()); |
1581 | 6.80k | } |
1582 | 1.36k | nonneg int id = base + 1; |
1583 | | // Find incomplete vars that are used in constant context |
1584 | 1.36k | std::unordered_map<std::string, nonneg int> unknownConstantIds; |
1585 | 1.36k | const Token* inConstExpr = nullptr; |
1586 | 92.2k | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
1587 | 90.8k | if (Token::Match(tok, "decltype|sizeof|typeof (") && tok->next()->link()) { |
1588 | 0 | tok = tok->next()->link()->previous(); |
1589 | 90.8k | } else if (tok == inConstExpr) { |
1590 | 4 | inConstExpr = nullptr; |
1591 | 90.8k | } else if (inConstExpr) { |
1592 | 11 | if (!tok->isIncompleteVar()) |
1593 | 11 | continue; |
1594 | 0 | if (!isExpression(tok->astParent())) |
1595 | 0 | continue; |
1596 | 0 | const std::string& name = getIncompleteNameID(tok); |
1597 | 0 | if (unknownConstantIds.count(name) > 0) |
1598 | 0 | continue; |
1599 | 0 | unknownConstantIds[name] = id++; |
1600 | 90.8k | } else if (tok->link() && tok->str() == "<") { |
1601 | 4 | inConstExpr = tok->link(); |
1602 | 90.8k | } else if (Token::Match(tok, "%var% [") && tok->variable() && tok->variable()->nameToken() == tok) { |
1603 | 0 | inConstExpr = tok->next()->link(); |
1604 | 0 | } |
1605 | 90.8k | } |
1606 | | |
1607 | 1.36k | auto exprScopes = functionScopes; // functions + global lambdas |
1608 | 1.73k | std::copy_if(scopeList.front().nestedList.begin(), scopeList.front().nestedList.end(), std::back_inserter(exprScopes), [](const Scope* scope) { |
1609 | 1.73k | return scope && scope->type == Scope::eLambda; |
1610 | 1.73k | }); |
1611 | | |
1612 | 1.73k | for (const Scope * scope : exprScopes) { |
1613 | 1.73k | nonneg int thisId = 0; |
1614 | 1.73k | std::unordered_map<std::string, std::vector<Token*>> exprs; |
1615 | | |
1616 | 1.73k | std::unordered_map<std::string, nonneg int> unknownIds; |
1617 | | // Assign IDs to incomplete vars which are part of an expression |
1618 | | // Such variables should be assumed global |
1619 | 37.6k | for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { |
1620 | 35.9k | if (!tok->isIncompleteVar()) |
1621 | 34.0k | continue; |
1622 | 1.82k | if (!isExpression(tok->astParent())) |
1623 | 63 | continue; |
1624 | 1.76k | const std::string& name = getIncompleteNameID(tok); |
1625 | 1.76k | nonneg int sid = 0; |
1626 | 1.76k | if (unknownConstantIds.count(name) > 0) { |
1627 | 0 | sid = unknownConstantIds.at(name); |
1628 | 0 | tok->isIncompleteConstant(true); |
1629 | 1.76k | } else if (unknownIds.count(name) == 0) { |
1630 | 996 | sid = id++; |
1631 | 996 | unknownIds[name] = sid; |
1632 | 996 | } else { |
1633 | 767 | sid = unknownIds.at(name); |
1634 | 767 | } |
1635 | 1.76k | assert(sid > 0); |
1636 | 0 | tok->exprId(sid); |
1637 | 1.76k | } |
1638 | | |
1639 | | // Assign IDs |
1640 | 37.6k | for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { |
1641 | 35.9k | if (tok->varId() > 0) { |
1642 | 6.22k | tok->exprId(tok->varId()); |
1643 | 29.6k | } else if (isExpression(tok)) { |
1644 | 10.3k | exprs[tok->str()].push_back(tok); |
1645 | 10.3k | tok->exprId(id++); |
1646 | | |
1647 | 10.3k | if (id == std::numeric_limits<nonneg int>::max() / 4) { |
1648 | 0 | throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL); |
1649 | 0 | } |
1650 | 19.3k | } else if (isCPP() && Token::simpleMatch(tok, "this")) { |
1651 | 0 | if (thisId == 0) |
1652 | 0 | thisId = id++; |
1653 | 0 | tok->exprId(thisId); |
1654 | 0 | } |
1655 | 35.9k | } |
1656 | | |
1657 | | // Apply CSE |
1658 | 6.94k | for (const auto& p:exprs) { |
1659 | 6.94k | const std::vector<Token*>& tokens = p.second; |
1660 | 6.94k | const std::size_t N = tokens.size(); |
1661 | 17.2k | for (std::size_t i = 0; i < N; ++i) { |
1662 | 10.3k | Token* const tok1 = tokens[i]; |
1663 | 16.1k | for (std::size_t j = i + 1; j < N; ++j) { |
1664 | 5.81k | Token* const tok2 = tokens[j]; |
1665 | 5.81k | if (tok1->exprId() == tok2->exprId()) |
1666 | 15 | continue; |
1667 | 5.80k | if (!isSameExpression(isCPP(), true, tok1, tok2, mSettings.library, false, false)) |
1668 | 5.74k | continue; |
1669 | 53 | nonneg int const cid = std::min(tok1->exprId(), tok2->exprId()); |
1670 | 53 | tok1->exprId(cid); |
1671 | 53 | tok2->exprId(cid); |
1672 | 53 | } |
1673 | 10.3k | } |
1674 | 6.94k | } |
1675 | | // Mark expressions that are unique |
1676 | 1.73k | std::unordered_map<nonneg int, Token*> exprMap; |
1677 | 37.6k | for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { |
1678 | 35.9k | if (tok->exprId() == 0) |
1679 | 17.6k | continue; |
1680 | 18.3k | auto p = exprMap.emplace(tok->exprId(), tok); |
1681 | | // Already exists so set it to null |
1682 | 18.3k | if (!p.second) { |
1683 | 3.69k | p.first->second = nullptr; |
1684 | 3.69k | } |
1685 | 18.3k | } |
1686 | 14.6k | for (const auto& p : exprMap) { |
1687 | 14.6k | if (!p.second) |
1688 | 2.06k | continue; |
1689 | 12.5k | if (p.second->variable()) { |
1690 | 1.73k | const Variable* var = p.second->variable(); |
1691 | 1.73k | if (var->nameToken() != p.second) |
1692 | 1.73k | continue; |
1693 | 1.73k | } |
1694 | 10.8k | p.second->setUniqueExprId(); |
1695 | 10.8k | } |
1696 | 1.73k | } |
1697 | 1.36k | } |
1698 | | |
1699 | | void SymbolDatabase::setArrayDimensionsUsingValueFlow() |
1700 | 1.36k | { |
1701 | | // set all unknown array dimensions |
1702 | 8.16k | for (const Variable *var : mVariableList) { |
1703 | | // check each array variable |
1704 | 8.16k | if (!var || !var->isArray()) |
1705 | 8.16k | continue; |
1706 | | // check each array dimension |
1707 | 0 | for (const Dimension &const_dimension : var->dimensions()) { |
1708 | 0 | Dimension &dimension = const_cast<Dimension &>(const_dimension); |
1709 | 0 | if (dimension.num != 0 || !dimension.tok) |
1710 | 0 | continue; |
1711 | | |
1712 | 0 | if (Token::Match(dimension.tok->previous(), "[<,]")) { |
1713 | 0 | if (dimension.known) |
1714 | 0 | continue; |
1715 | 0 | if (!Token::Match(dimension.tok->previous(), "[<,]")) |
1716 | 0 | continue; |
1717 | | |
1718 | | // In template arguments, there might not be AST |
1719 | | // Determine size by using the "raw tokens" |
1720 | 0 | TokenList tokenList(&mSettings); |
1721 | 0 | tokenList.addtoken(";", 0, 0, 0, false); |
1722 | 0 | bool fail = false; |
1723 | 0 | for (const Token *tok = dimension.tok; tok && !Token::Match(tok, "[,>]"); tok = tok->next()) { |
1724 | 0 | if (!tok->isName()) |
1725 | 0 | tokenList.addtoken(tok->str(), 0, 0, 0, false); |
1726 | | |
1727 | 0 | else if (tok->hasKnownIntValue()) |
1728 | 0 | tokenList.addtoken(std::to_string(tok->getKnownIntValue()), 0, 0, 0, false); |
1729 | | |
1730 | 0 | else { |
1731 | 0 | fail = true; |
1732 | 0 | break; |
1733 | 0 | } |
1734 | 0 | } |
1735 | |
|
1736 | 0 | if (fail) |
1737 | 0 | continue; |
1738 | | |
1739 | 0 | tokenList.addtoken(";", 0, 0, 0, false); |
1740 | |
|
1741 | 0 | for (Token *tok = tokenList.front(); tok;) { |
1742 | 0 | if (TemplateSimplifier::simplifyNumericCalculations(tok, false)) |
1743 | 0 | tok = tokenList.front(); |
1744 | 0 | else |
1745 | 0 | tok = tok->next(); |
1746 | 0 | } |
1747 | |
|
1748 | 0 | if (Token::Match(tokenList.front(), "; %num% ;")) { |
1749 | 0 | dimension.known = true; |
1750 | 0 | dimension.num = MathLib::toLongNumber(tokenList.front()->next()->str()); |
1751 | 0 | } |
1752 | |
|
1753 | 0 | continue; |
1754 | 0 | } |
1755 | | |
1756 | | // Normal array [..dimension..] |
1757 | 0 | dimension.known = false; |
1758 | | |
1759 | | // check for a single token dimension |
1760 | 0 | if (dimension.tok->hasKnownIntValue()) { |
1761 | 0 | dimension.known = true; |
1762 | 0 | dimension.num = dimension.tok->getKnownIntValue(); |
1763 | 0 | continue; |
1764 | 0 | } |
1765 | | |
1766 | 0 | if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) { |
1767 | 0 | int bits = 0; |
1768 | 0 | switch (dimension.tok->valueType()->type) { |
1769 | 0 | case ValueType::Type::CHAR: |
1770 | 0 | bits = mSettings.platform.char_bit; |
1771 | 0 | break; |
1772 | 0 | case ValueType::Type::SHORT: |
1773 | 0 | bits = mSettings.platform.short_bit; |
1774 | 0 | break; |
1775 | 0 | case ValueType::Type::INT: |
1776 | 0 | bits = mSettings.platform.int_bit; |
1777 | 0 | break; |
1778 | 0 | case ValueType::Type::LONG: |
1779 | 0 | bits = mSettings.platform.long_bit; |
1780 | 0 | break; |
1781 | 0 | case ValueType::Type::LONGLONG: |
1782 | 0 | bits = mSettings.platform.long_long_bit; |
1783 | 0 | break; |
1784 | 0 | default: |
1785 | 0 | break; |
1786 | 0 | } |
1787 | | |
1788 | 0 | if (bits > 0 && bits <= 62) { |
1789 | 0 | if (dimension.tok->valueType()->sign == ValueType::Sign::UNSIGNED) |
1790 | 0 | dimension.num = 1LL << bits; |
1791 | 0 | else |
1792 | 0 | dimension.num = 1LL << (bits - 1); |
1793 | 0 | } |
1794 | 0 | } |
1795 | 0 | } |
1796 | 0 | } |
1797 | 1.36k | } |
1798 | | |
1799 | | SymbolDatabase::~SymbolDatabase() |
1800 | 1.36k | { |
1801 | | // Clear scope, type, function and variable pointers |
1802 | 93.5k | for (const Token* tok = mTokenizer.list.front(); tok; tok = tok->next()) { |
1803 | 92.2k | const_cast<Token *>(tok)->scope(nullptr); |
1804 | 92.2k | const_cast<Token *>(tok)->type(nullptr); |
1805 | 92.2k | const_cast<Token *>(tok)->function(nullptr); |
1806 | 92.2k | const_cast<Token *>(tok)->variable(nullptr); |
1807 | 92.2k | const_cast<Token *>(tok)->enumerator(nullptr); |
1808 | 92.2k | const_cast<Token *>(tok)->setValueType(nullptr); |
1809 | 92.2k | } |
1810 | 1.36k | } |
1811 | | |
1812 | | bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const |
1813 | 51.1k | { |
1814 | 51.1k | if (tok->varId()) |
1815 | 13.6k | return false; |
1816 | | |
1817 | | // function returning function pointer? '... ( ... %name% ( ... ))( ... ) {' |
1818 | | // function returning reference to array '... ( & %name% ( ... ))[ ... ] {' |
1819 | | // TODO: Activate this again |
1820 | 37.5k | if ((false) && tok->str() == "(" && tok->strAt(1) != "*" && // NOLINT(readability-simplify-boolean-expr) |
1821 | 37.5k | (tok->link()->previous()->str() == ")" || Token::simpleMatch(tok->link()->tokAt(-2), ") const"))) { |
1822 | 0 | const Token* tok2 = tok->link()->next(); |
1823 | 0 | if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { |
1824 | 0 | const Token* argStartTok; |
1825 | 0 | if (tok->link()->previous()->str() == "const") |
1826 | 0 | argStartTok = tok->link()->linkAt(-2); |
1827 | 0 | else |
1828 | 0 | argStartTok = tok->link()->linkAt(-1); |
1829 | 0 | *funcStart = argStartTok->previous(); |
1830 | 0 | *argStart = argStartTok; |
1831 | 0 | *declEnd = Token::findmatch(tok2->link()->next(), "{|;"); |
1832 | 0 | return true; |
1833 | 0 | } |
1834 | 0 | if (tok2 && tok2->str() == "[") { |
1835 | 0 | while (tok2 && tok2->str() == "[") |
1836 | 0 | tok2 = tok2->link()->next(); |
1837 | 0 | if (Token::Match(tok2, "{|;|const|=")) { |
1838 | 0 | const Token* argStartTok; |
1839 | 0 | if (tok->link()->previous()->str() == "const") |
1840 | 0 | argStartTok = tok->link()->linkAt(-2); |
1841 | 0 | else |
1842 | 0 | argStartTok = tok->link()->linkAt(-1); |
1843 | 0 | *funcStart = argStartTok->previous(); |
1844 | 0 | *argStart = argStartTok; |
1845 | 0 | *declEnd = Token::findmatch(tok2, "{|;"); |
1846 | 0 | return true; |
1847 | 0 | } |
1848 | 0 | } |
1849 | 0 | } |
1850 | | |
1851 | 37.5k | else if (!tok->isName() || !tok->next() || !tok->next()->link()) |
1852 | 35.7k | return false; |
1853 | | |
1854 | | // regular function? |
1855 | 1.73k | else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && tok->previous() && |
1856 | 1.73k | (Token::Match(tok->previous(), "%name%|>|&|&&|*|::|~") || // Either a return type or scope qualifier in front of tok |
1857 | 1.73k | outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor |
1858 | 1.73k | const Token* tok1 = tok->previous(); |
1859 | 1.73k | const Token* tok2 = tok->next()->link()->next(); |
1860 | | |
1861 | 1.73k | if (!mTokenizer.isFunctionHead(tok->next(), ";:{")) |
1862 | 0 | return false; |
1863 | | |
1864 | | // skip over destructor "~" |
1865 | 1.73k | if (tok1->str() == "~") |
1866 | 0 | tok1 = tok1->previous(); |
1867 | | |
1868 | | // skip over qualification |
1869 | 1.73k | while (Token::simpleMatch(tok1, "::")) { |
1870 | 0 | tok1 = tok1->previous(); |
1871 | 0 | if (tok1 && tok1->isName()) |
1872 | 0 | tok1 = tok1->previous(); |
1873 | 0 | else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) |
1874 | 0 | tok1 = tok1->link()->tokAt(-2); |
1875 | 0 | } |
1876 | | |
1877 | | // skip over const, noexcept, throw, override, final and volatile specifiers |
1878 | 1.73k | while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) { |
1879 | 0 | tok2 = tok2->next(); |
1880 | 0 | if (tok2 && tok2->str() == "(") |
1881 | 0 | tok2 = tok2->link()->next(); |
1882 | 0 | } |
1883 | | |
1884 | | // skip over trailing return type |
1885 | 1.73k | if (tok2 && tok2->str() == ".") { |
1886 | 0 | for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { |
1887 | 0 | if (Token::Match(tok2, ";|{|=|override|final")) |
1888 | 0 | break; |
1889 | 0 | if (tok2->link() && Token::Match(tok2, "<|[|(")) |
1890 | 0 | tok2 = tok2->link(); |
1891 | 0 | } |
1892 | 0 | } |
1893 | | |
1894 | | // done if constructor or destructor |
1895 | 1.73k | if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) { |
1896 | | // skip over pointers and references |
1897 | 1.73k | while (Token::Match(tok1, "%type%|*|&|&&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const")) |
1898 | 0 | tok1 = tok1->previous(); |
1899 | | |
1900 | | // skip over decltype |
1901 | 1.73k | if (Token::simpleMatch(tok1, ")") && tok1->link() && |
1902 | 1.73k | Token::simpleMatch(tok1->link()->previous(), "decltype (")) |
1903 | 0 | tok1 = tok1->link()->tokAt(-2); |
1904 | | |
1905 | | // skip over template |
1906 | 1.73k | if (tok1 && tok1->str() == ">") { |
1907 | 0 | if (tok1->link()) |
1908 | 0 | tok1 = tok1->link()->previous(); |
1909 | 0 | else |
1910 | 0 | return false; |
1911 | 0 | } |
1912 | | |
1913 | | // function can't have number or variable as return type |
1914 | 1.73k | if (tok1 && (tok1->isNumber() || tok1->varId())) |
1915 | 0 | return false; |
1916 | | |
1917 | | // skip over return type |
1918 | 1.73k | if (tok1 && tok1->isName()) { |
1919 | 1.73k | if (tok1->str() == "return") |
1920 | 0 | return false; |
1921 | 1.73k | if (tok1->str() != "friend") |
1922 | 1.73k | tok1 = tok1->previous(); |
1923 | 1.73k | } |
1924 | | |
1925 | | // skip over qualification |
1926 | 1.73k | while (Token::simpleMatch(tok1, "::")) { |
1927 | 0 | tok1 = tok1->previous(); |
1928 | 0 | if (tok1 && tok1->isName()) |
1929 | 0 | tok1 = tok1->previous(); |
1930 | 0 | else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) |
1931 | 0 | tok1 = tok1->link()->tokAt(-2); |
1932 | 0 | else if (Token::simpleMatch(tok1, ")") && tok1->link() && |
1933 | 0 | Token::simpleMatch(tok1->link()->previous(), "decltype (")) |
1934 | 0 | tok1 = tok1->link()->tokAt(-2); |
1935 | 0 | } |
1936 | | |
1937 | | // skip over modifiers and other stuff |
1938 | 1.73k | while (Token::Match(tok1, "const|static|extern|template|virtual|struct|class|enum|%name%")) { |
1939 | | // friend type func(); is not a function |
1940 | 0 | if (isCPP() && tok1->str() == "friend" && tok2->str() == ";") |
1941 | 0 | return false; |
1942 | 0 | tok1 = tok1->previous(); |
1943 | 0 | } |
1944 | | |
1945 | | // should be at a sequence point if this is a function |
1946 | 1.73k | if (!Token::Match(tok1, ">|{|}|;|public:|protected:|private:") && tok1) |
1947 | 0 | return false; |
1948 | 1.73k | } |
1949 | | |
1950 | 1.73k | if (tok2 && |
1951 | 1.73k | (Token::Match(tok2, ";|{|=") || |
1952 | 1.73k | (tok2->isUpperCaseName() && Token::Match(tok2, "%name% ;|{")) || |
1953 | 1.73k | (tok2->isUpperCaseName() && Token::Match(tok2, "%name% (") && tok2->next()->link()->strAt(1) == "{") || |
1954 | 1.73k | Token::Match(tok2, ": ::| %name% (|::|<|{") || |
1955 | 1.73k | Token::Match(tok2, "&|&&| ;|{") || |
1956 | 1.73k | Token::Match(tok2, "= delete|default ;"))) { |
1957 | 1.73k | *funcStart = tok; |
1958 | 1.73k | *argStart = tok->next(); |
1959 | 1.73k | *declEnd = Token::findmatch(tok2, "{|;"); |
1960 | 1.73k | return true; |
1961 | 1.73k | } |
1962 | 1.73k | } |
1963 | | |
1964 | | // UNKNOWN_MACRO(a,b) { ... } |
1965 | 0 | else if (outerScope->type == Scope::eGlobal && |
1966 | 0 | Token::Match(tok, "%name% (") && |
1967 | 0 | tok->isUpperCaseName() && |
1968 | 0 | Token::simpleMatch(tok->linkAt(1), ") {") && |
1969 | 0 | (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { |
1970 | 0 | *funcStart = tok; |
1971 | 0 | *argStart = tok->next(); |
1972 | 0 | *declEnd = tok->linkAt(1)->next(); |
1973 | 0 | return true; |
1974 | 0 | } |
1975 | | |
1976 | | // template constructor? |
1977 | 0 | else if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) { |
1978 | 0 | const Token* tok2 = tok->next()->link()->next()->link(); |
1979 | 0 | if (Token::Match(tok2, ") const| ;|{|=") || |
1980 | 0 | Token::Match(tok2, ") : ::| %name% (|::|<|{") || |
1981 | 0 | Token::Match(tok2, ") const| noexcept {|;|(")) { |
1982 | 0 | *funcStart = tok; |
1983 | 0 | *argStart = tok2->link(); |
1984 | 0 | *declEnd = Token::findmatch(tok2->next(), "{|;"); |
1985 | 0 | return true; |
1986 | 0 | } |
1987 | 0 | } |
1988 | | |
1989 | | // regular C function with missing return or invalid C++ ? |
1990 | 0 | else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && |
1991 | 0 | Token::simpleMatch(tok->linkAt(1), ") {") && |
1992 | 0 | (!tok->previous() || Token::Match(tok->previous(), ";|}"))) { |
1993 | 0 | if (mTokenizer.isC()) { |
1994 | 0 | debugMessage(tok, "debug", "SymbolDatabase::isFunction found C function '" + tok->str() + "' without a return type."); |
1995 | 0 | *funcStart = tok; |
1996 | 0 | *argStart = tok->next(); |
1997 | 0 | *declEnd = tok->linkAt(1)->next(); |
1998 | 0 | return true; |
1999 | 0 | } |
2000 | 0 | mTokenizer.syntaxError(tok); |
2001 | 0 | } |
2002 | | |
2003 | 0 | return false; |
2004 | 37.5k | } |
2005 | | |
2006 | | void SymbolDatabase::validateExecutableScopes() const |
2007 | 0 | { |
2008 | 0 | const std::size_t functions = functionScopes.size(); |
2009 | 0 | for (std::size_t i = 0; i < functions; ++i) { |
2010 | 0 | const Scope* const scope = functionScopes[i]; |
2011 | 0 | const Function* const function = scope->function; |
2012 | 0 | if (scope->isExecutable() && !function) { |
2013 | 0 | const std::list<const Token*> callstack(1, scope->classDef); |
2014 | 0 | const std::string msg = std::string("Executable scope '") + scope->classDef->str() + "' with unknown function."; |
2015 | 0 | const ErrorMessage errmsg(callstack, &mTokenizer.list, Severity::debug, |
2016 | 0 | "symbolDatabaseWarning", |
2017 | 0 | msg, |
2018 | 0 | Certainty::normal); |
2019 | 0 | mErrorLogger->reportErr(errmsg); |
2020 | 0 | } |
2021 | 0 | } |
2022 | 0 | } |
2023 | | |
2024 | | namespace { |
2025 | | const Function* getFunctionForArgumentvariable(const Variable * const var, const std::vector<const Scope *>& functionScopes) |
2026 | 0 | { |
2027 | 0 | const std::size_t functions = functionScopes.size(); |
2028 | 0 | for (std::size_t i = 0; i < functions; ++i) { |
2029 | 0 | const Scope* const scope = functionScopes[i]; |
2030 | 0 | const Function* const function = scope->function; |
2031 | 0 | if (function) { |
2032 | 0 | for (std::size_t arg=0; arg < function->argCount(); ++arg) { |
2033 | 0 | if (var==function->getArgumentVar(arg)) |
2034 | 0 | return function; |
2035 | 0 | } |
2036 | 0 | } |
2037 | 0 | } |
2038 | 0 | return nullptr; |
2039 | 0 | } |
2040 | | } |
2041 | | |
2042 | | void SymbolDatabase::validateVariables() const |
2043 | 0 | { |
2044 | 0 | for (std::vector<const Variable *>::const_iterator iter = mVariableList.cbegin(); iter!=mVariableList.cend(); ++iter) { |
2045 | 0 | const Variable * const var = *iter; |
2046 | 0 | if (var) { |
2047 | 0 | if (!var->scope()) { |
2048 | 0 | const Function* function = getFunctionForArgumentvariable(var, functionScopes); |
2049 | 0 | if (!var->isArgument() || (function && function->hasBody())) { |
2050 | 0 | throw InternalError(var->nameToken(), "Analysis failed (variable without scope). If the code is valid then please report this failure.", InternalError::INTERNAL); |
2051 | | //std::cout << "!!!Variable found without scope: " << var->nameToken()->str() << std::endl; |
2052 | 0 | } |
2053 | 0 | } |
2054 | 0 | } |
2055 | 0 | } |
2056 | 0 | } |
2057 | | |
2058 | | void SymbolDatabase::validate() const |
2059 | 1.36k | { |
2060 | 1.36k | if (mSettings.debugwarnings) { |
2061 | 0 | validateExecutableScopes(); |
2062 | 0 | } |
2063 | | // TODO |
2064 | | //validateVariables(); |
2065 | 1.36k | } |
2066 | | |
2067 | | void SymbolDatabase::clangSetVariables(const std::vector<const Variable *> &variableList) |
2068 | 0 | { |
2069 | 0 | mVariableList = variableList; |
2070 | 0 | } |
2071 | | |
2072 | | void SymbolDatabase::debugSymbolDatabase() const |
2073 | 1.36k | { |
2074 | 1.36k | if (!mSettings.debugnormal && !mSettings.debugwarnings) |
2075 | 1.36k | return; |
2076 | 0 | for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { |
2077 | 0 | if (tok->astParent() && tok->astParent()->getTokenDebug() == tok->getTokenDebug()) |
2078 | 0 | continue; |
2079 | 0 | if (tok->getTokenDebug() == TokenDebug::ValueType) { |
2080 | |
|
2081 | 0 | std::string msg = "Value type is "; |
2082 | 0 | ErrorPath errorPath; |
2083 | 0 | if (tok->valueType()) { |
2084 | 0 | msg += tok->valueType()->str(); |
2085 | 0 | errorPath.insert(errorPath.end(), tok->valueType()->debugPath.cbegin(), tok->valueType()->debugPath.cend()); |
2086 | |
|
2087 | 0 | } else { |
2088 | 0 | msg += "missing"; |
2089 | 0 | } |
2090 | 0 | errorPath.emplace_back(tok, ""); |
2091 | 0 | mErrorLogger->reportErr( |
2092 | 0 | {errorPath, &mTokenizer.list, Severity::debug, "valueType", msg, CWE{0}, Certainty::normal}); |
2093 | 0 | } |
2094 | 0 | } |
2095 | 0 | } |
2096 | | |
2097 | | Variable::Variable(const Token *name_, const std::string &clangType, const Token *typeStart, |
2098 | | const Token *typeEnd, nonneg int index_, AccessControl access_, |
2099 | | const Type *type_, const Scope *scope_) |
2100 | | : mNameToken(name_), |
2101 | | mTypeStartToken(typeStart), |
2102 | | mTypeEndToken(typeEnd), |
2103 | | mIndex(index_), |
2104 | | mAccess(access_), |
2105 | | mFlags(0), |
2106 | | mType(type_), |
2107 | | mScope(scope_) |
2108 | 0 | { |
2109 | 0 | if (!mTypeStartToken && mTypeEndToken) { |
2110 | 0 | mTypeStartToken = mTypeEndToken; |
2111 | 0 | while (Token::Match(mTypeStartToken->previous(), "%type%|*|&")) |
2112 | 0 | mTypeStartToken = mTypeStartToken->previous(); |
2113 | 0 | } |
2114 | |
|
2115 | 0 | while (Token::Match(mTypeStartToken, "const|struct|static")) { |
2116 | 0 | if (mTypeStartToken->str() == "static") |
2117 | 0 | setFlag(fIsStatic, true); |
2118 | 0 | mTypeStartToken = mTypeStartToken->next(); |
2119 | 0 | } |
2120 | |
|
2121 | 0 | if (Token::simpleMatch(mTypeEndToken, "&")) |
2122 | 0 | setFlag(fIsReference, true); |
2123 | 0 | else if (Token::simpleMatch(mTypeEndToken, "&&")) { |
2124 | 0 | setFlag(fIsReference, true); |
2125 | 0 | setFlag(fIsRValueRef, true); |
2126 | 0 | } |
2127 | |
|
2128 | 0 | std::string::size_type pos = clangType.find('['); |
2129 | 0 | if (pos != std::string::npos) { |
2130 | 0 | setFlag(fIsArray, true); |
2131 | 0 | do { |
2132 | 0 | const std::string::size_type pos1 = pos+1; |
2133 | 0 | pos = clangType.find(']', pos1); |
2134 | 0 | Dimension dim; |
2135 | 0 | dim.tok = nullptr; |
2136 | 0 | dim.known = pos > pos1; |
2137 | 0 | if (pos > pos1) |
2138 | 0 | dim.num = MathLib::toLongNumber(clangType.substr(pos1, pos-pos1)); |
2139 | 0 | else |
2140 | 0 | dim.num = 0; |
2141 | 0 | mDimensions.push_back(dim); |
2142 | 0 | ++pos; |
2143 | 0 | } while (pos < clangType.size() && clangType[pos] == '['); |
2144 | 0 | } |
2145 | | |
2146 | | // Is there initialization in variable declaration |
2147 | 0 | const Token *initTok = mNameToken ? mNameToken->next() : nullptr; |
2148 | 0 | while (initTok && initTok->str() == "[") |
2149 | 0 | initTok = initTok->link()->next(); |
2150 | 0 | if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) |
2151 | 0 | setFlag(fIsInit, true); |
2152 | 0 | } |
2153 | | |
2154 | | Variable::Variable(const Variable &var, const Scope *scope) |
2155 | 0 | { |
2156 | 0 | *this = var; |
2157 | 0 | mScope = scope; |
2158 | 0 | } |
2159 | | |
2160 | | Variable::Variable(const Variable &var) |
2161 | 0 | { |
2162 | 0 | *this = var; |
2163 | 0 | } |
2164 | | |
2165 | | Variable::~Variable() |
2166 | 6.80k | { |
2167 | 6.80k | delete mValueType; |
2168 | 6.80k | } |
2169 | | |
2170 | | Variable& Variable::operator=(const Variable &var) |
2171 | 0 | { |
2172 | 0 | if (this == &var) |
2173 | 0 | return *this; |
2174 | | |
2175 | 0 | mNameToken = var.mNameToken; |
2176 | 0 | mTypeStartToken = var.mTypeStartToken; |
2177 | 0 | mTypeEndToken = var.mTypeEndToken; |
2178 | 0 | mIndex = var.mIndex; |
2179 | 0 | mAccess = var.mAccess; |
2180 | 0 | mFlags = var.mFlags; |
2181 | 0 | mType = var.mType; |
2182 | 0 | mScope = var.mScope; |
2183 | 0 | mDimensions = var.mDimensions; |
2184 | 0 | delete mValueType; |
2185 | 0 | if (var.mValueType) |
2186 | 0 | mValueType = new ValueType(*var.mValueType); |
2187 | 0 | else |
2188 | 0 | mValueType = nullptr; |
2189 | |
|
2190 | 0 | return *this; |
2191 | 0 | } |
2192 | | |
2193 | 2.83k | bool Variable::isMember() const { |
2194 | 2.83k | return mScope && mScope->isClassOrStructOrUnion(); |
2195 | 2.83k | } |
2196 | | |
2197 | | bool Variable::isPointerArray() const |
2198 | 0 | { |
2199 | 0 | return isArray() && nameToken() && nameToken()->previous() && (nameToken()->previous()->str() == "*"); |
2200 | 0 | } |
2201 | | |
2202 | | bool Variable::isUnsigned() const |
2203 | 0 | { |
2204 | 0 | return mValueType ? (mValueType->sign == ValueType::Sign::UNSIGNED) : mTypeStartToken->isUnsigned(); |
2205 | 0 | } |
2206 | | |
2207 | | const Token * Variable::declEndToken() const |
2208 | 5.24k | { |
2209 | 5.24k | Token const * declEnd = typeStartToken(); |
2210 | 15.7k | while (declEnd && !Token::Match(declEnd, "[;,)={]")) { |
2211 | 10.4k | if (declEnd->link() && Token::Match(declEnd,"(|[|<")) |
2212 | 0 | declEnd = declEnd->link(); |
2213 | 10.4k | declEnd = declEnd->next(); |
2214 | 10.4k | } |
2215 | 5.24k | return declEnd; |
2216 | 5.24k | } |
2217 | | |
2218 | | void Variable::evaluate(const Settings* settings) |
2219 | 6.80k | { |
2220 | | // Is there initialization in variable declaration |
2221 | 6.80k | const Token *initTok = mNameToken ? mNameToken->next() : nullptr; |
2222 | 6.80k | while (Token::Match(initTok, "[|(")) { |
2223 | 0 | initTok = initTok->link()->next(); |
2224 | 0 | if (Token::simpleMatch(initTok, ")")) |
2225 | 0 | initTok = initTok->next(); |
2226 | 0 | } |
2227 | 6.80k | if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) |
2228 | 6.80k | setFlag(fIsInit, true); |
2229 | | |
2230 | 6.80k | if (!settings) |
2231 | 0 | return; |
2232 | | |
2233 | 6.80k | const Library * const lib = &settings->library; |
2234 | | |
2235 | | // TODO: ValueType::parseDecl() is also performing a container lookup |
2236 | 6.80k | bool isContainer = false; |
2237 | 6.80k | if (mNameToken) |
2238 | 6.80k | setFlag(fIsArray, arrayDimensions(settings, isContainer)); |
2239 | | |
2240 | 6.80k | if (mTypeStartToken) |
2241 | 6.80k | setValueType(ValueType::parseDecl(mTypeStartToken,*settings)); |
2242 | | |
2243 | 6.80k | const Token* tok = mTypeStartToken; |
2244 | 6.80k | while (tok && tok->previous() && tok->previous()->isName()) |
2245 | 0 | tok = tok->previous(); |
2246 | 6.80k | const Token* end = mTypeEndToken; |
2247 | 6.80k | if (end) |
2248 | 6.80k | end = end->next(); |
2249 | 13.6k | while (tok != end) { |
2250 | 6.80k | if (tok->str() == "static") |
2251 | 0 | setFlag(fIsStatic, true); |
2252 | 6.80k | else if (tok->str() == "extern") |
2253 | 0 | setFlag(fIsExtern, true); |
2254 | 6.80k | else if (tok->str() == "volatile" || Token::simpleMatch(tok, "std :: atomic <")) |
2255 | 0 | setFlag(fIsVolatile, true); |
2256 | 6.80k | else if (tok->str() == "mutable") |
2257 | 0 | setFlag(fIsMutable, true); |
2258 | 6.80k | else if (tok->str() == "const") |
2259 | 0 | setFlag(fIsConst, true); |
2260 | 6.80k | else if (tok->str() == "constexpr") { |
2261 | 0 | setFlag(fIsConst, true); |
2262 | 0 | setFlag(fIsStatic, true); |
2263 | 6.80k | } else if (tok->str() == "*") { |
2264 | 0 | setFlag(fIsPointer, !isArray() || (isContainer && !Token::Match(tok->next(), "%name% [")) || Token::Match(tok, "* const| %name% )")); |
2265 | 0 | setFlag(fIsConst, false); // Points to const, isn't necessarily const itself |
2266 | 6.80k | } else if (tok->str() == "&") { |
2267 | 0 | if (isReference()) |
2268 | 0 | setFlag(fIsRValueRef, true); |
2269 | 0 | setFlag(fIsReference, true); |
2270 | 6.80k | } else if (tok->str() == "&&") { // Before simplification, && isn't split up |
2271 | 0 | setFlag(fIsRValueRef, true); |
2272 | 0 | setFlag(fIsReference, true); // Set also fIsReference |
2273 | 0 | } |
2274 | | |
2275 | 6.80k | if (tok->isAttributeMaybeUnused()) { |
2276 | 0 | setFlag(fIsMaybeUnused, true); |
2277 | 0 | } |
2278 | | |
2279 | 6.80k | if (tok->str() == "<" && tok->link()) |
2280 | 0 | tok = tok->link(); |
2281 | 6.80k | else |
2282 | 6.80k | tok = tok->next(); |
2283 | 6.80k | } |
2284 | | |
2285 | 6.80k | while (Token::Match(mTypeStartToken, "static|const|constexpr|volatile %any%")) |
2286 | 0 | mTypeStartToken = mTypeStartToken->next(); |
2287 | 6.80k | while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile")) |
2288 | 0 | mTypeEndToken = mTypeEndToken->previous(); |
2289 | | |
2290 | 6.80k | if (mTypeStartToken) { |
2291 | 6.80k | std::string strtype = mTypeStartToken->str(); |
2292 | 6.80k | for (const Token *typeToken = mTypeStartToken; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2)) |
2293 | 0 | strtype += "::" + typeToken->strAt(2); |
2294 | 6.80k | setFlag(fIsClass, !lib->podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && !isReference() && strtype != "..."); |
2295 | 6.80k | setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::")); |
2296 | 6.80k | setFlag(fIsStlString, ::isStlStringType(mTypeStartToken)); |
2297 | 6.80k | setFlag(fIsSmartPointer, mTypeStartToken->isCpp() && lib->isSmartPointer(mTypeStartToken)); |
2298 | 6.80k | } |
2299 | 6.80k | if (mAccess == AccessControl::Argument) { |
2300 | 0 | tok = mNameToken; |
2301 | 0 | if (!tok) { |
2302 | | // Argument without name |
2303 | 0 | tok = mTypeEndToken; |
2304 | | // back up to start of array dimensions |
2305 | 0 | while (tok && tok->str() == "]") |
2306 | 0 | tok = tok->link()->previous(); |
2307 | | // add array dimensions if present |
2308 | 0 | if (tok && tok->next()->str() == "[") |
2309 | 0 | setFlag(fIsArray, arrayDimensions(settings, isContainer)); |
2310 | 0 | } |
2311 | 0 | if (!tok) |
2312 | 0 | return; |
2313 | 0 | tok = tok->next(); |
2314 | 0 | while (tok->str() == "[") |
2315 | 0 | tok = tok->link(); |
2316 | 0 | setFlag(fHasDefault, tok->str() == "="); |
2317 | 0 | } |
2318 | | // check for C++11 member initialization |
2319 | 6.80k | if (mScope && mScope->isClassOrStruct()) { |
2320 | | // type var = x or |
2321 | | // type var = {x} |
2322 | | // type var = x; gets simplified to: type var ; var = x ; |
2323 | 0 | Token const * declEnd = declEndToken(); |
2324 | 0 | if ((Token::Match(declEnd, "; %name% =") && declEnd->strAt(1) == mNameToken->str()) || |
2325 | 0 | Token::Match(declEnd, "=|{")) |
2326 | 0 | setFlag(fHasDefault, true); |
2327 | 0 | } |
2328 | | |
2329 | 6.80k | if (mTypeStartToken) { |
2330 | 6.80k | if (Token::Match(mTypeStartToken, "float|double")) |
2331 | 0 | setFlag(fIsFloatType, true); |
2332 | 6.80k | } |
2333 | 6.80k | } |
2334 | | |
2335 | | void Variable::setValueType(const ValueType &valueType) |
2336 | 6.80k | { |
2337 | 6.80k | if (valueType.type == ValueType::Type::UNKNOWN_TYPE) { |
2338 | 0 | const Token *declType = Token::findsimplematch(mTypeStartToken, "decltype (", mTypeEndToken); |
2339 | 0 | if (declType && !declType->next()->valueType()) |
2340 | 0 | return; |
2341 | 0 | } |
2342 | 6.80k | delete mValueType; |
2343 | 6.80k | mValueType = new ValueType(valueType); |
2344 | 6.80k | if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )"))) |
2345 | 0 | setFlag(fIsPointer, true); |
2346 | 6.80k | setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer)); |
2347 | 6.80k | if (mValueType->smartPointerType) |
2348 | 0 | setFlag(fIsSmartPointer, true); |
2349 | 6.80k | } |
2350 | | |
2351 | | const Type* Variable::smartPointerType() const |
2352 | 79.3k | { |
2353 | 79.3k | if (!isSmartPointer()) |
2354 | 79.3k | return nullptr; |
2355 | | |
2356 | 0 | if (mValueType->smartPointerType) |
2357 | 0 | return mValueType->smartPointerType; |
2358 | | |
2359 | | // TODO: Cache result, handle more complex type expression |
2360 | 0 | const Token* typeTok = typeStartToken(); |
2361 | 0 | while (Token::Match(typeTok, "%name%|::")) |
2362 | 0 | typeTok = typeTok->next(); |
2363 | 0 | if (Token::Match(typeTok, "< %name% >")) { |
2364 | 0 | const Scope* scope = typeTok->scope(); |
2365 | 0 | const Type* ptrType{}; |
2366 | 0 | while (scope && !ptrType) { |
2367 | 0 | ptrType = scope->findType(typeTok->next()->str()); |
2368 | 0 | scope = scope->nestedIn; |
2369 | 0 | } |
2370 | 0 | return ptrType; |
2371 | 0 | } |
2372 | 0 | return nullptr; |
2373 | 0 | } |
2374 | | |
2375 | | const Type* Variable::iteratorType() const |
2376 | 0 | { |
2377 | 0 | if (!mValueType || mValueType->type != ValueType::ITERATOR) |
2378 | 0 | return nullptr; |
2379 | | |
2380 | 0 | if (mValueType->containerTypeToken) |
2381 | 0 | return mValueType->containerTypeToken->type(); |
2382 | | |
2383 | 0 | return nullptr; |
2384 | 0 | } |
2385 | | |
2386 | | bool Variable::isStlStringViewType() const |
2387 | 0 | { |
2388 | 0 | return getFlag(fIsStlType) && valueType() && valueType()->container && valueType()->container->stdStringLike && valueType()->container->view; |
2389 | 0 | } |
2390 | | |
2391 | | std::string Variable::getTypeName() const |
2392 | 0 | { |
2393 | 0 | std::string ret; |
2394 | | // TODO: For known types, generate the full type name |
2395 | 0 | for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next()) { |
2396 | 0 | ret += typeTok->str(); |
2397 | 0 | if (Token::simpleMatch(typeTok->next(), "<") && typeTok->next()->link()) // skip template arguments |
2398 | 0 | typeTok = typeTok->next()->link(); |
2399 | 0 | } |
2400 | 0 | return ret; |
2401 | 0 | } |
2402 | | |
2403 | | static bool isOperator(const Token *tokenDef) |
2404 | 1.73k | { |
2405 | 1.73k | if (!tokenDef) |
2406 | 0 | return false; |
2407 | 1.73k | if (tokenDef->isOperatorKeyword()) |
2408 | 0 | return true; |
2409 | 1.73k | const std::string &name = tokenDef->str(); |
2410 | 1.73k | return name.size() > 8 && startsWith(name,"operator") && std::strchr("+-*/%&|~^<>!=[(", name[8]); |
2411 | 1.73k | } |
2412 | | |
2413 | | Function::Function(const Tokenizer *mTokenizer, |
2414 | | const Token *tok, |
2415 | | const Scope *scope, |
2416 | | const Token *tokDef, |
2417 | | const Token *tokArgDef) |
2418 | | : tokenDef(tokDef), |
2419 | | argDef(tokArgDef), |
2420 | | nestedIn(scope) |
2421 | 1.73k | { |
2422 | | // operator function |
2423 | 1.73k | if (::isOperator(tokenDef)) { |
2424 | 0 | isOperator(true); |
2425 | | |
2426 | | // 'operator =' is special |
2427 | 0 | if (tokenDef->str() == "operator=") |
2428 | 0 | type = Function::eOperatorEqual; |
2429 | 0 | } |
2430 | | |
2431 | 1.73k | else if (tokenDef->str() == "[") { |
2432 | 0 | type = Function::eLambda; |
2433 | 0 | } |
2434 | | |
2435 | | // class constructor/destructor |
2436 | 1.73k | else if (((tokenDef->str() == scope->className) || |
2437 | 1.73k | (tokenDef->str().substr(0, scope->className.size()) == scope->className && |
2438 | 1.73k | tokenDef->str().size() > scope->className.size() + 1 && |
2439 | 1.73k | tokenDef->str()[scope->className.size() + 1] == '<')) && |
2440 | 1.73k | scope->type != Scope::ScopeType::eNamespace) { |
2441 | | // destructor |
2442 | 0 | if (tokenDef->previous()->str() == "~") |
2443 | 0 | type = Function::eDestructor; |
2444 | | // constructor of any kind |
2445 | 0 | else |
2446 | 0 | type = Function::eConstructor; |
2447 | |
|
2448 | 0 | isExplicit(tokenDef->strAt(-1) == "explicit" || tokenDef->strAt(-2) == "explicit"); |
2449 | 0 | } |
2450 | | |
2451 | 1.73k | const Token *tok1 = setFlags(tok, scope); |
2452 | | |
2453 | | // find the return type |
2454 | 1.73k | if (!isConstructor() && !isDestructor()) { |
2455 | | // @todo auto type deduction should be checked |
2456 | | // @todo attributes and exception specification can also precede trailing return type |
2457 | 1.73k | if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type |
2458 | 0 | hasTrailingReturnType(true); |
2459 | 0 | if (argDef->link()->strAt(1) == ".") |
2460 | 0 | retDef = argDef->link()->tokAt(2); |
2461 | 0 | else if (argDef->link()->strAt(2) == ".") |
2462 | 0 | retDef = argDef->link()->tokAt(3); |
2463 | 0 | else if (argDef->link()->strAt(3) == ".") |
2464 | 0 | retDef = argDef->link()->tokAt(4); |
2465 | 1.73k | } else if (!isLambda()) { |
2466 | 1.73k | if (tok1->str() == ">") |
2467 | 0 | tok1 = tok1->next(); |
2468 | 1.73k | while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) |
2469 | 0 | tok1 = tok1->next(); |
2470 | 1.73k | retDef = tok1; |
2471 | 1.73k | } |
2472 | 1.73k | } |
2473 | | |
2474 | 1.73k | const Token *end = argDef->link(); |
2475 | | |
2476 | | // parse function attributes.. |
2477 | 1.73k | tok = end->next(); |
2478 | 1.73k | while (tok) { |
2479 | 1.73k | if (tok->str() == "const") |
2480 | 0 | isConst(true); |
2481 | 1.73k | else if (tok->str() == "&") |
2482 | 0 | hasLvalRefQualifier(true); |
2483 | 1.73k | else if (tok->str() == "&&") |
2484 | 0 | hasRvalRefQualifier(true); |
2485 | 1.73k | else if (tok->str() == "override") |
2486 | 0 | setFlag(fHasOverrideSpecifier, true); |
2487 | 1.73k | else if (tok->str() == "final") |
2488 | 0 | setFlag(fHasFinalSpecifier, true); |
2489 | 1.73k | else if (tok->str() == "volatile") |
2490 | 0 | isVolatile(true); |
2491 | 1.73k | else if (tok->str() == "noexcept") { |
2492 | 0 | isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); |
2493 | 0 | if (tok->next()->str() == "(") |
2494 | 0 | tok = tok->linkAt(1); |
2495 | 1.73k | } else if (Token::simpleMatch(tok, "throw (")) { |
2496 | 0 | isThrow(true); |
2497 | 0 | if (tok->strAt(2) != ")") |
2498 | 0 | throwArg = tok->next(); |
2499 | 0 | tok = tok->linkAt(1); |
2500 | 1.73k | } else if (Token::Match(tok, "= 0|default|delete ;")) { |
2501 | 0 | const std::string& modifier = tok->strAt(1); |
2502 | 0 | isPure(modifier == "0"); |
2503 | 0 | isDefault(modifier == "default"); |
2504 | 0 | isDelete(modifier == "delete"); |
2505 | 1.73k | } else if (tok->str() == ".") { // trailing return type |
2506 | | // skip over return type |
2507 | 0 | while (tok && !Token::Match(tok->next(), ";|{|override|final")) |
2508 | 0 | tok = tok->next(); |
2509 | 0 | } else |
2510 | 1.73k | break; |
2511 | 0 | if (tok) |
2512 | 0 | tok = tok->next(); |
2513 | 0 | } |
2514 | | |
2515 | 1.73k | if (mTokenizer->isFunctionHead(end, ":{")) { |
2516 | | // assume implementation is inline (definition and implementation same) |
2517 | 1.73k | token = tokenDef; |
2518 | 1.73k | arg = argDef; |
2519 | 1.73k | isInline(true); |
2520 | 1.73k | hasBody(true); |
2521 | 1.73k | } |
2522 | 1.73k | } |
2523 | | |
2524 | | Function::Function(const Token *tokenDef, const std::string &clangType) |
2525 | | : tokenDef(tokenDef) |
2526 | 0 | { |
2527 | | // operator function |
2528 | 0 | if (::isOperator(tokenDef)) { |
2529 | 0 | isOperator(true); |
2530 | | |
2531 | | // 'operator =' is special |
2532 | 0 | if (tokenDef->str() == "operator=") |
2533 | 0 | type = Function::eOperatorEqual; |
2534 | 0 | } |
2535 | |
|
2536 | 0 | setFlags(tokenDef, tokenDef->scope()); |
2537 | |
|
2538 | 0 | if (endsWith(clangType, " const")) |
2539 | 0 | isConst(true); |
2540 | 0 | } |
2541 | | |
2542 | | const Token *Function::setFlags(const Token *tok1, const Scope *scope) |
2543 | 1.73k | { |
2544 | 1.73k | if (tok1->isInline()) |
2545 | 0 | isInlineKeyword(true); |
2546 | | |
2547 | | // look for end of previous statement |
2548 | 3.47k | while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) { |
2549 | 1.73k | tok1 = tok1->previous(); |
2550 | | |
2551 | 1.73k | if (tok1->isInline()) |
2552 | 0 | isInlineKeyword(true); |
2553 | | |
2554 | | // extern function |
2555 | 1.73k | if (tok1->isExternC() || tok1->str() == "extern") { |
2556 | 0 | isExtern(true); |
2557 | 0 | } |
2558 | | |
2559 | | // virtual function |
2560 | 1.73k | else if (tok1->str() == "virtual") { |
2561 | 0 | hasVirtualSpecifier(true); |
2562 | 0 | } |
2563 | | |
2564 | | // static function |
2565 | 1.73k | else if (tok1->str() == "static") { |
2566 | 0 | isStatic(true); |
2567 | 0 | if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) |
2568 | 0 | isStaticLocal(true); |
2569 | 0 | } |
2570 | | |
2571 | | // friend function |
2572 | 1.73k | else if (tok1->str() == "friend") { |
2573 | 0 | isFriend(true); |
2574 | 0 | } |
2575 | | |
2576 | | // constexpr function |
2577 | 1.73k | else if (tok1->str() == "constexpr") { |
2578 | 0 | isConstexpr(true); |
2579 | 0 | } |
2580 | | |
2581 | | // decltype |
2582 | 1.73k | else if (tok1->str() == ")" && Token::simpleMatch(tok1->link()->previous(), "decltype (")) { |
2583 | 0 | tok1 = tok1->link()->previous(); |
2584 | 0 | } |
2585 | | |
2586 | 1.73k | else if (tok1->link() && tok1->str() == ">") { |
2587 | | // Function template |
2588 | 0 | if (Token::simpleMatch(tok1->link()->previous(), "template <")) { |
2589 | 0 | templateDef = tok1->link()->previous(); |
2590 | 0 | break; |
2591 | 0 | } |
2592 | 0 | tok1 = tok1->link(); |
2593 | 0 | } |
2594 | 1.73k | } |
2595 | 1.73k | return tok1; |
2596 | 1.73k | } |
2597 | | |
2598 | | std::string Function::fullName() const |
2599 | 0 | { |
2600 | 0 | std::string ret = name(); |
2601 | 0 | for (const Scope *s = nestedIn; s; s = s->nestedIn) { |
2602 | 0 | if (!s->className.empty()) |
2603 | 0 | ret = s->className + "::" + ret; |
2604 | 0 | } |
2605 | 0 | ret += "("; |
2606 | 0 | for (const Variable &a : argumentList) |
2607 | 0 | ret += (a.index() == 0 ? "" : ",") + a.name(); |
2608 | 0 | return ret + ")"; |
2609 | 0 | } |
2610 | | |
2611 | | static std::string qualifiedName(const Scope *scope) |
2612 | 0 | { |
2613 | 0 | std::string name = scope->className; |
2614 | 0 | while (scope->nestedIn) { |
2615 | 0 | if (!scope->nestedIn->className.empty()) |
2616 | 0 | name = (scope->nestedIn->className + " :: ") + name; |
2617 | 0 | scope = scope->nestedIn; |
2618 | 0 | } |
2619 | 0 | return name; |
2620 | 0 | } |
2621 | | |
2622 | | static bool usingNamespace(const Scope *scope, const Token *first, const Token *second, int &offset) |
2623 | 0 | { |
2624 | | // check if qualifications match first before checking if using is needed |
2625 | 0 | const Token *tok1 = first; |
2626 | 0 | const Token *tok2 = second; |
2627 | 0 | bool match = false; |
2628 | 0 | while (Token::Match(tok1, "%type% :: %type%") && Token::Match(tok2, "%type% :: %type%")) { |
2629 | 0 | if (tok1->str() == tok2->str()) { |
2630 | 0 | tok1 = tok1->tokAt(2); |
2631 | 0 | tok2 = tok2->tokAt(2); |
2632 | 0 | match = true; |
2633 | 0 | } else { |
2634 | 0 | match = false; |
2635 | 0 | break; |
2636 | 0 | } |
2637 | 0 | } |
2638 | |
|
2639 | 0 | if (match) |
2640 | 0 | return false; |
2641 | | |
2642 | 0 | offset = 0; |
2643 | 0 | std::string name = first->str(); |
2644 | |
|
2645 | 0 | while (Token::Match(first, "%type% :: %type%")) { |
2646 | 0 | if (offset) |
2647 | 0 | name += (" :: " + first->str()); |
2648 | 0 | offset += 2; |
2649 | 0 | first = first->tokAt(2); |
2650 | 0 | if (first->str() == second->str()) { |
2651 | 0 | break; |
2652 | 0 | } |
2653 | 0 | } |
2654 | |
|
2655 | 0 | if (offset) { |
2656 | 0 | while (scope) { |
2657 | 0 | for (const auto & info : scope->usingList) { |
2658 | 0 | if (info.scope) { |
2659 | 0 | if (name == qualifiedName(info.scope)) |
2660 | 0 | return true; |
2661 | 0 | } |
2662 | | // no scope so get name from using |
2663 | 0 | else { |
2664 | 0 | const Token *start = info.start->tokAt(2); |
2665 | 0 | std::string nsName; |
2666 | 0 | while (start && start->str() != ";") { |
2667 | 0 | if (!nsName.empty()) |
2668 | 0 | nsName += " "; |
2669 | 0 | nsName += start->str(); |
2670 | 0 | start = start->next(); |
2671 | 0 | } |
2672 | 0 | if (nsName == name) |
2673 | 0 | return true; |
2674 | 0 | } |
2675 | 0 | } |
2676 | 0 | scope = scope->nestedIn; |
2677 | 0 | } |
2678 | 0 | } |
2679 | | |
2680 | 0 | return false; |
2681 | 0 | } |
2682 | | |
2683 | | static bool typesMatch( |
2684 | | const Scope *first_scope, |
2685 | | const Token *first_token, |
2686 | | const Scope *second_scope, |
2687 | | const Token *second_token, |
2688 | | const Token **new_first, |
2689 | | const Token **new_second) |
2690 | 0 | { |
2691 | | // get first type |
2692 | 0 | const Type* first_type = first_scope->check->findType(first_token, first_scope, /*lookOutside*/ true); |
2693 | 0 | if (first_type) { |
2694 | | // get second type |
2695 | 0 | const Type* second_type = second_scope->check->findType(second_token, second_scope, /*lookOutside*/ true); |
2696 | | // check if types match |
2697 | 0 | if (first_type == second_type) { |
2698 | 0 | const Token* tok1 = first_token; |
2699 | 0 | while (tok1 && tok1->str() != first_type->name()) |
2700 | 0 | tok1 = tok1->next(); |
2701 | 0 | const Token *tok2 = second_token; |
2702 | 0 | while (tok2 && tok2->str() != second_type->name()) |
2703 | 0 | tok2 = tok2->next(); |
2704 | | // update parser token positions |
2705 | 0 | if (tok1 && tok2) { |
2706 | 0 | *new_first = tok1->previous(); |
2707 | 0 | *new_second = tok2->previous(); |
2708 | 0 | return true; |
2709 | 0 | } |
2710 | 0 | } |
2711 | 0 | } |
2712 | 0 | return false; |
2713 | 0 | } |
2714 | | |
2715 | | bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const |
2716 | 0 | { |
2717 | 0 | const bool isCPP = scope->check->isCPP(); |
2718 | 0 | if (!isCPP) // C does not support overloads |
2719 | 0 | return true; |
2720 | | |
2721 | 0 | int arg_path_length = path_length; |
2722 | 0 | int offset = 0; |
2723 | 0 | int openParen = 0; |
2724 | | |
2725 | | // check for () == (void) and (void) == () |
2726 | 0 | if ((Token::simpleMatch(first, "( )") && Token::simpleMatch(second, "( void )")) || |
2727 | 0 | (Token::simpleMatch(first, "( void )") && Token::simpleMatch(second, "( )"))) |
2728 | 0 | return true; |
2729 | | |
2730 | 0 | auto skipTopLevelConst = [](const Token* start) -> const Token* { |
2731 | 0 | const Token* tok = start->next(); |
2732 | 0 | if (Token::simpleMatch(tok, "const")) { |
2733 | 0 | tok = tok->next(); |
2734 | 0 | while (Token::Match(tok, "%name%|%type%|::")) |
2735 | 0 | tok = tok->next(); |
2736 | 0 | if (Token::Match(tok, ",|)|=")) |
2737 | 0 | return start->next(); |
2738 | 0 | } |
2739 | 0 | return start; |
2740 | 0 | }; |
2741 | |
|
2742 | 0 | while (first->str() == second->str() && |
2743 | 0 | first->isLong() == second->isLong() && |
2744 | 0 | first->isUnsigned() == second->isUnsigned()) { |
2745 | 0 | if (first->str() == "(") |
2746 | 0 | openParen++; |
2747 | | |
2748 | | // at end of argument list |
2749 | 0 | else if (first->str() == ")") { |
2750 | 0 | if (openParen == 1) |
2751 | 0 | return true; |
2752 | 0 | --openParen; |
2753 | 0 | } |
2754 | | |
2755 | | // skip optional type information |
2756 | 0 | if (Token::Match(first->next(), "struct|enum|union|class")) |
2757 | 0 | first = first->next(); |
2758 | 0 | if (Token::Match(second->next(), "struct|enum|union|class")) |
2759 | 0 | second = second->next(); |
2760 | | |
2761 | | // skip const on type passed by value |
2762 | 0 | const Token* const oldSecond = second; |
2763 | 0 | first = skipTopLevelConst(first); |
2764 | 0 | second = skipTopLevelConst(second); |
2765 | | |
2766 | | // skip default value assignment |
2767 | 0 | if (oldSecond == second && first->next()->str() == "=") { |
2768 | 0 | first = first->nextArgument(); |
2769 | 0 | if (first) |
2770 | 0 | first = first->tokAt(-2); |
2771 | 0 | if (second->next()->str() == "=") { |
2772 | 0 | second = second->nextArgument(); |
2773 | 0 | if (second) |
2774 | 0 | second = second->tokAt(-2); |
2775 | 0 | if (!first || !second) { // End of argument list (first or second) |
2776 | 0 | return !first && !second; |
2777 | 0 | } |
2778 | 0 | } else if (!first) { // End of argument list (first) |
2779 | 0 | return !second->nextArgument(); // End of argument list (second) |
2780 | 0 | } |
2781 | 0 | } else if (oldSecond == second && second->next()->str() == "=") { |
2782 | 0 | second = second->nextArgument(); |
2783 | 0 | if (second) |
2784 | 0 | second = second->tokAt(-2); |
2785 | 0 | if (!second) { // End of argument list (second) |
2786 | 0 | return false; |
2787 | 0 | } |
2788 | 0 | } |
2789 | | |
2790 | | // definition missing variable name |
2791 | 0 | else if ((first->next()->str() == "," && second->next()->str() != ",") || |
2792 | 0 | (Token::Match(first, "!!( )") && second->next()->str() != ")")) { |
2793 | 0 | second = second->next(); |
2794 | | // skip default value assignment |
2795 | 0 | if (second->next()->str() == "=") { |
2796 | 0 | do { |
2797 | 0 | second = second->next(); |
2798 | 0 | } while (!Token::Match(second->next(), ",|)")); |
2799 | 0 | } |
2800 | 0 | } else if (first->next()->str() == "[" && second->next()->str() != "[") |
2801 | 0 | second = second->next(); |
2802 | | |
2803 | | // function missing variable name |
2804 | 0 | else if ((second->next()->str() == "," && first->next()->str() != ",") || |
2805 | 0 | (Token::Match(second, "!!( )") && first->next()->str() != ")")) { |
2806 | 0 | first = first->next(); |
2807 | | // skip default value assignment |
2808 | 0 | if (first->next()->str() == "=") { |
2809 | 0 | do { |
2810 | 0 | first = first->next(); |
2811 | 0 | } while (!Token::Match(first->next(), ",|)")); |
2812 | 0 | } |
2813 | 0 | } else if (second->next()->str() == "[" && first->next()->str() != "[") |
2814 | 0 | first = first->next(); |
2815 | | |
2816 | | // unnamed parameters |
2817 | 0 | else if (Token::Match(first, "(|, %type% ,|)") && Token::Match(second, "(|, %type% ,|)")) { |
2818 | 0 | if (first->next()->expressionString() != second->next()->expressionString()) |
2819 | 0 | break; |
2820 | 0 | first = first->next(); |
2821 | 0 | second = second->next(); |
2822 | 0 | continue; |
2823 | 0 | } |
2824 | | |
2825 | | // argument list has different number of arguments |
2826 | 0 | else if (openParen == 1 && second->str() == ")" && first->str() != ")") |
2827 | 0 | break; |
2828 | | |
2829 | | // ckeck for type * x == type x[] |
2830 | 0 | else if (Token::Match(first->next(), "* %name%| ,|)|=") && |
2831 | 0 | Token::Match(second->next(), "%name%| [ ] ,|)")) { |
2832 | 0 | do { |
2833 | 0 | first = first->next(); |
2834 | 0 | } while (!Token::Match(first->next(), ",|)")); |
2835 | 0 | do { |
2836 | 0 | second = second->next(); |
2837 | 0 | } while (!Token::Match(second->next(), ",|)")); |
2838 | 0 | } |
2839 | | |
2840 | | // const after * |
2841 | 0 | else if (first->next()->str() == "*" && second->next()->str() == "*" && |
2842 | 0 | ((first->strAt(2) != "const" && second->strAt(2) == "const") || |
2843 | 0 | (first->strAt(2) == "const" && second->strAt(2) != "const"))) { |
2844 | 0 | if (first->strAt(2) != "const") { |
2845 | 0 | if (Token::Match(first->tokAt(2), "%name%| ,|)") && Token::Match(second->tokAt(3), "%name%| ,|)")) { |
2846 | 0 | first = first->tokAt(Token::Match(first->tokAt(2), "%name%") ? 2 : 1); |
2847 | 0 | second = second->tokAt(Token::Match(second->tokAt(3), "%name%") ? 3 : 2); |
2848 | 0 | } else { |
2849 | 0 | first = first->next(); |
2850 | 0 | second = second->tokAt(2); |
2851 | 0 | } |
2852 | 0 | } else { |
2853 | 0 | if (Token::Match(second->tokAt(2), "%name%| ,|)") && Token::Match(first->tokAt(3), "%name%| ,|)")) { |
2854 | 0 | first = first->tokAt(Token::Match(first->tokAt(3), "%name%") ? 3 : 2); |
2855 | 0 | second = second->tokAt(Token::Match(second->tokAt(2), "%name%") ? 2 : 1); |
2856 | 0 | } else { |
2857 | 0 | first = first->tokAt(2); |
2858 | 0 | second = second->next(); |
2859 | 0 | } |
2860 | 0 | } |
2861 | 0 | } |
2862 | | |
2863 | | // variable names are different |
2864 | 0 | else if ((Token::Match(first->next(), "%name% ,|)|=|[") && |
2865 | 0 | Token::Match(second->next(), "%name% ,|)|[")) && |
2866 | 0 | (first->next()->str() != second->next()->str())) { |
2867 | | // skip variable names |
2868 | 0 | first = first->next(); |
2869 | 0 | second = second->next(); |
2870 | | |
2871 | | // skip default value assignment |
2872 | 0 | if (first->next()->str() == "=") { |
2873 | 0 | do { |
2874 | 0 | first = first->next(); |
2875 | 0 | } while (!Token::Match(first->next(), ",|)")); |
2876 | 0 | } |
2877 | 0 | } |
2878 | | |
2879 | | // using namespace |
2880 | 0 | else if (usingNamespace(scope, first->next(), second->next(), offset)) |
2881 | 0 | first = first->tokAt(offset); |
2882 | | |
2883 | | // same type with different qualification |
2884 | 0 | else if (typesMatch(scope, first->next(), nestedIn, second->next(), &first, &second)) |
2885 | 0 | ; |
2886 | | |
2887 | | // variable with class path |
2888 | 0 | else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") { |
2889 | 0 | std::string param = path; |
2890 | |
|
2891 | 0 | if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { |
2892 | | // check for redundant qualification before skipping it |
2893 | 0 | if (!Token::simpleMatch(first->next(), param.c_str(), param.size())) { |
2894 | 0 | second = second->tokAt(int(arg_path_length)); |
2895 | 0 | arg_path_length = 0; |
2896 | 0 | } |
2897 | 0 | } |
2898 | | |
2899 | | // nested or base class variable |
2900 | 0 | else if (arg_path_length <= 2 && Token::Match(first->next(), "%name%") && |
2901 | 0 | (Token::Match(second->next(), "%name% :: %name%") || |
2902 | 0 | (Token::Match(second->next(), "%name% <") && |
2903 | 0 | Token::Match(second->linkAt(1), "> :: %name%"))) && |
2904 | 0 | ((second->next()->str() == scope->className) || |
2905 | 0 | (scope->nestedIn && second->next()->str() == scope->nestedIn->className) || |
2906 | 0 | (scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) && |
2907 | 0 | (first->next()->str() == second->strAt(3))) { |
2908 | 0 | if (Token::Match(second->next(), "%name% <")) |
2909 | 0 | second = second->linkAt(1)->next(); |
2910 | 0 | else |
2911 | 0 | second = second->tokAt(2); |
2912 | 0 | } |
2913 | | |
2914 | | // remove class name |
2915 | 0 | else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) { |
2916 | 0 | std::string short_path = path; |
2917 | 0 | unsigned int short_path_length = arg_path_length; |
2918 | | |
2919 | | // remove last " :: " |
2920 | 0 | short_path.resize(short_path.size() - 4); |
2921 | 0 | short_path_length--; |
2922 | | |
2923 | | // remove last name |
2924 | 0 | std::string::size_type lastSpace = short_path.find_last_of(' '); |
2925 | 0 | if (lastSpace != std::string::npos) { |
2926 | 0 | short_path.resize(lastSpace+1); |
2927 | 0 | short_path_length--; |
2928 | 0 | if (short_path[short_path.size() - 1] == '>') { |
2929 | 0 | short_path.resize(short_path.size() - 3); |
2930 | 0 | while (short_path[short_path.size() - 1] == '<') { |
2931 | 0 | lastSpace = short_path.find_last_of(' '); |
2932 | 0 | short_path.resize(lastSpace+1); |
2933 | 0 | short_path_length--; |
2934 | 0 | } |
2935 | 0 | } |
2936 | 0 | } |
2937 | |
|
2938 | 0 | param = short_path; |
2939 | 0 | if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { |
2940 | 0 | second = second->tokAt(int(short_path_length)); |
2941 | 0 | arg_path_length = 0; |
2942 | 0 | } |
2943 | 0 | } |
2944 | 0 | } |
2945 | | |
2946 | 0 | first = first->next(); |
2947 | 0 | second = second->next(); |
2948 | | |
2949 | | // reset path length |
2950 | 0 | if (first->str() == "," || second->str() == ",") |
2951 | 0 | arg_path_length = path_length; |
2952 | 0 | } |
2953 | | |
2954 | 0 | return false; |
2955 | 0 | } |
2956 | | |
2957 | | static bool isUnknownType(const Token* start, const Token* end) |
2958 | 5.21k | { |
2959 | 5.21k | while (Token::Match(start, "const|volatile")) |
2960 | 0 | start = start->next(); |
2961 | 5.21k | start = skipScopeIdentifiers(start); |
2962 | 5.21k | if (start->tokAt(1) == end && !start->type() && !start->isStandardType()) |
2963 | 0 | return true; |
2964 | | // TODO: Try to deduce the type of the expression |
2965 | 5.21k | if (Token::Match(start, "decltype|typeof")) |
2966 | 0 | return true; |
2967 | 5.21k | return false; |
2968 | 5.21k | } |
2969 | | |
2970 | | static const Token* getEnableIfReturnType(const Token* start) |
2971 | 0 | { |
2972 | 0 | if (!start) |
2973 | 0 | return nullptr; |
2974 | 0 | for (const Token* tok = start->next(); precedes(tok, start->link()); tok = tok->next()) { |
2975 | 0 | if (tok->link() && Token::Match(tok, "(|[|{|<")) { |
2976 | 0 | tok = tok->link(); |
2977 | 0 | continue; |
2978 | 0 | } |
2979 | 0 | if (Token::simpleMatch(tok, ",")) |
2980 | 0 | return tok->next(); |
2981 | 0 | } |
2982 | 0 | return nullptr; |
2983 | 0 | } |
2984 | | |
2985 | | template<class Predicate> |
2986 | | static bool checkReturns(const Function* function, bool unknown, bool emptyEnableIf, Predicate pred) |
2987 | 7.13k | { |
2988 | 7.13k | if (!function) |
2989 | 0 | return false; |
2990 | 7.13k | if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) |
2991 | 0 | return false; |
2992 | 7.13k | const Token* defStart = function->retDef; |
2993 | 7.13k | if (!defStart) |
2994 | 0 | return unknown; |
2995 | 7.13k | const Token* defEnd = function->returnDefEnd(); |
2996 | 7.13k | if (!defEnd) |
2997 | 0 | return unknown; |
2998 | 7.13k | if (defEnd == defStart) |
2999 | 0 | return unknown; |
3000 | 7.13k | if (pred(defStart, defEnd)) |
3001 | 1.91k | return true; |
3002 | 5.21k | if (Token::Match(defEnd->tokAt(-1), "*|&|&&")) |
3003 | 0 | return false; |
3004 | | // void STDCALL foo() |
3005 | 5.21k | while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") && |
3006 | 5.21k | !Token::Match(defEnd->tokAt(-2), "const|volatile")) |
3007 | 0 | defEnd = defEnd->previous(); |
3008 | | // enable_if |
3009 | 5.21k | const Token* enableIfEnd = nullptr; |
3010 | 5.21k | if (Token::simpleMatch(defEnd->previous(), ">")) |
3011 | 0 | enableIfEnd = defEnd->previous(); |
3012 | 5.21k | else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type")) |
3013 | 0 | enableIfEnd = defEnd->tokAt(-3); |
3014 | 5.21k | if (enableIfEnd && enableIfEnd->link() && |
3015 | 5.21k | Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) { |
3016 | 0 | if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) { |
3017 | 0 | defStart = start; |
3018 | 0 | defEnd = enableIfEnd; |
3019 | 0 | } else { |
3020 | 0 | return emptyEnableIf; |
3021 | 0 | } |
3022 | 0 | } |
3023 | 5.21k | assert(defEnd != defStart); |
3024 | 5.21k | if (pred(defStart, defEnd)) |
3025 | 0 | return true; |
3026 | 5.21k | if (isUnknownType(defStart, defEnd)) |
3027 | 0 | return unknown; |
3028 | 5.21k | return false; |
3029 | 5.21k | } Unexecuted instantiation: symboldatabase.cpp:bool checkReturns<Function::returnsConst(Function const*, bool)::$_6>(Function const*, bool, bool, Function::returnsConst(Function const*, bool)::$_6) symboldatabase.cpp:bool checkReturns<Function::returnsReference(Function const*, bool, bool)::$_7>(Function const*, bool, bool, Function::returnsReference(Function const*, bool, bool)::$_7) Line | Count | Source | 2987 | 3.48k | { | 2988 | 3.48k | if (!function) | 2989 | 0 | return false; | 2990 | 3.48k | if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) | 2991 | 0 | return false; | 2992 | 3.48k | const Token* defStart = function->retDef; | 2993 | 3.48k | if (!defStart) | 2994 | 0 | return unknown; | 2995 | 3.48k | const Token* defEnd = function->returnDefEnd(); | 2996 | 3.48k | if (!defEnd) | 2997 | 0 | return unknown; | 2998 | 3.48k | if (defEnd == defStart) | 2999 | 0 | return unknown; | 3000 | 3.48k | if (pred(defStart, defEnd)) | 3001 | 0 | return true; | 3002 | 3.48k | if (Token::Match(defEnd->tokAt(-1), "*|&|&&")) | 3003 | 0 | return false; | 3004 | | // void STDCALL foo() | 3005 | 3.48k | while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") && | 3006 | 3.48k | !Token::Match(defEnd->tokAt(-2), "const|volatile")) | 3007 | 0 | defEnd = defEnd->previous(); | 3008 | | // enable_if | 3009 | 3.48k | const Token* enableIfEnd = nullptr; | 3010 | 3.48k | if (Token::simpleMatch(defEnd->previous(), ">")) | 3011 | 0 | enableIfEnd = defEnd->previous(); | 3012 | 3.48k | else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type")) | 3013 | 0 | enableIfEnd = defEnd->tokAt(-3); | 3014 | 3.48k | if (enableIfEnd && enableIfEnd->link() && | 3015 | 3.48k | Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) { | 3016 | 0 | if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) { | 3017 | 0 | defStart = start; | 3018 | 0 | defEnd = enableIfEnd; | 3019 | 0 | } else { | 3020 | 0 | return emptyEnableIf; | 3021 | 0 | } | 3022 | 0 | } | 3023 | 3.48k | assert(defEnd != defStart); | 3024 | 3.48k | if (pred(defStart, defEnd)) | 3025 | 0 | return true; | 3026 | 3.48k | if (isUnknownType(defStart, defEnd)) | 3027 | 0 | return unknown; | 3028 | 3.48k | return false; | 3029 | 3.48k | } |
Unexecuted instantiation: symboldatabase.cpp:bool checkReturns<Function::returnsPointer(Function const*, bool)::$_8>(Function const*, bool, bool, Function::returnsPointer(Function const*, bool)::$_8) symboldatabase.cpp:bool checkReturns<Function::returnsStandardType(Function const*, bool)::$_9>(Function const*, bool, bool, Function::returnsStandardType(Function const*, bool)::$_9) Line | Count | Source | 2987 | 1.91k | { | 2988 | 1.91k | if (!function) | 2989 | 0 | return false; | 2990 | 1.91k | if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) | 2991 | 0 | return false; | 2992 | 1.91k | const Token* defStart = function->retDef; | 2993 | 1.91k | if (!defStart) | 2994 | 0 | return unknown; | 2995 | 1.91k | const Token* defEnd = function->returnDefEnd(); | 2996 | 1.91k | if (!defEnd) | 2997 | 0 | return unknown; | 2998 | 1.91k | if (defEnd == defStart) | 2999 | 0 | return unknown; | 3000 | 1.91k | if (pred(defStart, defEnd)) | 3001 | 1.91k | return true; | 3002 | 0 | if (Token::Match(defEnd->tokAt(-1), "*|&|&&")) | 3003 | 0 | return false; | 3004 | | // void STDCALL foo() | 3005 | 0 | while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") && | 3006 | 0 | !Token::Match(defEnd->tokAt(-2), "const|volatile")) | 3007 | 0 | defEnd = defEnd->previous(); | 3008 | | // enable_if | 3009 | 0 | const Token* enableIfEnd = nullptr; | 3010 | 0 | if (Token::simpleMatch(defEnd->previous(), ">")) | 3011 | 0 | enableIfEnd = defEnd->previous(); | 3012 | 0 | else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type")) | 3013 | 0 | enableIfEnd = defEnd->tokAt(-3); | 3014 | 0 | if (enableIfEnd && enableIfEnd->link() && | 3015 | 0 | Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) { | 3016 | 0 | if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) { | 3017 | 0 | defStart = start; | 3018 | 0 | defEnd = enableIfEnd; | 3019 | 0 | } else { | 3020 | 0 | return emptyEnableIf; | 3021 | 0 | } | 3022 | 0 | } | 3023 | 0 | assert(defEnd != defStart); | 3024 | 0 | if (pred(defStart, defEnd)) | 3025 | 0 | return true; | 3026 | 0 | if (isUnknownType(defStart, defEnd)) | 3027 | 0 | return unknown; | 3028 | 0 | return false; | 3029 | 0 | } |
symboldatabase.cpp:bool checkReturns<Function::returnsVoid(Function const*, bool)::$_10>(Function const*, bool, bool, Function::returnsVoid(Function const*, bool)::$_10) Line | Count | Source | 2987 | 1.73k | { | 2988 | 1.73k | if (!function) | 2989 | 0 | return false; | 2990 | 1.73k | if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) | 2991 | 0 | return false; | 2992 | 1.73k | const Token* defStart = function->retDef; | 2993 | 1.73k | if (!defStart) | 2994 | 0 | return unknown; | 2995 | 1.73k | const Token* defEnd = function->returnDefEnd(); | 2996 | 1.73k | if (!defEnd) | 2997 | 0 | return unknown; | 2998 | 1.73k | if (defEnd == defStart) | 2999 | 0 | return unknown; | 3000 | 1.73k | if (pred(defStart, defEnd)) | 3001 | 0 | return true; | 3002 | 1.73k | if (Token::Match(defEnd->tokAt(-1), "*|&|&&")) | 3003 | 0 | return false; | 3004 | | // void STDCALL foo() | 3005 | 1.73k | while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") && | 3006 | 1.73k | !Token::Match(defEnd->tokAt(-2), "const|volatile")) | 3007 | 0 | defEnd = defEnd->previous(); | 3008 | | // enable_if | 3009 | 1.73k | const Token* enableIfEnd = nullptr; | 3010 | 1.73k | if (Token::simpleMatch(defEnd->previous(), ">")) | 3011 | 0 | enableIfEnd = defEnd->previous(); | 3012 | 1.73k | else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type")) | 3013 | 0 | enableIfEnd = defEnd->tokAt(-3); | 3014 | 1.73k | if (enableIfEnd && enableIfEnd->link() && | 3015 | 1.73k | Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) { | 3016 | 0 | if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) { | 3017 | 0 | defStart = start; | 3018 | 0 | defEnd = enableIfEnd; | 3019 | 0 | } else { | 3020 | 0 | return emptyEnableIf; | 3021 | 0 | } | 3022 | 0 | } | 3023 | 1.73k | assert(defEnd != defStart); | 3024 | 1.73k | if (pred(defStart, defEnd)) | 3025 | 0 | return true; | 3026 | 1.73k | if (isUnknownType(defStart, defEnd)) | 3027 | 0 | return unknown; | 3028 | 1.73k | return false; | 3029 | 1.73k | } |
|
3030 | | |
3031 | | bool Function::returnsConst(const Function* function, bool unknown) |
3032 | 0 | { |
3033 | 0 | return checkReturns(function, unknown, false, [](const Token* defStart, const Token* defEnd) { |
3034 | 0 | return Token::findsimplematch(defStart, "const", defEnd); |
3035 | 0 | }); |
3036 | 0 | } |
3037 | | |
3038 | | bool Function::returnsReference(const Function* function, bool unknown, bool includeRValueRef) |
3039 | 3.48k | { |
3040 | 6.96k | return checkReturns(function, unknown, false, [includeRValueRef](UNUSED const Token* defStart, const Token* defEnd) { |
3041 | 6.96k | return includeRValueRef ? Token::Match(defEnd->previous(), "&|&&") : Token::simpleMatch(defEnd->previous(), "&"); |
3042 | 6.96k | }); |
3043 | 3.48k | } |
3044 | | |
3045 | | bool Function::returnsPointer(const Function* function, bool unknown) |
3046 | 0 | { |
3047 | 0 | return checkReturns(function, unknown, false, [](const Token* /*defStart*/, const Token* defEnd) { |
3048 | 0 | return Token::simpleMatch(defEnd->previous(), "*"); |
3049 | 0 | }); |
3050 | 0 | } |
3051 | | |
3052 | | bool Function::returnsStandardType(const Function* function, bool unknown) |
3053 | 1.91k | { |
3054 | 1.91k | return checkReturns(function, unknown, true, [](const Token* /*defStart*/, const Token* defEnd) { |
3055 | 1.91k | return defEnd->previous() && defEnd->previous()->isStandardType(); |
3056 | 1.91k | }); |
3057 | 1.91k | } |
3058 | | |
3059 | | bool Function::returnsVoid(const Function* function, bool unknown) |
3060 | 1.73k | { |
3061 | 3.47k | return checkReturns(function, unknown, true, [](const Token* /*defStart*/, const Token* defEnd) { |
3062 | 3.47k | return Token::simpleMatch(defEnd->previous(), "void"); |
3063 | 3.47k | }); |
3064 | 1.73k | } |
3065 | | |
3066 | | std::vector<const Token*> Function::findReturns(const Function* f) |
3067 | 1.73k | { |
3068 | 1.73k | std::vector<const Token*> result; |
3069 | 1.73k | if (!f) |
3070 | 0 | return result; |
3071 | 1.73k | const Scope* scope = f->functionScope; |
3072 | 1.73k | if (!scope) |
3073 | 0 | return result; |
3074 | 1.73k | if (!scope->bodyStart) |
3075 | 0 | return result; |
3076 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { |
3077 | 34.1k | if (tok->str() == "{" && tok->scope() && |
3078 | 34.1k | (tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) { |
3079 | 0 | tok = tok->link(); |
3080 | 0 | continue; |
3081 | 0 | } |
3082 | 34.1k | if (Token::simpleMatch(tok->astParent(), "return")) { |
3083 | 1.91k | result.push_back(tok); |
3084 | 1.91k | } |
3085 | | // Skip lambda functions since the scope may not be set correctly |
3086 | 34.1k | const Token* lambdaEndToken = findLambdaEndToken(tok); |
3087 | 34.1k | if (lambdaEndToken) { |
3088 | 0 | tok = lambdaEndToken; |
3089 | 0 | } |
3090 | 34.1k | } |
3091 | 1.73k | return result; |
3092 | 1.73k | } |
3093 | | |
3094 | | const Token * Function::constructorMemberInitialization() const |
3095 | 4.64k | { |
3096 | 4.64k | if (!isConstructor() || !arg) |
3097 | 4.64k | return nullptr; |
3098 | 0 | if (Token::simpleMatch(arg->link(), ") :")) |
3099 | 0 | return arg->link()->next(); |
3100 | 0 | if (Token::simpleMatch(arg->link(), ") noexcept (") && arg->link()->linkAt(2)->strAt(1) == ":") |
3101 | 0 | return arg->link()->linkAt(2)->next(); |
3102 | 0 | return nullptr; |
3103 | 0 | } |
3104 | | |
3105 | | bool Function::isSafe(const Settings *settings) const |
3106 | 2.90k | { |
3107 | 2.90k | if (settings->safeChecks.externalFunctions) { |
3108 | 0 | if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() != 0) |
3109 | 0 | return true; |
3110 | 0 | if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() != 0 || !isStatic())) |
3111 | 0 | return true; |
3112 | 0 | } |
3113 | | |
3114 | 2.90k | if (settings->safeChecks.internalFunctions) { |
3115 | 0 | if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() == 0) |
3116 | 0 | return true; |
3117 | 0 | if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() == 0 || isStatic())) |
3118 | 0 | return true; |
3119 | 0 | } |
3120 | | |
3121 | 2.90k | if (settings->safeChecks.classes && access == AccessControl::Public && (nestedIn->type == Scope::ScopeType::eClass || nestedIn->type == Scope::ScopeType::eStruct)) |
3122 | 0 | return true; |
3123 | | |
3124 | 2.90k | return false; |
3125 | 2.90k | } |
3126 | | |
3127 | | Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) |
3128 | 1.73k | { |
3129 | 1.73k | Function* function = nullptr; |
3130 | | // Lambda functions are always unique |
3131 | 1.73k | if (tok->str() != "[") { |
3132 | 1.73k | auto range = scope->functionMap.equal_range(tok->str()); |
3133 | 1.73k | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
3134 | 0 | const Function *f = it->second; |
3135 | 0 | if (f->hasBody()) |
3136 | 0 | continue; |
3137 | 0 | if (f->argsMatch(scope, f->argDef, argStart, emptyString, 0)) { |
3138 | 0 | function = const_cast<Function *>(it->second); |
3139 | 0 | break; |
3140 | 0 | } |
3141 | 0 | } |
3142 | 1.73k | } |
3143 | | |
3144 | 1.73k | if (!function) |
3145 | 1.73k | function = addGlobalFunctionDecl(scope, tok, argStart, funcStart); |
3146 | | |
3147 | 1.73k | function->arg = argStart; |
3148 | 1.73k | function->token = funcStart; |
3149 | 1.73k | function->hasBody(true); |
3150 | | |
3151 | 1.73k | addNewFunction(&scope, &tok); |
3152 | | |
3153 | 1.73k | if (scope) { |
3154 | 1.73k | scope->function = function; |
3155 | 1.73k | function->functionScope = scope; |
3156 | 1.73k | return function; |
3157 | 1.73k | } |
3158 | 0 | return nullptr; |
3159 | 1.73k | } |
3160 | | |
3161 | | Function* SymbolDatabase::addGlobalFunctionDecl(Scope*& scope, const Token *tok, const Token *argStart, const Token* funcStart) |
3162 | 1.73k | { |
3163 | 1.73k | Function function(&mTokenizer, tok, scope, funcStart, argStart); |
3164 | 1.73k | scope->addFunction(std::move(function)); |
3165 | 1.73k | return &scope->functionList.back(); |
3166 | 1.73k | } |
3167 | | |
3168 | | void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const Token *argStart) |
3169 | 0 | { |
3170 | 0 | const bool destructor((*tok)->previous()->str() == "~"); |
3171 | 0 | const bool has_const(argStart->link()->strAt(1) == "const"); |
3172 | 0 | const bool lval(argStart->link()->strAt(has_const ? 2 : 1) == "&"); |
3173 | 0 | const bool rval(argStart->link()->strAt(has_const ? 2 : 1) == "&&"); |
3174 | 0 | int count = 0; |
3175 | 0 | std::string path; |
3176 | 0 | unsigned int path_length = 0; |
3177 | 0 | const Token *tok1 = (*tok); |
3178 | |
|
3179 | 0 | if (destructor) |
3180 | 0 | tok1 = tok1->previous(); |
3181 | | |
3182 | | // back up to head of path |
3183 | 0 | while (tok1 && tok1->previous() && tok1->previous()->str() == "::" && tok1->tokAt(-2) && |
3184 | 0 | ((tok1->tokAt(-2)->isName() && !tok1->tokAt(-2)->isStandardType()) || |
3185 | 0 | (tok1->strAt(-2) == ">" && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->previous(), "%name%")))) { |
3186 | 0 | count++; |
3187 | 0 | const Token * tok2 = tok1->tokAt(-2); |
3188 | 0 | if (tok2->str() == ">") |
3189 | 0 | tok2 = tok2->link()->previous(); |
3190 | |
|
3191 | 0 | if (tok2) { |
3192 | 0 | do { |
3193 | 0 | path = tok1->previous()->str() + " " + path; |
3194 | 0 | tok1 = tok1->previous(); |
3195 | 0 | path_length++; |
3196 | 0 | } while (tok1 != tok2); |
3197 | 0 | } else |
3198 | 0 | return; // syntax error ? |
3199 | 0 | } |
3200 | | |
3201 | | // syntax error? |
3202 | 0 | if (!tok1) |
3203 | 0 | return; |
3204 | | |
3205 | | // add global namespace if present |
3206 | 0 | if (tok1->strAt(-1) == "::") { |
3207 | 0 | path_length++; |
3208 | 0 | path.insert(0, ":: "); |
3209 | 0 | } |
3210 | |
|
3211 | 0 | std::list<Scope>::iterator it1; |
3212 | | |
3213 | | // search for match |
3214 | 0 | for (it1 = scopeList.begin(); it1 != scopeList.end(); ++it1) { |
3215 | 0 | Scope *scope1 = &(*it1); |
3216 | |
|
3217 | 0 | bool match = false; |
3218 | | |
3219 | | // check in namespace if using found |
3220 | 0 | if (*scope == scope1 && !scope1->usingList.empty()) { |
3221 | 0 | std::vector<Scope::UsingInfo>::const_iterator it2; |
3222 | 0 | for (it2 = scope1->usingList.cbegin(); it2 != scope1->usingList.cend(); ++it2) { |
3223 | 0 | if (it2->scope) { |
3224 | 0 | Function * func = findFunctionInScope(tok1, it2->scope, path, path_length); |
3225 | 0 | if (func) { |
3226 | 0 | if (!func->hasBody()) { |
3227 | 0 | const Token *closeParen = (*tok)->next()->link(); |
3228 | 0 | if (closeParen) { |
3229 | 0 | const Token *eq = mTokenizer.isFunctionHead(closeParen, ";"); |
3230 | 0 | if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { |
3231 | 0 | func->isDefault(true); |
3232 | 0 | return; |
3233 | 0 | } |
3234 | 0 | } |
3235 | 0 | func->hasBody(true); |
3236 | 0 | func->token = *tok; |
3237 | 0 | func->arg = argStart; |
3238 | 0 | addNewFunction(scope, tok); |
3239 | 0 | if (*scope) { |
3240 | 0 | (*scope)->functionOf = func->nestedIn; |
3241 | 0 | (*scope)->function = func; |
3242 | 0 | (*scope)->function->functionScope = *scope; |
3243 | 0 | } |
3244 | 0 | return; |
3245 | 0 | } |
3246 | 0 | } |
3247 | 0 | } |
3248 | 0 | } |
3249 | 0 | } |
3250 | | |
3251 | 0 | if (scope1->className == tok1->str() && (scope1->type != Scope::eFunction)) { |
3252 | | // do the scopes match (same scope) or do their names match (multiple namespaces) |
3253 | 0 | if ((*scope == scope1->nestedIn) || (*scope && |
3254 | 0 | (*scope)->className == scope1->nestedIn->className && |
3255 | 0 | !(*scope)->className.empty() && |
3256 | 0 | (*scope)->type == scope1->nestedIn->type)) { |
3257 | | |
3258 | | // nested scopes => check that they match |
3259 | 0 | { |
3260 | 0 | const Scope *s1 = *scope; |
3261 | 0 | const Scope *s2 = scope1->nestedIn; |
3262 | 0 | while (s1 && s2) { |
3263 | 0 | if (s1->className != s2->className) |
3264 | 0 | break; |
3265 | 0 | s1 = s1->nestedIn; |
3266 | 0 | s2 = s2->nestedIn; |
3267 | 0 | } |
3268 | | // Not matching scopes |
3269 | 0 | if (s1 || s2) |
3270 | 0 | continue; |
3271 | 0 | } |
3272 | | |
3273 | 0 | Scope *scope2 = scope1; |
3274 | |
|
3275 | 0 | while (scope2 && count > 1) { |
3276 | 0 | count--; |
3277 | 0 | if (tok1->strAt(1) == "<") |
3278 | 0 | tok1 = tok1->linkAt(1)->tokAt(2); |
3279 | 0 | else |
3280 | 0 | tok1 = tok1->tokAt(2); |
3281 | 0 | scope2 = scope2->findRecordInNestedList(tok1->str()); |
3282 | 0 | } |
3283 | |
|
3284 | 0 | if (count == 1 && scope2) { |
3285 | 0 | match = true; |
3286 | 0 | scope1 = scope2; |
3287 | 0 | } |
3288 | 0 | } |
3289 | 0 | } |
3290 | | |
3291 | 0 | if (match) { |
3292 | 0 | auto range = scope1->functionMap.equal_range((*tok)->str()); |
3293 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
3294 | 0 | Function * func = const_cast<Function *>(it->second); |
3295 | 0 | if (!func->hasBody()) { |
3296 | 0 | if (func->argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { |
3297 | 0 | const Token *closeParen = (*tok)->next()->link(); |
3298 | 0 | if (closeParen) { |
3299 | 0 | const Token *eq = mTokenizer.isFunctionHead(closeParen, ";"); |
3300 | 0 | if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { |
3301 | 0 | func->isDefault(true); |
3302 | 0 | return; |
3303 | 0 | } |
3304 | 0 | if (func->type == Function::eDestructor && destructor) { |
3305 | 0 | func->hasBody(true); |
3306 | 0 | } else if (func->type != Function::eDestructor && !destructor) { |
3307 | | // normal function? |
3308 | 0 | const bool hasConstKeyword = closeParen->next()->str() == "const"; |
3309 | 0 | if ((func->isConst() == hasConstKeyword) && |
3310 | 0 | (func->hasLvalRefQualifier() == lval) && |
3311 | 0 | (func->hasRvalRefQualifier() == rval)) { |
3312 | 0 | func->hasBody(true); |
3313 | 0 | } |
3314 | 0 | } |
3315 | 0 | } |
3316 | | |
3317 | 0 | if (func->hasBody()) { |
3318 | 0 | func->token = *tok; |
3319 | 0 | func->arg = argStart; |
3320 | 0 | addNewFunction(scope, tok); |
3321 | 0 | if (*scope) { |
3322 | 0 | (*scope)->functionOf = scope1; |
3323 | 0 | (*scope)->function = func; |
3324 | 0 | (*scope)->function->functionScope = *scope; |
3325 | 0 | } |
3326 | 0 | return; |
3327 | 0 | } |
3328 | 0 | } |
3329 | 0 | } |
3330 | 0 | } |
3331 | 0 | } |
3332 | 0 | } |
3333 | | |
3334 | | // class function of unknown class |
3335 | 0 | addNewFunction(scope, tok); |
3336 | 0 | } |
3337 | | |
3338 | | void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok) |
3339 | 1.73k | { |
3340 | 1.73k | const Token *tok1 = *tok; |
3341 | 1.73k | scopeList.emplace_back(this, tok1, *scope); |
3342 | 1.73k | Scope *newScope = &scopeList.back(); |
3343 | | |
3344 | | // find start of function '{' |
3345 | 1.73k | bool foundInitList = false; |
3346 | 6.95k | while (tok1 && tok1->str() != "{" && tok1->str() != ";") { |
3347 | 5.21k | if (tok1->link() && Token::Match(tok1, "(|[|<")) { |
3348 | 1.73k | tok1 = tok1->link(); |
3349 | 3.47k | } else if (foundInitList && Token::Match(tok1, "%name%|> {") && Token::Match(tok1->linkAt(1), "} ,|{")) { |
3350 | 0 | tok1 = tok1->linkAt(1); |
3351 | 3.47k | } else { |
3352 | 3.47k | if (tok1->str() == ":") |
3353 | 0 | foundInitList = true; |
3354 | 3.47k | tok1 = tok1->next(); |
3355 | 3.47k | } |
3356 | 5.21k | } |
3357 | | |
3358 | 1.73k | if (tok1 && tok1->str() == "{") { |
3359 | 1.73k | newScope->setBodyStartEnd(tok1); |
3360 | | |
3361 | | // syntax error? |
3362 | 1.73k | if (!newScope->bodyEnd) { |
3363 | 0 | mTokenizer.unmatchedToken(tok1); |
3364 | 1.73k | } else { |
3365 | 1.73k | (*scope)->nestedList.push_back(newScope); |
3366 | 1.73k | *scope = newScope; |
3367 | 1.73k | } |
3368 | 1.73k | } else if (tok1 && Token::Match(tok1->tokAt(-2), "= default|delete ;")) { |
3369 | 0 | scopeList.pop_back(); |
3370 | 0 | } else { |
3371 | 0 | throw InternalError(*tok, "Analysis failed (function not recognized). If the code is valid then please report this failure."); |
3372 | 0 | } |
3373 | 1.73k | *tok = tok1; |
3374 | 1.73k | } |
3375 | | |
3376 | | bool Type::isClassType() const |
3377 | 0 | { |
3378 | 0 | return classScope && classScope->type == Scope::ScopeType::eClass; |
3379 | 0 | } |
3380 | | |
3381 | | bool Type::isEnumType() const |
3382 | 0 | { |
3383 | | //We explicitly check for "enum" because a forward declared enum doesn't get its own scope |
3384 | 0 | return (classDef && classDef->str() == "enum") || |
3385 | 0 | (classScope && classScope->type == Scope::ScopeType::eEnum); |
3386 | 0 | } |
3387 | | |
3388 | | bool Type::isStructType() const |
3389 | 0 | { |
3390 | 0 | return classScope && classScope->type == Scope::ScopeType::eStruct; |
3391 | 0 | } |
3392 | | |
3393 | | bool Type::isUnionType() const |
3394 | 0 | { |
3395 | 0 | return classScope && classScope->type == Scope::ScopeType::eUnion; |
3396 | 0 | } |
3397 | | |
3398 | | const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) |
3399 | 0 | { |
3400 | | // goto initial '{' |
3401 | 0 | const Token *tok2 = tok1; |
3402 | 0 | while (tok2 && tok2->str() != "{") { |
3403 | | // skip unsupported templates |
3404 | 0 | if (tok2->str() == "<") |
3405 | 0 | tok2 = tok2->link(); |
3406 | | |
3407 | | // check for base classes |
3408 | 0 | else if (Token::Match(tok2, ":|,")) { |
3409 | 0 | tok2 = tok2->next(); |
3410 | | |
3411 | | // check for invalid code |
3412 | 0 | if (!tok2 || !tok2->next()) |
3413 | 0 | return nullptr; |
3414 | | |
3415 | 0 | Type::BaseInfo base; |
3416 | |
|
3417 | 0 | if (tok2->str() == "virtual") { |
3418 | 0 | base.isVirtual = true; |
3419 | 0 | tok2 = tok2->next(); |
3420 | 0 | } |
3421 | |
|
3422 | 0 | if (tok2->str() == "public") { |
3423 | 0 | base.access = AccessControl::Public; |
3424 | 0 | tok2 = tok2->next(); |
3425 | 0 | } else if (tok2->str() == "protected") { |
3426 | 0 | base.access = AccessControl::Protected; |
3427 | 0 | tok2 = tok2->next(); |
3428 | 0 | } else if (tok2->str() == "private") { |
3429 | 0 | base.access = AccessControl::Private; |
3430 | 0 | tok2 = tok2->next(); |
3431 | 0 | } else { |
3432 | 0 | if (tok->str() == "class") |
3433 | 0 | base.access = AccessControl::Private; |
3434 | 0 | else if (tok->str() == "struct") |
3435 | 0 | base.access = AccessControl::Public; |
3436 | 0 | } |
3437 | 0 | if (!tok2) |
3438 | 0 | return nullptr; |
3439 | 0 | if (tok2->str() == "virtual") { |
3440 | 0 | base.isVirtual = true; |
3441 | 0 | tok2 = tok2->next(); |
3442 | 0 | } |
3443 | 0 | if (!tok2) |
3444 | 0 | return nullptr; |
3445 | | |
3446 | 0 | base.nameTok = tok2; |
3447 | | // handle global namespace |
3448 | 0 | if (tok2->str() == "::") { |
3449 | 0 | tok2 = tok2->next(); |
3450 | 0 | } |
3451 | | |
3452 | | // handle derived base classes |
3453 | 0 | while (Token::Match(tok2, "%name% ::")) { |
3454 | 0 | tok2 = tok2->tokAt(2); |
3455 | 0 | } |
3456 | 0 | if (!tok2) |
3457 | 0 | return nullptr; |
3458 | | |
3459 | 0 | base.name = tok2->str(); |
3460 | |
|
3461 | 0 | tok2 = tok2->next(); |
3462 | | // add unhandled templates |
3463 | 0 | if (tok2 && tok2->link() && tok2->str() == "<") { |
3464 | 0 | for (const Token* const end = tok2->link()->next(); tok2 != end; tok2 = tok2->next()) { |
3465 | 0 | base.name += tok2->str(); |
3466 | 0 | } |
3467 | 0 | } |
3468 | |
|
3469 | 0 | const Type * baseType = classScope->check->findType(base.nameTok, enclosingScope); |
3470 | 0 | if (baseType && !baseType->findDependency(this)) |
3471 | 0 | base.type = baseType; |
3472 | | |
3473 | | // save pattern for base class name |
3474 | 0 | derivedFrom.push_back(std::move(base)); |
3475 | 0 | } else |
3476 | 0 | tok2 = tok2->next(); |
3477 | 0 | } |
3478 | | |
3479 | 0 | return tok2; |
3480 | 0 | } |
3481 | | |
3482 | | std::string Type::name() const |
3483 | 0 | { |
3484 | 0 | const Token* start = classDef->next(); |
3485 | 0 | if (classScope && classScope->enumClass && isEnumType()) |
3486 | 0 | start = start->tokAt(1); |
3487 | 0 | else if (start->str() == "class") |
3488 | 0 | start = start->tokAt(1); |
3489 | 0 | else if (!start->isName()) |
3490 | 0 | return emptyString; |
3491 | 0 | const Token* next = start; |
3492 | 0 | while (Token::Match(next, "::|<|>|(|)|[|]|*|&|&&|%name%")) { |
3493 | 0 | if (Token::Match(next, "<|(|[") && next->link()) |
3494 | 0 | next = next->link(); |
3495 | 0 | next = next->next(); |
3496 | 0 | } |
3497 | 0 | std::string result; |
3498 | 0 | for (const Token* tok = start; tok != next; tok = tok->next()) { |
3499 | 0 | if (!result.empty()) |
3500 | 0 | result += ' '; |
3501 | 0 | result += tok->str(); |
3502 | 0 | } |
3503 | 0 | return result; |
3504 | 0 | } |
3505 | | |
3506 | | void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const |
3507 | 0 | { |
3508 | 0 | if (tok && mSettings.debugwarnings && mErrorLogger) { |
3509 | 0 | const std::list<const Token*> locationList(1, tok); |
3510 | 0 | const ErrorMessage errmsg(locationList, &mTokenizer.list, |
3511 | 0 | Severity::debug, |
3512 | 0 | type, |
3513 | 0 | msg, |
3514 | 0 | Certainty::normal); |
3515 | 0 | mErrorLogger->reportErr(errmsg); |
3516 | 0 | } |
3517 | 0 | } |
3518 | | |
3519 | | const Function* Type::getFunction(const std::string& funcName) const |
3520 | 0 | { |
3521 | 0 | if (classScope) { |
3522 | 0 | const std::multimap<std::string, const Function *>::const_iterator it = classScope->functionMap.find(funcName); |
3523 | |
|
3524 | 0 | if (it != classScope->functionMap.end()) |
3525 | 0 | return it->second; |
3526 | 0 | } |
3527 | | |
3528 | 0 | for (const Type::BaseInfo & i : derivedFrom) { |
3529 | 0 | if (i.type) { |
3530 | 0 | const Function* const func = i.type->getFunction(funcName); |
3531 | 0 | if (func) |
3532 | 0 | return func; |
3533 | 0 | } |
3534 | 0 | } |
3535 | 0 | return nullptr; |
3536 | 0 | } |
3537 | | |
3538 | | bool Type::hasCircularDependencies(std::set<BaseInfo>* ancestors) const |
3539 | 0 | { |
3540 | 0 | std::set<BaseInfo> knownAncestors; |
3541 | 0 | if (!ancestors) { |
3542 | 0 | ancestors=&knownAncestors; |
3543 | 0 | } |
3544 | 0 | for (std::vector<BaseInfo>::const_iterator parent=derivedFrom.cbegin(); parent!=derivedFrom.cend(); ++parent) { |
3545 | 0 | if (!parent->type) |
3546 | 0 | continue; |
3547 | 0 | if (this==parent->type) |
3548 | 0 | return true; |
3549 | 0 | if (ancestors->find(*parent)!=ancestors->end()) |
3550 | 0 | return true; |
3551 | | |
3552 | 0 | ancestors->insert(*parent); |
3553 | 0 | if (parent->type->hasCircularDependencies(ancestors)) |
3554 | 0 | return true; |
3555 | 0 | } |
3556 | 0 | return false; |
3557 | 0 | } |
3558 | | |
3559 | | bool Type::findDependency(const Type* ancestor) const |
3560 | 0 | { |
3561 | 0 | return this == ancestor || std::any_of(derivedFrom.cbegin(), derivedFrom.cend(), [&](const BaseInfo& d) { |
3562 | 0 | return d.type && (d.type == this || d.type->findDependency(ancestor)); |
3563 | 0 | }); |
3564 | 0 | } |
3565 | | |
3566 | | bool Type::isDerivedFrom(const std::string & ancestor) const |
3567 | 0 | { |
3568 | 0 | for (std::vector<BaseInfo>::const_iterator parent=derivedFrom.cbegin(); parent!=derivedFrom.cend(); ++parent) { |
3569 | 0 | if (parent->name == ancestor) |
3570 | 0 | return true; |
3571 | 0 | if (parent->type && parent->type->isDerivedFrom(ancestor)) |
3572 | 0 | return true; |
3573 | 0 | } |
3574 | 0 | return false; |
3575 | 0 | } |
3576 | | |
3577 | | bool Variable::arrayDimensions(const Settings* settings, bool& isContainer) |
3578 | 6.80k | { |
3579 | 6.80k | isContainer = false; |
3580 | 6.80k | const Library::Container* container = (mTypeStartToken && mTypeStartToken->isCpp()) ? settings->library.detectContainer(mTypeStartToken) : nullptr; |
3581 | 6.80k | if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { |
3582 | 0 | const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); |
3583 | 0 | if (tok) { |
3584 | 0 | isContainer = true; |
3585 | 0 | Dimension dimension_; |
3586 | 0 | tok = tok->next(); |
3587 | 0 | for (int i = 0; i < container->size_templateArgNo && tok; i++) { |
3588 | 0 | tok = tok->nextTemplateArgument(); |
3589 | 0 | } |
3590 | 0 | if (Token::Match(tok, "%num% [,>]")) { |
3591 | 0 | dimension_.tok = tok; |
3592 | 0 | dimension_.known = true; |
3593 | 0 | dimension_.num = MathLib::toLongNumber(tok->str()); |
3594 | 0 | } else if (tok) { |
3595 | 0 | dimension_.tok = tok; |
3596 | 0 | dimension_.known = false; |
3597 | 0 | } |
3598 | 0 | mDimensions.push_back(dimension_); |
3599 | 0 | return true; |
3600 | 0 | } |
3601 | 0 | } |
3602 | | |
3603 | 6.80k | const Token *dim = mNameToken; |
3604 | 6.80k | if (!dim) { |
3605 | | // Argument without name |
3606 | 0 | dim = mTypeEndToken; |
3607 | | // back up to start of array dimensions |
3608 | 0 | while (dim && dim->str() == "]") |
3609 | 0 | dim = dim->link()->previous(); |
3610 | 0 | } |
3611 | 6.80k | if (dim) |
3612 | 6.80k | dim = dim->next(); |
3613 | 6.80k | if (dim && dim->str() == ")") |
3614 | 0 | dim = dim->next(); |
3615 | | |
3616 | 6.80k | bool arr = false; |
3617 | 6.80k | while (dim && dim->next() && dim->str() == "[") { |
3618 | 0 | Dimension dimension_; |
3619 | 0 | dimension_.known = false; |
3620 | | // check for empty array dimension [] |
3621 | 0 | if (dim->next()->str() != "]") { |
3622 | 0 | dimension_.tok = dim->astOperand2(); |
3623 | 0 | ValueFlow::valueFlowConstantFoldAST(const_cast<Token *>(dimension_.tok), settings); |
3624 | 0 | if (dimension_.tok && dimension_.tok->hasKnownIntValue()) { |
3625 | 0 | dimension_.num = dimension_.tok->getKnownIntValue(); |
3626 | 0 | dimension_.known = true; |
3627 | 0 | } |
3628 | 0 | } |
3629 | 0 | mDimensions.push_back(dimension_); |
3630 | 0 | dim = dim->link()->next(); |
3631 | 0 | arr = true; |
3632 | 0 | } |
3633 | 6.80k | return arr; |
3634 | 6.80k | } |
3635 | | |
3636 | | static std::string scopeTypeToString(Scope::ScopeType type) |
3637 | 0 | { |
3638 | 0 | switch (type) { |
3639 | 0 | case Scope::ScopeType::eGlobal: |
3640 | 0 | return "Global"; |
3641 | 0 | case Scope::ScopeType::eClass: |
3642 | 0 | return "Class"; |
3643 | 0 | case Scope::ScopeType::eStruct: |
3644 | 0 | return "Struct"; |
3645 | 0 | case Scope::ScopeType::eUnion: |
3646 | 0 | return "Union"; |
3647 | 0 | case Scope::ScopeType::eNamespace: |
3648 | 0 | return "Namespace"; |
3649 | 0 | case Scope::ScopeType::eFunction: |
3650 | 0 | return "Function"; |
3651 | 0 | case Scope::ScopeType::eIf: |
3652 | 0 | return "If"; |
3653 | 0 | case Scope::ScopeType::eElse: |
3654 | 0 | return "Else"; |
3655 | 0 | case Scope::ScopeType::eFor: |
3656 | 0 | return "For"; |
3657 | 0 | case Scope::ScopeType::eWhile: |
3658 | 0 | return "While"; |
3659 | 0 | case Scope::ScopeType::eDo: |
3660 | 0 | return "Do"; |
3661 | 0 | case Scope::ScopeType::eSwitch: |
3662 | 0 | return "Switch"; |
3663 | 0 | case Scope::ScopeType::eTry: |
3664 | 0 | return "Try"; |
3665 | 0 | case Scope::ScopeType::eCatch: |
3666 | 0 | return "Catch"; |
3667 | 0 | case Scope::ScopeType::eUnconditional: |
3668 | 0 | return "Unconditional"; |
3669 | 0 | case Scope::ScopeType::eLambda: |
3670 | 0 | return "Lambda"; |
3671 | 0 | case Scope::ScopeType::eEnum: |
3672 | 0 | return "Enum"; |
3673 | 0 | } |
3674 | 0 | return "Unknown"; |
3675 | 0 | } |
3676 | | |
3677 | | static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) |
3678 | 0 | { |
3679 | 0 | s << scopeTypeToString(type); |
3680 | 0 | return s; |
3681 | 0 | } |
3682 | | |
3683 | | static std::string accessControlToString(AccessControl access) |
3684 | 0 | { |
3685 | 0 | switch (access) { |
3686 | 0 | case AccessControl::Public: |
3687 | 0 | return "Public"; |
3688 | 0 | case AccessControl::Protected: |
3689 | 0 | return "Protected"; |
3690 | 0 | case AccessControl::Private: |
3691 | 0 | return "Private"; |
3692 | 0 | case AccessControl::Global: |
3693 | 0 | return "Global"; |
3694 | 0 | case AccessControl::Namespace: |
3695 | 0 | return "Namespace"; |
3696 | 0 | case AccessControl::Argument: |
3697 | 0 | return "Argument"; |
3698 | 0 | case AccessControl::Local: |
3699 | 0 | return "Local"; |
3700 | 0 | case AccessControl::Throw: |
3701 | 0 | return "Throw"; |
3702 | 0 | } |
3703 | 0 | return "Unknown"; |
3704 | 0 | } |
3705 | | |
3706 | | static std::string tokenToString(const Token* tok, const Tokenizer& tokenizer) |
3707 | 0 | { |
3708 | 0 | std::ostringstream oss; |
3709 | 0 | if (tok) { |
3710 | 0 | oss << tok->str() << " "; |
3711 | 0 | oss << tokenizer.list.fileLine(tok) << " "; |
3712 | 0 | } |
3713 | 0 | oss << tok; |
3714 | 0 | return oss.str(); |
3715 | 0 | } |
3716 | | |
3717 | | static std::string scopeToString(const Scope* scope, const Tokenizer& tokenizer) |
3718 | 0 | { |
3719 | 0 | std::ostringstream oss; |
3720 | 0 | if (scope) { |
3721 | 0 | oss << scope->type << " "; |
3722 | 0 | if (!scope->className.empty()) |
3723 | 0 | oss << scope->className << " "; |
3724 | 0 | if (scope->classDef) |
3725 | 0 | oss << tokenizer.list.fileLine(scope->classDef) << " "; |
3726 | 0 | } |
3727 | 0 | oss << scope; |
3728 | 0 | return oss.str(); |
3729 | 0 | } |
3730 | | |
3731 | | static std::string tokenType(const Token * tok) |
3732 | 0 | { |
3733 | 0 | std::ostringstream oss; |
3734 | 0 | if (tok) { |
3735 | 0 | if (tok->isUnsigned()) |
3736 | 0 | oss << "unsigned "; |
3737 | 0 | else if (tok->isSigned()) |
3738 | 0 | oss << "signed "; |
3739 | 0 | if (tok->isComplex()) |
3740 | 0 | oss << "_Complex "; |
3741 | 0 | if (tok->isLong()) |
3742 | 0 | oss << "long "; |
3743 | 0 | oss << tok->str(); |
3744 | 0 | } |
3745 | 0 | return oss.str(); |
3746 | 0 | } |
3747 | | |
3748 | | void SymbolDatabase::printVariable(const Variable *var, const char *indent) const |
3749 | 0 | { |
3750 | 0 | std::cout << indent << "mNameToken: " << tokenToString(var->nameToken(), mTokenizer) << std::endl; |
3751 | 0 | if (var->nameToken()) { |
3752 | 0 | std::cout << indent << " declarationId: " << var->declarationId() << std::endl; |
3753 | 0 | } |
3754 | 0 | std::cout << indent << "mTypeStartToken: " << tokenToString(var->typeStartToken(), mTokenizer) << std::endl; |
3755 | 0 | std::cout << indent << "mTypeEndToken: " << tokenToString(var->typeEndToken(), mTokenizer) << std::endl; |
3756 | |
|
3757 | 0 | if (var->typeStartToken()) { |
3758 | 0 | const Token * autoTok = nullptr; |
3759 | 0 | std::cout << indent << " "; |
3760 | 0 | for (const Token * tok = var->typeStartToken(); tok != var->typeEndToken()->next(); tok = tok->next()) { |
3761 | 0 | std::cout << " " << tokenType(tok); |
3762 | 0 | if (tok->str() == "auto") |
3763 | 0 | autoTok = tok; |
3764 | 0 | } |
3765 | 0 | std::cout << std::endl; |
3766 | 0 | if (autoTok) { |
3767 | 0 | const ValueType * valueType = autoTok->valueType(); |
3768 | 0 | std::cout << indent << " auto valueType: " << valueType << std::endl; |
3769 | 0 | if (var->typeStartToken()->valueType()) { |
3770 | 0 | std::cout << indent << " " << valueType->str() << std::endl; |
3771 | 0 | } |
3772 | 0 | } |
3773 | 0 | } else if (var->valueType()) { |
3774 | 0 | std::cout << indent << " " << var->valueType()->str() << std::endl; |
3775 | 0 | } |
3776 | 0 | std::cout << indent << "mIndex: " << var->index() << std::endl; |
3777 | 0 | std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl; |
3778 | 0 | std::cout << indent << "mFlags: " << std::endl; |
3779 | 0 | std::cout << indent << " isMutable: " << var->isMutable() << std::endl; |
3780 | 0 | std::cout << indent << " isStatic: " << var->isStatic() << std::endl; |
3781 | 0 | std::cout << indent << " isExtern: " << var->isExtern() << std::endl; |
3782 | 0 | std::cout << indent << " isLocal: " << var->isLocal() << std::endl; |
3783 | 0 | std::cout << indent << " isConst: " << var->isConst() << std::endl; |
3784 | 0 | std::cout << indent << " isClass: " << var->isClass() << std::endl; |
3785 | 0 | std::cout << indent << " isArray: " << var->isArray() << std::endl; |
3786 | 0 | std::cout << indent << " isPointer: " << var->isPointer() << std::endl; |
3787 | 0 | std::cout << indent << " isReference: " << var->isReference() << std::endl; |
3788 | 0 | std::cout << indent << " isRValueRef: " << var->isRValueReference() << std::endl; |
3789 | 0 | std::cout << indent << " hasDefault: " << var->hasDefault() << std::endl; |
3790 | 0 | std::cout << indent << " isStlType: " << var->isStlType() << std::endl; |
3791 | 0 | std::cout << indent << "mType: "; |
3792 | 0 | if (var->type()) { |
3793 | 0 | std::cout << var->type()->type() << " " << var->type()->name(); |
3794 | 0 | std::cout << " " << mTokenizer.list.fileLine(var->type()->classDef); |
3795 | 0 | std::cout << " " << var->type() << std::endl; |
3796 | 0 | } else |
3797 | 0 | std::cout << "none" << std::endl; |
3798 | |
|
3799 | 0 | if (var->nameToken()) { |
3800 | 0 | const ValueType * valueType = var->nameToken()->valueType(); |
3801 | 0 | std::cout << indent << "valueType: " << valueType << std::endl; |
3802 | 0 | if (valueType) { |
3803 | 0 | std::cout << indent << " " << valueType->str() << std::endl; |
3804 | 0 | } |
3805 | 0 | } |
3806 | |
|
3807 | 0 | std::cout << indent << "mScope: " << scopeToString(var->scope(), mTokenizer) << std::endl; |
3808 | |
|
3809 | 0 | std::cout << indent << "mDimensions:"; |
3810 | 0 | for (std::size_t i = 0; i < var->dimensions().size(); i++) { |
3811 | 0 | std::cout << " " << var->dimension(i); |
3812 | 0 | if (!var->dimensions()[i].known) |
3813 | 0 | std::cout << "?"; |
3814 | 0 | } |
3815 | 0 | std::cout << std::endl; |
3816 | 0 | } |
3817 | | |
3818 | | void SymbolDatabase::printOut(const char *title) const |
3819 | 0 | { |
3820 | 0 | std::cout << std::setiosflags(std::ios::boolalpha); |
3821 | 0 | if (title) |
3822 | 0 | std::cout << "\n### " << title << " ###\n"; |
3823 | |
|
3824 | 0 | for (std::list<Scope>::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { |
3825 | 0 | std::cout << "Scope: " << &*scope << " " << scope->type << std::endl; |
3826 | 0 | std::cout << " className: " << scope->className << std::endl; |
3827 | 0 | std::cout << " classDef: " << tokenToString(scope->classDef, mTokenizer) << std::endl; |
3828 | 0 | std::cout << " bodyStart: " << tokenToString(scope->bodyStart, mTokenizer) << std::endl; |
3829 | 0 | std::cout << " bodyEnd: " << tokenToString(scope->bodyEnd, mTokenizer) << std::endl; |
3830 | | |
3831 | | // find the function body if not implemented inline |
3832 | 0 | for (auto func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) { |
3833 | 0 | std::cout << " Function: " << &*func << std::endl; |
3834 | 0 | std::cout << " name: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; |
3835 | 0 | std::cout << " type: " << (func->type == Function::eConstructor? "Constructor" : |
3836 | 0 | func->type == Function::eCopyConstructor ? "CopyConstructor" : |
3837 | 0 | func->type == Function::eMoveConstructor ? "MoveConstructor" : |
3838 | 0 | func->type == Function::eOperatorEqual ? "OperatorEqual" : |
3839 | 0 | func->type == Function::eDestructor ? "Destructor" : |
3840 | 0 | func->type == Function::eFunction ? "Function" : |
3841 | 0 | func->type == Function::eLambda ? "Lambda" : |
3842 | 0 | "Unknown") << std::endl; |
3843 | 0 | std::cout << " access: " << accessControlToString(func->access) << std::endl; |
3844 | 0 | std::cout << " hasBody: " << func->hasBody() << std::endl; |
3845 | 0 | std::cout << " isInline: " << func->isInline() << std::endl; |
3846 | 0 | std::cout << " isConst: " << func->isConst() << std::endl; |
3847 | 0 | std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl; |
3848 | 0 | std::cout << " isPure: " << func->isPure() << std::endl; |
3849 | 0 | std::cout << " isStatic: " << func->isStatic() << std::endl; |
3850 | 0 | std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl; |
3851 | 0 | std::cout << " isExtern: " << func->isExtern() << std::endl; |
3852 | 0 | std::cout << " isFriend: " << func->isFriend() << std::endl; |
3853 | 0 | std::cout << " isExplicit: " << func->isExplicit() << std::endl; |
3854 | 0 | std::cout << " isDefault: " << func->isDefault() << std::endl; |
3855 | 0 | std::cout << " isDelete: " << func->isDelete() << std::endl; |
3856 | 0 | std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl; |
3857 | 0 | std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl; |
3858 | 0 | std::cout << " isNoExcept: " << func->isNoExcept() << std::endl; |
3859 | 0 | std::cout << " isThrow: " << func->isThrow() << std::endl; |
3860 | 0 | std::cout << " isOperator: " << func->isOperator() << std::endl; |
3861 | 0 | std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl; |
3862 | 0 | std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; |
3863 | 0 | std::cout << " isVariadic: " << func->isVariadic() << std::endl; |
3864 | 0 | std::cout << " isVolatile: " << func->isVolatile() << std::endl; |
3865 | 0 | std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl; |
3866 | 0 | std::cout << " attributes:"; |
3867 | 0 | if (func->isAttributeConst()) |
3868 | 0 | std::cout << " const "; |
3869 | 0 | if (func->isAttributePure()) |
3870 | 0 | std::cout << " pure "; |
3871 | 0 | if (func->isAttributeNoreturn()) |
3872 | 0 | std::cout << " noreturn "; |
3873 | 0 | if (func->isAttributeNothrow()) |
3874 | 0 | std::cout << " nothrow "; |
3875 | 0 | if (func->isAttributeConstructor()) |
3876 | 0 | std::cout << " constructor "; |
3877 | 0 | if (func->isAttributeDestructor()) |
3878 | 0 | std::cout << " destructor "; |
3879 | 0 | if (func->isAttributeNodiscard()) |
3880 | 0 | std::cout << " nodiscard "; |
3881 | 0 | std::cout << std::endl; |
3882 | 0 | std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl; |
3883 | 0 | std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl; |
3884 | 0 | std::cout << " tokenDef: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; |
3885 | 0 | std::cout << " argDef: " << tokenToString(func->argDef, mTokenizer) << std::endl; |
3886 | 0 | if (!func->isConstructor() && !func->isDestructor()) |
3887 | 0 | std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl; |
3888 | 0 | if (func->retDef) { |
3889 | 0 | std::cout << " "; |
3890 | 0 | for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next()) |
3891 | 0 | std::cout << " " << tokenType(tok); |
3892 | 0 | std::cout << std::endl; |
3893 | 0 | } |
3894 | 0 | std::cout << " retType: " << func->retType << std::endl; |
3895 | |
|
3896 | 0 | if (const ValueType* valueType = func->tokenDef->next()->valueType()) { |
3897 | 0 | std::cout << " valueType: " << valueType << std::endl; |
3898 | 0 | std::cout << " " << valueType->str() << std::endl; |
3899 | 0 | } |
3900 | |
|
3901 | 0 | if (func->hasBody()) { |
3902 | 0 | std::cout << " token: " << tokenToString(func->token, mTokenizer) << std::endl; |
3903 | 0 | std::cout << " arg: " << tokenToString(func->arg, mTokenizer) << std::endl; |
3904 | 0 | } |
3905 | 0 | std::cout << " nestedIn: " << scopeToString(func->nestedIn, mTokenizer) << std::endl; |
3906 | 0 | std::cout << " functionScope: " << scopeToString(func->functionScope, mTokenizer) << std::endl; |
3907 | |
|
3908 | 0 | for (auto var = func->argumentList.cbegin(); var != func->argumentList.cend(); ++var) { |
3909 | 0 | std::cout << " Variable: " << &*var << std::endl; |
3910 | 0 | printVariable(&*var, " "); |
3911 | 0 | } |
3912 | 0 | } |
3913 | |
|
3914 | 0 | for (auto var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { |
3915 | 0 | std::cout << " Variable: " << &*var << std::endl; |
3916 | 0 | printVariable(&*var, " "); |
3917 | 0 | } |
3918 | |
|
3919 | 0 | if (scope->type == Scope::eEnum) { |
3920 | 0 | std::cout << " enumType: "; |
3921 | 0 | if (scope->enumType) { |
3922 | 0 | std::cout << scope->enumType->stringify(false, true, false); |
3923 | 0 | } else |
3924 | 0 | std::cout << "int"; |
3925 | 0 | std::cout << std::endl; |
3926 | 0 | std::cout << " enumClass: " << scope->enumClass << std::endl; |
3927 | 0 | for (const Enumerator &enumerator : scope->enumeratorList) { |
3928 | 0 | std::cout << " Enumerator: " << enumerator.name->str() << " = "; |
3929 | 0 | if (enumerator.value_known) |
3930 | 0 | std::cout << enumerator.value; |
3931 | |
|
3932 | 0 | if (enumerator.start) { |
3933 | 0 | const Token * tok = enumerator.start; |
3934 | 0 | std::cout << (enumerator.value_known ? " " : "") << "[" << tok->str(); |
3935 | 0 | while (tok && tok != enumerator.end) { |
3936 | 0 | if (tok->next()) |
3937 | 0 | std::cout << " " << tok->next()->str(); |
3938 | 0 | tok = tok->next(); |
3939 | 0 | } |
3940 | |
|
3941 | 0 | std::cout << "]"; |
3942 | 0 | } |
3943 | |
|
3944 | 0 | std::cout << std::endl; |
3945 | 0 | } |
3946 | 0 | } |
3947 | |
|
3948 | 0 | std::cout << " nestedIn: " << scope->nestedIn; |
3949 | 0 | if (scope->nestedIn) { |
3950 | 0 | std::cout << " " << scope->nestedIn->type << " " |
3951 | 0 | << scope->nestedIn->className; |
3952 | 0 | } |
3953 | 0 | std::cout << std::endl; |
3954 | |
|
3955 | 0 | std::cout << " definedType: " << scope->definedType << std::endl; |
3956 | |
|
3957 | 0 | std::cout << " nestedList[" << scope->nestedList.size() << "] = ("; |
3958 | |
|
3959 | 0 | std::size_t count = scope->nestedList.size(); |
3960 | 0 | for (std::vector<Scope*>::const_iterator nsi = scope->nestedList.cbegin(); nsi != scope->nestedList.cend(); ++nsi) { |
3961 | 0 | std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className; |
3962 | 0 | if (count-- > 1) |
3963 | 0 | std::cout << ","; |
3964 | 0 | } |
3965 | |
|
3966 | 0 | std::cout << " )" << std::endl; |
3967 | |
|
3968 | 0 | for (auto use = scope->usingList.cbegin(); use != scope->usingList.cend(); ++use) { |
3969 | 0 | std::cout << " using: " << use->scope << " " << use->start->strAt(2); |
3970 | 0 | const Token *tok1 = use->start->tokAt(3); |
3971 | 0 | while (tok1 && tok1->str() == "::") { |
3972 | 0 | std::cout << "::" << tok1->strAt(1); |
3973 | 0 | tok1 = tok1->tokAt(2); |
3974 | 0 | } |
3975 | 0 | std::cout << " " << mTokenizer.list.fileLine(use->start) << std::endl; |
3976 | 0 | } |
3977 | |
|
3978 | 0 | std::cout << " functionOf: " << scopeToString(scope->functionOf, mTokenizer) << std::endl; |
3979 | |
|
3980 | 0 | std::cout << " function: " << scope->function; |
3981 | 0 | if (scope->function) |
3982 | 0 | std::cout << " " << scope->function->name(); |
3983 | 0 | std::cout << std::endl; |
3984 | 0 | } |
3985 | |
|
3986 | 0 | for (std::list<Type>::const_iterator type = typeList.cbegin(); type != typeList.cend(); ++type) { |
3987 | 0 | std::cout << "Type: " << &(*type) << std::endl; |
3988 | 0 | std::cout << " name: " << type->name() << std::endl; |
3989 | 0 | std::cout << " classDef: " << tokenToString(type->classDef, mTokenizer) << std::endl; |
3990 | 0 | std::cout << " classScope: " << type->classScope << std::endl; |
3991 | 0 | std::cout << " enclosingScope: " << type->enclosingScope; |
3992 | 0 | if (type->enclosingScope) { |
3993 | 0 | std::cout << " " << type->enclosingScope->type << " " |
3994 | 0 | << type->enclosingScope->className; |
3995 | 0 | } |
3996 | 0 | std::cout << std::endl; |
3997 | 0 | std::cout << " needInitialization: " << (type->needInitialization == Type::NeedInitialization::Unknown ? "Unknown" : |
3998 | 0 | type->needInitialization == Type::NeedInitialization::True ? "True" : |
3999 | 0 | type->needInitialization == Type::NeedInitialization::False ? "False" : |
4000 | 0 | "Invalid") << std::endl; |
4001 | |
|
4002 | 0 | std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = ("; |
4003 | 0 | std::size_t count = type->derivedFrom.size(); |
4004 | 0 | for (const Type::BaseInfo & i : type->derivedFrom) { |
4005 | 0 | if (i.isVirtual) |
4006 | 0 | std::cout << "Virtual "; |
4007 | |
|
4008 | 0 | std::cout << (i.access == AccessControl::Public ? " Public" : |
4009 | 0 | i.access == AccessControl::Protected ? " Protected" : |
4010 | 0 | i.access == AccessControl::Private ? " Private" : |
4011 | 0 | " Unknown"); |
4012 | |
|
4013 | 0 | if (i.type) |
4014 | 0 | std::cout << " " << i.type; |
4015 | 0 | else |
4016 | 0 | std::cout << " Unknown"; |
4017 | |
|
4018 | 0 | std::cout << " " << i.name; |
4019 | 0 | if (count-- > 1) |
4020 | 0 | std::cout << ","; |
4021 | 0 | } |
4022 | |
|
4023 | 0 | std::cout << " )" << std::endl; |
4024 | |
|
4025 | 0 | std::cout << " friendList[" << type->friendList.size() << "] = ("; |
4026 | 0 | for (size_t i = 0; i < type->friendList.size(); i++) { |
4027 | 0 | if (type->friendList[i].type) |
4028 | 0 | std::cout << type->friendList[i].type; |
4029 | 0 | else |
4030 | 0 | std::cout << " Unknown"; |
4031 | |
|
4032 | 0 | std::cout << ' '; |
4033 | 0 | if (type->friendList[i].nameEnd) |
4034 | 0 | std::cout << type->friendList[i].nameEnd->str(); |
4035 | 0 | if (i+1 < type->friendList.size()) |
4036 | 0 | std::cout << ','; |
4037 | 0 | } |
4038 | |
|
4039 | 0 | std::cout << " )" << std::endl; |
4040 | 0 | } |
4041 | |
|
4042 | 0 | for (std::size_t i = 1; i < mVariableList.size(); i++) { |
4043 | 0 | std::cout << "mVariableList[" << i << "]: " << mVariableList[i]; |
4044 | 0 | if (mVariableList[i]) { |
4045 | 0 | std::cout << " " << mVariableList[i]->name() << " " |
4046 | 0 | << mTokenizer.list.fileLine(mVariableList[i]->nameToken()); |
4047 | 0 | } |
4048 | 0 | std::cout << std::endl; |
4049 | 0 | } |
4050 | 0 | std::cout << std::resetiosflags(std::ios::boolalpha); |
4051 | 0 | } |
4052 | | |
4053 | | void SymbolDatabase::printXml(std::ostream &out) const |
4054 | 0 | { |
4055 | 0 | std::string outs; |
4056 | |
|
4057 | 0 | std::set<const Variable *> variables; |
4058 | | |
4059 | | // Scopes.. |
4060 | 0 | outs += " <scopes>\n"; |
4061 | 0 | for (std::list<Scope>::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { |
4062 | 0 | outs += " <scope"; |
4063 | 0 | outs += " id=\""; |
4064 | 0 | outs += id_string(&*scope); |
4065 | 0 | outs += "\""; |
4066 | 0 | outs += " type=\""; |
4067 | 0 | outs += scopeTypeToString(scope->type); |
4068 | 0 | outs += "\""; |
4069 | 0 | if (!scope->className.empty()) { |
4070 | 0 | outs += " className=\""; |
4071 | 0 | outs += ErrorLogger::toxml(scope->className); |
4072 | 0 | outs += "\""; |
4073 | 0 | } |
4074 | 0 | if (scope->bodyStart) { |
4075 | 0 | outs += " bodyStart=\""; |
4076 | 0 | outs += id_string(scope->bodyStart); |
4077 | 0 | outs += '\"'; |
4078 | 0 | } |
4079 | 0 | if (scope->bodyEnd) { |
4080 | 0 | outs += " bodyEnd=\""; |
4081 | 0 | outs += id_string(scope->bodyEnd); |
4082 | 0 | outs += '\"'; |
4083 | 0 | } |
4084 | 0 | if (scope->nestedIn) { |
4085 | 0 | outs += " nestedIn=\""; |
4086 | 0 | outs += id_string(scope->nestedIn); |
4087 | 0 | outs += "\""; |
4088 | 0 | } |
4089 | 0 | if (scope->function) { |
4090 | 0 | outs += " function=\""; |
4091 | 0 | outs += id_string(scope->function); |
4092 | 0 | outs += "\""; |
4093 | 0 | } |
4094 | 0 | if (scope->definedType) { |
4095 | 0 | outs += " definedType=\""; |
4096 | 0 | outs += id_string(scope->definedType); |
4097 | 0 | outs += "\""; |
4098 | 0 | } |
4099 | 0 | if (scope->functionList.empty() && scope->varlist.empty()) |
4100 | 0 | outs += "/>\n"; |
4101 | 0 | else { |
4102 | 0 | outs += ">\n"; |
4103 | 0 | if (!scope->functionList.empty()) { |
4104 | 0 | outs += " <functionList>\n"; |
4105 | 0 | for (std::list<Function>::const_iterator function = scope->functionList.cbegin(); function != scope->functionList.cend(); ++function) { |
4106 | 0 | outs += " <function id=\""; |
4107 | 0 | outs += id_string(&*function); |
4108 | 0 | outs += "\" token=\""; |
4109 | 0 | outs += id_string(function->token); |
4110 | 0 | outs += "\" tokenDef=\""; |
4111 | 0 | outs += id_string(function->tokenDef); |
4112 | 0 | outs += "\" name=\""; |
4113 | 0 | outs += ErrorLogger::toxml(function->name()); |
4114 | 0 | outs += '\"'; |
4115 | 0 | outs += " type=\""; |
4116 | 0 | outs += (function->type == Function::eConstructor? "Constructor" : |
4117 | 0 | function->type == Function::eCopyConstructor ? "CopyConstructor" : |
4118 | 0 | function->type == Function::eMoveConstructor ? "MoveConstructor" : |
4119 | 0 | function->type == Function::eOperatorEqual ? "OperatorEqual" : |
4120 | 0 | function->type == Function::eDestructor ? "Destructor" : |
4121 | 0 | function->type == Function::eFunction ? "Function" : |
4122 | 0 | function->type == Function::eLambda ? "Lambda" : |
4123 | 0 | "Unknown"); |
4124 | 0 | outs += '\"'; |
4125 | 0 | if (function->nestedIn->definedType) { |
4126 | 0 | if (function->hasVirtualSpecifier()) |
4127 | 0 | outs += " hasVirtualSpecifier=\"true\""; |
4128 | 0 | else if (function->isImplicitlyVirtual()) |
4129 | 0 | outs += " isImplicitlyVirtual=\"true\""; |
4130 | 0 | } |
4131 | 0 | if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) { |
4132 | 0 | outs += " access=\""; |
4133 | 0 | outs += accessControlToString(function->access); |
4134 | 0 | outs +="\""; |
4135 | 0 | } |
4136 | 0 | if (function->isInlineKeyword()) |
4137 | 0 | outs += " isInlineKeyword=\"true\""; |
4138 | 0 | if (function->isStatic()) |
4139 | 0 | outs += " isStatic=\"true\""; |
4140 | 0 | if (function->isAttributeNoreturn()) |
4141 | 0 | outs += " isAttributeNoreturn=\"true\""; |
4142 | 0 | if (const Function* overriddenFunction = function->getOverriddenFunction()) { |
4143 | 0 | outs += " overriddenFunction=\""; |
4144 | 0 | outs += id_string(overriddenFunction); |
4145 | 0 | outs += "\""; |
4146 | 0 | } |
4147 | 0 | if (function->argCount() == 0U) |
4148 | 0 | outs += "/>\n"; |
4149 | 0 | else { |
4150 | 0 | outs += ">\n"; |
4151 | 0 | for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { |
4152 | 0 | const Variable *arg = function->getArgumentVar(argnr); |
4153 | 0 | outs += " <arg nr=\""; |
4154 | 0 | outs += std::to_string(argnr+1); |
4155 | 0 | outs += "\" variable=\""; |
4156 | 0 | outs += id_string(arg); |
4157 | 0 | outs += "\"/>\n"; |
4158 | 0 | variables.insert(arg); |
4159 | 0 | } |
4160 | 0 | outs += " </function>\n"; |
4161 | 0 | } |
4162 | 0 | } |
4163 | 0 | outs += " </functionList>\n"; |
4164 | 0 | } |
4165 | 0 | if (!scope->varlist.empty()) { |
4166 | 0 | outs += " <varlist>\n"; |
4167 | 0 | for (std::list<Variable>::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { |
4168 | 0 | outs += " <var id=\""; |
4169 | 0 | outs += id_string(&*var); |
4170 | 0 | outs += "\"/>\n"; |
4171 | 0 | } |
4172 | 0 | outs += " </varlist>\n"; |
4173 | 0 | } |
4174 | 0 | outs += " </scope>\n"; |
4175 | 0 | } |
4176 | 0 | } |
4177 | 0 | outs += " </scopes>\n"; |
4178 | |
|
4179 | 0 | if (!typeList.empty()) { |
4180 | 0 | outs += " <types>\n"; |
4181 | 0 | for (const Type& type:typeList) { |
4182 | 0 | outs += " <type id=\""; |
4183 | 0 | outs += id_string(&type); |
4184 | 0 | outs += "\" classScope=\""; |
4185 | 0 | outs += id_string(type.classScope); |
4186 | 0 | outs += "\""; |
4187 | 0 | if (type.derivedFrom.empty()) { |
4188 | 0 | outs += "/>\n"; |
4189 | 0 | continue; |
4190 | 0 | } |
4191 | 0 | outs += ">\n"; |
4192 | 0 | for (const Type::BaseInfo& baseInfo: type.derivedFrom) { |
4193 | 0 | outs += " <derivedFrom"; |
4194 | 0 | outs += " access=\""; |
4195 | 0 | outs += accessControlToString(baseInfo.access); |
4196 | 0 | outs += "\""; |
4197 | 0 | outs += " type=\""; |
4198 | 0 | outs += id_string(baseInfo.type); |
4199 | 0 | outs += "\""; |
4200 | 0 | outs += " isVirtual=\""; |
4201 | 0 | outs += bool_to_string(baseInfo.isVirtual); |
4202 | 0 | outs += "\""; |
4203 | 0 | outs += " nameTok=\""; |
4204 | 0 | outs += id_string(baseInfo.nameTok); |
4205 | 0 | outs += "\""; |
4206 | 0 | outs += "/>\n"; |
4207 | 0 | } |
4208 | 0 | outs += " </type>\n"; |
4209 | 0 | } |
4210 | 0 | outs += " </types>\n"; |
4211 | 0 | } |
4212 | | |
4213 | | // Variables.. |
4214 | 0 | for (const Variable *var : mVariableList) |
4215 | 0 | variables.insert(var); |
4216 | 0 | outs += " <variables>\n"; |
4217 | 0 | for (const Variable *var : variables) { |
4218 | 0 | if (!var) |
4219 | 0 | continue; |
4220 | 0 | outs += " <var id=\""; |
4221 | 0 | outs += id_string(var); |
4222 | 0 | outs += '\"'; |
4223 | 0 | outs += " nameToken=\""; |
4224 | 0 | outs += id_string(var->nameToken()); |
4225 | 0 | outs += '\"'; |
4226 | 0 | outs += " typeStartToken=\""; |
4227 | 0 | outs += id_string(var->typeStartToken()); |
4228 | 0 | outs += '\"'; |
4229 | 0 | outs += " typeEndToken=\""; |
4230 | 0 | outs += id_string(var->typeEndToken()); |
4231 | 0 | outs += '\"'; |
4232 | 0 | outs += " access=\""; |
4233 | 0 | outs += accessControlToString(var->mAccess); |
4234 | 0 | outs += '\"'; |
4235 | 0 | outs += " scope=\""; |
4236 | 0 | outs += id_string(var->scope()); |
4237 | 0 | outs += '\"'; |
4238 | 0 | if (var->valueType()) { |
4239 | 0 | outs += " constness=\""; |
4240 | 0 | outs += std::to_string(var->valueType()->constness); |
4241 | 0 | outs += '\"'; |
4242 | 0 | } |
4243 | 0 | outs += " isArray=\""; |
4244 | 0 | outs += bool_to_string(var->isArray()); |
4245 | 0 | outs += '\"'; |
4246 | 0 | outs += " isClass=\""; |
4247 | 0 | outs += bool_to_string(var->isClass()); |
4248 | 0 | outs += '\"'; |
4249 | 0 | outs += " isConst=\""; |
4250 | 0 | outs += bool_to_string(var->isConst()); |
4251 | 0 | outs += '\"'; |
4252 | 0 | outs += " isExtern=\""; |
4253 | 0 | outs += bool_to_string(var->isExtern()); |
4254 | 0 | outs += '\"'; |
4255 | 0 | outs += " isPointer=\""; |
4256 | 0 | outs += bool_to_string(var->isPointer()); |
4257 | 0 | outs += '\"'; |
4258 | 0 | outs += " isReference=\""; |
4259 | 0 | outs += bool_to_string(var->isReference()); |
4260 | 0 | outs += '\"'; |
4261 | 0 | outs += " isStatic=\""; |
4262 | 0 | outs += bool_to_string(var->isStatic()); |
4263 | 0 | outs += '\"'; |
4264 | 0 | outs += " isVolatile=\""; |
4265 | 0 | outs += bool_to_string(var->isVolatile()); |
4266 | 0 | outs += '\"'; |
4267 | 0 | outs += "/>\n"; |
4268 | 0 | } |
4269 | 0 | outs += " </variables>\n"; |
4270 | |
|
4271 | 0 | out << outs; |
4272 | 0 | } |
4273 | | |
4274 | | //--------------------------------------------------------------------------- |
4275 | | |
4276 | | static const Type* findVariableTypeIncludingUsedNamespaces(const SymbolDatabase* symbolDatabase, const Scope* scope, const Token* typeTok) |
4277 | 6.80k | { |
4278 | 6.80k | const Type* argType = symbolDatabase->findVariableType(scope, typeTok); |
4279 | 6.80k | if (argType) |
4280 | 0 | return argType; |
4281 | | |
4282 | | // look for variable type in any using namespace in this scope or above |
4283 | 13.6k | while (scope) { |
4284 | 6.80k | for (const Scope::UsingInfo &ui : scope->usingList) { |
4285 | 0 | if (ui.scope) { |
4286 | 0 | argType = symbolDatabase->findVariableType(ui.scope, typeTok); |
4287 | 0 | if (argType) |
4288 | 0 | return argType; |
4289 | 0 | } |
4290 | 0 | } |
4291 | 6.80k | scope = scope->nestedIn; |
4292 | 6.80k | } |
4293 | 6.80k | return nullptr; |
4294 | 6.80k | } |
4295 | | |
4296 | | //--------------------------------------------------------------------------- |
4297 | | |
4298 | | void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope) |
4299 | 1.73k | { |
4300 | | // check for non-empty argument list "( ... )" |
4301 | 1.73k | const Token * start = arg ? arg : argDef; |
4302 | 1.73k | if (!Token::simpleMatch(start, "(")) |
4303 | 0 | return; |
4304 | 1.73k | if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )"))) |
4305 | 1.73k | return; |
4306 | | |
4307 | 0 | unsigned int count = 0; |
4308 | |
|
4309 | 0 | for (const Token* tok = start->next(); tok; tok = tok->next()) { |
4310 | 0 | if (Token::Match(tok, ",|)")) |
4311 | 0 | return; // Syntax error |
4312 | | |
4313 | 0 | const Token* startTok = tok; |
4314 | 0 | const Token* endTok = nullptr; |
4315 | 0 | const Token* nameTok = nullptr; |
4316 | |
|
4317 | 0 | do { |
4318 | 0 | if (Token::simpleMatch(tok, "decltype (")) { |
4319 | 0 | tok = tok->linkAt(1)->next(); |
4320 | 0 | continue; |
4321 | 0 | } |
4322 | 0 | if (tok != startTok && !nameTok && Token::Match(tok, "( & %var% ) [")) { |
4323 | 0 | nameTok = tok->tokAt(2); |
4324 | 0 | endTok = nameTok->previous(); |
4325 | 0 | tok = tok->link(); |
4326 | 0 | } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) (") && Token::Match(tok->link()->linkAt(1), ") [,)]")) { |
4327 | 0 | nameTok = tok->tokAt(2); |
4328 | 0 | endTok = nameTok->previous(); |
4329 | 0 | tok = tok->link()->linkAt(1); |
4330 | 0 | } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) [")) { |
4331 | 0 | nameTok = tok->tokAt(2); |
4332 | 0 | endTok = nameTok->previous(); |
4333 | 0 | tok = tok->link(); |
4334 | 0 | } else if (tok->varId() != 0) { |
4335 | 0 | nameTok = tok; |
4336 | 0 | endTok = tok->previous(); |
4337 | 0 | } else if (tok->str() == "[") { |
4338 | | // skip array dimension(s) |
4339 | 0 | tok = tok->link(); |
4340 | 0 | while (tok->next()->str() == "[") |
4341 | 0 | tok = tok->next()->link(); |
4342 | 0 | } else if (tok->str() == "<") { |
4343 | 0 | tok = tok->link(); |
4344 | 0 | if (!tok) // something is wrong so just bail out |
4345 | 0 | return; |
4346 | 0 | } |
4347 | | |
4348 | 0 | tok = tok->next(); |
4349 | |
|
4350 | 0 | if (!tok) // something is wrong so just bail |
4351 | 0 | return; |
4352 | 0 | } while (tok->str() != "," && tok->str() != ")" && tok->str() != "="); |
4353 | | |
4354 | 0 | const Token *typeTok = startTok; |
4355 | | // skip over stuff to get to type |
4356 | 0 | while (Token::Match(typeTok, "const|volatile|enum|struct|::")) |
4357 | 0 | typeTok = typeTok->next(); |
4358 | 0 | if (Token::Match(typeTok, ",|)")) { // #8333 |
4359 | 0 | symbolDatabase->mTokenizer.syntaxError(typeTok); |
4360 | 0 | } |
4361 | | // skip over qualification |
4362 | 0 | while (Token::Match(typeTok, "%type% ::")) |
4363 | 0 | typeTok = typeTok->tokAt(2); |
4364 | | |
4365 | | // check for argument with no name or missing varid |
4366 | 0 | if (!endTok) { |
4367 | 0 | if (tok->previous()->isName() && !Token::Match(tok->tokAt(-1), "const|volatile")) { |
4368 | 0 | if (tok->previous() != typeTok) { |
4369 | 0 | nameTok = tok->previous(); |
4370 | 0 | endTok = nameTok->previous(); |
4371 | |
|
4372 | 0 | if (hasBody()) |
4373 | 0 | symbolDatabase->debugMessage(nameTok, "varid0", "Function::addArguments found argument \'" + nameTok->str() + "\' with varid 0."); |
4374 | 0 | } else |
4375 | 0 | endTok = typeTok; |
4376 | 0 | } else |
4377 | 0 | endTok = tok->previous(); |
4378 | 0 | } |
4379 | |
|
4380 | 0 | const ::Type *argType = nullptr; |
4381 | 0 | if (!typeTok->isStandardType()) { |
4382 | 0 | argType = findVariableTypeIncludingUsedNamespaces(symbolDatabase, scope, typeTok); |
4383 | | |
4384 | | // save type |
4385 | 0 | const_cast<Token *>(typeTok)->type(argType); |
4386 | 0 | } |
4387 | | |
4388 | | // skip default values |
4389 | 0 | if (tok->str() == "=") { |
4390 | 0 | do { |
4391 | 0 | if (tok->link() && Token::Match(tok, "[{[(<]")) |
4392 | 0 | tok = tok->link(); |
4393 | 0 | tok = tok->next(); |
4394 | 0 | } while (tok->str() != "," && tok->str() != ")"); |
4395 | 0 | } |
4396 | | |
4397 | | // skip over stuff before type |
4398 | 0 | while (Token::Match(startTok, "enum|struct|const|volatile")) |
4399 | 0 | startTok = startTok->next(); |
4400 | |
|
4401 | 0 | if (startTok == nameTok) |
4402 | 0 | break; |
4403 | | |
4404 | 0 | argumentList.emplace_back(nameTok, startTok, endTok, count++, AccessControl::Argument, argType, functionScope, &symbolDatabase->mSettings); |
4405 | |
|
4406 | 0 | if (tok->str() == ")") { |
4407 | | // check for a variadic function or a variadic template function |
4408 | 0 | if (Token::simpleMatch(endTok, "...")) |
4409 | 0 | isVariadic(true); |
4410 | |
|
4411 | 0 | break; |
4412 | 0 | } |
4413 | 0 | } |
4414 | | |
4415 | | // count default arguments |
4416 | 0 | for (const Token* tok = argDef->next(); tok && tok != argDef->link(); tok = tok->next()) { |
4417 | 0 | if (tok->str() == "=") { |
4418 | 0 | initArgCount++; |
4419 | 0 | if (tok->strAt(1) == "[") { |
4420 | 0 | const Token* lambdaStart = tok->next(); |
4421 | 0 | if (type == eLambda) |
4422 | 0 | tok = findLambdaEndTokenWithoutAST(lambdaStart); |
4423 | 0 | else { |
4424 | 0 | tok = findLambdaEndToken(lambdaStart); |
4425 | 0 | if (!tok) |
4426 | 0 | tok = findLambdaEndTokenWithoutAST(lambdaStart); |
4427 | 0 | } |
4428 | 0 | if (!tok) |
4429 | 0 | throw InternalError(lambdaStart, "Analysis failed (lambda not recognized). If the code is valid then please report this failure.", InternalError::INTERNAL); |
4430 | 0 | } |
4431 | 0 | } |
4432 | 0 | } |
4433 | 0 | } |
4434 | | |
4435 | | bool Function::isImplicitlyVirtual(bool defaultVal) const |
4436 | 0 | { |
4437 | 0 | if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual |
4438 | 0 | return true; |
4439 | 0 | if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile |
4440 | 0 | return true; |
4441 | 0 | bool foundAllBaseClasses = true; |
4442 | 0 | if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual |
4443 | 0 | return true; |
4444 | 0 | if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual |
4445 | 0 | return false; |
4446 | 0 | return defaultVal; //If we can't see all the bases classes then we can't say conclusively |
4447 | 0 | } |
4448 | | |
4449 | | std::vector<const Function*> Function::getOverloadedFunctions() const |
4450 | 0 | { |
4451 | 0 | std::vector<const Function*> result; |
4452 | 0 | const Scope* scope = nestedIn; |
4453 | |
|
4454 | 0 | while (scope) { |
4455 | 0 | const bool isMemberFunction = scope->isClassOrStruct() && !isStatic(); |
4456 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = scope->functionMap.find(tokenDef->str()); |
4457 | 0 | it != scope->functionMap.end() && it->first == tokenDef->str(); |
4458 | 0 | ++it) { |
4459 | 0 | const Function* func = it->second; |
4460 | 0 | if (isMemberFunction && isMemberFunction == func->isStatic()) |
4461 | 0 | continue; |
4462 | 0 | result.push_back(func); |
4463 | 0 | } |
4464 | 0 | if (isMemberFunction) |
4465 | 0 | break; |
4466 | 0 | scope = scope->nestedIn; |
4467 | 0 | } |
4468 | |
|
4469 | 0 | return result; |
4470 | 0 | } |
4471 | | |
4472 | | const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const |
4473 | 0 | { |
4474 | 0 | if (foundAllBaseClasses) |
4475 | 0 | *foundAllBaseClasses = true; |
4476 | 0 | if (!nestedIn->isClassOrStruct()) |
4477 | 0 | return nullptr; |
4478 | 0 | return getOverriddenFunctionRecursive(nestedIn->definedType, foundAllBaseClasses); |
4479 | 0 | } |
4480 | | |
4481 | | // prevent recursion if base is the same except for different template parameters |
4482 | | static bool isDerivedFromItself(const std::string& thisName, const std::string& baseName) |
4483 | 0 | { |
4484 | 0 | if (thisName.back() != '>') |
4485 | 0 | return false; |
4486 | 0 | const auto pos = thisName.find('<'); |
4487 | 0 | if (pos == std::string::npos) |
4488 | 0 | return false; |
4489 | 0 | return thisName.compare(0, pos + 1, baseName, 0, pos + 1) == 0; |
4490 | 0 | } |
4491 | | |
4492 | | const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const |
4493 | 0 | { |
4494 | | // check each base class |
4495 | 0 | for (const ::Type::BaseInfo & i : baseType->derivedFrom) { |
4496 | 0 | const ::Type* derivedFromType = i.type; |
4497 | | // check if base class exists in database |
4498 | 0 | if (!derivedFromType || !derivedFromType->classScope) { |
4499 | 0 | if (foundAllBaseClasses) |
4500 | 0 | *foundAllBaseClasses = false; |
4501 | 0 | continue; |
4502 | 0 | } |
4503 | | |
4504 | 0 | const Scope *parent = derivedFromType->classScope; |
4505 | | |
4506 | | // check if function defined in base class |
4507 | 0 | auto range = parent->functionMap.equal_range(tokenDef->str()); |
4508 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
4509 | 0 | const Function * func = it->second; |
4510 | 0 | if (func->isImplicitlyVirtual()) { // Base is virtual and of same name |
4511 | 0 | const Token *temp1 = func->tokenDef->previous(); |
4512 | 0 | const Token *temp2 = tokenDef->previous(); |
4513 | 0 | bool match = true; |
4514 | | |
4515 | | // check for matching return parameters |
4516 | 0 | while (!Token::Match(temp1, "virtual|public:|private:|protected:|{|}|;")) { |
4517 | 0 | if (temp1->str() != temp2->str() && |
4518 | 0 | !(temp1->type() && temp2->type() && temp2->type()->isDerivedFrom(temp1->type()->name()))) { |
4519 | 0 | match = false; |
4520 | 0 | break; |
4521 | 0 | } |
4522 | | |
4523 | 0 | temp1 = temp1->previous(); |
4524 | 0 | temp2 = temp2->previous(); |
4525 | 0 | } |
4526 | | |
4527 | | // check for matching function parameters |
4528 | 0 | match = match && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0); |
4529 | | |
4530 | | // check for matching cv-ref qualifiers |
4531 | 0 | match = match |
4532 | 0 | && isConst() == func->isConst() |
4533 | 0 | && isVolatile() == func->isVolatile() |
4534 | 0 | && hasRvalRefQualifier() == func->hasRvalRefQualifier() |
4535 | 0 | && hasLvalRefQualifier() == func->hasLvalRefQualifier(); |
4536 | | |
4537 | | // it's a match |
4538 | 0 | if (match) { |
4539 | 0 | return func; |
4540 | 0 | } |
4541 | 0 | } |
4542 | 0 | } |
4543 | | |
4544 | 0 | if (!derivedFromType->derivedFrom.empty() && !derivedFromType->hasCircularDependencies() && !isDerivedFromItself(baseType->classScope->className, i.name)) { |
4545 | | // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN and |
4546 | | // #5590 with a loop within the class hierarchy. |
4547 | 0 | const Function *func = getOverriddenFunctionRecursive(derivedFromType, foundAllBaseClasses); |
4548 | 0 | if (func) { |
4549 | 0 | return func; |
4550 | 0 | } |
4551 | 0 | } |
4552 | 0 | } |
4553 | 0 | return nullptr; |
4554 | 0 | } |
4555 | | |
4556 | | const Variable* Function::getArgumentVar(nonneg int num) const |
4557 | 0 | { |
4558 | 0 | if (num < argumentList.size()) { |
4559 | 0 | auto it = argumentList.begin(); |
4560 | 0 | std::advance(it, num); |
4561 | 0 | return &*it; |
4562 | 0 | } |
4563 | 0 | return nullptr; |
4564 | 0 | } |
4565 | | |
4566 | | |
4567 | | //--------------------------------------------------------------------------- |
4568 | | |
4569 | | Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_) : |
4570 | | check(check_), |
4571 | | classDef(classDef_), |
4572 | | nestedIn(nestedIn_), |
4573 | | type(type_) |
4574 | 1.84k | { |
4575 | 1.84k | setBodyStartEnd(start_); |
4576 | 1.84k | } |
4577 | | |
4578 | | Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_) : |
4579 | | check(check_), |
4580 | | classDef(classDef_), |
4581 | | nestedIn(nestedIn_) |
4582 | 3.10k | { |
4583 | 3.10k | const Token *nameTok = classDef; |
4584 | 3.10k | if (!classDef) { |
4585 | 1.36k | type = Scope::eGlobal; |
4586 | 1.73k | } else if (classDef->str() == "class" && check && check->isCPP()) { |
4587 | 0 | type = Scope::eClass; |
4588 | 0 | nameTok = nameTok->next(); |
4589 | 1.73k | } else if (classDef->str() == "struct") { |
4590 | 0 | type = Scope::eStruct; |
4591 | 0 | nameTok = nameTok->next(); |
4592 | 1.73k | } else if (classDef->str() == "union") { |
4593 | 0 | type = Scope::eUnion; |
4594 | 0 | nameTok = nameTok->next(); |
4595 | 1.73k | } else if (classDef->str() == "namespace") { |
4596 | 0 | type = Scope::eNamespace; |
4597 | 0 | nameTok = nameTok->next(); |
4598 | 1.73k | } else if (classDef->str() == "enum") { |
4599 | 0 | type = Scope::eEnum; |
4600 | 0 | nameTok = nameTok->next(); |
4601 | 0 | if (nameTok->str() == "class") { |
4602 | 0 | enumClass = true; |
4603 | 0 | nameTok = nameTok->next(); |
4604 | 0 | } |
4605 | 1.73k | } else if (classDef->str() == "[") { |
4606 | 0 | type = Scope::eLambda; |
4607 | 1.73k | } else { |
4608 | 1.73k | type = Scope::eFunction; |
4609 | 1.73k | } |
4610 | | // skip over qualification if present |
4611 | 3.10k | nameTok = skipScopeIdentifiers(nameTok); |
4612 | 3.10k | if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name |
4613 | 1.73k | className = nameTok->str(); |
4614 | 3.10k | } |
4615 | | |
4616 | | AccessControl Scope::defaultAccess() const |
4617 | 4.95k | { |
4618 | 4.95k | switch (type) { |
4619 | 1.36k | case eGlobal: |
4620 | 1.36k | return AccessControl::Global; |
4621 | 0 | case eClass: |
4622 | 0 | return AccessControl::Private; |
4623 | 0 | case eStruct: |
4624 | 0 | return AccessControl::Public; |
4625 | 0 | case eUnion: |
4626 | 0 | return AccessControl::Public; |
4627 | 0 | case eNamespace: |
4628 | 0 | return AccessControl::Namespace; |
4629 | 3.58k | default: |
4630 | 3.58k | return AccessControl::Local; |
4631 | 4.95k | } |
4632 | 4.95k | } |
4633 | | |
4634 | | void Scope::addVariable(const Token *token_, const Token *start_, const Token *end_, |
4635 | | AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) |
4636 | 6.80k | { |
4637 | | // keep possible size_t -> int truncation outside emplace_back() to have a single line |
4638 | | // C4267 VC++ warning instead of several dozens lines |
4639 | 6.80k | const int varIndex = varlist.size(); |
4640 | 6.80k | varlist.emplace_back(token_, start_, end_, varIndex, access_, type_, scope_, settings); |
4641 | 6.80k | } |
4642 | | |
4643 | | // Get variable list.. |
4644 | | void Scope::getVariableList(const Settings* settings) |
4645 | 4.94k | { |
4646 | 4.94k | if (!bodyStartList.empty()) { |
4647 | 3.58k | for (const Token *bs: bodyStartList) |
4648 | 3.58k | getVariableList(settings, bs->next(), bs->link()); |
4649 | 3.58k | } |
4650 | | |
4651 | | // global scope |
4652 | 1.36k | else if (type == Scope::eGlobal) |
4653 | 1.36k | getVariableList(settings, check->mTokenizer.tokens(), nullptr); |
4654 | | |
4655 | | // forward declaration |
4656 | 0 | else |
4657 | 0 | return; |
4658 | 4.94k | } |
4659 | | |
4660 | | void Scope::getVariableList(const Settings* settings, const Token* start, const Token* end) |
4661 | 4.94k | { |
4662 | | // Variable declared in condition: if (auto x = bar()) |
4663 | 4.94k | if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) { |
4664 | 6 | checkVariable(classDef->tokAt(2), defaultAccess(), settings); |
4665 | 6 | } |
4666 | | |
4667 | 4.94k | AccessControl varaccess = defaultAccess(); |
4668 | 76.7k | for (const Token *tok = start; tok && tok != end; tok = tok->next()) { |
4669 | | // syntax error? |
4670 | 71.7k | if (tok->next() == nullptr) |
4671 | 0 | break; |
4672 | | |
4673 | | // Is it a function? |
4674 | 71.7k | if (tok->str() == "{") { |
4675 | 3.58k | tok = tok->link(); |
4676 | 3.58k | continue; |
4677 | 3.58k | } |
4678 | | |
4679 | | // Is it a nested class or structure? |
4680 | 68.1k | if (tok->isKeyword() && Token::Match(tok, "class|struct|union|namespace %type% :|{")) { |
4681 | 0 | tok = tok->tokAt(2); |
4682 | 0 | while (tok && tok->str() != "{") |
4683 | 0 | tok = tok->next(); |
4684 | 0 | if (tok) { |
4685 | | // skip implementation |
4686 | 0 | tok = tok->link(); |
4687 | 0 | continue; |
4688 | 0 | } |
4689 | 0 | break; |
4690 | 0 | } |
4691 | 68.1k | if (tok->isKeyword() && Token::Match(tok, "struct|union {")) { |
4692 | 0 | if (Token::Match(tok->next()->link(), "} %name% ;|[")) { |
4693 | 0 | tok = tok->next()->link()->tokAt(2); |
4694 | 0 | continue; |
4695 | 0 | } |
4696 | 0 | if (Token::simpleMatch(tok->next()->link(), "} ;")) { |
4697 | 0 | tok = tok->next(); |
4698 | 0 | continue; |
4699 | 0 | } |
4700 | 0 | } |
4701 | | |
4702 | | // Borland C++: Skip all variables in the __published section. |
4703 | | // These are automatically initialized. |
4704 | 68.1k | else if (tok->str() == "__published:") { |
4705 | 0 | for (; tok; tok = tok->next()) { |
4706 | 0 | if (tok->str() == "{") |
4707 | 0 | tok = tok->link(); |
4708 | 0 | if (Token::Match(tok->next(), "private:|protected:|public:")) |
4709 | 0 | break; |
4710 | 0 | } |
4711 | 0 | if (tok) |
4712 | 0 | continue; |
4713 | 0 | break; |
4714 | 0 | } |
4715 | | |
4716 | | // "private:" "public:" "protected:" etc |
4717 | 68.1k | else if (tok->str() == "public:") { |
4718 | 0 | varaccess = AccessControl::Public; |
4719 | 0 | continue; |
4720 | 68.1k | } else if (tok->str() == "protected:") { |
4721 | 0 | varaccess = AccessControl::Protected; |
4722 | 0 | continue; |
4723 | 68.1k | } else if (tok->str() == "private:") { |
4724 | 0 | varaccess = AccessControl::Private; |
4725 | 0 | continue; |
4726 | 0 | } |
4727 | | |
4728 | | // Is it a forward declaration? |
4729 | 68.1k | else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;")) { |
4730 | 0 | tok = tok->tokAt(2); |
4731 | 0 | continue; |
4732 | 0 | } |
4733 | | |
4734 | | // Borland C++: Ignore properties.. |
4735 | 68.1k | else if (tok->str() == "__property") |
4736 | 0 | continue; |
4737 | | |
4738 | | // skip return, goto and delete |
4739 | 68.1k | else if (tok->isKeyword() && Token::Match(tok, "return|delete|goto")) { |
4740 | 5.18k | while (tok->next() && |
4741 | 5.18k | tok->next()->str() != ";" && |
4742 | 5.18k | tok->next()->str() != "}" /* ticket #4994 */) { |
4743 | 3.27k | tok = tok->next(); |
4744 | 3.27k | } |
4745 | 1.91k | continue; |
4746 | 1.91k | } |
4747 | | |
4748 | | // skip case/default |
4749 | 66.2k | if (tok->isKeyword() && Token::Match(tok, "case|default")) { |
4750 | 0 | while (tok->next() && !Token::Match(tok->next(), "[:;{}]")) |
4751 | 0 | tok = tok->next(); |
4752 | 0 | continue; |
4753 | 0 | } |
4754 | | |
4755 | | // Search for start of statement.. |
4756 | 66.2k | if (tok->previous() && !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:")) |
4757 | 47.5k | continue; |
4758 | 18.7k | if (tok->str() == ";") |
4759 | 0 | continue; |
4760 | | |
4761 | 18.7k | tok = checkVariable(tok, varaccess, settings); |
4762 | | |
4763 | 18.7k | if (!tok) |
4764 | 0 | break; |
4765 | 18.7k | } |
4766 | 4.94k | } |
4767 | | |
4768 | | const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings) |
4769 | 18.7k | { |
4770 | | // Is it a throw..? |
4771 | 18.7k | if (tok->isKeyword() && Token::Match(tok, "throw %any% (") && |
4772 | 18.7k | Token::simpleMatch(tok->linkAt(2), ") ;")) { |
4773 | 0 | return tok->linkAt(2); |
4774 | 0 | } |
4775 | | |
4776 | 18.7k | if (tok->isKeyword() && Token::Match(tok, "throw %any% :: %any% (") && |
4777 | 18.7k | Token::simpleMatch(tok->linkAt(4), ") ;")) { |
4778 | 0 | return tok->linkAt(4); |
4779 | 0 | } |
4780 | | |
4781 | | // friend? |
4782 | 18.7k | if (tok->isKeyword() && Token::Match(tok, "friend %type%") && tok->next()->varId() == 0) { |
4783 | 0 | const Token *next = Token::findmatch(tok->tokAt(2), ";|{"); |
4784 | 0 | if (next && next->str() == "{") |
4785 | 0 | next = next->link(); |
4786 | 0 | return next; |
4787 | 0 | } |
4788 | | |
4789 | | // skip const|volatile|static|mutable|extern |
4790 | 18.7k | while (tok && tok->isKeyword() && Token::Match(tok, "const|constexpr|volatile|static|mutable|extern")) { |
4791 | 0 | tok = tok->next(); |
4792 | 0 | } |
4793 | | |
4794 | | // the start of the type tokens does not include the above modifiers |
4795 | 18.7k | const Token *typestart = tok; |
4796 | | |
4797 | | // C++17 structured bindings |
4798 | 18.7k | if (settings->standards.cpp >= Standards::CPP17 && Token::Match(tok, "auto &|&&| [")) { |
4799 | 0 | const Token *typeend = Token::findsimplematch(typestart, "[")->previous(); |
4800 | 0 | for (tok = typeend->tokAt(2); Token::Match(tok, "%name%|,"); tok = tok->next()) { |
4801 | 0 | if (tok->varId()) |
4802 | 0 | addVariable(tok, typestart, typeend, varaccess, nullptr, this, settings); |
4803 | 0 | } |
4804 | 0 | return typeend->linkAt(1); |
4805 | 0 | } |
4806 | | |
4807 | 18.7k | while (tok && tok->isKeyword() && Token::Match(tok, "class|struct|union|enum")) { |
4808 | 0 | tok = tok->next(); |
4809 | 0 | } |
4810 | | |
4811 | | // This is the start of a statement |
4812 | 18.7k | const Token *vartok = nullptr; |
4813 | 18.7k | const Token *typetok = nullptr; |
4814 | | |
4815 | 18.7k | if (tok && isVariableDeclaration(tok, vartok, typetok)) { |
4816 | | // If the vartok was set in the if-blocks above, create a entry for this variable.. |
4817 | 6.80k | tok = vartok->next(); |
4818 | 6.80k | while (Token::Match(tok, "[|{")) |
4819 | 0 | tok = tok->link()->next(); |
4820 | | |
4821 | 6.80k | if (vartok->varId() == 0) { |
4822 | 0 | if (!vartok->isBoolean()) |
4823 | 0 | check->debugMessage(vartok, "varid0", "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0."); |
4824 | 0 | return tok; |
4825 | 0 | } |
4826 | | |
4827 | 6.80k | const Type *vType = nullptr; |
4828 | | |
4829 | 6.80k | if (typetok) { |
4830 | 6.80k | vType = findVariableTypeIncludingUsedNamespaces(check, this, typetok); |
4831 | | |
4832 | 6.80k | const_cast<Token *>(typetok)->type(vType); |
4833 | 6.80k | } |
4834 | | |
4835 | | // skip "enum" or "struct" |
4836 | 6.80k | if (Token::Match(typestart, "enum|struct")) |
4837 | 0 | typestart = typestart->next(); |
4838 | | |
4839 | 6.80k | addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, settings); |
4840 | 6.80k | } |
4841 | | |
4842 | 18.7k | return tok; |
4843 | 18.7k | } |
4844 | | |
4845 | | const Variable *Scope::getVariable(const std::string &varname) const |
4846 | 0 | { |
4847 | 0 | auto it = std::find_if(varlist.begin(), varlist.end(), [&varname](const Variable& var) { |
4848 | 0 | return var.name() == varname; |
4849 | 0 | }); |
4850 | 0 | if (it != varlist.end()) |
4851 | 0 | return &*it; |
4852 | | |
4853 | 0 | if (definedType) { |
4854 | 0 | for (const Type::BaseInfo& baseInfo: definedType->derivedFrom) { |
4855 | 0 | if (baseInfo.type && baseInfo.type->classScope) { |
4856 | 0 | if (const Variable* var = baseInfo.type->classScope->getVariable(varname)) |
4857 | 0 | return var; |
4858 | 0 | } |
4859 | 0 | } |
4860 | 0 | } |
4861 | 0 | return nullptr; |
4862 | 0 | } |
4863 | | |
4864 | | static const Token* skipPointers(const Token* tok) |
4865 | 10.7k | { |
4866 | 10.7k | while (Token::Match(tok, "*|&|&&") || (Token::Match(tok, "( [*&]") && Token::Match(tok->link()->next(), "(|["))) { |
4867 | 0 | tok = tok->next(); |
4868 | 0 | if (tok->strAt(-1) == "(" && Token::Match(tok, "%type% ::")) |
4869 | 0 | tok = tok->tokAt(2); |
4870 | 0 | } |
4871 | | |
4872 | 10.7k | if (Token::simpleMatch(tok, "( *") && Token::simpleMatch(tok->link()->previous(), "] ) ;")) { |
4873 | 0 | const Token *tok2 = skipPointers(tok->next()); |
4874 | 0 | if (Token::Match(tok2, "%name% [") && Token::simpleMatch(tok2->linkAt(1), "] ) ;")) |
4875 | 0 | return tok2; |
4876 | 0 | } |
4877 | | |
4878 | 10.7k | return tok; |
4879 | 10.7k | } |
4880 | | |
4881 | | static const Token* skipPointersAndQualifiers(const Token* tok) |
4882 | 10.7k | { |
4883 | 10.7k | tok = skipPointers(tok); |
4884 | 10.7k | while (Token::Match(tok, "const|static|volatile")) { |
4885 | 0 | tok = tok->next(); |
4886 | 0 | tok = skipPointers(tok); |
4887 | 0 | } |
4888 | | |
4889 | 10.7k | return tok; |
4890 | 10.7k | } |
4891 | | |
4892 | | bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const |
4893 | 18.7k | { |
4894 | 18.7k | if (!tok) |
4895 | 0 | return false; |
4896 | | |
4897 | 18.7k | const bool isCPP = check && check->mTokenizer.isCPP(); |
4898 | | |
4899 | 18.7k | if (isCPP && Token::Match(tok, "throw|new")) |
4900 | 0 | return false; |
4901 | | |
4902 | 18.7k | const bool isCPP11 = isCPP && check->mSettings.standards.cpp >= Standards::CPP11; |
4903 | | |
4904 | 18.7k | if (isCPP11 && tok->str() == "using") |
4905 | 0 | return false; |
4906 | | |
4907 | 18.7k | const Token* localTypeTok = skipScopeIdentifiers(tok); |
4908 | 18.7k | const Token* localVarTok = nullptr; |
4909 | | |
4910 | 18.7k | while (Token::simpleMatch(localTypeTok, "alignas (") && Token::Match(localTypeTok->linkAt(1), ") %name%")) |
4911 | 0 | localTypeTok = localTypeTok->linkAt(1)->next(); |
4912 | | |
4913 | 18.7k | if (Token::Match(localTypeTok, "%type% <")) { |
4914 | 0 | if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) |
4915 | 0 | return false; |
4916 | | |
4917 | 0 | const Token* closeTok = localTypeTok->next()->link(); |
4918 | 0 | if (closeTok) { |
4919 | 0 | localVarTok = skipPointers(closeTok->next()); |
4920 | |
|
4921 | 0 | if (Token::Match(localVarTok, ":: %type% %name% [;=({]")) { |
4922 | 0 | if (localVarTok->strAt(3) != "(" || |
4923 | 0 | Token::Match(localVarTok->linkAt(3), "[)}] ;")) { |
4924 | 0 | localTypeTok = localVarTok->next(); |
4925 | 0 | localVarTok = localVarTok->tokAt(2); |
4926 | 0 | } |
4927 | 0 | } |
4928 | 0 | } |
4929 | 18.7k | } else if (Token::Match(localTypeTok, "%type%")) { |
4930 | | |
4931 | 10.7k | if (isCPP11 && Token::simpleMatch(localTypeTok, "decltype (") && Token::Match(localTypeTok->linkAt(1), ") %name%|*|&|&&")) |
4932 | 0 | localVarTok = skipPointersAndQualifiers(localTypeTok->linkAt(1)->next()); |
4933 | 10.7k | else { |
4934 | 10.7k | localVarTok = skipPointersAndQualifiers(localTypeTok->next()); |
4935 | 10.7k | if (isCPP11 && Token::simpleMatch(localVarTok, "decltype (") && Token::Match(localVarTok->linkAt(1), ") %name%|*|&|&&")) |
4936 | 0 | localVarTok = skipPointersAndQualifiers(localVarTok->linkAt(1)->next()); |
4937 | 10.7k | } |
4938 | 10.7k | } |
4939 | | |
4940 | 18.7k | if (!localVarTok) |
4941 | 7.95k | return false; |
4942 | | |
4943 | 10.7k | while (Token::Match(localVarTok, "const|*|&")) |
4944 | 0 | localVarTok = localVarTok->next(); |
4945 | | |
4946 | 10.7k | if (Token::Match(localVarTok, "%name% ;|=") || (localVarTok && localVarTok->varId() && localVarTok->strAt(1) == ":")) { |
4947 | 6.80k | vartok = localVarTok; |
4948 | 6.80k | typetok = localTypeTok; |
4949 | 6.80k | } else if (Token::Match(localVarTok, "%name% )|[") && localVarTok->str() != "operator") { |
4950 | 0 | vartok = localVarTok; |
4951 | 0 | typetok = localTypeTok; |
4952 | 3.98k | } else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%name% (|{") && |
4953 | 3.98k | Token::Match(localVarTok->next()->link(), ")|} ;")) { |
4954 | 0 | vartok = localVarTok; |
4955 | 0 | typetok = localTypeTok; |
4956 | 3.98k | } else if (type == eCatch && |
4957 | 3.98k | Token::Match(localVarTok, "%name% )")) { |
4958 | 0 | vartok = localVarTok; |
4959 | 0 | typetok = localTypeTok; |
4960 | 0 | } |
4961 | | |
4962 | 10.7k | return nullptr != vartok; |
4963 | 18.7k | } |
4964 | | |
4965 | | const Token * Scope::addEnum(const Token * tok, bool isCpp) |
4966 | 0 | { |
4967 | 0 | const Token * tok2 = tok->next(); |
4968 | | |
4969 | | // skip over class if present |
4970 | 0 | if (isCpp && tok2->str() == "class") |
4971 | 0 | tok2 = tok2->next(); |
4972 | | |
4973 | | // skip over name |
4974 | 0 | tok2 = tok2->next(); |
4975 | | |
4976 | | // save type if present |
4977 | 0 | if (tok2->str() == ":") { |
4978 | 0 | tok2 = tok2->next(); |
4979 | |
|
4980 | 0 | enumType = tok2; |
4981 | 0 | tok2 = tok2->next(); |
4982 | 0 | } |
4983 | | |
4984 | | // add enumerators |
4985 | 0 | if (tok2->str() == "{") { |
4986 | 0 | const Token * end = tok2->link(); |
4987 | 0 | tok2 = tok2->next(); |
4988 | |
|
4989 | 0 | while (Token::Match(tok2, "%name% =|,|}") || |
4990 | 0 | (Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") ,|}"))) { |
4991 | 0 | Enumerator enumerator(this); |
4992 | | |
4993 | | // save enumerator name |
4994 | 0 | enumerator.name = tok2; |
4995 | | |
4996 | | // skip over name |
4997 | 0 | tok2 = tok2->next(); |
4998 | |
|
4999 | 0 | if (tok2->str() == "=") { |
5000 | | // skip over "=" |
5001 | 0 | tok2 = tok2->next(); |
5002 | |
|
5003 | 0 | if (tok2->str() == "}") |
5004 | 0 | return nullptr; |
5005 | | |
5006 | 0 | enumerator.start = tok2; |
5007 | |
|
5008 | 0 | while (!Token::Match(tok2, ",|}")) { |
5009 | 0 | if (tok2->link()) |
5010 | 0 | tok2 = tok2->link(); |
5011 | 0 | enumerator.end = tok2; |
5012 | 0 | tok2 = tok2->next(); |
5013 | 0 | } |
5014 | 0 | } else if (tok2->str() == "(") { |
5015 | | // skip over unknown macro |
5016 | 0 | tok2 = tok2->link()->next(); |
5017 | 0 | } |
5018 | | |
5019 | 0 | if (tok2->str() == ",") { |
5020 | 0 | enumeratorList.push_back(enumerator); |
5021 | 0 | tok2 = tok2->next(); |
5022 | 0 | } else if (tok2->str() == "}") { |
5023 | 0 | enumeratorList.push_back(enumerator); |
5024 | 0 | break; |
5025 | 0 | } |
5026 | 0 | } |
5027 | | |
5028 | 0 | if (tok2 == end) { |
5029 | 0 | tok2 = tok2->next(); |
5030 | |
|
5031 | 0 | if (tok2 && tok2->str() != ";" && (isCpp || tok2->str() != ")")) |
5032 | 0 | tok2 = nullptr; |
5033 | 0 | } else |
5034 | 0 | tok2 = nullptr; |
5035 | 0 | } else |
5036 | 0 | tok2 = nullptr; |
5037 | | |
5038 | 0 | return tok2; |
5039 | 0 | } |
5040 | | |
5041 | | const Enumerator * SymbolDatabase::findEnumerator(const Token * tok, std::set<std::string>& tokensThatAreNotEnumeratorValues) const |
5042 | 8.67k | { |
5043 | 8.67k | if (tok->isKeyword()) |
5044 | 0 | return nullptr; |
5045 | | |
5046 | 8.67k | const std::string& tokStr = tok->str(); |
5047 | | |
5048 | 8.67k | if (tokensThatAreNotEnumeratorValues.find(tokStr) != tokensThatAreNotEnumeratorValues.end()) |
5049 | 6.33k | return nullptr; |
5050 | | |
5051 | 2.33k | const Scope* scope = tok->scope(); |
5052 | | |
5053 | | // check for qualified name |
5054 | 2.33k | if (tok->strAt(-1) == "::") { |
5055 | | // find first scope |
5056 | 0 | const Token *tok1 = tok; |
5057 | 0 | while (Token::Match(tok1->tokAt(-2), "%name% ::")) |
5058 | 0 | tok1 = tok1->tokAt(-2); |
5059 | |
|
5060 | 0 | if (tok1->strAt(-1) == "::") |
5061 | 0 | scope = &scopeList.front(); |
5062 | 0 | else { |
5063 | | // FIXME search base class here |
5064 | | |
5065 | | // find first scope |
5066 | 0 | while (scope && scope->nestedIn) { |
5067 | 0 | const Scope* temp = scope->nestedIn->findRecordInNestedList(tok1->str()); |
5068 | 0 | if (!temp && scope->functionOf) |
5069 | 0 | temp = scope->functionOf->findRecordInNestedList(tok1->str()); |
5070 | 0 | if (temp) { |
5071 | 0 | scope = temp; |
5072 | 0 | break; |
5073 | 0 | } |
5074 | 0 | scope = scope->nestedIn; |
5075 | 0 | } |
5076 | 0 | } |
5077 | |
|
5078 | 0 | if (scope) { |
5079 | 0 | tok1 = tok1->tokAt(2); |
5080 | 0 | while (scope && Token::Match(tok1, "%name% ::")) { |
5081 | 0 | scope = scope->findRecordInNestedList(tok1->str()); |
5082 | 0 | tok1 = tok1->tokAt(2); |
5083 | 0 | } |
5084 | |
|
5085 | 0 | if (scope) { |
5086 | 0 | const Enumerator * enumerator = scope->findEnumerator(tokStr); |
5087 | |
|
5088 | 0 | if (enumerator) // enum class |
5089 | 0 | return enumerator; |
5090 | | // enum |
5091 | 0 | for (std::vector<Scope *>::const_iterator it = scope->nestedList.cbegin(), end = scope->nestedList.cend(); it != end; ++it) { |
5092 | 0 | enumerator = (*it)->findEnumerator(tokStr); |
5093 | |
|
5094 | 0 | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5095 | 0 | return enumerator; |
5096 | 0 | } |
5097 | 0 | } |
5098 | 0 | } |
5099 | 2.33k | } else { // unqualified name |
5100 | 2.33k | const Enumerator * enumerator = scope->findEnumerator(tokStr); |
5101 | | |
5102 | 2.33k | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5103 | 0 | return enumerator; |
5104 | | |
5105 | 2.33k | if (Token::simpleMatch(tok->astParent(), ".")) { |
5106 | 0 | const Token* varTok = tok->astParent()->astOperand1(); |
5107 | 0 | if (varTok && varTok->variable() && varTok->variable()->type() && varTok->variable()->type()->classScope) |
5108 | 0 | scope = varTok->variable()->type()->classScope; |
5109 | 0 | } |
5110 | 2.33k | else if (Token::simpleMatch(tok->astParent(), "[")) { |
5111 | 0 | const Token* varTok = tok->astParent()->previous(); |
5112 | 0 | if (varTok && varTok->variable() && varTok->variable()->scope() && Token::simpleMatch(tok->astParent()->astOperand1(), "::")) |
5113 | 0 | scope = varTok->variable()->scope(); |
5114 | 0 | } |
5115 | | |
5116 | 4.87k | for (std::vector<Scope *>::const_iterator s = scope->nestedList.cbegin(); s != scope->nestedList.cend(); ++s) { |
5117 | 2.53k | enumerator = (*s)->findEnumerator(tokStr); |
5118 | | |
5119 | 2.53k | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5120 | 0 | return enumerator; |
5121 | 2.53k | } |
5122 | | |
5123 | 2.33k | if (scope->definedType) { |
5124 | 0 | const std::vector<Type::BaseInfo> & derivedFrom = scope->definedType->derivedFrom; |
5125 | 0 | for (const Type::BaseInfo & i : derivedFrom) { |
5126 | 0 | const Type *derivedFromType = i.type; |
5127 | 0 | if (derivedFromType && derivedFromType->classScope) { |
5128 | 0 | enumerator = derivedFromType->classScope->findEnumerator(tokStr); |
5129 | |
|
5130 | 0 | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5131 | 0 | return enumerator; |
5132 | 0 | } |
5133 | 0 | } |
5134 | 0 | } |
5135 | | |
5136 | 3.59k | while (scope->nestedIn) { |
5137 | 1.25k | if (scope->type == Scope::eFunction && scope->functionOf) |
5138 | 0 | scope = scope->functionOf; |
5139 | 1.25k | else |
5140 | 1.25k | scope = scope->nestedIn; |
5141 | | |
5142 | 1.25k | enumerator = scope->findEnumerator(tokStr); |
5143 | | |
5144 | 1.25k | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5145 | 0 | return enumerator; |
5146 | | |
5147 | 3.04k | for (std::vector<Scope*>::const_iterator s = scope->nestedList.cbegin(); s != scope->nestedList.cend(); ++s) { |
5148 | 1.78k | enumerator = (*s)->findEnumerator(tokStr); |
5149 | | |
5150 | 1.78k | if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) |
5151 | 0 | return enumerator; |
5152 | 1.78k | } |
5153 | 1.25k | } |
5154 | 2.33k | } |
5155 | | |
5156 | 2.33k | tokensThatAreNotEnumeratorValues.insert(tokStr); |
5157 | | |
5158 | 2.33k | return nullptr; |
5159 | 2.33k | } |
5160 | | |
5161 | | //--------------------------------------------------------------------------- |
5162 | | |
5163 | | const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Token* typeTok) |
5164 | 1.73k | { |
5165 | 1.73k | if (scope && scope->definedType && !scope->definedType->derivedFrom.empty()) { |
5166 | 0 | const std::vector<Type::BaseInfo> &derivedFrom = scope->definedType->derivedFrom; |
5167 | 0 | for (const Type::BaseInfo & i : derivedFrom) { |
5168 | 0 | const Type *base = i.type; |
5169 | 0 | if (base && base->classScope) { |
5170 | 0 | if (base->classScope == scope) |
5171 | 0 | return nullptr; |
5172 | 0 | const Type * type = base->classScope->findType(typeTok->str()); |
5173 | 0 | if (type) |
5174 | 0 | return type; |
5175 | 0 | type = findVariableTypeInBase(base->classScope, typeTok); |
5176 | 0 | if (type) |
5177 | 0 | return type; |
5178 | 0 | } |
5179 | 0 | } |
5180 | 0 | } |
5181 | | |
5182 | 1.73k | return nullptr; |
5183 | 1.73k | } |
5184 | | |
5185 | | //--------------------------------------------------------------------------- |
5186 | | |
5187 | | const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const |
5188 | 6.80k | { |
5189 | 6.80k | const Scope *scope = start; |
5190 | | |
5191 | | // check if type does not have a namespace |
5192 | 6.80k | if (typeTok->strAt(-1) != "::" && typeTok->strAt(1) != "::") { |
5193 | | // check if type same as scope |
5194 | 6.80k | if (start->isClassOrStruct() && typeTok->str() == start->className) |
5195 | 0 | return start->definedType; |
5196 | | |
5197 | 13.6k | while (scope) { |
5198 | | // look for type in this scope |
5199 | 6.80k | const Type * type = scope->findType(typeTok->str()); |
5200 | | |
5201 | 6.80k | if (type) |
5202 | 0 | return type; |
5203 | | |
5204 | | // look for type in base classes if possible |
5205 | 6.80k | if (scope->isClassOrStruct()) { |
5206 | 0 | type = findVariableTypeInBase(scope, typeTok); |
5207 | |
|
5208 | 0 | if (type) |
5209 | 0 | return type; |
5210 | 0 | } |
5211 | | |
5212 | | // check if in member function class to see if it's present in class |
5213 | 6.80k | if (scope->type == Scope::eFunction && scope->functionOf) { |
5214 | 0 | const Scope *scope1 = scope->functionOf; |
5215 | |
|
5216 | 0 | type = scope1->findType(typeTok->str()); |
5217 | |
|
5218 | 0 | if (type) |
5219 | 0 | return type; |
5220 | | |
5221 | 0 | type = findVariableTypeInBase(scope1, typeTok); |
5222 | |
|
5223 | 0 | if (type) |
5224 | 0 | return type; |
5225 | 0 | } |
5226 | | |
5227 | 6.80k | scope = scope->nestedIn; |
5228 | 6.80k | } |
5229 | 6.80k | } |
5230 | | |
5231 | | // check for a qualified name and use it when given |
5232 | 0 | else if (typeTok->strAt(-1) == "::") { |
5233 | | // check if type is not part of qualification |
5234 | 0 | if (typeTok->strAt(1) == "::") |
5235 | 0 | return nullptr; |
5236 | | |
5237 | | // find start of qualified function name |
5238 | 0 | const Token *tok1 = typeTok; |
5239 | |
|
5240 | 0 | while ((Token::Match(tok1->tokAt(-2), "%type% ::") && !tok1->tokAt(-2)->isKeyword()) || |
5241 | 0 | (Token::simpleMatch(tok1->tokAt(-2), "> ::") && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->tokAt(-1), "%type%"))) { |
5242 | 0 | if (tok1->strAt(-1) == "::") |
5243 | 0 | tok1 = tok1->tokAt(-2); |
5244 | 0 | else |
5245 | 0 | tok1 = tok1->linkAt(-2)->tokAt(-1); |
5246 | 0 | } |
5247 | | |
5248 | | // check for global scope |
5249 | 0 | if (tok1->strAt(-1) == "::") { |
5250 | 0 | scope = &scopeList.front(); |
5251 | |
|
5252 | 0 | scope = scope->findRecordInNestedList(tok1->str()); |
5253 | 0 | } |
5254 | | |
5255 | | // find start of qualification |
5256 | 0 | else { |
5257 | 0 | while (scope) { |
5258 | 0 | if (scope->className == tok1->str()) |
5259 | 0 | break; |
5260 | | |
5261 | 0 | const Scope *scope1 = scope->findRecordInNestedList(tok1->str()); |
5262 | |
|
5263 | 0 | if (scope1) { |
5264 | 0 | scope = scope1; |
5265 | 0 | break; |
5266 | 0 | } |
5267 | 0 | if (scope->type == Scope::eFunction && scope->functionOf) |
5268 | 0 | scope = scope->functionOf; |
5269 | 0 | else |
5270 | 0 | scope = scope->nestedIn; |
5271 | 0 | } |
5272 | 0 | } |
5273 | |
|
5274 | 0 | if (scope) { |
5275 | | // follow qualification |
5276 | 0 | while (scope && (Token::Match(tok1, "%type% ::") || |
5277 | 0 | (Token::Match(tok1, "%type% <") && Token::simpleMatch(tok1->linkAt(1), "> ::")))) { |
5278 | 0 | if (tok1->strAt(1) == "::") |
5279 | 0 | tok1 = tok1->tokAt(2); |
5280 | 0 | else |
5281 | 0 | tok1 = tok1->linkAt(1)->tokAt(2); |
5282 | 0 | const Scope * temp = scope->findRecordInNestedList(tok1->str()); |
5283 | 0 | if (!temp) { |
5284 | | // look in base classes |
5285 | 0 | const Type * type = findVariableTypeInBase(scope, tok1); |
5286 | |
|
5287 | 0 | if (type) |
5288 | 0 | return type; |
5289 | 0 | } |
5290 | 0 | scope = temp; |
5291 | 0 | } |
5292 | | |
5293 | 0 | if (scope && scope->definedType) |
5294 | 0 | return scope->definedType; |
5295 | 0 | } |
5296 | 0 | } |
5297 | | |
5298 | 6.80k | return nullptr; |
5299 | 6.80k | } |
5300 | | |
5301 | 0 | static bool hasEmptyCaptureList(const Token* tok) { |
5302 | 0 | if (!Token::simpleMatch(tok, "{")) |
5303 | 0 | return false; |
5304 | 0 | const Token* listTok = tok->astParent(); |
5305 | 0 | if (Token::simpleMatch(listTok, "(")) |
5306 | 0 | listTok = listTok->astParent(); |
5307 | 0 | return Token::simpleMatch(listTok, "[ ]"); |
5308 | 0 | } |
5309 | | |
5310 | | bool Scope::hasInlineOrLambdaFunction() const |
5311 | 7.16k | { |
5312 | 7.16k | return std::any_of(nestedList.begin(), nestedList.end(), [&](const Scope* s) { |
5313 | | // Inline function |
5314 | 3.68k | if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {")) |
5315 | 0 | return true; |
5316 | | // Lambda function |
5317 | 3.68k | if (s->type == Scope::eLambda && !hasEmptyCaptureList(s->bodyStart)) |
5318 | 0 | return true; |
5319 | 3.68k | if (s->hasInlineOrLambdaFunction()) |
5320 | 0 | return true; |
5321 | 3.68k | return false; |
5322 | 3.68k | }); |
5323 | 7.16k | } |
5324 | | |
5325 | | void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector<const Function *> & matches) const |
5326 | 4.21k | { |
5327 | 4.21k | if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { |
5328 | 0 | const std::vector<Type::BaseInfo> &derivedFrom = definedType->derivedFrom; |
5329 | 0 | for (const Type::BaseInfo & i : derivedFrom) { |
5330 | 0 | const Type *base = i.type; |
5331 | 0 | if (base && base->classScope) { |
5332 | 0 | if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already |
5333 | 0 | continue; |
5334 | | |
5335 | 0 | auto range = base->classScope->functionMap.equal_range(name); |
5336 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
5337 | 0 | const Function *func = it->second; |
5338 | 0 | if ((func->isVariadic() && args >= (func->argCount() - 1)) || |
5339 | 0 | (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount()))) { |
5340 | 0 | matches.push_back(func); |
5341 | 0 | } |
5342 | 0 | } |
5343 | |
|
5344 | 0 | base->classScope->findFunctionInBase(name, args, matches); |
5345 | 0 | } |
5346 | 0 | } |
5347 | 0 | } |
5348 | 4.21k | } |
5349 | | |
5350 | | const Scope *Scope::findRecordInBase(const std::string & name) const |
5351 | 0 | { |
5352 | 0 | if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { |
5353 | 0 | const std::vector<Type::BaseInfo> &derivedFrom = definedType->derivedFrom; |
5354 | 0 | for (const Type::BaseInfo & i : derivedFrom) { |
5355 | 0 | const Type *base = i.type; |
5356 | 0 | if (base && base->classScope) { |
5357 | 0 | if (base->classScope == this) // Recursive class; tok should have been found already |
5358 | 0 | continue; |
5359 | | |
5360 | 0 | if (base->name() == name) { |
5361 | 0 | return base->classScope; |
5362 | 0 | } |
5363 | | |
5364 | 0 | const ::Type * t = base->classScope->findType(name); |
5365 | 0 | if (t) |
5366 | 0 | return t->classScope; |
5367 | 0 | } |
5368 | 0 | } |
5369 | 0 | } |
5370 | | |
5371 | 0 | return nullptr; |
5372 | 0 | } |
5373 | | |
5374 | | std::vector<const Scope*> Scope::findAssociatedScopes() const |
5375 | 0 | { |
5376 | 0 | std::vector<const Scope*> result = {this}; |
5377 | 0 | if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { |
5378 | 0 | const std::vector<Type::BaseInfo>& derivedFrom = definedType->derivedFrom; |
5379 | 0 | for (const Type::BaseInfo& i : derivedFrom) { |
5380 | 0 | const Type* base = i.type; |
5381 | 0 | if (base && base->classScope) { |
5382 | 0 | if (contains(result, base->classScope)) |
5383 | 0 | continue; |
5384 | 0 | std::vector<const Scope*> baseScopes = base->classScope->findAssociatedScopes(); |
5385 | 0 | result.insert(result.end(), baseScopes.cbegin(), baseScopes.cend()); |
5386 | 0 | } |
5387 | 0 | } |
5388 | 0 | } |
5389 | 0 | return result; |
5390 | 0 | } |
5391 | | |
5392 | | //--------------------------------------------------------------------------- |
5393 | | |
5394 | | static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2) |
5395 | 0 | { |
5396 | 0 | if (callarg) { |
5397 | 0 | const ValueType::MatchResult res = ValueType::matchParameter(callarg->valueType(), callarg, funcarg); |
5398 | 0 | if (res == ValueType::MatchResult::SAME) { |
5399 | 0 | same++; |
5400 | 0 | return; |
5401 | 0 | } |
5402 | 0 | if (res == ValueType::MatchResult::FALLBACK1) { |
5403 | 0 | fallback1++; |
5404 | 0 | return; |
5405 | 0 | } |
5406 | 0 | if (res == ValueType::MatchResult::FALLBACK2) { |
5407 | 0 | fallback2++; |
5408 | 0 | return; |
5409 | 0 | } |
5410 | 0 | if (res == ValueType::MatchResult::NOMATCH) |
5411 | 0 | return; |
5412 | | |
5413 | 0 | const bool ptrequals = callarg->isArrayOrPointer() == funcarg->isArrayOrPointer(); |
5414 | 0 | const bool constEquals = !callarg->isArrayOrPointer() || ((callarg->typeStartToken()->strAt(-1) == "const") == (funcarg->typeStartToken()->strAt(-1) == "const")); |
5415 | 0 | if (ptrequals && constEquals && |
5416 | 0 | callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && |
5417 | 0 | callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && |
5418 | 0 | callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) { |
5419 | 0 | same++; |
5420 | 0 | } else if (callarg->isArrayOrPointer()) { |
5421 | 0 | if (ptrequals && constEquals && funcarg->typeStartToken()->str() == "void") |
5422 | 0 | fallback1++; |
5423 | 0 | else if (constEquals && funcarg->isStlStringType() && Token::Match(callarg->typeStartToken(), "char|wchar_t")) |
5424 | 0 | fallback2++; |
5425 | 0 | } else if (ptrequals) { |
5426 | 0 | const bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long"); |
5427 | 0 | const bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double"); |
5428 | 0 | const bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long"); |
5429 | 0 | const bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double"); |
5430 | 0 | if ((takesInt && passesInt) || (takesFloat && passesFloat)) |
5431 | 0 | fallback1++; |
5432 | 0 | else if ((takesInt && passesFloat) || (takesFloat && passesInt)) |
5433 | 0 | fallback2++; |
5434 | 0 | } |
5435 | 0 | } |
5436 | 0 | } |
5437 | | |
5438 | | static std::string getTypeString(const Token *typeToken) |
5439 | 0 | { |
5440 | 0 | if (!typeToken) |
5441 | 0 | return ""; |
5442 | 0 | while (Token::Match(typeToken, "%name%|*|&|::")) { |
5443 | 0 | if (typeToken->str() == "::") { |
5444 | 0 | std::string ret; |
5445 | 0 | while (Token::Match(typeToken, ":: %name%")) { |
5446 | 0 | ret += "::" + typeToken->strAt(1); |
5447 | 0 | typeToken = typeToken->tokAt(2); |
5448 | 0 | if (typeToken->str() == "<") { |
5449 | 0 | for (const Token *tok = typeToken; tok != typeToken->link(); tok = tok->next()) |
5450 | 0 | ret += tok->str(); |
5451 | 0 | ret += ">"; |
5452 | 0 | typeToken = typeToken->link()->next(); |
5453 | 0 | } |
5454 | 0 | } |
5455 | 0 | return ret; |
5456 | 0 | } |
5457 | 0 | if (Token::Match(typeToken, "%name% const| %var%|*|&")) { |
5458 | 0 | return typeToken->str(); |
5459 | 0 | } |
5460 | 0 | typeToken = typeToken->next(); |
5461 | 0 | } |
5462 | 0 | return ""; |
5463 | 0 | } |
5464 | | |
5465 | 0 | static bool hasMatchingConstructor(const Scope* classScope, const ValueType* argType) { |
5466 | 0 | if (!classScope || !argType) |
5467 | 0 | return false; |
5468 | 0 | return std::any_of(classScope->functionList.cbegin(), |
5469 | 0 | classScope->functionList.cend(), |
5470 | 0 | [&](const Function& f) { |
5471 | 0 | if (!f.isConstructor() || f.argCount() != 1 || !f.getArgumentVar(0)) |
5472 | 0 | return false; |
5473 | 0 | const ValueType* vt = f.getArgumentVar(0)->valueType(); |
5474 | 0 | return vt && |
5475 | 0 | vt->type == argType->type && |
5476 | 0 | (argType->sign == ValueType::Sign::UNKNOWN_SIGN || vt->sign == argType->sign) && |
5477 | 0 | vt->pointer == argType->pointer && |
5478 | 0 | (vt->constness & 1) >= (argType->constness & 1); |
5479 | 0 | }); |
5480 | 0 | } |
5481 | | |
5482 | | const Function* Scope::findFunction(const Token *tok, bool requireConst) const |
5483 | 4.21k | { |
5484 | 4.21k | const bool isCall = Token::Match(tok->next(), "(|{"); |
5485 | | |
5486 | 4.21k | const std::vector<const Token *> arguments = getArguments(tok); |
5487 | | |
5488 | 4.21k | std::vector<const Function *> matches; |
5489 | | |
5490 | | // find all the possible functions that could match |
5491 | 4.21k | const std::size_t args = arguments.size(); |
5492 | | |
5493 | 4.21k | auto addMatchingFunctions = [&](const Scope *scope) { |
5494 | 4.21k | auto range = scope->functionMap.equal_range(tok->str()); |
5495 | 5.95k | for (std::multimap<std::string, const Function *>::const_iterator it = range.first; it != range.second; ++it) { |
5496 | 1.73k | const Function *func = it->second; |
5497 | 1.73k | if (!isCall || args == func->argCount() || |
5498 | 1.73k | (func->isVariadic() && args >= (func->minArgCount() - 1)) || |
5499 | 1.73k | (args < func->argCount() && args >= func->minArgCount())) { |
5500 | 1.73k | matches.push_back(func); |
5501 | 1.73k | } |
5502 | 1.73k | } |
5503 | 4.21k | }; |
5504 | | |
5505 | 4.21k | addMatchingFunctions(this); |
5506 | | |
5507 | | // check in anonymous namespaces |
5508 | 7.21k | for (const Scope *nestedScope : nestedList) { |
5509 | 7.21k | if (nestedScope->type == eNamespace && nestedScope->className.empty()) |
5510 | 0 | addMatchingFunctions(nestedScope); |
5511 | 7.21k | } |
5512 | | |
5513 | 4.21k | const std::size_t numberOfMatchesNonBase = matches.size(); |
5514 | | |
5515 | | // check in base classes |
5516 | 4.21k | findFunctionInBase(tok->str(), args, matches); |
5517 | | |
5518 | | // Non-call => Do not match parameters |
5519 | 4.21k | if (!isCall) { |
5520 | 2.47k | return matches.empty() ? nullptr : matches[0]; |
5521 | 2.47k | } |
5522 | | |
5523 | 1.73k | std::vector<const Function*> fallback1Func, fallback2Func; |
5524 | | |
5525 | | // check each function against the arguments in the function call for a match |
5526 | 1.73k | for (std::size_t i = 0; i < matches.size();) { |
5527 | 1.73k | if (i > 0 && i == numberOfMatchesNonBase && fallback1Func.empty() && !fallback2Func.empty()) |
5528 | 0 | break; |
5529 | | |
5530 | 1.73k | bool constFallback = false; |
5531 | 1.73k | const Function * func = matches[i]; |
5532 | 1.73k | size_t same = 0; |
5533 | | |
5534 | 1.73k | if (requireConst && !func->isConst()) { |
5535 | 0 | i++; |
5536 | 0 | continue; |
5537 | 0 | } |
5538 | | |
5539 | 1.73k | if (!requireConst || !func->isConst()) { |
5540 | | // get the function this call is in |
5541 | 1.73k | const Scope * scope = tok->scope(); |
5542 | | |
5543 | | // check if this function is a member function |
5544 | 1.73k | if (scope && scope->functionOf && scope->functionOf->isClassOrStruct() && scope->function && |
5545 | 1.73k | func->nestedIn == scope->functionOf) { |
5546 | | // check if isConst mismatches |
5547 | 0 | if (scope->function->isConst() != func->isConst()) { |
5548 | 0 | if (scope->function->isConst()) { |
5549 | 0 | ++i; |
5550 | 0 | continue; |
5551 | 0 | } |
5552 | 0 | constFallback = true; |
5553 | 0 | } |
5554 | 0 | } |
5555 | 1.73k | } |
5556 | | |
5557 | 1.73k | size_t fallback1 = 0; |
5558 | 1.73k | size_t fallback2 = 0; |
5559 | 1.73k | bool erased = false; |
5560 | 1.73k | for (std::size_t j = 0; j < args; ++j) { |
5561 | | |
5562 | | // don't check variadic arguments |
5563 | 0 | if (func->isVariadic() && j > (func->argCount() - 1)) { |
5564 | 0 | break; |
5565 | 0 | } |
5566 | 0 | const Variable *funcarg = func->getArgumentVar(j); |
5567 | |
|
5568 | 0 | if (!arguments[j]->valueType()) { |
5569 | 0 | const Token *vartok = arguments[j]; |
5570 | 0 | int pointer = 0; |
5571 | 0 | while (vartok && (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))) { |
5572 | 0 | pointer += vartok->isUnaryOp("&") ? 1 : -1; |
5573 | 0 | vartok = vartok->astOperand1(); |
5574 | 0 | } |
5575 | 0 | if (vartok && vartok->variable()) { |
5576 | 0 | const Token *callArgTypeToken = vartok->variable()->typeStartToken(); |
5577 | 0 | const Token *funcArgTypeToken = funcarg->typeStartToken(); |
5578 | |
|
5579 | 0 | auto parseDecl = [](const Token *typeToken) -> ValueType { |
5580 | 0 | ValueType ret; |
5581 | 0 | while (Token::Match(typeToken->previous(), "%name%")) |
5582 | 0 | typeToken = typeToken->previous(); |
5583 | 0 | while (Token::Match(typeToken, "%name%|*|&|::|<")) |
5584 | 0 | { |
5585 | 0 | if (typeToken->str() == "const") |
5586 | 0 | ret.constness |= (1 << ret.pointer); |
5587 | 0 | else if (typeToken->str() == "*") |
5588 | 0 | ret.pointer++; |
5589 | 0 | else if (typeToken->str() == "<") { |
5590 | 0 | if (!typeToken->link()) |
5591 | 0 | break; |
5592 | 0 | typeToken = typeToken->link(); |
5593 | 0 | } |
5594 | 0 | typeToken = typeToken->next(); |
5595 | 0 | } |
5596 | 0 | return ret; |
5597 | 0 | }; |
5598 | |
|
5599 | 0 | const std::string type1 = getTypeString(callArgTypeToken); |
5600 | 0 | const std::string type2 = getTypeString(funcArgTypeToken); |
5601 | 0 | if (!type1.empty() && type1 == type2) { |
5602 | 0 | ValueType callArgType = parseDecl(callArgTypeToken); |
5603 | 0 | callArgType.pointer += pointer; |
5604 | 0 | ValueType funcArgType = parseDecl(funcArgTypeToken); |
5605 | |
|
5606 | 0 | callArgType.sign = funcArgType.sign = ValueType::Sign::SIGNED; |
5607 | 0 | callArgType.type = funcArgType.type = ValueType::Type::INT; |
5608 | |
|
5609 | 0 | const ValueType::MatchResult res = ValueType::matchParameter(&callArgType, &funcArgType); |
5610 | 0 | if (res == ValueType::MatchResult::SAME) |
5611 | 0 | ++same; |
5612 | 0 | else if (res == ValueType::MatchResult::FALLBACK1) |
5613 | 0 | ++fallback1; |
5614 | 0 | else if (res == ValueType::MatchResult::FALLBACK2) |
5615 | 0 | ++fallback2; |
5616 | 0 | continue; |
5617 | 0 | } |
5618 | 0 | } |
5619 | 0 | } |
5620 | | |
5621 | | // check for a match with a variable |
5622 | 0 | if (Token::Match(arguments[j], "%var% ,|)")) { |
5623 | 0 | const Variable * callarg = arguments[j]->variable(); |
5624 | 0 | checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); |
5625 | 0 | } |
5626 | | |
5627 | 0 | else if (funcarg->isStlStringType() && arguments[j]->valueType() && arguments[j]->valueType()->pointer == 1 && arguments[j]->valueType()->type == ValueType::Type::CHAR) |
5628 | 0 | fallback2++; |
5629 | | |
5630 | | // check for a match with nullptr |
5631 | 0 | else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)")) |
5632 | 0 | same++; |
5633 | | |
5634 | 0 | else if (funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str())) |
5635 | 0 | fallback1++; |
5636 | | |
5637 | 0 | else if (!funcarg->isPointer() && funcarg->type() && hasMatchingConstructor(funcarg->type()->classScope, arguments[j]->valueType())) |
5638 | 0 | fallback2++; |
5639 | | |
5640 | | // Try to evaluate the apparently more complex expression |
5641 | 0 | else if (check->isCPP()) { |
5642 | 0 | const Token *vartok = arguments[j]; |
5643 | 0 | if (vartok->str() == ".") { |
5644 | 0 | const Token* rml = nextAfterAstRightmostLeaf(vartok); |
5645 | 0 | if (rml) |
5646 | 0 | vartok = rml->previous(); |
5647 | 0 | } |
5648 | 0 | while (vartok->isUnaryOp("&") || vartok->isUnaryOp("*")) |
5649 | 0 | vartok = vartok->astOperand1(); |
5650 | 0 | const Variable* var = vartok->variable(); |
5651 | | // smart pointer deref? |
5652 | 0 | bool unknownDeref = false; |
5653 | 0 | if (var && vartok->astParent() && vartok->astParent()->str() == "*") { |
5654 | 0 | if (var->isSmartPointer() && var->valueType() && var->valueType()->smartPointerTypeToken) |
5655 | 0 | var = var->valueType()->smartPointerTypeToken->variable(); |
5656 | 0 | else |
5657 | 0 | unknownDeref = true; |
5658 | 0 | } |
5659 | 0 | const Token* valuetok = arguments[j]; |
5660 | 0 | if (valuetok->str() == "::") { |
5661 | 0 | const Token* rml = nextAfterAstRightmostLeaf(valuetok); |
5662 | 0 | if (rml) |
5663 | 0 | valuetok = rml->previous(); |
5664 | 0 | } |
5665 | 0 | if (vartok->isEnumerator()) |
5666 | 0 | valuetok = vartok; |
5667 | 0 | const ValueType::MatchResult res = ValueType::matchParameter(valuetok->valueType(), var, funcarg); |
5668 | 0 | if (res == ValueType::MatchResult::SAME) |
5669 | 0 | ++same; |
5670 | 0 | else if (res == ValueType::MatchResult::FALLBACK1) |
5671 | 0 | ++fallback1; |
5672 | 0 | else if (res == ValueType::MatchResult::FALLBACK2) |
5673 | 0 | ++fallback2; |
5674 | 0 | else if (res == ValueType::MatchResult::NOMATCH) { |
5675 | 0 | if (unknownDeref) |
5676 | 0 | continue; |
5677 | | // can't match so remove this function from possible matches |
5678 | 0 | matches.erase(matches.begin() + i); |
5679 | 0 | erased = true; |
5680 | 0 | break; |
5681 | 0 | } |
5682 | 0 | } |
5683 | | |
5684 | 0 | else |
5685 | | // C code: if number of arguments match then do not match types |
5686 | 0 | fallback1++; |
5687 | 0 | } |
5688 | | |
5689 | 1.73k | const size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args; |
5690 | | |
5691 | | // check if all arguments matched |
5692 | 1.73k | if (same == hasToBe) { |
5693 | 1.73k | if (constFallback || (!requireConst && func->isConst())) |
5694 | 0 | fallback1Func.emplace_back(func); |
5695 | 1.73k | else |
5696 | 1.73k | return func; |
5697 | 1.73k | } |
5698 | | |
5699 | 0 | else { |
5700 | 0 | if (same + fallback1 == hasToBe) |
5701 | 0 | fallback1Func.emplace_back(func); |
5702 | 0 | else if (same + fallback2 + fallback1 == hasToBe) |
5703 | 0 | fallback2Func.emplace_back(func); |
5704 | 0 | } |
5705 | | |
5706 | 0 | if (!erased) |
5707 | 0 | ++i; |
5708 | 0 | } |
5709 | | |
5710 | | // Fallback cases |
5711 | 0 | for (const auto& fb : { fallback1Func, fallback2Func }) { |
5712 | 0 | if (fb.size() == 1) |
5713 | 0 | return fb.front(); |
5714 | 0 | if (fb.size() == 2) { |
5715 | 0 | if (fb[0]->isConst() && !fb[1]->isConst()) |
5716 | 0 | return fb[1]; |
5717 | 0 | if (fb[1]->isConst() && !fb[0]->isConst()) |
5718 | 0 | return fb[0]; |
5719 | 0 | } |
5720 | 0 | } |
5721 | | |
5722 | | // remove pure virtual function if there is an overrider |
5723 | 0 | auto itPure = std::find_if(matches.begin(), matches.end(), [](const Function* m) { |
5724 | 0 | return m->isPure(); |
5725 | 0 | }); |
5726 | 0 | if (itPure != matches.end() && std::any_of(matches.begin(), matches.end(), [&](const Function* m) { |
5727 | 0 | return m->isImplicitlyVirtual() && m != *itPure; |
5728 | 0 | })) |
5729 | 0 | matches.erase(itPure); |
5730 | | |
5731 | | // Only one candidate left |
5732 | 0 | if (matches.size() == 1) |
5733 | 0 | return matches[0]; |
5734 | | |
5735 | | // Prioritize matches in derived scopes |
5736 | 0 | for (const auto& fb : { fallback1Func, fallback2Func }) { |
5737 | 0 | const Function* ret = nullptr; |
5738 | 0 | for (int i = 0; i < fb.size(); ++i) { |
5739 | 0 | if (std::find(matches.cbegin(), matches.cend(), fb[i]) == matches.cend()) |
5740 | 0 | continue; |
5741 | 0 | if (this == fb[i]->nestedIn) { |
5742 | 0 | if (!ret) |
5743 | 0 | ret = fb[i]; |
5744 | 0 | else { |
5745 | 0 | ret = nullptr; |
5746 | 0 | break; |
5747 | 0 | } |
5748 | 0 | } |
5749 | 0 | } |
5750 | 0 | if (ret) |
5751 | 0 | return ret; |
5752 | 0 | } |
5753 | | |
5754 | 0 | return nullptr; |
5755 | 0 | } |
5756 | | |
5757 | | //--------------------------------------------------------------------------- |
5758 | | |
5759 | | const Function* SymbolDatabase::findFunction(const Token* const tok) const |
5760 | 4.21k | { |
5761 | | // find the scope this function is in |
5762 | 4.21k | const Scope *currScope = tok->scope(); |
5763 | 8.05k | while (currScope && currScope->isExecutable()) { |
5764 | 3.84k | if (currScope->functionOf) |
5765 | 0 | currScope = currScope->functionOf; |
5766 | 3.84k | else |
5767 | 3.84k | currScope = currScope->nestedIn; |
5768 | 3.84k | } |
5769 | | |
5770 | | // check for a qualified name and use it when given |
5771 | 4.21k | if (tok->strAt(-1) == "::") { |
5772 | | // find start of qualified function name |
5773 | 0 | const Token *tok1 = tok; |
5774 | |
|
5775 | 0 | while (Token::Match(tok1->tokAt(-2), ">|%type% ::")) { |
5776 | 0 | if (tok1->strAt(-2) == ">") { |
5777 | 0 | if (tok1->linkAt(-2)) |
5778 | 0 | tok1 = tok1->linkAt(-2)->tokAt(-1); |
5779 | 0 | else |
5780 | 0 | break; |
5781 | 0 | } else |
5782 | 0 | tok1 = tok1->tokAt(-2); |
5783 | 0 | } |
5784 | | |
5785 | | // check for global scope |
5786 | 0 | if (tok1->strAt(-1) == "::") { |
5787 | 0 | currScope = &scopeList.front(); |
5788 | |
|
5789 | 0 | if (const Function* f = currScope->findFunction(tok)) |
5790 | 0 | return f; |
5791 | | |
5792 | 0 | currScope = currScope->findRecordInNestedList(tok1->str()); |
5793 | 0 | } |
5794 | | |
5795 | | // find start of qualification |
5796 | 0 | else { |
5797 | 0 | while (currScope) { |
5798 | 0 | if (currScope->className == tok1->str()) |
5799 | 0 | break; |
5800 | | |
5801 | 0 | const Scope *scope = currScope->findRecordInNestedList(tok1->str()); |
5802 | |
|
5803 | 0 | if (scope) { |
5804 | 0 | currScope = scope; |
5805 | 0 | break; |
5806 | 0 | } |
5807 | 0 | currScope = currScope->nestedIn; |
5808 | 0 | } |
5809 | 0 | } |
5810 | | |
5811 | 0 | if (currScope) { |
5812 | 0 | while (currScope && tok1 && !(Token::Match(tok1, "%type% :: %name% [(),>]") || |
5813 | 0 | (Token::Match(tok1, "%type% <") && Token::Match(tok1->linkAt(1), "> :: %name% (")))) { |
5814 | 0 | if (tok1->strAt(1) == "::") |
5815 | 0 | tok1 = tok1->tokAt(2); |
5816 | 0 | else if (tok1->strAt(1) == "<") |
5817 | 0 | tok1 = tok1->linkAt(1)->tokAt(2); |
5818 | 0 | else |
5819 | 0 | tok1 = nullptr; |
5820 | |
|
5821 | 0 | if (tok1) { |
5822 | 0 | const Function* func = currScope->findFunction(tok1); |
5823 | 0 | if (func) |
5824 | 0 | return func; |
5825 | | |
5826 | 0 | currScope = currScope->findRecordInNestedList(tok1->str()); |
5827 | 0 | } |
5828 | 0 | } |
5829 | | |
5830 | 0 | if (tok1) |
5831 | 0 | tok1 = tok1->tokAt(2); |
5832 | |
|
5833 | 0 | if (currScope && tok1) |
5834 | 0 | return currScope->findFunction(tok1); |
5835 | 0 | } |
5836 | 0 | } |
5837 | | |
5838 | | // check for member function |
5839 | 4.21k | else if (Token::Match(tok->tokAt(-2), "!!this .")) { |
5840 | 0 | const Token* tok1 = tok->previous()->astOperand1(); |
5841 | 0 | if (tok1 && tok1->valueType() && tok1->valueType()->typeScope) |
5842 | 0 | return tok1->valueType()->typeScope->findFunction(tok, tok1->valueType()->constness == 1); |
5843 | 0 | if (tok1 && Token::Match(tok1->previous(), "%name% (") && tok1->previous()->function() && |
5844 | 0 | tok1->previous()->function()->retDef) { |
5845 | 0 | ValueType vt = ValueType::parseDecl(tok1->previous()->function()->retDef, mSettings); |
5846 | 0 | if (vt.typeScope) |
5847 | 0 | return vt.typeScope->findFunction(tok, vt.constness == 1); |
5848 | 0 | } else if (Token::Match(tok1, "%var% .")) { |
5849 | 0 | const Variable *var = getVariableFromVarId(tok1->varId()); |
5850 | 0 | if (var && var->typeScope()) |
5851 | 0 | return var->typeScope()->findFunction(tok, var->valueType()->constness == 1); |
5852 | 0 | if (var && var->smartPointerType() && var->smartPointerType()->classScope && tok1->next()->originalName() == "->") |
5853 | 0 | return var->smartPointerType()->classScope->findFunction(tok, var->valueType()->constness == 1); |
5854 | 0 | if (var && var->iteratorType() && var->iteratorType()->classScope && tok1->next()->originalName() == "->") |
5855 | 0 | return var->iteratorType()->classScope->findFunction(tok, var->valueType()->constness == 1); |
5856 | 0 | } else if (Token::simpleMatch(tok->previous()->astOperand1(), "(")) { |
5857 | 0 | const Token *castTok = tok->previous()->astOperand1(); |
5858 | 0 | if (castTok->isCast()) { |
5859 | 0 | ValueType vt = ValueType::parseDecl(castTok->next(),mSettings); |
5860 | 0 | if (vt.typeScope) |
5861 | 0 | return vt.typeScope->findFunction(tok, vt.constness == 1); |
5862 | 0 | } |
5863 | 0 | } |
5864 | 0 | } |
5865 | | |
5866 | | // check in enclosing scopes |
5867 | 4.21k | else { |
5868 | 6.68k | while (currScope) { |
5869 | 4.21k | const Function *func = currScope->findFunction(tok); |
5870 | 4.21k | if (func) |
5871 | 1.73k | return func; |
5872 | 2.47k | currScope = currScope->nestedIn; |
5873 | 2.47k | } |
5874 | | // check using namespace |
5875 | 2.47k | currScope = tok->scope(); |
5876 | 8.79k | while (currScope) { |
5877 | 6.32k | for (const auto& ul : currScope->usingList) { |
5878 | 0 | if (ul.scope) { |
5879 | 0 | const Function* func = ul.scope->findFunction(tok); |
5880 | 0 | if (func) |
5881 | 0 | return func; |
5882 | 0 | } |
5883 | 0 | } |
5884 | 6.32k | currScope = currScope->nestedIn; |
5885 | 6.32k | } |
5886 | 2.47k | } |
5887 | | // Check for constructor |
5888 | 2.47k | if (Token::Match(tok, "%name% (|{")) { |
5889 | 0 | ValueType vt = ValueType::parseDecl(tok, mSettings); |
5890 | 0 | if (vt.typeScope) |
5891 | 0 | return vt.typeScope->findFunction(tok, false); |
5892 | 0 | } |
5893 | 2.47k | return nullptr; |
5894 | 2.47k | } |
5895 | | |
5896 | | //--------------------------------------------------------------------------- |
5897 | | |
5898 | | const Scope *SymbolDatabase::findScopeByName(const std::string& name) const |
5899 | 0 | { |
5900 | 0 | auto it = std::find_if(scopeList.cbegin(), scopeList.cend(), [&](const Scope& s) { |
5901 | 0 | return s.className == name; |
5902 | 0 | }); |
5903 | 0 | return it == scopeList.end() ? nullptr : &*it; |
5904 | 0 | } |
5905 | | |
5906 | | //--------------------------------------------------------------------------- |
5907 | | |
5908 | | const Scope *Scope::findRecordInNestedList(const std::string & name, bool isC) const |
5909 | 0 | { |
5910 | 0 | for (const Scope* scope: nestedList) { |
5911 | 0 | if (scope->className == name && scope->type != eFunction) |
5912 | 0 | return scope; |
5913 | 0 | if (isC) { |
5914 | 0 | const Scope* nestedScope = scope->findRecordInNestedList(name, isC); |
5915 | 0 | if (nestedScope) |
5916 | 0 | return nestedScope; |
5917 | 0 | } |
5918 | 0 | } |
5919 | | |
5920 | 0 | const Type * nested_type = findType(name); |
5921 | |
|
5922 | 0 | if (nested_type) { |
5923 | 0 | if (nested_type->isTypeAlias()) { |
5924 | 0 | if (nested_type->typeStart == nested_type->typeEnd) |
5925 | 0 | return findRecordInNestedList(nested_type->typeStart->str()); |
5926 | 0 | } else |
5927 | 0 | return nested_type->classScope; |
5928 | 0 | } |
5929 | | |
5930 | 0 | return nullptr; |
5931 | 0 | } |
5932 | | |
5933 | | //--------------------------------------------------------------------------- |
5934 | | |
5935 | | const Type* Scope::findType(const std::string & name) const |
5936 | 8.54k | { |
5937 | 8.54k | auto it = definedTypesMap.find(name); |
5938 | | |
5939 | | // Type was found |
5940 | 8.54k | if (definedTypesMap.end() != it) |
5941 | 0 | return (*it).second; |
5942 | | |
5943 | | // is type defined in anonymous namespace.. |
5944 | 8.54k | it = definedTypesMap.find(emptyString); |
5945 | 8.54k | if (it != definedTypesMap.end()) { |
5946 | 0 | for (const Scope *scope : nestedList) { |
5947 | 0 | if (scope->className.empty() && (scope->type == eNamespace || scope->isClassOrStructOrUnion())) { |
5948 | 0 | const Type *t = scope->findType(name); |
5949 | 0 | if (t) |
5950 | 0 | return t; |
5951 | 0 | } |
5952 | 0 | } |
5953 | 0 | } |
5954 | | |
5955 | | // Type was not found |
5956 | 8.54k | return nullptr; |
5957 | 8.54k | } |
5958 | | |
5959 | | //--------------------------------------------------------------------------- |
5960 | | |
5961 | | Scope *Scope::findInNestedListRecursive(const std::string & name) |
5962 | 0 | { |
5963 | 0 | auto it = std::find_if(nestedList.cbegin(), nestedList.cend(), [&](const Scope* s) { |
5964 | 0 | return s->className == name; |
5965 | 0 | }); |
5966 | 0 | if (it != nestedList.end()) |
5967 | 0 | return *it; |
5968 | | |
5969 | 0 | for (Scope* scope: nestedList) { |
5970 | 0 | Scope *child = scope->findInNestedListRecursive(name); |
5971 | 0 | if (child) |
5972 | 0 | return child; |
5973 | 0 | } |
5974 | 0 | return nullptr; |
5975 | 0 | } |
5976 | | |
5977 | | //--------------------------------------------------------------------------- |
5978 | | |
5979 | | const Function *Scope::getDestructor() const |
5980 | 0 | { |
5981 | 0 | auto it = std::find_if(functionList.cbegin(), functionList.cend(), [](const Function& f) { |
5982 | 0 | return f.type == Function::eDestructor; |
5983 | 0 | }); |
5984 | 0 | return it == functionList.end() ? nullptr : &*it; |
5985 | 0 | } |
5986 | | |
5987 | | //--------------------------------------------------------------------------- |
5988 | | |
5989 | | bool SymbolDatabase::isCPP() const |
5990 | 46.2k | { |
5991 | 46.2k | return mTokenizer.isCPP(); |
5992 | 46.2k | } |
5993 | | |
5994 | | //--------------------------------------------------------------------------- |
5995 | | |
5996 | | const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const |
5997 | 0 | { |
5998 | 0 | const Scope *scope = nullptr; |
5999 | | // absolute path |
6000 | 0 | if (tok->str() == "::") { |
6001 | 0 | tok = tok->next(); |
6002 | 0 | scope = &scopeList.front(); |
6003 | 0 | } |
6004 | | // relative path |
6005 | 0 | else if (tok->isName()) { |
6006 | 0 | scope = startScope; |
6007 | 0 | } |
6008 | |
|
6009 | 0 | while (scope && tok && tok->isName()) { |
6010 | 0 | if (tok->strAt(1) == "::") { |
6011 | 0 | scope = scope->findRecordInNestedList(tok->str()); |
6012 | 0 | tok = tok->tokAt(2); |
6013 | 0 | } else if (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::")) { |
6014 | 0 | scope = scope->findRecordInNestedList(tok->str()); |
6015 | 0 | tok = tok->linkAt(1)->tokAt(2); |
6016 | 0 | } else |
6017 | 0 | return scope->findRecordInNestedList(tok->str()); |
6018 | 0 | } |
6019 | | |
6020 | | // not a valid path |
6021 | 0 | return nullptr; |
6022 | 0 | } |
6023 | | |
6024 | | //--------------------------------------------------------------------------- |
6025 | | |
6026 | | const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startScope, bool lookOutside) const |
6027 | 0 | { |
6028 | | // skip over struct or union |
6029 | 0 | if (Token::Match(startTok, "struct|union")) |
6030 | 0 | startTok = startTok->next(); |
6031 | | |
6032 | | // type same as scope |
6033 | 0 | if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::") |
6034 | 0 | return startScope->definedType; |
6035 | | |
6036 | 0 | if (mTokenizer.isC()) { |
6037 | 0 | const Scope* scope = startScope; |
6038 | 0 | while (scope) { |
6039 | 0 | if (startTok->str() == scope->className && scope->isClassOrStruct()) |
6040 | 0 | return scope->definedType; |
6041 | 0 | const Scope* typeScope = scope->findRecordInNestedList(startTok->str(), /*isC*/ true); |
6042 | 0 | if (typeScope) { |
6043 | 0 | if (startTok->str() == typeScope->className && typeScope->isClassOrStruct()) { |
6044 | 0 | if (const Type* type = typeScope->definedType) |
6045 | 0 | return type; |
6046 | 0 | } |
6047 | 0 | } |
6048 | 0 | scope = scope->nestedIn; |
6049 | 0 | } |
6050 | 0 | return nullptr; |
6051 | 0 | } |
6052 | | |
6053 | 0 | const Scope* start_scope = startScope; |
6054 | | |
6055 | | // absolute path - directly start in global scope |
6056 | 0 | if (startTok->str() == "::") { |
6057 | 0 | startTok = startTok->next(); |
6058 | 0 | start_scope = &scopeList.front(); |
6059 | 0 | } |
6060 | |
|
6061 | 0 | const Token* tok = startTok; |
6062 | 0 | const Scope* scope = start_scope; |
6063 | |
|
6064 | 0 | while (scope && tok && tok->isName()) { |
6065 | 0 | if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { |
6066 | 0 | scope = scope->findRecordInNestedList(tok->str()); |
6067 | 0 | if (scope) { |
6068 | 0 | if (tok->strAt(1) == "::") |
6069 | 0 | tok = tok->tokAt(2); |
6070 | 0 | else |
6071 | 0 | tok = tok->linkAt(1)->tokAt(2); |
6072 | 0 | } else { |
6073 | 0 | start_scope = start_scope->nestedIn; |
6074 | 0 | if (!start_scope) |
6075 | 0 | break; |
6076 | 0 | scope = start_scope; |
6077 | 0 | tok = startTok; |
6078 | 0 | } |
6079 | 0 | } else { |
6080 | 0 | const Scope* scope1{}; |
6081 | 0 | const Type* type = scope->findType(tok->str()); |
6082 | 0 | if (type) |
6083 | 0 | return type; |
6084 | 0 | if (lookOutside && (scope1 = scope->findRecordInBase(tok->str()))) { |
6085 | 0 | type = scope1->definedType; |
6086 | 0 | if (type) |
6087 | 0 | return type; |
6088 | 0 | } else if (lookOutside && scope->type == Scope::ScopeType::eNamespace) { |
6089 | 0 | scope = scope->nestedIn; |
6090 | 0 | continue; |
6091 | 0 | } else |
6092 | 0 | break; |
6093 | 0 | } |
6094 | 0 | } |
6095 | | |
6096 | | // check using namespaces |
6097 | 0 | while (startScope) { |
6098 | 0 | for (std::vector<Scope::UsingInfo>::const_iterator it = startScope->usingList.cbegin(); |
6099 | 0 | it != startScope->usingList.cend(); ++it) { |
6100 | 0 | tok = startTok; |
6101 | 0 | scope = it->scope; |
6102 | 0 | start_scope = startScope; |
6103 | |
|
6104 | 0 | while (scope && tok && tok->isName()) { |
6105 | 0 | if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { |
6106 | 0 | scope = scope->findRecordInNestedList(tok->str()); |
6107 | 0 | if (scope) { |
6108 | 0 | if (tok->strAt(1) == "::") |
6109 | 0 | tok = tok->tokAt(2); |
6110 | 0 | else |
6111 | 0 | tok = tok->linkAt(1)->tokAt(2); |
6112 | 0 | } else { |
6113 | 0 | start_scope = start_scope->nestedIn; |
6114 | 0 | if (!start_scope) |
6115 | 0 | break; |
6116 | 0 | scope = start_scope; |
6117 | 0 | tok = startTok; |
6118 | 0 | } |
6119 | 0 | } else { |
6120 | 0 | const Type * type = scope->findType(tok->str()); |
6121 | 0 | if (type) |
6122 | 0 | return type; |
6123 | 0 | if (const Scope *scope1 = scope->findRecordInBase(tok->str())) { |
6124 | 0 | type = scope1->definedType; |
6125 | 0 | if (type) |
6126 | 0 | return type; |
6127 | 0 | } else |
6128 | 0 | break; |
6129 | 0 | } |
6130 | 0 | } |
6131 | 0 | } |
6132 | 0 | startScope = startScope->nestedIn; |
6133 | 0 | } |
6134 | | |
6135 | | // not a valid path |
6136 | 0 | return nullptr; |
6137 | 0 | } |
6138 | | |
6139 | | //--------------------------------------------------------------------------- |
6140 | | |
6141 | | const Type* SymbolDatabase::findTypeInNested(const Token *startTok, const Scope *startScope) const |
6142 | 1.73k | { |
6143 | | // skip over struct or union |
6144 | 1.73k | if (Token::Match(startTok, "struct|union|enum")) |
6145 | 0 | startTok = startTok->next(); |
6146 | | |
6147 | | // type same as scope |
6148 | 1.73k | if (startTok->str() == startScope->className && startScope->isClassOrStruct()) |
6149 | 0 | return startScope->definedType; |
6150 | | |
6151 | 1.73k | bool hasPath = false; |
6152 | | |
6153 | | // absolute path - directly start in global scope |
6154 | 1.73k | if (startTok->str() == "::") { |
6155 | 0 | hasPath = true; |
6156 | 0 | startTok = startTok->next(); |
6157 | 0 | startScope = &scopeList.front(); |
6158 | 0 | } |
6159 | | |
6160 | 1.73k | const Token* tok = startTok; |
6161 | 1.73k | const Scope* scope = startScope; |
6162 | | |
6163 | 1.73k | while (scope && tok && tok->isName()) { |
6164 | 1.73k | if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { |
6165 | 0 | hasPath = true; |
6166 | 0 | scope = scope->findRecordInNestedList(tok->str()); |
6167 | 0 | if (scope) { |
6168 | 0 | if (tok->strAt(1) == "::") |
6169 | 0 | tok = tok->tokAt(2); |
6170 | 0 | else |
6171 | 0 | tok = tok->linkAt(1)->tokAt(2); |
6172 | 0 | } else { |
6173 | 0 | startScope = startScope->nestedIn; |
6174 | 0 | if (!startScope) |
6175 | 0 | break; |
6176 | 0 | scope = startScope; |
6177 | 0 | tok = startTok; |
6178 | 0 | } |
6179 | 1.73k | } else { |
6180 | 1.73k | const Type * type = scope->findType(tok->str()); |
6181 | 1.73k | if (hasPath || type) |
6182 | 0 | return type; |
6183 | | |
6184 | 1.73k | scope = scope->nestedIn; |
6185 | 1.73k | if (!scope) |
6186 | 1.73k | break; |
6187 | 1.73k | } |
6188 | 1.73k | } |
6189 | | |
6190 | | // not a valid path |
6191 | 1.73k | return nullptr; |
6192 | 1.73k | } |
6193 | | |
6194 | | //--------------------------------------------------------------------------- |
6195 | | |
6196 | | const Scope * SymbolDatabase::findNamespace(const Token * tok, const Scope * scope) const |
6197 | 0 | { |
6198 | 0 | const Scope * s = findScope(tok, scope); |
6199 | |
|
6200 | 0 | if (s) |
6201 | 0 | return s; |
6202 | 0 | if (scope->nestedIn) |
6203 | 0 | return findNamespace(tok, scope->nestedIn); |
6204 | | |
6205 | 0 | return nullptr; |
6206 | 0 | } |
6207 | | |
6208 | | //--------------------------------------------------------------------------- |
6209 | | |
6210 | | Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length) |
6211 | 0 | { |
6212 | 0 | const Function * function = nullptr; |
6213 | 0 | const bool destructor = func->strAt(-1) == "~"; |
6214 | |
|
6215 | 0 | auto range = ns->functionMap.equal_range(func->str()); |
6216 | 0 | for (std::multimap<std::string, const Function*>::const_iterator it = range.first; it != range.second; ++it) { |
6217 | 0 | if (it->second->argsMatch(ns, it->second->argDef, func->next(), path, path_length) && |
6218 | 0 | it->second->isDestructor() == destructor) { |
6219 | 0 | function = it->second; |
6220 | 0 | break; |
6221 | 0 | } |
6222 | 0 | } |
6223 | |
|
6224 | 0 | if (!function) { |
6225 | 0 | const Scope * scope = ns->findRecordInNestedList(func->str()); |
6226 | 0 | if (scope && Token::Match(func->tokAt(1), "::|<")) { |
6227 | 0 | if (func->strAt(1) == "::") |
6228 | 0 | func = func->tokAt(2); |
6229 | 0 | else if (func->linkAt(1)) |
6230 | 0 | func = func->linkAt(1)->tokAt(2); |
6231 | 0 | else |
6232 | 0 | return nullptr; |
6233 | 0 | if (func->str() == "~") |
6234 | 0 | func = func->next(); |
6235 | 0 | function = findFunctionInScope(func, scope, path, path_length); |
6236 | 0 | } |
6237 | 0 | } |
6238 | | |
6239 | 0 | return const_cast<Function *>(function); |
6240 | 0 | } |
6241 | | |
6242 | | //--------------------------------------------------------------------------- |
6243 | | |
6244 | | bool SymbolDatabase::isReservedName(const std::string& iName) const |
6245 | 18.5k | { |
6246 | 18.5k | if (isCPP()) { |
6247 | 18.5k | static const auto& cpp_keywords = Keywords::getAll(Standards::cppstd_t::CPPLatest); |
6248 | 18.5k | return cpp_keywords.find(iName) != cpp_keywords.cend(); |
6249 | 18.5k | } |
6250 | 0 | static const auto& c_keywords = Keywords::getAll(Standards::cstd_t::CLatest); |
6251 | 0 | return c_keywords.find(iName) != c_keywords.cend(); |
6252 | 18.5k | } |
6253 | | |
6254 | | nonneg int SymbolDatabase::sizeOfType(const Token *type) const |
6255 | 0 | { |
6256 | 0 | int size = mTokenizer.sizeOfType(type); |
6257 | |
|
6258 | 0 | if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) { |
6259 | 0 | size = mSettings.platform.sizeof_int; |
6260 | 0 | const Token * enum_type = type->type()->classScope->enumType; |
6261 | 0 | if (enum_type) |
6262 | 0 | size = mTokenizer.sizeOfType(enum_type); |
6263 | 0 | } |
6264 | |
|
6265 | 0 | return size; |
6266 | 0 | } |
6267 | | |
6268 | | static const Token* parsedecl(const Token* type, |
6269 | | ValueType* const valuetype, |
6270 | | ValueType::Sign defaultSignedness, |
6271 | | const Settings& settings, |
6272 | | bool isCpp, |
6273 | | SourceLocation loc = SourceLocation::current()); |
6274 | | |
6275 | | void SymbolDatabase::setValueType(Token* tok, const Variable& var, SourceLocation loc) |
6276 | 79.3k | { |
6277 | 79.3k | ValueType valuetype; |
6278 | 79.3k | if (mSettings.debugnormal || mSettings.debugwarnings) |
6279 | 0 | valuetype.setDebugPath(tok, loc); |
6280 | 79.3k | if (var.nameToken()) |
6281 | 79.3k | valuetype.bits = var.nameToken()->bits(); |
6282 | | |
6283 | 79.3k | valuetype.pointer = var.dimensions().size(); |
6284 | | // HACK: don't set pointer for plain std::array |
6285 | 79.3k | if (var.valueType() && var.valueType()->container && Token::simpleMatch(var.typeStartToken(), "std :: array") && !Token::simpleMatch(var.nameToken()->next(), "[")) |
6286 | 0 | valuetype.pointer = 0; |
6287 | | |
6288 | 79.3k | valuetype.typeScope = var.typeScope(); |
6289 | 79.3k | if (var.valueType()) { |
6290 | 79.3k | valuetype.container = var.valueType()->container; |
6291 | 79.3k | valuetype.containerTypeToken = var.valueType()->containerTypeToken; |
6292 | 79.3k | } |
6293 | 79.3k | valuetype.smartPointerType = var.smartPointerType(); |
6294 | 79.3k | if (parsedecl(var.typeStartToken(), &valuetype, mDefaultSignedness, mSettings, mIsCpp)) { |
6295 | 79.3k | if (tok->str() == "." && tok->astOperand1()) { |
6296 | 0 | const ValueType * const vt = tok->astOperand1()->valueType(); |
6297 | 0 | if (vt && (vt->constness & 1) != 0) |
6298 | 0 | valuetype.constness |= 1; |
6299 | 0 | } |
6300 | 79.3k | setValueType(tok, valuetype); |
6301 | 79.3k | } |
6302 | 79.3k | } |
6303 | | |
6304 | | static ValueType::Type getEnumType(const Scope* scope, const cppcheck::Platform& platform); |
6305 | | |
6306 | | void SymbolDatabase::setValueType(Token* tok, const Enumerator& enumerator, SourceLocation loc) |
6307 | 0 | { |
6308 | 0 | ValueType valuetype; |
6309 | 0 | if (mSettings.debugnormal || mSettings.debugwarnings) |
6310 | 0 | valuetype.setDebugPath(tok, loc); |
6311 | 0 | valuetype.typeScope = enumerator.scope; |
6312 | 0 | const Token * type = enumerator.scope->enumType; |
6313 | 0 | if (type) { |
6314 | 0 | valuetype.type = ValueType::typeFromString(type->str(), type->isLong()); |
6315 | 0 | if (valuetype.type == ValueType::Type::UNKNOWN_TYPE && type->isStandardType()) |
6316 | 0 | valuetype.fromLibraryType(type->str(), mSettings); |
6317 | |
|
6318 | 0 | if (valuetype.isIntegral()) { |
6319 | 0 | if (type->isSigned()) |
6320 | 0 | valuetype.sign = ValueType::Sign::SIGNED; |
6321 | 0 | else if (type->isUnsigned()) |
6322 | 0 | valuetype.sign = ValueType::Sign::UNSIGNED; |
6323 | 0 | else if (valuetype.type == ValueType::Type::CHAR) |
6324 | 0 | valuetype.sign = mDefaultSignedness; |
6325 | 0 | else |
6326 | 0 | valuetype.sign = ValueType::Sign::SIGNED; |
6327 | 0 | } |
6328 | |
|
6329 | 0 | setValueType(tok, valuetype); |
6330 | 0 | } else { |
6331 | 0 | valuetype.sign = ValueType::SIGNED; |
6332 | 0 | valuetype.type = getEnumType(enumerator.scope, mSettings.platform); |
6333 | 0 | setValueType(tok, valuetype); |
6334 | 0 | } |
6335 | 0 | } |
6336 | | |
6337 | | static void setAutoTokenProperties(Token * const autoTok) |
6338 | 0 | { |
6339 | 0 | const ValueType *valuetype = autoTok->valueType(); |
6340 | 0 | if (valuetype->isIntegral() || valuetype->isFloat()) |
6341 | 0 | autoTok->isStandardType(true); |
6342 | 0 | } |
6343 | | |
6344 | | bool isContainerYieldElement(Library::Container::Yield yield) |
6345 | 0 | { |
6346 | 0 | return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX || |
6347 | 0 | yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; |
6348 | 0 | } |
6349 | | |
6350 | | static bool isContainerYieldPointer(Library::Container::Yield yield) |
6351 | 0 | { |
6352 | 0 | return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; |
6353 | 0 | } |
6354 | | |
6355 | | void SymbolDatabase::setValueType(Token* tok, const ValueType& valuetype, SourceLocation loc) |
6356 | 184k | { |
6357 | 184k | ValueType* valuetypePtr = new ValueType(valuetype); |
6358 | 184k | if (mSettings.debugnormal || mSettings.debugwarnings) |
6359 | 0 | valuetypePtr->setDebugPath(tok, loc); |
6360 | 184k | tok->setValueType(valuetypePtr); |
6361 | 184k | Token *parent = tok->astParent(); |
6362 | 184k | if (!parent || parent->valueType()) |
6363 | 117k | return; |
6364 | 67.1k | if (!parent->astOperand1()) |
6365 | 0 | return; |
6366 | | |
6367 | 67.1k | const ValueType *vt1 = parent->astOperand1()->valueType(); |
6368 | 67.1k | const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr; |
6369 | | |
6370 | 67.1k | if (vt1 && Token::Match(parent, "<<|>>")) { |
6371 | 0 | if (!mIsCpp || (vt2 && vt2->isIntegral())) { |
6372 | 0 | if (vt1->type < ValueType::Type::BOOL || vt1->type >= ValueType::Type::INT) { |
6373 | 0 | ValueType vt(*vt1); |
6374 | 0 | vt.reference = Reference::None; |
6375 | 0 | setValueType(parent, vt); |
6376 | 0 | } else { |
6377 | 0 | ValueType vt(*vt1); |
6378 | 0 | vt.type = ValueType::Type::INT; // Integer promotion |
6379 | 0 | vt.sign = ValueType::Sign::SIGNED; |
6380 | 0 | vt.reference = Reference::None; |
6381 | 0 | setValueType(parent, vt); |
6382 | 0 | } |
6383 | |
|
6384 | 0 | } |
6385 | 0 | return; |
6386 | 0 | } |
6387 | | |
6388 | 67.1k | if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") && |
6389 | 67.1k | isContainerYieldElement(vt1->container->getYield(parent->next()->str()))) { |
6390 | 0 | ValueType item; |
6391 | 0 | if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings, mIsCpp)) { |
6392 | 0 | if (item.constness == 0) |
6393 | 0 | item.constness = vt1->constness; |
6394 | 0 | if (isContainerYieldPointer(vt1->container->getYield(parent->next()->str()))) |
6395 | 0 | item.pointer += 1; |
6396 | 0 | else |
6397 | 0 | item.reference = Reference::LValue; |
6398 | 0 | setValueType(parent->tokAt(2), item); |
6399 | 0 | } |
6400 | 0 | } |
6401 | | |
6402 | 67.1k | if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) { |
6403 | 0 | const Scope *scope = vt1->smartPointerType->classScope; |
6404 | 0 | const Function *f = scope ? scope->findFunction(parent->next(), false) : nullptr; |
6405 | 0 | if (f) |
6406 | 0 | parent->next()->function(f); |
6407 | 0 | } |
6408 | | |
6409 | 67.1k | if (parent->isAssignmentOp()) { |
6410 | 34.6k | if (vt1) { |
6411 | 33.2k | auto vt = *vt1; |
6412 | 33.2k | vt.reference = Reference::None; |
6413 | 33.2k | setValueType(parent, vt); |
6414 | 33.2k | } else if (mIsCpp && ((Token::Match(parent->tokAt(-3), "%var% ; %var% =") && parent->strAt(-3) == parent->strAt(-1)) || |
6415 | 1.34k | Token::Match(parent->tokAt(-1), "%var% ="))) { |
6416 | 8 | Token *var1Tok = parent->strAt(-2) == ";" ? parent->tokAt(-3) : parent->tokAt(-1); |
6417 | 8 | Token *autoTok = nullptr; |
6418 | 8 | if (Token::simpleMatch(var1Tok->tokAt(-1), "auto")) |
6419 | 0 | autoTok = var1Tok->previous(); |
6420 | 8 | else if (Token::Match(var1Tok->tokAt(-2), "auto *|&|&&")) |
6421 | 0 | autoTok = var1Tok->tokAt(-2); |
6422 | 8 | else if (Token::simpleMatch(var1Tok->tokAt(-3), "auto * const")) |
6423 | 0 | autoTok = var1Tok->tokAt(-3); |
6424 | 8 | if (autoTok) { |
6425 | 0 | ValueType vt(*vt2); |
6426 | 0 | if (vt.constness & (1 << vt.pointer)) |
6427 | 0 | vt.constness &= ~(1 << vt.pointer); |
6428 | 0 | if (autoTok->strAt(1) == "*" && vt.pointer) |
6429 | 0 | vt.pointer--; |
6430 | 0 | if (Token::Match(autoTok->tokAt(-1), "const|constexpr")) |
6431 | 0 | vt.constness |= (1 << vt.pointer); |
6432 | 0 | setValueType(autoTok, vt); |
6433 | 0 | setAutoTokenProperties(autoTok); |
6434 | 0 | if (vt2->pointer > vt.pointer) |
6435 | 0 | vt.pointer++; |
6436 | 0 | setValueType(var1Tok, vt); |
6437 | 0 | if (var1Tok != parent->previous()) |
6438 | 0 | setValueType(parent->previous(), vt); |
6439 | 0 | Variable *var = const_cast<Variable *>(parent->previous()->variable()); |
6440 | 0 | if (var) { |
6441 | 0 | ValueType vt2_(*vt2); |
6442 | 0 | if (vt2_.pointer == 0 && autoTok->strAt(1) == "*") |
6443 | 0 | vt2_.pointer = 1; |
6444 | 0 | if ((vt.constness & (1 << vt2->pointer)) != 0) |
6445 | 0 | vt2_.constness |= (1 << vt2->pointer); |
6446 | 0 | if (!Token::Match(autoTok->tokAt(1), "*|&")) |
6447 | 0 | vt2_.constness = vt.constness; |
6448 | 0 | if (Token::simpleMatch(autoTok->tokAt(1), "* const")) |
6449 | 0 | vt2_.constness |= (1 << vt2->pointer); |
6450 | 0 | var->setValueType(vt2_); |
6451 | 0 | if (vt2->typeScope && vt2->typeScope->definedType) { |
6452 | 0 | var->type(vt2->typeScope->definedType); |
6453 | 0 | if (autoTok->valueType()->pointer == 0) |
6454 | 0 | autoTok->type(vt2->typeScope->definedType); |
6455 | 0 | } |
6456 | 0 | } |
6457 | 0 | } |
6458 | 8 | } |
6459 | 34.6k | return; |
6460 | 34.6k | } |
6461 | | |
6462 | 32.5k | if (parent->str() == "[" && (!mIsCpp || parent->astOperand1() == tok) && valuetype.pointer > 0U && !Token::Match(parent->previous(), "[{,]")) { |
6463 | 0 | const Token *op1 = parent->astOperand1(); |
6464 | 0 | while (op1 && op1->str() == "[") |
6465 | 0 | op1 = op1->astOperand1(); |
6466 | |
|
6467 | 0 | ValueType vt(valuetype); |
6468 | | // the "[" is a dereference unless this is a variable declaration |
6469 | 0 | if (!(op1 && op1->variable() && op1->variable()->nameToken() == op1)) |
6470 | 0 | vt.pointer -= 1U; |
6471 | 0 | setValueType(parent, vt); |
6472 | 0 | return; |
6473 | 0 | } |
6474 | 32.5k | if (Token::Match(parent->previous(), "%name% (") && parent->astOperand1() == tok && valuetype.pointer > 0U) { |
6475 | 0 | ValueType vt(valuetype); |
6476 | 0 | vt.pointer -= 1U; |
6477 | 0 | setValueType(parent, vt); |
6478 | 0 | return; |
6479 | 0 | } |
6480 | | // std::move |
6481 | 32.5k | if (vt2 && parent->str() == "(" && Token::simpleMatch(parent->tokAt(-3), "std :: move (")) { |
6482 | 0 | ValueType vt = valuetype; |
6483 | 0 | vt.reference = Reference::RValue; |
6484 | 0 | setValueType(parent, vt); |
6485 | 0 | return; |
6486 | 0 | } |
6487 | 32.5k | if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { |
6488 | 0 | ValueType vt(valuetype); |
6489 | 0 | vt.pointer -= 1U; |
6490 | 0 | setValueType(parent, vt); |
6491 | 0 | return; |
6492 | 0 | } |
6493 | | // Dereference iterator |
6494 | 32.5k | if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::ITERATOR && |
6495 | 32.5k | valuetype.containerTypeToken) { |
6496 | 0 | ValueType vt; |
6497 | 0 | if (parsedecl(valuetype.containerTypeToken, &vt, mDefaultSignedness, mSettings, mIsCpp)) { |
6498 | 0 | if (vt.constness == 0) |
6499 | 0 | vt.constness = valuetype.constness; |
6500 | 0 | vt.reference = Reference::LValue; |
6501 | 0 | setValueType(parent, vt); |
6502 | 0 | return; |
6503 | 0 | } |
6504 | 0 | } |
6505 | | // Dereference smart pointer |
6506 | 32.5k | if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::SMART_POINTER && |
6507 | 32.5k | valuetype.smartPointerTypeToken) { |
6508 | 0 | ValueType vt; |
6509 | 0 | if (parsedecl(valuetype.smartPointerTypeToken, &vt, mDefaultSignedness, mSettings, mIsCpp)) { |
6510 | 0 | if (vt.constness == 0) |
6511 | 0 | vt.constness = valuetype.constness; |
6512 | 0 | setValueType(parent, vt); |
6513 | 0 | return; |
6514 | 0 | } |
6515 | 0 | } |
6516 | 32.5k | if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) { |
6517 | 0 | const Token *op1 = parent->astOperand2()->astOperand1(); |
6518 | 0 | while (op1 && op1->str() == "[") |
6519 | 0 | op1 = op1->astOperand1(); |
6520 | 0 | const ValueType& vt(valuetype); |
6521 | 0 | if (op1 && op1->variable() && op1->variable()->nameToken() == op1) { |
6522 | 0 | setValueType(parent, vt); |
6523 | 0 | return; |
6524 | 0 | } |
6525 | 0 | } |
6526 | 32.5k | if (parent->str() == "&" && !parent->astOperand2()) { |
6527 | 0 | ValueType vt(valuetype); |
6528 | 0 | vt.reference = Reference::None; //Given int& x; the type of &x is int* not int&* |
6529 | 0 | bool isArrayToPointerDecay = false; |
6530 | 0 | for (const Token* child = parent->astOperand1(); child;) { |
6531 | 0 | if (Token::Match(child, ".|::")) |
6532 | 0 | child = child->astOperand2(); |
6533 | 0 | else { |
6534 | 0 | isArrayToPointerDecay = child->variable() && child->variable()->isArray(); |
6535 | 0 | break; |
6536 | 0 | } |
6537 | 0 | } |
6538 | 0 | if (!isArrayToPointerDecay) |
6539 | 0 | vt.pointer += 1U; |
6540 | 0 | setValueType(parent, vt); |
6541 | 0 | return; |
6542 | 0 | } |
6543 | | |
6544 | 32.5k | if ((parent->str() == "." || parent->str() == "::") && |
6545 | 32.5k | parent->astOperand2() && parent->astOperand2()->isName()) { |
6546 | 0 | const Variable* var = parent->astOperand2()->variable(); |
6547 | 0 | if (!var && valuetype.typeScope && vt1) { |
6548 | 0 | const std::string &name = parent->astOperand2()->str(); |
6549 | 0 | const Scope *typeScope = vt1->typeScope; |
6550 | 0 | if (!typeScope) |
6551 | 0 | return; |
6552 | 0 | auto it = std::find_if(typeScope->varlist.begin(), typeScope->varlist.end(), [&name](const Variable& v) { |
6553 | 0 | return v.nameToken()->str() == name; |
6554 | 0 | }); |
6555 | 0 | if (it != typeScope->varlist.end()) |
6556 | 0 | var = &*it; |
6557 | 0 | } |
6558 | 0 | if (var) { |
6559 | 0 | setValueType(parent, *var); |
6560 | 0 | return; |
6561 | 0 | } |
6562 | 0 | if (const Enumerator* enu = parent->astOperand2()->enumerator()) |
6563 | 0 | setValueType(parent, *enu); |
6564 | 0 | return; |
6565 | 0 | } |
6566 | | |
6567 | | // range for loop, auto |
6568 | 32.5k | if (vt2 && |
6569 | 32.5k | parent->str() == ":" && |
6570 | 32.5k | Token::Match(parent->astParent(), "( const| auto *|&|&&| %var% :") && // TODO: east-const, multiple const, ref to ptr |
6571 | 32.5k | !parent->previous()->valueType() && |
6572 | 32.5k | Token::simpleMatch(parent->astParent()->astOperand1(), "for")) { |
6573 | 0 | const bool isconst = Token::simpleMatch(parent->astParent()->next(), "const"); |
6574 | 0 | Token * const autoToken = parent->astParent()->tokAt(isconst ? 2 : 1); |
6575 | 0 | if (vt2->pointer) { |
6576 | 0 | ValueType autovt(*vt2); |
6577 | 0 | autovt.pointer--; |
6578 | 0 | autovt.constness = 0; |
6579 | 0 | setValueType(autoToken, autovt); |
6580 | 0 | setAutoTokenProperties(autoToken); |
6581 | 0 | ValueType varvt(*vt2); |
6582 | 0 | varvt.pointer--; |
6583 | 0 | if (Token::simpleMatch(autoToken->next(), "&")) |
6584 | 0 | varvt.reference = Reference::LValue; |
6585 | 0 | if (isconst) { |
6586 | 0 | if (varvt.pointer && varvt.reference != Reference::None) |
6587 | 0 | varvt.constness |= (1 << varvt.pointer); |
6588 | 0 | else |
6589 | 0 | varvt.constness |= 1; |
6590 | 0 | } |
6591 | 0 | setValueType(parent->previous(), varvt); |
6592 | 0 | Variable *var = const_cast<Variable *>(parent->previous()->variable()); |
6593 | 0 | if (var) { |
6594 | 0 | var->setValueType(varvt); |
6595 | 0 | if (vt2->typeScope && vt2->typeScope->definedType) { |
6596 | 0 | var->type(vt2->typeScope->definedType); |
6597 | 0 | autoToken->type(vt2->typeScope->definedType); |
6598 | 0 | } |
6599 | 0 | } |
6600 | 0 | } else if (vt2->container) { |
6601 | | // TODO: Determine exact type of RHS |
6602 | 0 | const Token *typeStart = parent->astOperand2(); |
6603 | 0 | while (typeStart) { |
6604 | 0 | if (typeStart->variable()) |
6605 | 0 | typeStart = typeStart->variable()->typeStartToken(); |
6606 | 0 | else if (typeStart->str() == "(" && typeStart->previous() && typeStart->previous()->function()) |
6607 | 0 | typeStart = typeStart->previous()->function()->retDef; |
6608 | 0 | else |
6609 | 0 | break; |
6610 | 0 | } |
6611 | | |
6612 | | // Try to determine type of "auto" token. |
6613 | | // TODO: Get type better |
6614 | 0 | bool setType = false; |
6615 | 0 | ValueType autovt; |
6616 | 0 | const Type *templateArgType = nullptr; // container element type / smart pointer type |
6617 | 0 | if (!vt2->container->rangeItemRecordType.empty()) { |
6618 | 0 | setType = true; |
6619 | 0 | autovt.type = ValueType::Type::RECORD; |
6620 | 0 | } else if (vt2->containerTypeToken) { |
6621 | 0 | if (mSettings.library.isSmartPointer(vt2->containerTypeToken)) { |
6622 | 0 | const Token *smartPointerTypeTok = vt2->containerTypeToken; |
6623 | 0 | while (Token::Match(smartPointerTypeTok, "%name%|::")) |
6624 | 0 | smartPointerTypeTok = smartPointerTypeTok->next(); |
6625 | 0 | if (Token::simpleMatch(smartPointerTypeTok, "<")) { |
6626 | 0 | if ((templateArgType = findTypeInNested(smartPointerTypeTok->next(), tok->scope()))) { |
6627 | 0 | setType = true; |
6628 | 0 | autovt.smartPointerType = templateArgType; |
6629 | 0 | autovt.type = ValueType::Type::NONSTD; |
6630 | 0 | } |
6631 | 0 | } |
6632 | 0 | } else if (parsedecl(vt2->containerTypeToken, &autovt, mDefaultSignedness, mSettings, mIsCpp)) { |
6633 | 0 | setType = true; |
6634 | 0 | templateArgType = vt2->containerTypeToken->type(); |
6635 | 0 | if (Token::simpleMatch(autoToken->next(), "&")) |
6636 | 0 | autovt.reference = Reference::LValue; |
6637 | 0 | else if (Token::simpleMatch(autoToken->next(), "&&")) |
6638 | 0 | autovt.reference = Reference::RValue; |
6639 | 0 | if (autoToken->previous()->str() == "const") { |
6640 | 0 | if (autovt.pointer && autovt.reference != Reference::None) |
6641 | 0 | autovt.constness |= 2; |
6642 | 0 | else |
6643 | 0 | autovt.constness |= 1; |
6644 | 0 | } |
6645 | 0 | } |
6646 | 0 | } |
6647 | |
|
6648 | 0 | if (setType) { |
6649 | | // Type of "auto" has been determined.. set type information for "auto" and variable tokens |
6650 | 0 | setValueType(autoToken, autovt); |
6651 | 0 | setAutoTokenProperties(autoToken); |
6652 | 0 | ValueType varvt(autovt); |
6653 | 0 | if (autoToken->strAt(1) == "*" && autovt.pointer) |
6654 | 0 | autovt.pointer--; |
6655 | 0 | if (isconst) |
6656 | 0 | varvt.constness |= (1 << autovt.pointer); |
6657 | 0 | setValueType(parent->previous(), varvt); |
6658 | 0 | Variable * var = const_cast<Variable *>(parent->previous()->variable()); |
6659 | 0 | if (var) { |
6660 | 0 | var->setValueType(varvt); |
6661 | 0 | if (templateArgType && templateArgType->classScope && templateArgType->classScope->definedType) { |
6662 | 0 | autoToken->type(templateArgType->classScope->definedType); |
6663 | 0 | var->type(templateArgType->classScope->definedType); |
6664 | 0 | } |
6665 | 0 | } |
6666 | 0 | } |
6667 | 0 | } |
6668 | 0 | } |
6669 | | |
6670 | 32.5k | if (vt1 && vt1->containerTypeToken && parent->str() == "[") { |
6671 | 0 | ValueType vtParent; |
6672 | 0 | if (parsedecl(vt1->containerTypeToken, &vtParent, mDefaultSignedness, mSettings, mIsCpp)) { |
6673 | 0 | setValueType(parent, vtParent); |
6674 | 0 | return; |
6675 | 0 | } |
6676 | 0 | } |
6677 | | |
6678 | 32.5k | if (mIsCpp && vt2 && Token::simpleMatch(parent->previous(), "decltype (")) { |
6679 | 0 | setValueType(parent, *vt2); |
6680 | 0 | return; |
6681 | 0 | } |
6682 | | |
6683 | | // c++17 auto type deduction of braced init list |
6684 | 32.5k | if (mIsCpp && mSettings.standards.cpp >= Standards::CPP17 && vt2 && Token::Match(parent->tokAt(-2), "auto %var% {")) { |
6685 | 0 | Token *autoTok = parent->tokAt(-2); |
6686 | 0 | setValueType(autoTok, *vt2); |
6687 | 0 | setAutoTokenProperties(autoTok); |
6688 | 0 | if (parent->previous()->variable()) |
6689 | 0 | const_cast<Variable*>(parent->previous()->variable())->setValueType(*vt2); |
6690 | 0 | else |
6691 | 0 | debugMessage(parent->previous(), "debug", "Missing variable class for variable with varid"); |
6692 | 0 | return; |
6693 | 0 | } |
6694 | | |
6695 | 32.5k | if (!vt1) |
6696 | 7.00k | return; |
6697 | 25.5k | if (parent->astOperand2() && !vt2) |
6698 | 12.5k | return; |
6699 | | |
6700 | 13.0k | const bool ternary = parent->str() == ":" && parent->astParent() && parent->astParent()->str() == "?"; |
6701 | 13.0k | if (ternary) { |
6702 | 0 | if (vt2 && vt1->pointer == vt2->pointer && vt1->type == vt2->type && vt1->sign == vt2->sign) |
6703 | 0 | setValueType(parent, *vt2); |
6704 | 0 | parent = parent->astParent(); |
6705 | 0 | } |
6706 | | |
6707 | 13.0k | if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) { |
6708 | | |
6709 | | // CONTAINER + x => CONTAINER |
6710 | 8.80k | if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->isIntegral()) { |
6711 | 0 | setValueType(parent, *vt1); |
6712 | 0 | return; |
6713 | 0 | } |
6714 | | // x + CONTAINER => CONTAINER |
6715 | 8.80k | if (parent->str() == "+" && vt1->isIntegral() && vt2 && vt2->type == ValueType::Type::CONTAINER) { |
6716 | 0 | setValueType(parent, *vt2); |
6717 | 0 | return; |
6718 | 0 | } |
6719 | | |
6720 | 8.80k | if (parent->isArithmeticalOp()) { |
6721 | 3.08k | if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { |
6722 | 0 | setValueType(parent, *vt1); |
6723 | 0 | return; |
6724 | 0 | } |
6725 | | |
6726 | 3.08k | if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { |
6727 | 0 | setValueType(parent, *vt2); |
6728 | 0 | return; |
6729 | 0 | } |
6730 | 5.72k | } else if (ternary) { |
6731 | 0 | if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { |
6732 | 0 | if (vt2->isPrimitive()) |
6733 | 0 | setValueType(parent, *vt1); |
6734 | 0 | else |
6735 | 0 | setValueType(parent, *vt2); |
6736 | 0 | return; |
6737 | 0 | } |
6738 | | |
6739 | 0 | if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { |
6740 | 0 | if (vt1->isPrimitive()) |
6741 | 0 | setValueType(parent, *vt2); |
6742 | 0 | else |
6743 | 0 | setValueType(parent, *vt1); |
6744 | 0 | return; |
6745 | 0 | } |
6746 | | |
6747 | 0 | if (vt1->isTypeEqual(vt2)) { |
6748 | 0 | setValueType(parent, *vt1); |
6749 | 0 | return; |
6750 | 0 | } |
6751 | 0 | } |
6752 | | |
6753 | 8.80k | if (vt1->pointer != 0U) { |
6754 | 0 | if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer |
6755 | 0 | setValueType(parent, *vt1); |
6756 | 0 | else // result is pointer diff |
6757 | 0 | setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t")); |
6758 | 0 | return; |
6759 | 0 | } |
6760 | | |
6761 | 8.80k | if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) { |
6762 | 0 | setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U)); |
6763 | 0 | return; |
6764 | 0 | } |
6765 | 8.80k | if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) { |
6766 | 0 | setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U)); |
6767 | 0 | return; |
6768 | 0 | } |
6769 | 8.80k | if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) { |
6770 | 0 | setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U)); |
6771 | 0 | return; |
6772 | 0 | } |
6773 | | |
6774 | | // iterator +/- integral = iterator |
6775 | 8.80k | if (vt1->type == ValueType::Type::ITERATOR && vt2 && vt2->isIntegral() && |
6776 | 8.80k | (parent->str() == "+" || parent->str() == "-")) { |
6777 | 0 | setValueType(parent, *vt1); |
6778 | 0 | return; |
6779 | 0 | } |
6780 | | |
6781 | 8.80k | if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->type == ValueType::Type::CONTAINER && vt1->container == vt2->container) { |
6782 | 0 | setValueType(parent, *vt1); |
6783 | 0 | return; |
6784 | 0 | } |
6785 | 8.80k | } |
6786 | | |
6787 | 13.0k | if (vt1->isIntegral() && vt1->pointer == 0U && |
6788 | 13.0k | (!vt2 || (vt2->isIntegral() && vt2->pointer == 0U)) && |
6789 | 13.0k | (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eBitOp || parent->tokType() == Token::eIncDecOp || parent->isAssignmentOp())) { |
6790 | | |
6791 | 13.0k | ValueType vt; |
6792 | 13.0k | if (!vt2 || vt1->type > vt2->type) { |
6793 | 8.58k | vt.type = vt1->type; |
6794 | 8.58k | vt.sign = vt1->sign; |
6795 | 8.58k | vt.originalTypeName = vt1->originalTypeName; |
6796 | 8.58k | } else if (vt1->type == vt2->type) { |
6797 | 4.06k | vt.type = vt1->type; |
6798 | 4.06k | if (vt1->sign == ValueType::Sign::UNSIGNED || vt2->sign == ValueType::Sign::UNSIGNED) |
6799 | 0 | vt.sign = ValueType::Sign::UNSIGNED; |
6800 | 4.06k | else if (vt1->sign == ValueType::Sign::UNKNOWN_SIGN || vt2->sign == ValueType::Sign::UNKNOWN_SIGN) |
6801 | 164 | vt.sign = ValueType::Sign::UNKNOWN_SIGN; |
6802 | 3.90k | else |
6803 | 3.90k | vt.sign = ValueType::Sign::SIGNED; |
6804 | 4.06k | vt.originalTypeName = (vt1->originalTypeName.empty() ? vt2 : vt1)->originalTypeName; |
6805 | 4.06k | } else { |
6806 | 412 | vt.type = vt2->type; |
6807 | 412 | vt.sign = vt2->sign; |
6808 | 412 | vt.originalTypeName = vt2->originalTypeName; |
6809 | 412 | } |
6810 | 13.0k | if (vt.type < ValueType::Type::INT && !(ternary && vt.type==ValueType::Type::BOOL)) { |
6811 | 188 | vt.type = ValueType::Type::INT; |
6812 | 188 | vt.sign = ValueType::Sign::SIGNED; |
6813 | 188 | vt.originalTypeName.clear(); |
6814 | 188 | } |
6815 | | |
6816 | 13.0k | setValueType(parent, vt); |
6817 | 13.0k | return; |
6818 | 13.0k | } |
6819 | 13.0k | } |
6820 | | |
6821 | | static ValueType::Type getEnumType(const Scope* scope, const cppcheck::Platform& platform) // TODO: also determine sign? |
6822 | 0 | { |
6823 | 0 | ValueType::Type type = ValueType::Type::INT; |
6824 | 0 | for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { |
6825 | 0 | if (!tok->isAssignmentOp()) |
6826 | 0 | continue; |
6827 | 0 | const Token* vTok = tok->astOperand2(); |
6828 | 0 | if (!vTok->hasKnownIntValue()) { |
6829 | 0 | if (!vTok->isLiteral()) |
6830 | 0 | continue; |
6831 | 0 | if (const ValueType* vt = vTok->valueType()) { |
6832 | 0 | if ((vt->type > type && (vt->type == ValueType::Type::LONG || vt->type == ValueType::Type::LONGLONG))) |
6833 | 0 | type = vt->type; |
6834 | 0 | } |
6835 | 0 | continue; |
6836 | 0 | } |
6837 | 0 | const MathLib::bigint value = vTok->getKnownIntValue(); |
6838 | 0 | if (!platform.isIntValue(value)) { |
6839 | 0 | type = ValueType::Type::LONG; |
6840 | 0 | if (!platform.isLongValue(value)) |
6841 | 0 | type = ValueType::Type::LONGLONG; |
6842 | 0 | } |
6843 | 0 | } |
6844 | 0 | return type; |
6845 | 0 | } |
6846 | | |
6847 | | static const Token* parsedecl(const Token* type, |
6848 | | ValueType* const valuetype, |
6849 | | ValueType::Sign defaultSignedness, |
6850 | | const Settings& settings, |
6851 | | bool isCpp, |
6852 | | SourceLocation loc) |
6853 | 108k | { |
6854 | 108k | if (settings.debugnormal || settings.debugwarnings) |
6855 | 0 | valuetype->setDebugPath(type, loc); |
6856 | 108k | const Token * const previousType = type; |
6857 | 108k | const unsigned int pointer0 = valuetype->pointer; |
6858 | 110k | while (Token::Match(type->previous(), "%name%") && !endsWith(type->previous()->str(), ':')) |
6859 | 1.73k | type = type->previous(); |
6860 | 108k | valuetype->sign = ValueType::Sign::UNKNOWN_SIGN; |
6861 | 108k | if (!valuetype->typeScope && !valuetype->smartPointerType) |
6862 | 108k | valuetype->type = ValueType::Type::UNKNOWN_TYPE; |
6863 | 0 | else if (valuetype->smartPointerType) |
6864 | 0 | valuetype->type = ValueType::Type::SMART_POINTER; |
6865 | 0 | else if (valuetype->typeScope->type == Scope::eEnum) { |
6866 | 0 | const Token * enum_type = valuetype->typeScope->enumType; |
6867 | 0 | if (enum_type) { |
6868 | 0 | if (enum_type->isSigned()) |
6869 | 0 | valuetype->sign = ValueType::Sign::SIGNED; |
6870 | 0 | else if (enum_type->isUnsigned()) |
6871 | 0 | valuetype->sign = ValueType::Sign::UNSIGNED; |
6872 | 0 | else |
6873 | 0 | valuetype->sign = defaultSignedness; |
6874 | 0 | const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong()); |
6875 | 0 | if (t != ValueType::Type::UNKNOWN_TYPE) |
6876 | 0 | valuetype->type = t; |
6877 | 0 | else if (enum_type->isStandardType()) |
6878 | 0 | valuetype->fromLibraryType(enum_type->str(), settings); |
6879 | 0 | } else |
6880 | 0 | valuetype->type = getEnumType(valuetype->typeScope, settings.platform); |
6881 | 0 | } else |
6882 | 0 | valuetype->type = ValueType::Type::RECORD; |
6883 | 108k | bool par = false; |
6884 | 220k | while (Token::Match(type, "%name%|*|&|&&|::|(") && !Token::Match(type, "typename|template") && type->varId() == 0 && |
6885 | 220k | !type->variable() && !type->function()) { |
6886 | 121k | bool isIterator = false; |
6887 | 121k | if (type->str() == "(") { |
6888 | 9.50k | if (Token::Match(type->link(), ") const| {")) |
6889 | 9.20k | break; |
6890 | 300 | if (par) |
6891 | 4 | break; |
6892 | 296 | par = true; |
6893 | 296 | } |
6894 | 112k | if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) { |
6895 | 0 | const ValueType *vt2 = type->next()->valueType(); |
6896 | 0 | if (valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) |
6897 | 0 | valuetype->sign = vt2->sign; |
6898 | 0 | if (valuetype->type == ValueType::Type::UNKNOWN_TYPE) |
6899 | 0 | valuetype->type = vt2->type; |
6900 | 0 | valuetype->constness += vt2->constness; |
6901 | 0 | valuetype->pointer += vt2->pointer; |
6902 | 0 | valuetype->reference = vt2->reference; |
6903 | 0 | type = type->linkAt(1)->next(); |
6904 | 0 | continue; |
6905 | 0 | } |
6906 | 112k | if (type->isSigned()) |
6907 | 0 | valuetype->sign = ValueType::Sign::SIGNED; |
6908 | 112k | else if (type->isUnsigned()) |
6909 | 0 | valuetype->sign = ValueType::Sign::UNSIGNED; |
6910 | 112k | if (valuetype->type == ValueType::Type::UNKNOWN_TYPE && |
6911 | 112k | type->type() && type->type()->isTypeAlias() && type->type()->typeStart && |
6912 | 112k | type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType) |
6913 | 0 | parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings, isCpp); |
6914 | 112k | else if (Token::Match(type, "const|constexpr")) |
6915 | 0 | valuetype->constness |= (1 << (valuetype->pointer - pointer0)); |
6916 | 112k | else if (settings.clang && type->str().size() > 2 && type->str().find("::") < type->str().find('<')) { |
6917 | 0 | TokenList typeTokens(&settings); |
6918 | 0 | std::string::size_type pos1 = 0; |
6919 | 0 | do { |
6920 | 0 | const std::string::size_type pos2 = type->str().find("::", pos1); |
6921 | 0 | if (pos2 == std::string::npos) { |
6922 | 0 | typeTokens.addtoken(type->str().substr(pos1), 0, 0, 0, false); |
6923 | 0 | break; |
6924 | 0 | } |
6925 | 0 | typeTokens.addtoken(type->str().substr(pos1, pos2 - pos1), 0, 0, 0, false); |
6926 | 0 | typeTokens.addtoken("::", 0, 0, 0, false); |
6927 | 0 | pos1 = pos2 + 2; |
6928 | 0 | } while (pos1 < type->str().size()); |
6929 | 0 | const Library::Container* container = |
6930 | 0 | settings.library.detectContainerOrIterator(typeTokens.front(), &isIterator); |
6931 | 0 | if (container) { |
6932 | 0 | if (isIterator) |
6933 | 0 | valuetype->type = ValueType::Type::ITERATOR; |
6934 | 0 | else |
6935 | 0 | valuetype->type = ValueType::Type::CONTAINER; |
6936 | 0 | valuetype->container = container; |
6937 | 0 | } else { |
6938 | 0 | const Scope *scope = type->scope(); |
6939 | 0 | valuetype->typeScope = scope->check->findScope(typeTokens.front(), scope); |
6940 | 0 | if (valuetype->typeScope) |
6941 | 0 | valuetype->type = (scope->type == Scope::ScopeType::eClass) ? ValueType::Type::RECORD : ValueType::Type::NONSTD; |
6942 | 0 | } |
6943 | 112k | } else if (const Library::Container* container = (isCpp ? settings.library.detectContainerOrIterator(type, &isIterator) : nullptr)) { |
6944 | 0 | if (isIterator) |
6945 | 0 | valuetype->type = ValueType::Type::ITERATOR; |
6946 | 0 | else |
6947 | 0 | valuetype->type = ValueType::Type::CONTAINER; |
6948 | 0 | valuetype->container = container; |
6949 | 0 | while (Token::Match(type, "%type%|::|<") && type->str() != "const") { |
6950 | 0 | if (type->str() == "<" && type->link()) { |
6951 | 0 | if (container->type_templateArgNo >= 0) { |
6952 | 0 | const Token *templateType = type->next(); |
6953 | 0 | for (int j = 0; templateType && j < container->type_templateArgNo; j++) |
6954 | 0 | templateType = templateType->nextTemplateArgument(); |
6955 | 0 | valuetype->containerTypeToken = templateType; |
6956 | 0 | } |
6957 | 0 | type = type->link(); |
6958 | 0 | } |
6959 | 0 | type = type->next(); |
6960 | 0 | } |
6961 | 0 | if (type && type->str() == "(" && type->previous()->function()) |
6962 | | // we are past the end of the type |
6963 | 0 | type = type->previous(); |
6964 | 0 | continue; |
6965 | 112k | } else if (const Library::SmartPointer* smartPointer = (isCpp ? settings.library.detectSmartPointer(type) : nullptr)) { |
6966 | 0 | const Token* argTok = Token::findsimplematch(type, "<"); |
6967 | 0 | if (!argTok) |
6968 | 0 | break; |
6969 | 0 | valuetype->smartPointer = smartPointer; |
6970 | 0 | valuetype->smartPointerTypeToken = argTok->next(); |
6971 | 0 | valuetype->smartPointerType = argTok->next()->type(); |
6972 | 0 | valuetype->type = ValueType::Type::SMART_POINTER; |
6973 | 0 | type = argTok->link(); |
6974 | 0 | if (type) |
6975 | 0 | type = type->next(); |
6976 | 0 | continue; |
6977 | 112k | } else if (Token::Match(type, "%name% :: %name%")) { |
6978 | 0 | std::string typestr; |
6979 | 0 | const Token *end = type; |
6980 | 0 | while (Token::Match(end, "%name% :: %name%")) { |
6981 | 0 | typestr += end->str() + "::"; |
6982 | 0 | end = end->tokAt(2); |
6983 | 0 | } |
6984 | 0 | typestr += end->str(); |
6985 | 0 | if (valuetype->fromLibraryType(typestr, settings)) |
6986 | 0 | type = end; |
6987 | 112k | } else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) { |
6988 | 100k | const ValueType::Type t0 = valuetype->type; |
6989 | 100k | valuetype->type = ValueType::typeFromString(type->str(), type->isLong()); |
6990 | 100k | if (t0 == ValueType::Type::LONG) { |
6991 | 0 | if (valuetype->type == ValueType::Type::LONG) |
6992 | 0 | valuetype->type = ValueType::Type::LONGLONG; |
6993 | 0 | else if (valuetype->type == ValueType::Type::DOUBLE) |
6994 | 0 | valuetype->type = ValueType::Type::LONGDOUBLE; |
6995 | 0 | } |
6996 | 100k | } else if (type->str() == "auto") { |
6997 | 0 | const ValueType *vt = type->valueType(); |
6998 | 0 | if (!vt) |
6999 | 0 | return nullptr; |
7000 | 0 | valuetype->type = vt->type; |
7001 | 0 | valuetype->pointer = vt->pointer; |
7002 | 0 | valuetype->reference = vt->reference; |
7003 | 0 | if (vt->sign != ValueType::Sign::UNKNOWN_SIGN) |
7004 | 0 | valuetype->sign = vt->sign; |
7005 | 0 | valuetype->constness = vt->constness; |
7006 | 0 | valuetype->originalTypeName = vt->originalTypeName; |
7007 | 0 | const bool hasConst = Token::simpleMatch(type->previous(), "const"); |
7008 | 0 | while (Token::Match(type, "%name%|*|&|&&|::") && !type->variable()) { |
7009 | 0 | if (type->str() == "*") { |
7010 | 0 | valuetype->pointer = 1; |
7011 | 0 | if (hasConst) |
7012 | 0 | valuetype->constness = 1; |
7013 | 0 | } else if (type->str() == "&") { |
7014 | 0 | valuetype->reference = Reference::LValue; |
7015 | 0 | } else if (type->str() == "&&") { |
7016 | 0 | valuetype->reference = Reference::RValue; |
7017 | 0 | } |
7018 | 0 | if (type->str() == "const") |
7019 | 0 | valuetype->constness |= (1 << valuetype->pointer); |
7020 | 0 | type = type->next(); |
7021 | 0 | } |
7022 | 0 | break; |
7023 | 11.6k | } else if (!valuetype->typeScope && (type->str() == "struct" || type->str() == "enum")) |
7024 | 0 | valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD; |
7025 | 11.6k | else if (!valuetype->typeScope && type->type() && type->type()->classScope) { |
7026 | 0 | if (type->type()->classScope->type == Scope::ScopeType::eEnum) { |
7027 | 0 | valuetype->sign = ValueType::Sign::SIGNED; |
7028 | 0 | valuetype->type = getEnumType(type->type()->classScope, settings.platform); |
7029 | 0 | } else { |
7030 | 0 | valuetype->type = ValueType::Type::RECORD; |
7031 | 0 | } |
7032 | 0 | valuetype->typeScope = type->type()->classScope; |
7033 | 11.6k | } else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U) |
7034 | 0 | return nullptr; |
7035 | 11.6k | else if (type->str() == "*") |
7036 | 4 | valuetype->pointer++; |
7037 | 11.6k | else if (type->str() == "&") |
7038 | 4 | valuetype->reference = Reference::LValue; |
7039 | 11.6k | else if (type->str() == "&&") |
7040 | 0 | valuetype->reference = Reference::RValue; |
7041 | 11.6k | else if (type->isStandardType()) |
7042 | 0 | valuetype->fromLibraryType(type->str(), settings); |
7043 | 11.6k | else if (Token::Match(type->previous(), "!!:: %name% !!::")) |
7044 | 11.3k | valuetype->fromLibraryType(type->str(), settings); |
7045 | 112k | if (!type->originalName().empty()) |
7046 | 0 | valuetype->originalTypeName = type->originalName(); |
7047 | 112k | type = type->next(); |
7048 | 112k | } |
7049 | | |
7050 | | // Set signedness for integral types.. |
7051 | 108k | if (valuetype->isIntegral() && valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) { |
7052 | 100k | if (valuetype->type == ValueType::Type::CHAR) |
7053 | 0 | valuetype->sign = defaultSignedness; |
7054 | 100k | else if (valuetype->type >= ValueType::Type::SHORT) |
7055 | 100k | valuetype->sign = ValueType::Sign::SIGNED; |
7056 | 100k | } |
7057 | | |
7058 | 108k | return (type && (valuetype->type != ValueType::Type::UNKNOWN_TYPE || valuetype->pointer > 0 || valuetype->reference != Reference::None)) ? type : nullptr; |
7059 | 108k | } |
7060 | | |
7061 | | static const Scope *getClassScope(const Token *tok) |
7062 | 15.9k | { |
7063 | 15.9k | return tok && tok->valueType() && tok->valueType()->typeScope && tok->valueType()->typeScope->isClassOrStruct() ? |
7064 | 0 | tok->valueType()->typeScope : |
7065 | 15.9k | nullptr; |
7066 | 15.9k | } |
7067 | | |
7068 | | static const Function *getOperatorFunction(const Token * const tok) |
7069 | 0 | { |
7070 | 0 | const std::string functionName("operator" + tok->str()); |
7071 | 0 | std::multimap<std::string, const Function *>::const_iterator it; |
7072 | |
|
7073 | 0 | const Scope *classScope = getClassScope(tok->astOperand1()); |
7074 | 0 | if (classScope) { |
7075 | 0 | it = classScope->functionMap.find(functionName); |
7076 | 0 | if (it != classScope->functionMap.end()) |
7077 | 0 | return it->second; |
7078 | 0 | } |
7079 | | |
7080 | 0 | classScope = getClassScope(tok->astOperand2()); |
7081 | 0 | if (classScope) { |
7082 | 0 | it = classScope->functionMap.find(functionName); |
7083 | 0 | if (it != classScope->functionMap.end()) |
7084 | 0 | return it->second; |
7085 | 0 | } |
7086 | | |
7087 | 0 | return nullptr; |
7088 | 0 | } |
7089 | | |
7090 | 29.6k | static const Function* getFunction(const Token* tok) { |
7091 | 29.6k | if (!tok) |
7092 | 0 | return nullptr; |
7093 | 29.6k | if (tok->function() && tok->function()->retDef) |
7094 | 5.21k | return tok->function(); |
7095 | 24.3k | if (const Variable* lvar = tok->variable()) { // lambda |
7096 | 0 | const Function* lambda{}; |
7097 | 0 | if (Token::Match(lvar->nameToken()->next(), "; %varid% = [", lvar->declarationId())) |
7098 | 0 | lambda = lvar->nameToken()->tokAt(4)->function(); |
7099 | 0 | else if (Token::simpleMatch(lvar->nameToken()->next(), "{ [")) |
7100 | 0 | lambda = lvar->nameToken()->tokAt(2)->function(); |
7101 | 0 | if (lambda && lambda->retDef) |
7102 | 0 | return lambda; |
7103 | 0 | } |
7104 | 24.3k | return nullptr; |
7105 | 24.3k | } |
7106 | | |
7107 | | void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) |
7108 | 5.44k | { |
7109 | 5.44k | if (!tokens) |
7110 | 5.44k | tokens = const_cast<Tokenizer &>(mTokenizer).list.front(); |
7111 | | |
7112 | 374k | for (Token *tok = tokens; tok; tok = tok->next()) |
7113 | 368k | tok->setValueType(nullptr); |
7114 | | |
7115 | 374k | for (Token *tok = tokens; tok; tok = tok->next()) { |
7116 | 368k | if (tok->isNumber()) { |
7117 | 38.3k | if (MathLib::isFloat(tok->str())) { |
7118 | 0 | ValueType::Type type = ValueType::Type::DOUBLE; |
7119 | 0 | const char suffix = tok->str()[tok->str().size() - 1]; |
7120 | 0 | if (suffix == 'f' || suffix == 'F') |
7121 | 0 | type = ValueType::Type::FLOAT; |
7122 | 0 | else if (suffix == 'L' || suffix == 'l') |
7123 | 0 | type = ValueType::Type::LONGDOUBLE; |
7124 | 0 | setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U)); |
7125 | 38.3k | } else if (MathLib::isInt(tok->str())) { |
7126 | 38.3k | const std::string tokStr = MathLib::abs(tok->str()); |
7127 | 38.3k | const bool unsignedSuffix = (tokStr.find_last_of("uU") != std::string::npos); |
7128 | 38.3k | ValueType::Sign sign = unsignedSuffix ? ValueType::Sign::UNSIGNED : ValueType::Sign::SIGNED; |
7129 | 38.3k | ValueType::Type type = ValueType::Type::INT; |
7130 | 38.3k | const MathLib::biguint value = MathLib::toULongNumber(tokStr); |
7131 | 38.3k | for (std::size_t pos = tokStr.size() - 1U; pos > 0U; --pos) { |
7132 | 5.46k | const char suffix = tokStr[pos]; |
7133 | 5.46k | if (suffix == 'u' || suffix == 'U') |
7134 | 0 | sign = ValueType::Sign::UNSIGNED; |
7135 | 5.46k | else if (suffix == 'l' || suffix == 'L') |
7136 | 0 | type = (type == ValueType::Type::INT) ? ValueType::Type::LONG : ValueType::Type::LONGLONG; |
7137 | 5.46k | else if (pos > 2U && suffix == '4' && tokStr[pos - 1] == '6' && tokStr[pos - 2] == 'i') { |
7138 | 0 | type = ValueType::Type::LONGLONG; |
7139 | 0 | pos -= 2; |
7140 | 5.46k | } else break; |
7141 | 5.46k | } |
7142 | 38.3k | if (mSettings.platform.type != cppcheck::Platform::Type::Unspecified) { |
7143 | 38.3k | if (type <= ValueType::Type::INT && mSettings.platform.isIntValue(unsignedSuffix ? (value >> 1) : value)) |
7144 | 38.3k | type = ValueType::Type::INT; |
7145 | 0 | else if (type <= ValueType::Type::INT && !MathLib::isDec(tokStr) && mSettings.platform.isIntValue(value >> 2)) { |
7146 | 0 | type = ValueType::Type::INT; |
7147 | 0 | sign = ValueType::Sign::UNSIGNED; |
7148 | 0 | } else if (type <= ValueType::Type::LONG && mSettings.platform.isLongValue(unsignedSuffix ? (value >> 1) : value)) |
7149 | 0 | type = ValueType::Type::LONG; |
7150 | 0 | else if (type <= ValueType::Type::LONG && !MathLib::isDec(tokStr) && mSettings.platform.isLongValue(value >> 2)) { |
7151 | 0 | type = ValueType::Type::LONG; |
7152 | 0 | sign = ValueType::Sign::UNSIGNED; |
7153 | 0 | } else if (mSettings.platform.isLongLongValue(unsignedSuffix ? (value >> 1) : value)) |
7154 | 0 | type = ValueType::Type::LONGLONG; |
7155 | 0 | else { |
7156 | 0 | type = ValueType::Type::LONGLONG; |
7157 | 0 | sign = ValueType::Sign::UNSIGNED; |
7158 | 0 | } |
7159 | 38.3k | } |
7160 | | |
7161 | 38.3k | setValueType(tok, ValueType(sign, type, 0U)); |
7162 | 38.3k | } |
7163 | 330k | } else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) { |
7164 | 7.96k | if (mIsCpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { |
7165 | 0 | const Function *function = getOperatorFunction(tok); |
7166 | 0 | if (function) { |
7167 | 0 | ValueType vt; |
7168 | 0 | parsedecl(function->retDef, &vt, mDefaultSignedness, mSettings, mIsCpp); |
7169 | 0 | setValueType(tok, vt); |
7170 | 0 | continue; |
7171 | 0 | } |
7172 | 0 | } |
7173 | 7.96k | setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); |
7174 | 322k | } else if (tok->isBoolean()) { |
7175 | 0 | setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); |
7176 | 322k | } else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) { |
7177 | 0 | nonneg int const pointer = tok->tokType() == Token::eChar ? 0U : 1U; |
7178 | 0 | nonneg int const constness = tok->tokType() == Token::eChar ? 0U : 1U; |
7179 | 0 | ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness); |
7180 | |
|
7181 | 0 | if (mIsCpp && mSettings.standards.cpp >= Standards::CPP20 && tok->isUtf8()) { |
7182 | 0 | valuetype.originalTypeName = "char8_t"; |
7183 | 0 | valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); |
7184 | 0 | } else if (tok->isUtf16()) { |
7185 | 0 | valuetype.originalTypeName = "char16_t"; |
7186 | 0 | valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); |
7187 | 0 | } else if (tok->isUtf32()) { |
7188 | 0 | valuetype.originalTypeName = "char32_t"; |
7189 | 0 | valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); |
7190 | 0 | } else if (tok->isLong()) { |
7191 | 0 | valuetype.originalTypeName = "wchar_t"; |
7192 | 0 | valuetype.type = ValueType::Type::WCHAR_T; |
7193 | 0 | } else if ((tok->tokType() == Token::eChar) && ((tok->isCChar() && !mIsCpp) || (tok->isCMultiChar()))) { |
7194 | 0 | valuetype.type = ValueType::Type::INT; |
7195 | 0 | valuetype.sign = ValueType::Sign::SIGNED; |
7196 | 0 | } |
7197 | 0 | setValueType(tok, valuetype); |
7198 | 322k | } else if (tok->link() && Token::Match(tok, "(|{")) { |
7199 | 29.6k | const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr; |
7200 | | // cast |
7201 | 29.6k | if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) { |
7202 | 0 | ValueType valuetype; |
7203 | 0 | if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, mDefaultSignedness, mSettings, mIsCpp), ")")) |
7204 | 0 | setValueType(tok, valuetype); |
7205 | 0 | } |
7206 | | |
7207 | | // C++ cast |
7208 | 29.6k | else if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) { |
7209 | 0 | ValueType valuetype; |
7210 | 0 | if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, mDefaultSignedness, mSettings, mIsCpp), ">")) |
7211 | 0 | setValueType(tok, valuetype); |
7212 | 0 | } |
7213 | | |
7214 | | // Construct smart pointer |
7215 | 29.6k | else if (mIsCpp && mSettings.library.isSmartPointer(start)) { |
7216 | 0 | ValueType valuetype; |
7217 | 0 | if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings, mIsCpp)) { |
7218 | 0 | setValueType(tok, valuetype); |
7219 | 0 | setValueType(tok->astOperand1(), valuetype); |
7220 | 0 | } |
7221 | |
|
7222 | 0 | } |
7223 | | |
7224 | | // function or lambda |
7225 | 29.6k | else if (const Function* f = getFunction(tok->previous())) { |
7226 | 5.21k | ValueType valuetype; |
7227 | 5.21k | if (parsedecl(f->retDef, &valuetype, mDefaultSignedness, mSettings, mIsCpp)) |
7228 | 5.21k | setValueType(tok, valuetype); |
7229 | 5.21k | } |
7230 | | |
7231 | 24.3k | else if (Token::simpleMatch(tok->previous(), "sizeof (")) { |
7232 | 0 | ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U); |
7233 | 0 | if (mSettings.platform.type == cppcheck::Platform::Type::Win64) |
7234 | 0 | valuetype.type = ValueType::Type::LONGLONG; |
7235 | |
|
7236 | 0 | valuetype.originalTypeName = "size_t"; |
7237 | 0 | setValueType(tok, valuetype); |
7238 | |
|
7239 | 0 | if (Token::Match(tok, "( %type% %type%| *| *| )")) { |
7240 | 0 | ValueType vt; |
7241 | 0 | if (parsedecl(tok->next(), &vt, mDefaultSignedness, mSettings, mIsCpp)) { |
7242 | 0 | setValueType(tok->next(), vt); |
7243 | 0 | } |
7244 | 0 | } |
7245 | 0 | } |
7246 | | |
7247 | | // function style cast |
7248 | 24.3k | else if (tok->previous() && tok->previous()->isStandardType()) { |
7249 | 0 | ValueType valuetype; |
7250 | 0 | if (tok->astOperand1() && valuetype.fromLibraryType(tok->astOperand1()->expressionString(), mSettings)) { |
7251 | 0 | setValueType(tok, valuetype); |
7252 | 0 | continue; |
7253 | 0 | } |
7254 | | |
7255 | 0 | valuetype.type = ValueType::typeFromString(tok->previous()->str(), tok->previous()->isLong()); |
7256 | 0 | if (tok->previous()->isUnsigned()) |
7257 | 0 | valuetype.sign = ValueType::Sign::UNSIGNED; |
7258 | 0 | else if (tok->previous()->isSigned()) |
7259 | 0 | valuetype.sign = ValueType::Sign::SIGNED; |
7260 | 0 | else if (valuetype.isIntegral() && valuetype.type != ValueType::UNKNOWN_INT) |
7261 | 0 | valuetype.sign = mDefaultSignedness; |
7262 | 0 | setValueType(tok, valuetype); |
7263 | 0 | } |
7264 | | |
7265 | | // constructor call |
7266 | 24.3k | else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->isConstructor()) { |
7267 | 0 | ValueType valuetype; |
7268 | 0 | valuetype.type = ValueType::RECORD; |
7269 | 0 | valuetype.typeScope = tok->previous()->function()->token->scope(); |
7270 | 0 | setValueType(tok, valuetype); |
7271 | 0 | } |
7272 | | |
7273 | 24.3k | else if (Token::simpleMatch(tok->previous(), "= {") && tok->tokAt(-2) && tok->tokAt(-2)->valueType()) { |
7274 | 0 | ValueType vt = *tok->tokAt(-2)->valueType(); |
7275 | 0 | setValueType(tok, vt); |
7276 | 0 | } |
7277 | | |
7278 | | // library type/function |
7279 | 24.3k | else if (tok->previous()) { |
7280 | | // Aggregate constructor |
7281 | 24.3k | if (Token::Match(tok->previous(), "%name%")) { |
7282 | 9.41k | ValueType valuetype; |
7283 | 9.41k | if (parsedecl(tok->previous(), &valuetype, mDefaultSignedness, mSettings, mIsCpp)) { |
7284 | 1.74k | if (valuetype.typeScope) { |
7285 | 0 | setValueType(tok, valuetype); |
7286 | 0 | continue; |
7287 | 0 | } |
7288 | 1.74k | } |
7289 | 9.41k | } |
7290 | 24.3k | if (mIsCpp && tok->astParent() && Token::Match(tok->astOperand1(), "%name%|::")) { |
7291 | 0 | const Token *typeStartToken = tok->astOperand1(); |
7292 | 0 | while (typeStartToken && typeStartToken->str() == "::") |
7293 | 0 | typeStartToken = typeStartToken->astOperand1(); |
7294 | 0 | if (mSettings.library.detectContainerOrIterator(typeStartToken) || |
7295 | 0 | mSettings.library.detectSmartPointer(typeStartToken)) { |
7296 | 0 | ValueType vt; |
7297 | 0 | if (parsedecl(typeStartToken, &vt, mDefaultSignedness, mSettings, mIsCpp)) { |
7298 | 0 | setValueType(tok, vt); |
7299 | 0 | continue; |
7300 | 0 | } |
7301 | 0 | } |
7302 | | |
7303 | 0 | const std::string e = tok->astOperand1()->expressionString(); |
7304 | |
|
7305 | 0 | if ((e == "std::make_shared" || e == "std::make_unique") && Token::Match(tok->astOperand1(), ":: %name% < %name%")) { |
7306 | 0 | ValueType vt; |
7307 | 0 | parsedecl(tok->astOperand1()->tokAt(3), &vt, mDefaultSignedness, mSettings, mIsCpp); |
7308 | 0 | if (vt.typeScope) { |
7309 | 0 | vt.smartPointerType = vt.typeScope->definedType; |
7310 | 0 | vt.typeScope = nullptr; |
7311 | 0 | } |
7312 | 0 | if (e == "std::make_shared" && mSettings.library.smartPointers.count("std::shared_ptr") > 0) |
7313 | 0 | vt.smartPointer = &mSettings.library.smartPointers.at("std::shared_ptr"); |
7314 | 0 | if (e == "std::make_unique" && mSettings.library.smartPointers.count("std::unique_ptr") > 0) |
7315 | 0 | vt.smartPointer = &mSettings.library.smartPointers.at("std::unique_ptr"); |
7316 | 0 | vt.type = ValueType::Type::SMART_POINTER; |
7317 | 0 | vt.smartPointerTypeToken = tok->astOperand1()->tokAt(3); |
7318 | 0 | setValueType(tok, vt); |
7319 | 0 | continue; |
7320 | 0 | } |
7321 | | |
7322 | 0 | ValueType podtype; |
7323 | 0 | if (podtype.fromLibraryType(e, mSettings)) { |
7324 | 0 | setValueType(tok, podtype); |
7325 | 0 | continue; |
7326 | 0 | } |
7327 | 0 | } |
7328 | | |
7329 | 24.3k | const std::string& typestr(mSettings.library.returnValueType(tok->previous())); |
7330 | 24.3k | if (!typestr.empty()) { |
7331 | 0 | ValueType valuetype; |
7332 | 0 | TokenList tokenList(&mSettings); |
7333 | 0 | std::istringstream istr(typestr+";"); |
7334 | 0 | tokenList.createTokens(istr); |
7335 | 0 | tokenList.simplifyStdType(); |
7336 | 0 | if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings, mIsCpp)) { |
7337 | 0 | valuetype.originalTypeName = typestr; |
7338 | 0 | setValueType(tok, valuetype); |
7339 | 0 | } |
7340 | 0 | } |
7341 | | |
7342 | | //Is iterator fetching function invoked on container? |
7343 | 24.3k | const bool isReturnIter = typestr == "iterator"; |
7344 | 24.3k | if (typestr.empty() || isReturnIter) { |
7345 | 24.3k | if (Token::simpleMatch(tok->astOperand1(), ".") && |
7346 | 24.3k | tok->astOperand1()->astOperand1() && |
7347 | 24.3k | tok->astOperand1()->astOperand2() && |
7348 | 24.3k | tok->astOperand1()->astOperand1()->valueType() && |
7349 | 24.3k | tok->astOperand1()->astOperand1()->valueType()->container) { |
7350 | 0 | const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container; |
7351 | 0 | const auto it = cont->functions.find(tok->astOperand1()->astOperand2()->str()); |
7352 | 0 | if (it != cont->functions.end()) { |
7353 | 0 | if (it->second.yield == Library::Container::Yield::START_ITERATOR || |
7354 | 0 | it->second.yield == Library::Container::Yield::END_ITERATOR || |
7355 | 0 | it->second.yield == Library::Container::Yield::ITERATOR) { |
7356 | 0 | ValueType vt; |
7357 | 0 | vt.type = ValueType::Type::ITERATOR; |
7358 | 0 | vt.container = cont; |
7359 | 0 | vt.containerTypeToken = |
7360 | 0 | tok->astOperand1()->astOperand1()->valueType()->containerTypeToken; |
7361 | 0 | setValueType(tok, vt); |
7362 | 0 | continue; |
7363 | 0 | } |
7364 | 0 | } |
7365 | | //Is iterator fetching function called? |
7366 | 24.3k | } else if (Token::simpleMatch(tok->astOperand1(), "::") && |
7367 | 24.3k | tok->astOperand2() && |
7368 | 24.3k | tok->astOperand2()->isVariable()) { |
7369 | 0 | const auto* const paramVariable = tok->astOperand2()->variable(); |
7370 | 0 | if (!paramVariable || |
7371 | 0 | !paramVariable->valueType() || |
7372 | 0 | !paramVariable->valueType()->container) { |
7373 | 0 | continue; |
7374 | 0 | } |
7375 | | |
7376 | 0 | const auto yield = astFunctionYield(tok->previous(), &mSettings); |
7377 | 0 | if (yield == Library::Container::Yield::START_ITERATOR || |
7378 | 0 | yield == Library::Container::Yield::END_ITERATOR || |
7379 | 0 | yield == Library::Container::Yield::ITERATOR) { |
7380 | 0 | ValueType vt; |
7381 | 0 | vt.type = ValueType::Type::ITERATOR; |
7382 | 0 | vt.container = paramVariable->valueType()->container; |
7383 | 0 | vt.containerTypeToken = paramVariable->valueType()->containerTypeToken; |
7384 | 0 | setValueType(tok, vt); |
7385 | 0 | } |
7386 | 0 | } |
7387 | 24.3k | if (isReturnIter) { |
7388 | 0 | const std::vector<const Token*> args = getArguments(tok); |
7389 | 0 | if (!args.empty()) { |
7390 | 0 | const Library::ArgumentChecks::IteratorInfo* info = mSettings.library.getArgIteratorInfo(tok->previous(), 1); |
7391 | 0 | if (info && info->it) { |
7392 | 0 | const Token* contTok = args[0]; |
7393 | 0 | if (Token::simpleMatch(args[0]->astOperand1(), ".") && args[0]->astOperand1()->astOperand1()) // .begin() |
7394 | 0 | contTok = args[0]->astOperand1()->astOperand1(); |
7395 | 0 | else if (Token::simpleMatch(args[0], "(") && args[0]->astOperand2()) // std::begin() |
7396 | 0 | contTok = args[0]->astOperand2(); |
7397 | 0 | while (Token::simpleMatch(contTok, "[")) // move to container token |
7398 | 0 | contTok = contTok->astOperand1(); |
7399 | 0 | if (Token::simpleMatch(contTok, ".")) |
7400 | 0 | contTok = contTok->astOperand2(); |
7401 | 0 | if (contTok && contTok->variable() && contTok->variable()->valueType() && contTok->variable()->valueType()->container) { |
7402 | 0 | ValueType vt; |
7403 | 0 | vt.type = ValueType::Type::ITERATOR; |
7404 | 0 | vt.container = contTok->variable()->valueType()->container; |
7405 | 0 | vt.containerTypeToken = contTok->variable()->valueType()->containerTypeToken; |
7406 | 0 | setValueType(tok, vt); |
7407 | 0 | } else if (Token::simpleMatch(contTok, "(") && contTok->astOperand1() && contTok->astOperand1()->function()) { |
7408 | 0 | const Function* func = contTok->astOperand1()->function(); |
7409 | 0 | if (const ValueType* funcVt = func->tokenDef->next()->valueType()) { |
7410 | 0 | ValueType vt; |
7411 | 0 | vt.type = ValueType::Type::ITERATOR; |
7412 | 0 | vt.container = funcVt->container; |
7413 | 0 | vt.containerTypeToken = funcVt->containerTypeToken; |
7414 | 0 | setValueType(tok, vt); |
7415 | 0 | } |
7416 | 0 | } |
7417 | 0 | } |
7418 | 0 | } |
7419 | 0 | } |
7420 | 24.3k | continue; |
7421 | 24.3k | } |
7422 | 0 | TokenList tokenList(&mSettings); |
7423 | 0 | std::istringstream istr(typestr+";"); |
7424 | 0 | if (tokenList.createTokens(istr)) { |
7425 | 0 | ValueType vt; |
7426 | 0 | tokenList.simplifyPlatformTypes(); |
7427 | 0 | tokenList.simplifyStdType(); |
7428 | 0 | if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings, mIsCpp)) { |
7429 | 0 | vt.originalTypeName = typestr; |
7430 | 0 | setValueType(tok, vt); |
7431 | 0 | } |
7432 | 0 | } |
7433 | 0 | } |
7434 | 293k | } else if (tok->str() == "return") { |
7435 | 7.66k | const Scope *functionScope = tok->scope(); |
7436 | 8.82k | while (functionScope && functionScope->isExecutable() && functionScope->type != Scope::eLambda && functionScope->type != Scope::eFunction) |
7437 | 1.16k | functionScope = functionScope->nestedIn; |
7438 | 7.66k | if (functionScope && functionScope->type == Scope::eFunction && functionScope->function && |
7439 | 7.66k | functionScope->function->retDef) { |
7440 | 7.66k | ValueType vt = ValueType::parseDecl(functionScope->function->retDef, mSettings); |
7441 | 7.66k | setValueType(tok, vt); |
7442 | 7.66k | if (Token::simpleMatch(tok, "return {")) |
7443 | 0 | setValueType(tok->next(), vt); |
7444 | 7.66k | } |
7445 | 285k | } else if (tok->variable()) { |
7446 | 79.3k | setValueType(tok, *tok->variable()); |
7447 | 79.3k | if (!tok->variable()->valueType() && tok->valueType()) |
7448 | 0 | const_cast<Variable*>(tok->variable())->setValueType(*tok->valueType()); |
7449 | 206k | } else if (tok->enumerator()) { |
7450 | 0 | setValueType(tok, *tok->enumerator()); |
7451 | 206k | } else if (tok->isKeyword() && tok->str() == "new") { |
7452 | 0 | const Token *typeTok = tok->next(); |
7453 | 0 | if (Token::Match(typeTok, "( std| ::| nothrow )")) |
7454 | 0 | typeTok = typeTok->link()->next(); |
7455 | 0 | bool isIterator = false; |
7456 | 0 | if (const Library::Container* c = mSettings.library.detectContainerOrIterator(typeTok, &isIterator)) { |
7457 | 0 | ValueType vt; |
7458 | 0 | vt.pointer = 1; |
7459 | 0 | vt.container = c; |
7460 | 0 | vt.type = isIterator ? ValueType::Type::ITERATOR : ValueType::Type::CONTAINER; |
7461 | 0 | setValueType(tok, vt); |
7462 | 0 | continue; |
7463 | 0 | } |
7464 | 0 | std::string typestr; |
7465 | 0 | while (Token::Match(typeTok, "%name% :: %name%")) { |
7466 | 0 | typestr += typeTok->str() + "::"; |
7467 | 0 | typeTok = typeTok->tokAt(2); |
7468 | 0 | } |
7469 | 0 | if (!Token::Match(typeTok, "%type% ;|[|(")) |
7470 | 0 | continue; |
7471 | 0 | typestr += typeTok->str(); |
7472 | 0 | ValueType vt; |
7473 | 0 | vt.pointer = 1; |
7474 | 0 | if (typeTok->type() && typeTok->type()->classScope) { |
7475 | 0 | vt.type = ValueType::Type::RECORD; |
7476 | 0 | vt.typeScope = typeTok->type()->classScope; |
7477 | 0 | } else { |
7478 | 0 | vt.type = ValueType::typeFromString(typestr, typeTok->isLong()); |
7479 | 0 | if (vt.type == ValueType::Type::UNKNOWN_TYPE) |
7480 | 0 | vt.fromLibraryType(typestr, mSettings); |
7481 | 0 | if (vt.type == ValueType::Type::UNKNOWN_TYPE) |
7482 | 0 | continue; |
7483 | 0 | if (typeTok->isUnsigned()) |
7484 | 0 | vt.sign = ValueType::Sign::UNSIGNED; |
7485 | 0 | else if (typeTok->isSigned()) |
7486 | 0 | vt.sign = ValueType::Sign::SIGNED; |
7487 | 0 | if (vt.sign == ValueType::Sign::UNKNOWN_SIGN && vt.isIntegral()) |
7488 | 0 | vt.sign = (vt.type == ValueType::Type::CHAR) ? mDefaultSignedness : ValueType::Sign::SIGNED; |
7489 | 0 | } |
7490 | 0 | setValueType(tok, vt); |
7491 | 0 | if (Token::simpleMatch(tok->astOperand1(), "(")) { |
7492 | 0 | vt.pointer--; |
7493 | 0 | setValueType(tok->astOperand1(), vt); |
7494 | 0 | } |
7495 | 206k | } else if (tok->isKeyword() && tok->str() == "return" && tok->scope()) { |
7496 | 0 | const Scope* fscope = tok->scope(); |
7497 | 0 | while (fscope && !fscope->function) |
7498 | 0 | fscope = fscope->nestedIn; |
7499 | 0 | if (fscope && fscope->function && fscope->function->retDef) { |
7500 | 0 | ValueType vt; |
7501 | 0 | parsedecl(fscope->function->retDef, &vt, mDefaultSignedness, mSettings, mIsCpp); |
7502 | 0 | setValueType(tok, vt); |
7503 | 0 | } |
7504 | 206k | } else if (tok->isKeyword() && tok->str() == "this" && tok->scope()->isExecutable()) { |
7505 | 0 | const Scope* fscope = tok->scope(); |
7506 | 0 | while (fscope && !fscope->function) |
7507 | 0 | fscope = fscope->nestedIn; |
7508 | 0 | const Scope* defScope = fscope && fscope->function->tokenDef ? fscope->function->tokenDef->scope() : nullptr; |
7509 | 0 | if (defScope && defScope->isClassOrStruct()) { |
7510 | 0 | ValueType vt(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::RECORD, 1); |
7511 | 0 | vt.typeScope = defScope; |
7512 | 0 | if (fscope->function->isConst()) |
7513 | 0 | vt.constness = 1; |
7514 | 0 | setValueType(tok, vt); |
7515 | 0 | } |
7516 | 0 | } |
7517 | 368k | } |
7518 | | |
7519 | 5.44k | if (reportDebugWarnings && mSettings.debugwarnings) { |
7520 | 0 | for (Token *tok = tokens; tok; tok = tok->next()) { |
7521 | 0 | if (tok->str() == "auto" && !tok->valueType()) { |
7522 | 0 | if (Token::Match(tok->next(), "%name% ; %name% = [") && isLambdaCaptureList(tok->tokAt(5))) |
7523 | 0 | continue; |
7524 | 0 | if (Token::Match(tok->next(), "%name% {|= [") && isLambdaCaptureList(tok->tokAt(3))) |
7525 | 0 | continue; |
7526 | 0 | debugMessage(tok, "autoNoType", "auto token with no type."); |
7527 | 0 | } |
7528 | 0 | } |
7529 | 0 | } |
7530 | | |
7531 | | // Update functions with new type information. |
7532 | 5.44k | createSymbolDatabaseSetFunctionPointers(false); |
7533 | | |
7534 | | // Update auto variables with new type information. |
7535 | 5.44k | createSymbolDatabaseSetVariablePointers(); |
7536 | 5.44k | } |
7537 | | |
7538 | | ValueType ValueType::parseDecl(const Token *type, const Settings &settings) |
7539 | 14.4k | { |
7540 | 14.4k | ValueType vt; |
7541 | 14.4k | parsedecl(type, &vt, settings.platform.defaultSign == 'u' ? Sign::UNSIGNED : Sign::SIGNED, settings, type->isCpp()); |
7542 | 14.4k | return vt; |
7543 | 14.4k | } |
7544 | | |
7545 | | ValueType::Type ValueType::typeFromString(const std::string &typestr, bool longType) |
7546 | 213k | { |
7547 | 213k | if (typestr == "void") |
7548 | 0 | return ValueType::Type::VOID; |
7549 | 213k | if (typestr == "bool" || typestr == "_Bool") |
7550 | 0 | return ValueType::Type::BOOL; |
7551 | 213k | if (typestr== "char") |
7552 | 0 | return ValueType::Type::CHAR; |
7553 | 213k | if (typestr == "short") |
7554 | 0 | return ValueType::Type::SHORT; |
7555 | 213k | if (typestr == "wchar_t") |
7556 | 0 | return ValueType::Type::WCHAR_T; |
7557 | 213k | if (typestr == "int") |
7558 | 201k | return ValueType::Type::INT; |
7559 | 11.6k | if (typestr == "long") |
7560 | 0 | return longType ? ValueType::Type::LONGLONG : ValueType::Type::LONG; |
7561 | 11.6k | if (typestr == "float") |
7562 | 0 | return ValueType::Type::FLOAT; |
7563 | 11.6k | if (typestr == "double") |
7564 | 0 | return longType ? ValueType::Type::LONGDOUBLE : ValueType::Type::DOUBLE; |
7565 | 11.6k | return ValueType::Type::UNKNOWN_TYPE; |
7566 | 11.6k | } |
7567 | | |
7568 | | bool ValueType::fromLibraryType(const std::string &typestr, const Settings &settings) |
7569 | 11.3k | { |
7570 | 11.3k | const Library::PodType* podtype = settings.library.podtype(typestr); |
7571 | 11.3k | if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { |
7572 | 0 | if (podtype->size == 1) |
7573 | 0 | type = ValueType::Type::CHAR; |
7574 | 0 | else if (podtype->size == settings.platform.sizeof_int) |
7575 | 0 | type = ValueType::Type::INT; |
7576 | 0 | else if (podtype->size == settings.platform.sizeof_short) |
7577 | 0 | type = ValueType::Type::SHORT; |
7578 | 0 | else if (podtype->size == settings.platform.sizeof_long) |
7579 | 0 | type = ValueType::Type::LONG; |
7580 | 0 | else if (podtype->size == settings.platform.sizeof_long_long) |
7581 | 0 | type = ValueType::Type::LONGLONG; |
7582 | 0 | else if (podtype->stdtype == Library::PodType::Type::BOOL) |
7583 | 0 | type = ValueType::Type::BOOL; |
7584 | 0 | else if (podtype->stdtype == Library::PodType::Type::CHAR) |
7585 | 0 | type = ValueType::Type::CHAR; |
7586 | 0 | else if (podtype->stdtype == Library::PodType::Type::SHORT) |
7587 | 0 | type = ValueType::Type::SHORT; |
7588 | 0 | else if (podtype->stdtype == Library::PodType::Type::INT) |
7589 | 0 | type = ValueType::Type::INT; |
7590 | 0 | else if (podtype->stdtype == Library::PodType::Type::LONG) |
7591 | 0 | type = ValueType::Type::LONG; |
7592 | 0 | else if (podtype->stdtype == Library::PodType::Type::LONGLONG) |
7593 | 0 | type = ValueType::Type::LONGLONG; |
7594 | 0 | else |
7595 | 0 | type = ValueType::Type::UNKNOWN_INT; |
7596 | 0 | sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; |
7597 | 0 | return true; |
7598 | 0 | } |
7599 | 11.3k | if (podtype && podtype->stdtype == Library::PodType::Type::NO) { |
7600 | 0 | type = ValueType::Type::POD; |
7601 | 0 | sign = ValueType::UNKNOWN_SIGN; |
7602 | 0 | return true; |
7603 | 0 | } |
7604 | | |
7605 | 11.3k | const Library::PlatformType *platformType = settings.library.platform_type(typestr, settings.platform.toString()); |
7606 | 11.3k | if (platformType) { |
7607 | 0 | if (platformType->mType == "char") |
7608 | 0 | type = ValueType::Type::CHAR; |
7609 | 0 | else if (platformType->mType == "short") |
7610 | 0 | type = ValueType::Type::SHORT; |
7611 | 0 | else if (platformType->mType == "wchar_t") |
7612 | 0 | type = ValueType::Type::WCHAR_T; |
7613 | 0 | else if (platformType->mType == "int") |
7614 | 0 | type = platformType->mLong ? ValueType::Type::LONG : ValueType::Type::INT; |
7615 | 0 | else if (platformType->mType == "long") |
7616 | 0 | type = platformType->mLong ? ValueType::Type::LONGLONG : ValueType::Type::LONG; |
7617 | 0 | if (platformType->mSigned) |
7618 | 0 | sign = ValueType::SIGNED; |
7619 | 0 | else if (platformType->mUnsigned) |
7620 | 0 | sign = ValueType::UNSIGNED; |
7621 | 0 | if (platformType->mPointer) |
7622 | 0 | pointer = 1; |
7623 | 0 | if (platformType->mPtrPtr) |
7624 | 0 | pointer = 2; |
7625 | 0 | if (platformType->mConstPtr) |
7626 | 0 | constness = 1; |
7627 | 0 | return true; |
7628 | 0 | } |
7629 | 11.3k | if (!podtype && (typestr == "size_t" || typestr == "std::size_t")) { |
7630 | 0 | originalTypeName = "size_t"; |
7631 | 0 | sign = ValueType::UNSIGNED; |
7632 | 0 | if (settings.platform.sizeof_size_t == settings.platform.sizeof_long) |
7633 | 0 | type = ValueType::Type::LONG; |
7634 | 0 | else if (settings.platform.sizeof_size_t == settings.platform.sizeof_long_long) |
7635 | 0 | type = ValueType::Type::LONGLONG; |
7636 | 0 | else if (settings.platform.sizeof_size_t == settings.platform.sizeof_int) |
7637 | 0 | type = ValueType::Type::INT; |
7638 | 0 | else |
7639 | 0 | type = ValueType::Type::UNKNOWN_INT; |
7640 | 0 | return true; |
7641 | 0 | } |
7642 | | |
7643 | 11.3k | return false; |
7644 | 11.3k | } |
7645 | | |
7646 | | std::string ValueType::dump() const |
7647 | 0 | { |
7648 | 0 | std::string ret; |
7649 | 0 | switch (type) { |
7650 | 0 | case UNKNOWN_TYPE: |
7651 | 0 | return ""; |
7652 | 0 | case NONSTD: |
7653 | 0 | ret += "valueType-type=\"nonstd\""; |
7654 | 0 | break; |
7655 | 0 | case POD: |
7656 | 0 | ret += "valueType-type=\"pod\""; |
7657 | 0 | break; |
7658 | 0 | case RECORD: |
7659 | 0 | ret += "valueType-type=\"record\""; |
7660 | 0 | break; |
7661 | 0 | case SMART_POINTER: |
7662 | 0 | ret += "valueType-type=\"smart-pointer\""; |
7663 | 0 | break; |
7664 | 0 | case CONTAINER: { |
7665 | 0 | ret += "valueType-type=\"container\""; |
7666 | 0 | ret += " valueType-containerId=\""; |
7667 | 0 | ret += id_string(container); |
7668 | 0 | ret += "\""; |
7669 | 0 | break; |
7670 | 0 | } |
7671 | 0 | case ITERATOR: |
7672 | 0 | ret += "valueType-type=\"iterator\""; |
7673 | 0 | break; |
7674 | 0 | case VOID: |
7675 | 0 | ret += "valueType-type=\"void\""; |
7676 | 0 | break; |
7677 | 0 | case BOOL: |
7678 | 0 | ret += "valueType-type=\"bool\""; |
7679 | 0 | break; |
7680 | 0 | case CHAR: |
7681 | 0 | ret += "valueType-type=\"char\""; |
7682 | 0 | break; |
7683 | 0 | case SHORT: |
7684 | 0 | ret += "valueType-type=\"short\""; |
7685 | 0 | break; |
7686 | 0 | case WCHAR_T: |
7687 | 0 | ret += "valueType-type=\"wchar_t\""; |
7688 | 0 | break; |
7689 | 0 | case INT: |
7690 | 0 | ret += "valueType-type=\"int\""; |
7691 | 0 | break; |
7692 | 0 | case LONG: |
7693 | 0 | ret += "valueType-type=\"long\""; |
7694 | 0 | break; |
7695 | 0 | case LONGLONG: |
7696 | 0 | ret += "valueType-type=\"long long\""; |
7697 | 0 | break; |
7698 | 0 | case UNKNOWN_INT: |
7699 | 0 | ret += "valueType-type=\"unknown int\""; |
7700 | 0 | break; |
7701 | 0 | case FLOAT: |
7702 | 0 | ret += "valueType-type=\"float\""; |
7703 | 0 | break; |
7704 | 0 | case DOUBLE: |
7705 | 0 | ret += "valueType-type=\"double\""; |
7706 | 0 | break; |
7707 | 0 | case LONGDOUBLE: |
7708 | 0 | ret += "valueType-type=\"long double\""; |
7709 | 0 | break; |
7710 | 0 | } |
7711 | | |
7712 | 0 | switch (sign) { |
7713 | 0 | case Sign::UNKNOWN_SIGN: |
7714 | 0 | break; |
7715 | 0 | case Sign::SIGNED: |
7716 | 0 | ret += " valueType-sign=\"signed\""; |
7717 | 0 | break; |
7718 | 0 | case Sign::UNSIGNED: |
7719 | 0 | ret += " valueType-sign=\"unsigned\""; |
7720 | 0 | break; |
7721 | 0 | } |
7722 | | |
7723 | 0 | if (bits > 0) { |
7724 | 0 | ret += " valueType-bits=\""; |
7725 | 0 | ret += std::to_string(bits); |
7726 | 0 | ret += '\"'; |
7727 | 0 | } |
7728 | |
|
7729 | 0 | if (pointer > 0) { |
7730 | 0 | ret += " valueType-pointer=\""; |
7731 | 0 | ret += std::to_string(pointer); |
7732 | 0 | ret += '\"'; |
7733 | 0 | } |
7734 | |
|
7735 | 0 | if (constness > 0) { |
7736 | 0 | ret += " valueType-constness=\""; |
7737 | 0 | ret += std::to_string(constness); |
7738 | 0 | ret += '\"'; |
7739 | 0 | } |
7740 | |
|
7741 | 0 | if (reference == Reference::None) |
7742 | 0 | ret += " valueType-reference=\"None\""; |
7743 | 0 | else if (reference == Reference::LValue) |
7744 | 0 | ret += " valueType-reference=\"LValue\""; |
7745 | 0 | else if (reference == Reference::RValue) |
7746 | 0 | ret += " valueType-reference=\"RValue\""; |
7747 | |
|
7748 | 0 | if (typeScope) { |
7749 | 0 | ret += " valueType-typeScope=\""; |
7750 | 0 | ret += id_string(typeScope); |
7751 | 0 | ret += '\"'; |
7752 | 0 | } |
7753 | |
|
7754 | 0 | if (!originalTypeName.empty()) { |
7755 | 0 | ret += " valueType-originalTypeName=\""; |
7756 | 0 | ret += ErrorLogger::toxml(originalTypeName); |
7757 | 0 | ret += '\"'; |
7758 | 0 | } |
7759 | |
|
7760 | 0 | return ret; |
7761 | 0 | } |
7762 | | |
7763 | | bool ValueType::isConst(nonneg int indirect) const |
7764 | 5.31k | { |
7765 | 5.31k | if (indirect > pointer) |
7766 | 86 | return false; |
7767 | 5.23k | return constness & (1 << (pointer - indirect)); |
7768 | 5.31k | } |
7769 | | |
7770 | | MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform, bool p) const |
7771 | 0 | { |
7772 | 0 | if (p && pointer) |
7773 | 0 | return platform.sizeof_pointer; |
7774 | | |
7775 | 0 | if (typeScope && typeScope->definedType && typeScope->definedType->sizeOf) |
7776 | 0 | return typeScope->definedType->sizeOf; |
7777 | | |
7778 | 0 | switch (type) { |
7779 | 0 | case ValueType::Type::BOOL: |
7780 | 0 | return platform.sizeof_bool; |
7781 | 0 | case ValueType::Type::CHAR: |
7782 | 0 | return 1; |
7783 | 0 | case ValueType::Type::SHORT: |
7784 | 0 | return platform.sizeof_short; |
7785 | 0 | case ValueType::Type::WCHAR_T: |
7786 | 0 | return platform.sizeof_wchar_t; |
7787 | 0 | case ValueType::Type::INT: |
7788 | 0 | return platform.sizeof_int; |
7789 | 0 | case ValueType::Type::LONG: |
7790 | 0 | return platform.sizeof_long; |
7791 | 0 | case ValueType::Type::LONGLONG: |
7792 | 0 | return platform.sizeof_long_long; |
7793 | 0 | case ValueType::Type::FLOAT: |
7794 | 0 | return platform.sizeof_float; |
7795 | 0 | case ValueType::Type::DOUBLE: |
7796 | 0 | return platform.sizeof_double; |
7797 | 0 | case ValueType::Type::LONGDOUBLE: |
7798 | 0 | return platform.sizeof_long_double; |
7799 | 0 | default: |
7800 | 0 | break; |
7801 | 0 | } |
7802 | | |
7803 | | // Unknown invalid size |
7804 | 0 | return 0; |
7805 | 0 | } |
7806 | | |
7807 | | bool ValueType::isTypeEqual(const ValueType* that) const |
7808 | 0 | { |
7809 | 0 | if (!that) |
7810 | 0 | return false; |
7811 | 0 | auto tie = [](const ValueType* vt) { |
7812 | 0 | return std::tie(vt->type, vt->container, vt->pointer, vt->typeScope, vt->smartPointer); |
7813 | 0 | }; |
7814 | 0 | return tie(this) == tie(that); |
7815 | 0 | } |
7816 | | |
7817 | | std::string ValueType::str() const |
7818 | 0 | { |
7819 | 0 | std::string ret; |
7820 | 0 | if (constness & 1) |
7821 | 0 | ret = " const"; |
7822 | 0 | if (type == VOID) |
7823 | 0 | ret += " void"; |
7824 | 0 | else if (isIntegral()) { |
7825 | 0 | if (sign == SIGNED) |
7826 | 0 | ret += " signed"; |
7827 | 0 | else if (sign == UNSIGNED) |
7828 | 0 | ret += " unsigned"; |
7829 | 0 | if (type == BOOL) |
7830 | 0 | ret += " bool"; |
7831 | 0 | else if (type == CHAR) |
7832 | 0 | ret += " char"; |
7833 | 0 | else if (type == SHORT) |
7834 | 0 | ret += " short"; |
7835 | 0 | else if (type == WCHAR_T) |
7836 | 0 | ret += " wchar_t"; |
7837 | 0 | else if (type == INT) |
7838 | 0 | ret += " int"; |
7839 | 0 | else if (type == LONG) |
7840 | 0 | ret += " long"; |
7841 | 0 | else if (type == LONGLONG) |
7842 | 0 | ret += " long long"; |
7843 | 0 | else if (type == UNKNOWN_INT) |
7844 | 0 | ret += " unknown_int"; |
7845 | 0 | } else if (type == FLOAT) |
7846 | 0 | ret += " float"; |
7847 | 0 | else if (type == DOUBLE) |
7848 | 0 | ret += " double"; |
7849 | 0 | else if (type == LONGDOUBLE) |
7850 | 0 | ret += " long double"; |
7851 | 0 | else if ((type == ValueType::Type::NONSTD || type == ValueType::Type::RECORD) && typeScope) { |
7852 | 0 | std::string className(typeScope->className); |
7853 | 0 | const Scope *scope = typeScope->definedType ? typeScope->definedType->enclosingScope : typeScope->nestedIn; |
7854 | 0 | while (scope && scope->type != Scope::eGlobal) { |
7855 | 0 | if (scope->type == Scope::eClass || scope->type == Scope::eStruct || scope->type == Scope::eNamespace) |
7856 | 0 | className = scope->className + "::" + className; |
7857 | 0 | scope = (scope->definedType && scope->definedType->enclosingScope) ? scope->definedType->enclosingScope : scope->nestedIn; |
7858 | 0 | } |
7859 | 0 | ret += ' ' + className; |
7860 | 0 | } else if (type == ValueType::Type::CONTAINER && container) { |
7861 | 0 | ret += " container(" + container->startPattern + ')'; |
7862 | 0 | } else if (type == ValueType::Type::ITERATOR && container) { |
7863 | 0 | ret += " iterator(" + container->startPattern + ')'; |
7864 | 0 | } else if (type == ValueType::Type::SMART_POINTER && smartPointer) { |
7865 | 0 | ret += " smart-pointer(" + smartPointer->name + ")"; |
7866 | 0 | } |
7867 | 0 | for (unsigned int p = 0; p < pointer; p++) { |
7868 | 0 | ret += " *"; |
7869 | 0 | if (constness & (2 << p)) |
7870 | 0 | ret += " const"; |
7871 | 0 | } |
7872 | 0 | if (reference == Reference::LValue) |
7873 | 0 | ret += " &"; |
7874 | 0 | else if (reference == Reference::RValue) |
7875 | 0 | ret += " &&"; |
7876 | 0 | return ret.empty() ? ret : ret.substr(1); |
7877 | 0 | } |
7878 | | |
7879 | | void ValueType::setDebugPath(const Token* tok, SourceLocation ctx, SourceLocation local) |
7880 | 0 | { |
7881 | 0 | std::string file = ctx.file_name(); |
7882 | 0 | if (file.empty()) |
7883 | 0 | return; |
7884 | 0 | std::string s = Path::stripDirectoryPart(file) + ":" + std::to_string(ctx.line()) + ": " + ctx.function_name() + |
7885 | 0 | " => " + local.function_name(); |
7886 | 0 | debugPath.emplace_back(tok, std::move(s)); |
7887 | 0 | } |
7888 | | |
7889 | | ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const ValueType *func) |
7890 | 0 | { |
7891 | 0 | if (!call || !func) |
7892 | 0 | return ValueType::MatchResult::UNKNOWN; |
7893 | 0 | if (call->pointer != func->pointer) { |
7894 | 0 | if (call->pointer > 1 && func->pointer == 1 && func->type == ValueType::Type::VOID) |
7895 | 0 | return ValueType::MatchResult::FALLBACK1; |
7896 | 0 | if (call->pointer == 1 && func->pointer == 0 && func->isIntegral() && func->sign != ValueType::Sign::SIGNED) |
7897 | 0 | return ValueType::MatchResult::FALLBACK1; |
7898 | 0 | if (call->pointer == 1 && call->type == ValueType::Type::CHAR && func->pointer == 0 && func->container && func->container->stdStringLike) |
7899 | 0 | return ValueType::MatchResult::FALLBACK2; |
7900 | 0 | return ValueType::MatchResult::NOMATCH; // TODO |
7901 | 0 | } |
7902 | 0 | if (call->pointer > 0) { |
7903 | 0 | if ((call->constness | func->constness) != func->constness) |
7904 | 0 | return ValueType::MatchResult::NOMATCH; |
7905 | 0 | if (call->constness == 0 && func->constness != 0 && func->reference != Reference::None) |
7906 | 0 | return ValueType::MatchResult::NOMATCH; |
7907 | 0 | } |
7908 | 0 | if (call->type != func->type || (call->isEnum() && !func->isEnum())) { |
7909 | 0 | if (call->type == ValueType::Type::VOID || func->type == ValueType::Type::VOID) |
7910 | 0 | return ValueType::MatchResult::FALLBACK1; |
7911 | 0 | if (call->pointer > 0) |
7912 | 0 | return func->type == ValueType::UNKNOWN_TYPE ? ValueType::MatchResult::UNKNOWN : ValueType::MatchResult::NOMATCH; |
7913 | 0 | if (call->isIntegral() && func->isIntegral()) |
7914 | 0 | return call->type < func->type ? |
7915 | 0 | ValueType::MatchResult::FALLBACK1 : |
7916 | 0 | ValueType::MatchResult::FALLBACK2; |
7917 | 0 | if (call->isFloat() && func->isFloat()) |
7918 | 0 | return ValueType::MatchResult::FALLBACK1; |
7919 | 0 | if (call->isIntegral() && func->isFloat()) |
7920 | 0 | return ValueType::MatchResult::FALLBACK2; |
7921 | 0 | if (call->isFloat() && func->isIntegral()) |
7922 | 0 | return ValueType::MatchResult::FALLBACK2; |
7923 | 0 | return ValueType::MatchResult::UNKNOWN; // TODO |
7924 | 0 | } |
7925 | | |
7926 | 0 | if (call->typeScope != nullptr || func->typeScope != nullptr) { |
7927 | 0 | if (call->typeScope != func->typeScope && |
7928 | 0 | !(call->typeScope && func->typeScope && call->typeScope->definedType && call->typeScope->definedType->isDerivedFrom(func->typeScope->className))) |
7929 | 0 | return ValueType::MatchResult::NOMATCH; |
7930 | 0 | } |
7931 | | |
7932 | 0 | if (call->container != nullptr || func->container != nullptr) { |
7933 | 0 | if (call->container != func->container) |
7934 | 0 | return ValueType::MatchResult::NOMATCH; |
7935 | 0 | } |
7936 | | |
7937 | 0 | if (func->typeScope != nullptr && func->container != nullptr) { |
7938 | 0 | if (func->type < ValueType::Type::VOID || func->type == ValueType::Type::UNKNOWN_INT) |
7939 | 0 | return ValueType::MatchResult::UNKNOWN; |
7940 | 0 | } |
7941 | | |
7942 | 0 | if (call->isIntegral() && func->isIntegral() && call->sign != ValueType::Sign::UNKNOWN_SIGN && func->sign != ValueType::Sign::UNKNOWN_SIGN && call->sign != func->sign) |
7943 | 0 | return ValueType::MatchResult::FALLBACK1; |
7944 | | |
7945 | 0 | if (func->reference != Reference::None && func->constness > call->constness) |
7946 | 0 | return ValueType::MatchResult::FALLBACK1; |
7947 | | |
7948 | 0 | return ValueType::MatchResult::SAME; |
7949 | 0 | } |
7950 | | |
7951 | | ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar) |
7952 | 0 | { |
7953 | 0 | ValueType vt; |
7954 | 0 | const ValueType* pvt = funcVar->valueType(); |
7955 | 0 | if (pvt && funcVar->isArray() && !(funcVar->isStlType() && Token::simpleMatch(funcVar->typeStartToken(), "std :: array"))) { // std::array doesn't decay to a pointer |
7956 | 0 | vt = *pvt; |
7957 | 0 | if (vt.pointer == 0) // don't bump array of pointers |
7958 | 0 | ++vt.pointer; |
7959 | 0 | pvt = &vt; |
7960 | 0 | } |
7961 | 0 | const ValueType::MatchResult res = ValueType::matchParameter(call, pvt); |
7962 | 0 | if (callVar && ((res == ValueType::MatchResult::SAME && call->container) || res == ValueType::MatchResult::UNKNOWN)) { |
7963 | 0 | const std::string type1 = getTypeString(callVar->typeStartToken()); |
7964 | 0 | const std::string type2 = getTypeString(funcVar->typeStartToken()); |
7965 | 0 | const bool templateVar = |
7966 | 0 | funcVar->scope() && funcVar->scope()->function && funcVar->scope()->function->templateDef; |
7967 | 0 | if (type1 == type2) |
7968 | 0 | return ValueType::MatchResult::SAME; |
7969 | 0 | if (!templateVar && type1.find("auto") == std::string::npos && type2.find("auto") == std::string::npos) |
7970 | 0 | return ValueType::MatchResult::NOMATCH; |
7971 | 0 | } |
7972 | 0 | return res; |
7973 | 0 | } |