/src/cppcheck/lib/library.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 | | #include "library.h" |
20 | | |
21 | | #include "astutils.h" |
22 | | #include "mathlib.h" |
23 | | #include "path.h" |
24 | | #include "symboldatabase.h" |
25 | | #include "token.h" |
26 | | #include "tokenlist.h" |
27 | | #include "utils.h" |
28 | | #include "valueflow.h" |
29 | | #include "vfvalue.h" |
30 | | |
31 | | #include <algorithm> |
32 | | #include <cctype> |
33 | | #include <climits> |
34 | | #include <cstring> |
35 | | #include <list> |
36 | | #include <memory> |
37 | | #include <sstream> // IWYU pragma: keep |
38 | | #include <stack> |
39 | | #include <stdexcept> |
40 | | #include <string> |
41 | | |
42 | | #include <tinyxml2.h> |
43 | | |
44 | | static std::vector<std::string> getnames(const char *names) |
45 | 0 | { |
46 | 0 | std::vector<std::string> ret; |
47 | 0 | while (const char *p = std::strchr(names,',')) { |
48 | 0 | ret.emplace_back(names, p-names); |
49 | 0 | names = p + 1; |
50 | 0 | } |
51 | 0 | ret.emplace_back(names); |
52 | 0 | return ret; |
53 | 0 | } |
54 | | |
55 | | static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList) |
56 | 0 | { |
57 | 0 | std::istringstream istr(valid + ','); |
58 | 0 | tokenList.createTokens(istr); |
59 | 0 | for (Token *tok = tokenList.front(); tok; tok = tok->next()) { |
60 | 0 | if (Token::Match(tok,"- %num%")) { |
61 | 0 | tok->str("-" + tok->strAt(1)); |
62 | 0 | tok->deleteNext(); |
63 | 0 | } |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | Library::Error Library::load(const char exename[], const char path[]) |
68 | 0 | { |
69 | 0 | if (std::strchr(path,',') != nullptr) { |
70 | 0 | std::string p(path); |
71 | 0 | for (;;) { |
72 | 0 | const std::string::size_type pos = p.find(','); |
73 | 0 | if (pos == std::string::npos) |
74 | 0 | break; |
75 | 0 | const Error &e = load(exename, p.substr(0,pos).c_str()); |
76 | 0 | if (e.errorcode != ErrorCode::OK) |
77 | 0 | return e; |
78 | 0 | p = p.substr(pos+1); |
79 | 0 | } |
80 | 0 | if (!p.empty()) |
81 | 0 | return load(exename, p.c_str()); |
82 | 0 | return Error(); |
83 | 0 | } |
84 | | |
85 | 0 | std::string absolute_path; |
86 | | // open file.. |
87 | 0 | tinyxml2::XMLDocument doc; |
88 | 0 | tinyxml2::XMLError error = doc.LoadFile(path); |
89 | 0 | if (error == tinyxml2::XML_ERROR_FILE_READ_ERROR && Path::getFilenameExtension(path).empty()) |
90 | | // Reading file failed, try again... |
91 | 0 | error = tinyxml2::XML_ERROR_FILE_NOT_FOUND; |
92 | 0 | if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { |
93 | | // failed to open file.. is there no extension? |
94 | 0 | std::string fullfilename(path); |
95 | 0 | if (Path::getFilenameExtension(fullfilename).empty()) { |
96 | 0 | fullfilename += ".cfg"; |
97 | 0 | error = doc.LoadFile(fullfilename.c_str()); |
98 | 0 | if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) |
99 | 0 | absolute_path = Path::getAbsoluteFilePath(fullfilename); |
100 | 0 | } |
101 | |
|
102 | 0 | std::list<std::string> cfgfolders; |
103 | | #ifdef FILESDIR |
104 | | cfgfolders.emplace_back(FILESDIR "/cfg"); |
105 | | #endif |
106 | 0 | if (exename) { |
107 | 0 | const std::string exepath(Path::fromNativeSeparators(Path::getPathFromFilename(Path::getCurrentExecutablePath(exename)))); |
108 | 0 | cfgfolders.push_back(exepath + "cfg"); |
109 | 0 | cfgfolders.push_back(exepath + "../cfg"); |
110 | 0 | cfgfolders.push_back(exepath); |
111 | 0 | } |
112 | |
|
113 | 0 | while (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND && !cfgfolders.empty()) { |
114 | 0 | const std::string cfgfolder(cfgfolders.back()); |
115 | 0 | cfgfolders.pop_back(); |
116 | 0 | const char *sep = (!cfgfolder.empty() && endsWith(cfgfolder,'/') ? "" : "/"); |
117 | 0 | const std::string filename(cfgfolder + sep + fullfilename); |
118 | 0 | error = doc.LoadFile(filename.c_str()); |
119 | 0 | if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) |
120 | 0 | absolute_path = Path::getAbsoluteFilePath(filename); |
121 | 0 | } |
122 | 0 | } else |
123 | 0 | absolute_path = Path::getAbsoluteFilePath(path); |
124 | |
|
125 | 0 | if (error == tinyxml2::XML_SUCCESS) { |
126 | 0 | if (mFiles.find(absolute_path) == mFiles.end()) { |
127 | 0 | Error err = load(doc); |
128 | 0 | if (err.errorcode == ErrorCode::OK) |
129 | 0 | mFiles.insert(absolute_path); |
130 | 0 | return err; |
131 | 0 | } |
132 | | |
133 | 0 | return Error(ErrorCode::OK); // ignore duplicates |
134 | 0 | } |
135 | | |
136 | 0 | if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) |
137 | 0 | return Error(ErrorCode::FILE_NOT_FOUND); |
138 | | |
139 | 0 | doc.PrintError(); |
140 | 0 | return Error(ErrorCode::BAD_XML); |
141 | 0 | } |
142 | | |
143 | | Library::Container::Yield Library::Container::yieldFrom(const std::string& yieldName) |
144 | 0 | { |
145 | 0 | if (yieldName == "at_index") |
146 | 0 | return Container::Yield::AT_INDEX; |
147 | 0 | if (yieldName == "item") |
148 | 0 | return Container::Yield::ITEM; |
149 | 0 | if (yieldName == "buffer") |
150 | 0 | return Container::Yield::BUFFER; |
151 | 0 | if (yieldName == "buffer-nt") |
152 | 0 | return Container::Yield::BUFFER_NT; |
153 | 0 | if (yieldName == "start-iterator") |
154 | 0 | return Container::Yield::START_ITERATOR; |
155 | 0 | if (yieldName == "end-iterator") |
156 | 0 | return Container::Yield::END_ITERATOR; |
157 | 0 | if (yieldName == "iterator") |
158 | 0 | return Container::Yield::ITERATOR; |
159 | 0 | if (yieldName == "size") |
160 | 0 | return Container::Yield::SIZE; |
161 | 0 | if (yieldName == "empty") |
162 | 0 | return Container::Yield::EMPTY; |
163 | 0 | return Container::Yield::NO_YIELD; |
164 | 0 | } |
165 | | Library::Container::Action Library::Container::actionFrom(const std::string& actionName) |
166 | 0 | { |
167 | 0 | if (actionName == "resize") |
168 | 0 | return Container::Action::RESIZE; |
169 | 0 | if (actionName == "clear") |
170 | 0 | return Container::Action::CLEAR; |
171 | 0 | if (actionName == "push") |
172 | 0 | return Container::Action::PUSH; |
173 | 0 | if (actionName == "pop") |
174 | 0 | return Container::Action::POP; |
175 | 0 | if (actionName == "find") |
176 | 0 | return Container::Action::FIND; |
177 | 0 | if (actionName == "insert") |
178 | 0 | return Container::Action::INSERT; |
179 | 0 | if (actionName == "erase") |
180 | 0 | return Container::Action::ERASE; |
181 | 0 | if (actionName == "change-content") |
182 | 0 | return Container::Action::CHANGE_CONTENT; |
183 | 0 | if (actionName == "change-internal") |
184 | 0 | return Container::Action::CHANGE_INTERNAL; |
185 | 0 | if (actionName == "change") |
186 | 0 | return Container::Action::CHANGE; |
187 | 0 | return Container::Action::NO_ACTION; |
188 | 0 | } |
189 | | |
190 | | // cppcheck-suppress unusedFunction - only used in unit tests |
191 | | bool Library::loadxmldata(const char xmldata[], std::size_t len) |
192 | 0 | { |
193 | 0 | tinyxml2::XMLDocument doc; |
194 | 0 | return (tinyxml2::XML_SUCCESS == doc.Parse(xmldata, len)) && (load(doc).errorcode == ErrorCode::OK); |
195 | 0 | } |
196 | | |
197 | | Library::Error Library::load(const tinyxml2::XMLDocument &doc) |
198 | 0 | { |
199 | 0 | const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); |
200 | |
|
201 | 0 | if (rootnode == nullptr) { |
202 | 0 | doc.PrintError(); |
203 | 0 | return Error(ErrorCode::BAD_XML); |
204 | 0 | } |
205 | | |
206 | 0 | if (strcmp(rootnode->Name(),"def") != 0) |
207 | 0 | return Error(ErrorCode::UNSUPPORTED_FORMAT, rootnode->Name()); |
208 | | |
209 | 0 | const int format = rootnode->IntAttribute("format", 1); // Assume format version 1 if nothing else is specified (very old .cfg files had no 'format' attribute) |
210 | |
|
211 | 0 | if (format > 2 || format <= 0) |
212 | 0 | return Error(ErrorCode::UNSUPPORTED_FORMAT); |
213 | | |
214 | 0 | std::set<std::string> unknown_elements; |
215 | |
|
216 | 0 | for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { |
217 | 0 | const std::string nodename = node->Name(); |
218 | 0 | if (nodename == "memory" || nodename == "resource") { |
219 | | // get allocationId to use.. |
220 | 0 | int allocationId = 0; |
221 | 0 | for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { |
222 | 0 | if (strcmp(memorynode->Name(),"dealloc")==0) { |
223 | 0 | const std::map<std::string, AllocFunc>::const_iterator it = mDealloc.find(memorynode->GetText()); |
224 | 0 | if (it != mDealloc.end()) { |
225 | 0 | allocationId = it->second.groupId; |
226 | 0 | break; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | } |
230 | 0 | if (allocationId == 0) { |
231 | 0 | if (nodename == "memory") |
232 | 0 | while (!ismemory(++mAllocId)); |
233 | 0 | else |
234 | 0 | while (!isresource(++mAllocId)); |
235 | 0 | allocationId = mAllocId; |
236 | 0 | } |
237 | | |
238 | | // add alloc/dealloc/use functions.. |
239 | 0 | for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { |
240 | 0 | const std::string memorynodename = memorynode->Name(); |
241 | 0 | if (memorynodename == "alloc" || memorynodename == "realloc") { |
242 | 0 | AllocFunc temp = {0}; |
243 | 0 | temp.groupId = allocationId; |
244 | |
|
245 | 0 | temp.initData = memorynode->BoolAttribute("init", true); |
246 | 0 | temp.arg = memorynode->IntAttribute("arg", -1); |
247 | |
|
248 | 0 | const char *bufferSize = memorynode->Attribute("buffer-size"); |
249 | 0 | if (!bufferSize) |
250 | 0 | temp.bufferSize = AllocFunc::BufferSize::none; |
251 | 0 | else { |
252 | 0 | if (std::strncmp(bufferSize, "malloc", 6) == 0) |
253 | 0 | temp.bufferSize = AllocFunc::BufferSize::malloc; |
254 | 0 | else if (std::strncmp(bufferSize, "calloc", 6) == 0) |
255 | 0 | temp.bufferSize = AllocFunc::BufferSize::calloc; |
256 | 0 | else if (std::strncmp(bufferSize, "strdup", 6) == 0) |
257 | 0 | temp.bufferSize = AllocFunc::BufferSize::strdup; |
258 | 0 | else |
259 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); |
260 | 0 | temp.bufferSizeArg1 = 1; |
261 | 0 | temp.bufferSizeArg2 = 2; |
262 | 0 | if (bufferSize[6] == 0) { |
263 | | // use default values |
264 | 0 | } else if (bufferSize[6] == ':' && bufferSize[7] >= '1' && bufferSize[7] <= '5') { |
265 | 0 | temp.bufferSizeArg1 = bufferSize[7] - '0'; |
266 | 0 | if (bufferSize[8] == ',' && bufferSize[9] >= '1' && bufferSize[9] <= '5') |
267 | 0 | temp.bufferSizeArg2 = bufferSize[9] - '0'; |
268 | 0 | } else |
269 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); |
270 | 0 | } |
271 | | |
272 | 0 | if (memorynodename == "realloc") |
273 | 0 | temp.reallocArg = memorynode->IntAttribute("realloc-arg", 1); |
274 | |
|
275 | 0 | if (memorynodename != "realloc") |
276 | 0 | mAlloc[memorynode->GetText()] = temp; |
277 | 0 | else |
278 | 0 | mRealloc[memorynode->GetText()] = temp; |
279 | 0 | } else if (memorynodename == "dealloc") { |
280 | 0 | AllocFunc temp = {0}; |
281 | 0 | temp.groupId = allocationId; |
282 | 0 | temp.arg = memorynode->IntAttribute("arg", 1); |
283 | 0 | mDealloc[memorynode->GetText()] = temp; |
284 | 0 | } else if (memorynodename == "use") |
285 | 0 | functions[memorynode->GetText()].use = true; |
286 | 0 | else |
287 | 0 | unknown_elements.insert(memorynodename); |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | 0 | else if (nodename == "define") { |
292 | 0 | const char *name = node->Attribute("name"); |
293 | 0 | if (name == nullptr) |
294 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
295 | 0 | const char *value = node->Attribute("value"); |
296 | 0 | if (value == nullptr) |
297 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); |
298 | 0 | defines.push_back(std::string(name) + |
299 | 0 | " " + |
300 | 0 | value); |
301 | 0 | } |
302 | | |
303 | 0 | else if (nodename == "function") { |
304 | 0 | const char *name = node->Attribute("name"); |
305 | 0 | if (name == nullptr) |
306 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
307 | 0 | for (const std::string &s : getnames(name)) { |
308 | 0 | const Error &err = loadFunction(node, s, unknown_elements); |
309 | 0 | if (err.errorcode != ErrorCode::OK) |
310 | 0 | return err; |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | 0 | else if (nodename == "reflection") { |
315 | 0 | for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { |
316 | 0 | if (strcmp(reflectionnode->Name(), "call") != 0) { |
317 | 0 | unknown_elements.insert(reflectionnode->Name()); |
318 | 0 | continue; |
319 | 0 | } |
320 | | |
321 | 0 | const char * const argString = reflectionnode->Attribute("arg"); |
322 | 0 | if (!argString) |
323 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); |
324 | | |
325 | 0 | mReflection[reflectionnode->GetText()] = strToInt<int>(argString); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | 0 | else if (nodename == "markup") { |
330 | 0 | const char * const extension = node->Attribute("ext"); |
331 | 0 | if (!extension) |
332 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "ext"); |
333 | 0 | mMarkupExtensions.insert(extension); |
334 | |
|
335 | 0 | mReportErrors[extension] = (node->Attribute("reporterrors", "true") != nullptr); |
336 | 0 | mProcessAfterCode[extension] = (node->Attribute("aftercode", "true") != nullptr); |
337 | |
|
338 | 0 | for (const tinyxml2::XMLElement *markupnode = node->FirstChildElement(); markupnode; markupnode = markupnode->NextSiblingElement()) { |
339 | 0 | const std::string markupnodename = markupnode->Name(); |
340 | 0 | if (markupnodename == "keywords") { |
341 | 0 | for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { |
342 | 0 | if (strcmp(librarynode->Name(), "keyword") == 0) { |
343 | 0 | const char* nodeName = librarynode->Attribute("name"); |
344 | 0 | if (nodeName == nullptr) |
345 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
346 | 0 | mKeywords[extension].insert(nodeName); |
347 | 0 | } else |
348 | 0 | unknown_elements.insert(librarynode->Name()); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | else if (markupnodename == "exported") { |
353 | 0 | for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { |
354 | 0 | if (strcmp(exporter->Name(), "exporter") != 0) { |
355 | 0 | unknown_elements.insert(exporter->Name()); |
356 | 0 | continue; |
357 | 0 | } |
358 | | |
359 | 0 | const char * const prefix = exporter->Attribute("prefix"); |
360 | 0 | if (!prefix) |
361 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "prefix"); |
362 | | |
363 | 0 | for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) { |
364 | 0 | const std::string ename = e->Name(); |
365 | 0 | if (ename == "prefix") |
366 | 0 | mExporters[prefix].addPrefix(e->GetText()); |
367 | 0 | else if (ename == "suffix") |
368 | 0 | mExporters[prefix].addSuffix(e->GetText()); |
369 | 0 | else |
370 | 0 | unknown_elements.insert(ename); |
371 | 0 | } |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | 0 | else if (markupnodename == "imported") { |
376 | 0 | for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { |
377 | 0 | if (strcmp(librarynode->Name(), "importer") == 0) |
378 | 0 | mImporters[extension].insert(librarynode->GetText()); |
379 | 0 | else |
380 | 0 | unknown_elements.insert(librarynode->Name()); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | else if (markupnodename == "codeblocks") { |
385 | 0 | for (const tinyxml2::XMLElement *blocknode = markupnode->FirstChildElement(); blocknode; blocknode = blocknode->NextSiblingElement()) { |
386 | 0 | const std::string blocknodename = blocknode->Name(); |
387 | 0 | if (blocknodename == "block") { |
388 | 0 | const char * blockName = blocknode->Attribute("name"); |
389 | 0 | if (blockName) |
390 | 0 | mExecutableBlocks[extension].addBlock(blockName); |
391 | 0 | } else if (blocknodename == "structure") { |
392 | 0 | const char * start = blocknode->Attribute("start"); |
393 | 0 | if (start) |
394 | 0 | mExecutableBlocks[extension].setStart(start); |
395 | 0 | const char * end = blocknode->Attribute("end"); |
396 | 0 | if (end) |
397 | 0 | mExecutableBlocks[extension].setEnd(end); |
398 | 0 | const char * offset = blocknode->Attribute("offset"); |
399 | 0 | if (offset) |
400 | 0 | mExecutableBlocks[extension].setOffset(strToInt<int>(offset)); |
401 | 0 | } |
402 | | |
403 | 0 | else |
404 | 0 | unknown_elements.insert(blocknodename); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | 0 | else |
409 | 0 | unknown_elements.insert(markupnodename); |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | 0 | else if (nodename == "container") { |
414 | 0 | const char* const id = node->Attribute("id"); |
415 | 0 | if (!id) |
416 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "id"); |
417 | | |
418 | 0 | Container& container = containers[id]; |
419 | |
|
420 | 0 | const char* const inherits = node->Attribute("inherits"); |
421 | 0 | if (inherits) { |
422 | 0 | const std::unordered_map<std::string, Container>::const_iterator i = containers.find(inherits); |
423 | 0 | if (i != containers.end()) |
424 | 0 | container = i->second; // Take values from parent and overwrite them if necessary |
425 | 0 | else |
426 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, inherits); |
427 | 0 | } |
428 | | |
429 | 0 | const char* const startPattern = node->Attribute("startPattern"); |
430 | 0 | if (startPattern) { |
431 | 0 | container.startPattern = startPattern; |
432 | 0 | container.startPattern2 = container.startPattern + " !!::"; |
433 | 0 | } |
434 | 0 | const char* const endPattern = node->Attribute("endPattern"); |
435 | 0 | if (endPattern) |
436 | 0 | container.endPattern = endPattern; |
437 | 0 | const char* const itEndPattern = node->Attribute("itEndPattern"); |
438 | 0 | if (itEndPattern) |
439 | 0 | container.itEndPattern = itEndPattern; |
440 | 0 | const char* const opLessAllowed = node->Attribute("opLessAllowed"); |
441 | 0 | if (opLessAllowed) |
442 | 0 | container.opLessAllowed = strcmp(opLessAllowed, "true") == 0; |
443 | 0 | const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor"); |
444 | 0 | if (hasInitializerListConstructor) |
445 | 0 | container.hasInitializerListConstructor = strcmp(hasInitializerListConstructor, "true") == 0; |
446 | 0 | const char* const view = node->Attribute("view"); |
447 | 0 | if (view) |
448 | 0 | container.view = strcmp(view, "true") == 0; |
449 | |
|
450 | 0 | for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) { |
451 | 0 | const std::string containerNodeName = containerNode->Name(); |
452 | 0 | if (containerNodeName == "size" || containerNodeName == "access" || containerNodeName == "other") { |
453 | 0 | for (const tinyxml2::XMLElement *functionNode = containerNode->FirstChildElement(); functionNode; functionNode = functionNode->NextSiblingElement()) { |
454 | 0 | if (strcmp(functionNode->Name(), "function") != 0) { |
455 | 0 | unknown_elements.insert(functionNode->Name()); |
456 | 0 | continue; |
457 | 0 | } |
458 | | |
459 | 0 | const char* const functionName = functionNode->Attribute("name"); |
460 | 0 | if (!functionName) |
461 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
462 | | |
463 | 0 | const char* const action_ptr = functionNode->Attribute("action"); |
464 | 0 | Container::Action action = Container::Action::NO_ACTION; |
465 | 0 | if (action_ptr) { |
466 | 0 | std::string actionName = action_ptr; |
467 | 0 | action = Container::actionFrom(actionName); |
468 | 0 | if (action == Container::Action::NO_ACTION) |
469 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); |
470 | 0 | } |
471 | | |
472 | 0 | const char* const yield_ptr = functionNode->Attribute("yields"); |
473 | 0 | Container::Yield yield = Container::Yield::NO_YIELD; |
474 | 0 | if (yield_ptr) { |
475 | 0 | std::string yieldName = yield_ptr; |
476 | 0 | yield = Container::yieldFrom(yieldName); |
477 | 0 | if (yield == Container::Yield::NO_YIELD) |
478 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); |
479 | 0 | } |
480 | | |
481 | 0 | const char* const returnType = functionNode->Attribute("returnType"); |
482 | 0 | if (returnType) |
483 | 0 | container.functions[functionName].returnType = returnType; |
484 | |
|
485 | 0 | container.functions[functionName].action = action; |
486 | 0 | container.functions[functionName].yield = yield; |
487 | 0 | } |
488 | | |
489 | 0 | if (containerNodeName == "size") { |
490 | 0 | const char* const templateArg = containerNode->Attribute("templateParameter"); |
491 | 0 | if (templateArg) |
492 | 0 | container.size_templateArgNo = strToInt<int>(templateArg); |
493 | 0 | } else if (containerNodeName == "access") { |
494 | 0 | const char* const indexArg = containerNode->Attribute("indexOperator"); |
495 | 0 | if (indexArg) |
496 | 0 | container.arrayLike_indexOp = strcmp(indexArg, "array-like") == 0; |
497 | 0 | } |
498 | 0 | } else if (containerNodeName == "type") { |
499 | 0 | const char* const templateArg = containerNode->Attribute("templateParameter"); |
500 | 0 | if (templateArg) |
501 | 0 | container.type_templateArgNo = strToInt<int>(templateArg); |
502 | |
|
503 | 0 | const char* const string = containerNode->Attribute("string"); |
504 | 0 | if (string) |
505 | 0 | container.stdStringLike = strcmp(string, "std-like") == 0; |
506 | 0 | const char* const associative = containerNode->Attribute("associative"); |
507 | 0 | if (associative) |
508 | 0 | container.stdAssociativeLike = strcmp(associative, "std-like") == 0; |
509 | 0 | const char* const unstable = containerNode->Attribute("unstable"); |
510 | 0 | if (unstable) { |
511 | 0 | std::string unstableType = unstable; |
512 | 0 | if (unstableType.find("erase") != std::string::npos) |
513 | 0 | container.unstableErase = true; |
514 | 0 | if (unstableType.find("insert") != std::string::npos) |
515 | 0 | container.unstableInsert = true; |
516 | 0 | } |
517 | 0 | } else if (containerNodeName == "rangeItemRecordType") { |
518 | 0 | for (const tinyxml2::XMLElement* memberNode = node->FirstChildElement(); memberNode; memberNode = memberNode->NextSiblingElement()) { |
519 | 0 | const char *memberName = memberNode->Attribute("name"); |
520 | 0 | const char *memberTemplateParameter = memberNode->Attribute("templateParameter"); |
521 | 0 | struct Container::RangeItemRecordTypeItem member; |
522 | 0 | member.name = memberName ? memberName : ""; |
523 | 0 | member.templateParameter = memberTemplateParameter ? strToInt<int>(memberTemplateParameter) : -1; |
524 | 0 | container.rangeItemRecordType.emplace_back(std::move(member)); |
525 | 0 | } |
526 | 0 | } else |
527 | 0 | unknown_elements.insert(containerNodeName); |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | 0 | else if (nodename == "smart-pointer") { |
532 | 0 | const char *className = node->Attribute("class-name"); |
533 | 0 | if (!className) |
534 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name"); |
535 | 0 | SmartPointer& smartPointer = smartPointers[className]; |
536 | 0 | smartPointer.name = className; |
537 | 0 | for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode; |
538 | 0 | smartPointerNode = smartPointerNode->NextSiblingElement()) { |
539 | 0 | const std::string smartPointerNodeName = smartPointerNode->Name(); |
540 | 0 | if (smartPointerNodeName == "unique") |
541 | 0 | smartPointer.unique = true; |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | 0 | else if (nodename == "type-checks") { |
546 | 0 | for (const tinyxml2::XMLElement *checkNode = node->FirstChildElement(); checkNode; checkNode = checkNode->NextSiblingElement()) { |
547 | 0 | const std::string &checkName = checkNode->Name(); |
548 | 0 | for (const tinyxml2::XMLElement *checkTypeNode = checkNode->FirstChildElement(); checkTypeNode; checkTypeNode = checkTypeNode->NextSiblingElement()) { |
549 | 0 | const std::string checkTypeName = checkTypeNode->Name(); |
550 | 0 | const char *typeName = checkTypeNode->GetText(); |
551 | 0 | if (!typeName) |
552 | 0 | continue; |
553 | 0 | if (checkTypeName == "check") |
554 | 0 | mTypeChecks[std::pair<std::string,std::string>(checkName, typeName)] = TypeCheck::check; |
555 | 0 | else if (checkTypeName == "suppress") |
556 | 0 | mTypeChecks[std::pair<std::string,std::string>(checkName, typeName)] = TypeCheck::suppress; |
557 | 0 | else if (checkTypeName == "checkFiniteLifetime") |
558 | 0 | mTypeChecks[std::pair<std::string,std::string>(checkName, typeName)] = TypeCheck::checkFiniteLifetime; |
559 | 0 | } |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | 0 | else if (nodename == "podtype") { |
564 | 0 | const char * const name = node->Attribute("name"); |
565 | 0 | if (!name) |
566 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
567 | 0 | PodType podType = {0}; |
568 | 0 | podType.stdtype = PodType::Type::NO; |
569 | 0 | const char * const stdtype = node->Attribute("stdtype"); |
570 | 0 | if (stdtype) { |
571 | 0 | if (std::strcmp(stdtype, "bool") == 0) |
572 | 0 | podType.stdtype = PodType::Type::BOOL; |
573 | 0 | else if (std::strcmp(stdtype, "char") == 0) |
574 | 0 | podType.stdtype = PodType::Type::CHAR; |
575 | 0 | else if (std::strcmp(stdtype, "short") == 0) |
576 | 0 | podType.stdtype = PodType::Type::SHORT; |
577 | 0 | else if (std::strcmp(stdtype, "int") == 0) |
578 | 0 | podType.stdtype = PodType::Type::INT; |
579 | 0 | else if (std::strcmp(stdtype, "long") == 0) |
580 | 0 | podType.stdtype = PodType::Type::LONG; |
581 | 0 | else if (std::strcmp(stdtype, "long long") == 0) |
582 | 0 | podType.stdtype = PodType::Type::LONGLONG; |
583 | 0 | } |
584 | 0 | const char * const size = node->Attribute("size"); |
585 | 0 | if (size) |
586 | 0 | podType.size = strToInt<unsigned int>(size); |
587 | 0 | const char * const sign = node->Attribute("sign"); |
588 | 0 | if (sign) |
589 | 0 | podType.sign = *sign; |
590 | 0 | for (const std::string &s : getnames(name)) |
591 | 0 | mPodTypes[s] = podType; |
592 | 0 | } |
593 | | |
594 | 0 | else if (nodename == "platformtype") { |
595 | 0 | const char * const type_name = node->Attribute("name"); |
596 | 0 | if (type_name == nullptr) |
597 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
598 | 0 | const char *value = node->Attribute("value"); |
599 | 0 | if (value == nullptr) |
600 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); |
601 | 0 | PlatformType type; |
602 | 0 | type.mType = value; |
603 | 0 | std::set<std::string> platform; |
604 | 0 | for (const tinyxml2::XMLElement *typenode = node->FirstChildElement(); typenode; typenode = typenode->NextSiblingElement()) { |
605 | 0 | const std::string typenodename = typenode->Name(); |
606 | 0 | if (typenodename == "platform") { |
607 | 0 | const char * const type_attribute = typenode->Attribute("type"); |
608 | 0 | if (type_attribute == nullptr) |
609 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); |
610 | 0 | platform.insert(type_attribute); |
611 | 0 | } else if (typenodename == "signed") |
612 | 0 | type.mSigned = true; |
613 | 0 | else if (typenodename == "unsigned") |
614 | 0 | type.mUnsigned = true; |
615 | 0 | else if (typenodename == "long") |
616 | 0 | type.mLong = true; |
617 | 0 | else if (typenodename == "pointer") |
618 | 0 | type.mPointer= true; |
619 | 0 | else if (typenodename == "ptr_ptr") |
620 | 0 | type.mPtrPtr = true; |
621 | 0 | else if (typenodename == "const_ptr") |
622 | 0 | type.mConstPtr = true; |
623 | 0 | else |
624 | 0 | unknown_elements.insert(typenodename); |
625 | 0 | } |
626 | 0 | if (platform.empty()) { |
627 | 0 | const PlatformType * const type_ptr = platform_type(type_name, emptyString); |
628 | 0 | if (type_ptr) { |
629 | 0 | if (*type_ptr == type) |
630 | 0 | return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); |
631 | 0 | return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); |
632 | 0 | } |
633 | 0 | mPlatformTypes[type_name] = type; |
634 | 0 | } else { |
635 | 0 | for (const std::string &p : platform) { |
636 | 0 | const PlatformType * const type_ptr = platform_type(type_name, p); |
637 | 0 | if (type_ptr) { |
638 | 0 | if (*type_ptr == type) |
639 | 0 | return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); |
640 | 0 | return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); |
641 | 0 | } |
642 | 0 | mPlatforms[p].mPlatformTypes[type_name] = type; |
643 | 0 | } |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | 0 | else if (nodename == "entrypoint") { |
648 | 0 | const char * const type_name = node->Attribute("name"); |
649 | 0 | if (type_name == nullptr) |
650 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); |
651 | 0 | mEntrypoints.emplace(type_name); |
652 | 0 | } |
653 | | |
654 | 0 | else |
655 | 0 | unknown_elements.insert(nodename); |
656 | 0 | } |
657 | 0 | if (!unknown_elements.empty()) { |
658 | 0 | std::string str; |
659 | 0 | for (std::set<std::string>::const_iterator i = unknown_elements.cbegin(); i != unknown_elements.cend();) { |
660 | 0 | str += *i; |
661 | 0 | if (++i != unknown_elements.end()) |
662 | 0 | str += ", "; |
663 | 0 | } |
664 | 0 | return Error(ErrorCode::UNKNOWN_ELEMENT, str); |
665 | 0 | } |
666 | 0 | return Error(ErrorCode::OK); |
667 | 0 | } |
668 | | |
669 | | Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements) |
670 | 0 | { |
671 | 0 | if (name.empty()) |
672 | 0 | return Error(ErrorCode::OK); |
673 | | |
674 | | // TODO: write debug warning if we modify an existing entry |
675 | 0 | Function& func = functions[name]; |
676 | |
|
677 | 0 | for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { |
678 | 0 | const std::string functionnodename = functionnode->Name(); |
679 | 0 | if (functionnodename == "noreturn") { |
680 | 0 | const char * const text = functionnode->GetText(); |
681 | 0 | if (strcmp(text, "false") == 0) |
682 | 0 | mNoReturn[name] = FalseTrueMaybe::False; |
683 | 0 | else if (strcmp(text, "maybe") == 0) |
684 | 0 | mNoReturn[name] = FalseTrueMaybe::Maybe; |
685 | 0 | else |
686 | 0 | mNoReturn[name] = FalseTrueMaybe::True; // Safe |
687 | 0 | } else if (functionnodename == "pure") |
688 | 0 | func.ispure = true; |
689 | 0 | else if (functionnodename == "const") { |
690 | 0 | func.ispure = true; |
691 | 0 | func.isconst = true; // a constant function is pure |
692 | 0 | } else if (functionnodename == "leak-ignore") |
693 | 0 | func.leakignore = true; |
694 | 0 | else if (functionnodename == "not-overlapping-data") { |
695 | 0 | NonOverlappingData nonOverlappingData; |
696 | 0 | nonOverlappingData.ptr1Arg = functionnode->IntAttribute("ptr1-arg", -1); |
697 | 0 | nonOverlappingData.ptr2Arg = functionnode->IntAttribute("ptr2-arg", -1); |
698 | 0 | nonOverlappingData.sizeArg = functionnode->IntAttribute("size-arg", -1); |
699 | 0 | nonOverlappingData.strlenArg = functionnode->IntAttribute("strlen-arg", -1); |
700 | 0 | mNonOverlappingData[name] = nonOverlappingData; |
701 | 0 | } else if (functionnodename == "use-retval") { |
702 | 0 | func.useretval = Library::UseRetValType::DEFAULT; |
703 | 0 | if (const char *type = functionnode->Attribute("type")) |
704 | 0 | if (std::strcmp(type, "error-code") == 0) |
705 | 0 | func.useretval = Library::UseRetValType::ERROR_CODE; |
706 | 0 | } else if (functionnodename == "returnValue") { |
707 | 0 | if (const char *expr = functionnode->GetText()) |
708 | 0 | mReturnValue[name] = expr; |
709 | 0 | if (const char *type = functionnode->Attribute("type")) |
710 | 0 | mReturnValueType[name] = type; |
711 | 0 | if (const char *container = functionnode->Attribute("container")) |
712 | 0 | mReturnValueContainer[name] = strToInt<int>(container); |
713 | 0 | if (const char *unknownReturnValues = functionnode->Attribute("unknownValues")) { |
714 | 0 | if (std::strcmp(unknownReturnValues, "all") == 0) { |
715 | 0 | std::vector<MathLib::bigint> values{LLONG_MIN, LLONG_MAX}; |
716 | 0 | mUnknownReturnValues[name] = values; |
717 | 0 | } |
718 | 0 | } |
719 | 0 | } else if (functionnodename == "arg") { |
720 | 0 | const char* argNrString = functionnode->Attribute("nr"); |
721 | 0 | if (!argNrString) |
722 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "nr"); |
723 | 0 | const bool bAnyArg = strcmp(argNrString, "any") == 0; |
724 | 0 | const bool bVariadicArg = strcmp(argNrString, "variadic") == 0; |
725 | 0 | const int nr = (bAnyArg || bVariadicArg) ? -1 : strToInt<int>(argNrString); |
726 | 0 | ArgumentChecks &ac = func.argumentChecks[nr]; |
727 | 0 | ac.optional = functionnode->Attribute("default") != nullptr; |
728 | 0 | ac.variadic = bVariadicArg; |
729 | 0 | const char * const argDirection = functionnode->Attribute("direction"); |
730 | 0 | if (argDirection) { |
731 | 0 | const size_t argDirLen = strlen(argDirection); |
732 | 0 | if (!strncmp(argDirection, "in", argDirLen)) { |
733 | 0 | ac.direction = ArgumentChecks::Direction::DIR_IN; |
734 | 0 | } else if (!strncmp(argDirection, "out", argDirLen)) { |
735 | 0 | ac.direction = ArgumentChecks::Direction::DIR_OUT; |
736 | 0 | } else if (!strncmp(argDirection, "inout", argDirLen)) { |
737 | 0 | ac.direction = ArgumentChecks::Direction::DIR_INOUT; |
738 | 0 | } |
739 | 0 | } |
740 | 0 | for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { |
741 | 0 | const std::string argnodename = argnode->Name(); |
742 | 0 | int indirect = 0; |
743 | 0 | const char * const indirectStr = argnode->Attribute("indirect"); |
744 | 0 | if (indirectStr) |
745 | 0 | indirect = strToInt<int>(indirectStr); |
746 | 0 | if (argnodename == "not-bool") |
747 | 0 | ac.notbool = true; |
748 | 0 | else if (argnodename == "not-null") |
749 | 0 | ac.notnull = true; |
750 | 0 | else if (argnodename == "not-uninit") |
751 | 0 | ac.notuninit = indirect; |
752 | 0 | else if (argnodename == "formatstr") |
753 | 0 | ac.formatstr = true; |
754 | 0 | else if (argnodename == "strz") |
755 | 0 | ac.strz = true; |
756 | 0 | else if (argnodename == "valid") { |
757 | | // Validate the validation expression |
758 | 0 | const char *p = argnode->GetText(); |
759 | 0 | if (!isCompliantValidationExpression(p)) |
760 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, (!p ? "\"\"" : p)); |
761 | | // Set validation expression |
762 | 0 | ac.valid = p; |
763 | 0 | } |
764 | 0 | else if (argnodename == "minsize") { |
765 | 0 | const char *typeattr = argnode->Attribute("type"); |
766 | 0 | if (!typeattr) |
767 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); |
768 | | |
769 | 0 | ArgumentChecks::MinSize::Type type; |
770 | 0 | if (strcmp(typeattr,"strlen")==0) |
771 | 0 | type = ArgumentChecks::MinSize::Type::STRLEN; |
772 | 0 | else if (strcmp(typeattr,"argvalue")==0) |
773 | 0 | type = ArgumentChecks::MinSize::Type::ARGVALUE; |
774 | 0 | else if (strcmp(typeattr,"sizeof")==0) |
775 | 0 | type = ArgumentChecks::MinSize::Type::SIZEOF; |
776 | 0 | else if (strcmp(typeattr,"mul")==0) |
777 | 0 | type = ArgumentChecks::MinSize::Type::MUL; |
778 | 0 | else if (strcmp(typeattr,"value")==0) |
779 | 0 | type = ArgumentChecks::MinSize::Type::VALUE; |
780 | 0 | else |
781 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, typeattr); |
782 | | |
783 | 0 | if (type == ArgumentChecks::MinSize::Type::VALUE) { |
784 | 0 | const char *valueattr = argnode->Attribute("value"); |
785 | 0 | if (!valueattr) |
786 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); |
787 | 0 | long long minsizevalue = 0; |
788 | 0 | try { |
789 | 0 | minsizevalue = strToInt<long long>(valueattr); |
790 | 0 | } catch (const std::runtime_error&) { |
791 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); |
792 | 0 | } |
793 | 0 | if (minsizevalue <= 0) |
794 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); |
795 | 0 | ac.minsizes.emplace_back(type, 0); |
796 | 0 | ac.minsizes.back().value = minsizevalue; |
797 | 0 | } else { |
798 | 0 | const char *argattr = argnode->Attribute("arg"); |
799 | 0 | if (!argattr) |
800 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); |
801 | 0 | if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9') |
802 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argattr); |
803 | | |
804 | 0 | ac.minsizes.reserve(type == ArgumentChecks::MinSize::Type::MUL ? 2 : 1); |
805 | 0 | ac.minsizes.emplace_back(type, argattr[0] - '0'); |
806 | 0 | if (type == ArgumentChecks::MinSize::Type::MUL) { |
807 | 0 | const char *arg2attr = argnode->Attribute("arg2"); |
808 | 0 | if (!arg2attr) |
809 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "arg2"); |
810 | 0 | if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9') |
811 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, arg2attr); |
812 | 0 | ac.minsizes.back().arg2 = arg2attr[0] - '0'; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | const char* baseTypeAttr = argnode->Attribute("baseType"); // used by VALUE, ARGVALUE |
816 | 0 | if (baseTypeAttr) |
817 | 0 | ac.minsizes.back().baseType = baseTypeAttr; |
818 | 0 | } |
819 | | |
820 | 0 | else if (argnodename == "iterator") { |
821 | 0 | ac.iteratorInfo.it = true; |
822 | 0 | const char* str = argnode->Attribute("type"); |
823 | 0 | ac.iteratorInfo.first = (str && std::strcmp(str, "first") == 0); |
824 | 0 | ac.iteratorInfo.last = (str && std::strcmp(str, "last") == 0); |
825 | 0 | ac.iteratorInfo.container = argnode->IntAttribute("container", 0); |
826 | 0 | } |
827 | | |
828 | 0 | else |
829 | 0 | unknown_elements.insert(argnodename); |
830 | 0 | } |
831 | 0 | if (ac.notuninit == 0) |
832 | 0 | ac.notuninit = ac.notnull ? 1 : 0; |
833 | 0 | } else if (functionnodename == "ignorefunction") { |
834 | 0 | func.ignore = true; |
835 | 0 | } else if (functionnodename == "formatstr") { |
836 | 0 | func.formatstr = true; |
837 | 0 | const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan"); |
838 | 0 | const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); |
839 | 0 | func.formatstr_scan = scan && scan->BoolValue(); |
840 | 0 | func.formatstr_secure = secure && secure->BoolValue(); |
841 | 0 | } else if (functionnodename == "warn") { |
842 | 0 | WarnInfo wi; |
843 | 0 | const char* const severity = functionnode->Attribute("severity"); |
844 | 0 | if (severity == nullptr) |
845 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "severity"); |
846 | 0 | wi.severity = Severity::fromString(severity); |
847 | |
|
848 | 0 | const char* const cstd = functionnode->Attribute("cstd"); |
849 | 0 | if (cstd) { |
850 | 0 | if (!wi.standards.setC(cstd)) |
851 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cstd); |
852 | 0 | } else |
853 | 0 | wi.standards.c = Standards::C89; |
854 | | |
855 | 0 | const char* const cppstd = functionnode->Attribute("cppstd"); |
856 | 0 | if (cppstd) { |
857 | 0 | if (!wi.standards.setCPP(cppstd)) |
858 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cppstd); |
859 | 0 | } else |
860 | 0 | wi.standards.cpp = Standards::CPP03; |
861 | | |
862 | 0 | const char* const reason = functionnode->Attribute("reason"); |
863 | 0 | const char* const alternatives = functionnode->Attribute("alternatives"); |
864 | 0 | if (reason && alternatives) { |
865 | | // Construct message |
866 | 0 | wi.message = std::string(reason) + " function '" + name + "' called. It is recommended to use "; |
867 | 0 | std::vector<std::string> alt = getnames(alternatives); |
868 | 0 | for (std::size_t i = 0; i < alt.size(); ++i) { |
869 | 0 | wi.message += "'" + alt[i] + "'"; |
870 | 0 | if (i == alt.size() - 1) |
871 | 0 | wi.message += " instead."; |
872 | 0 | else if (i == alt.size() - 2) |
873 | 0 | wi.message += " or "; |
874 | 0 | else |
875 | 0 | wi.message += ", "; |
876 | 0 | } |
877 | 0 | } else { |
878 | 0 | const char * const message = functionnode->GetText(); |
879 | 0 | if (!message) |
880 | 0 | return Error(ErrorCode::MISSING_ATTRIBUTE, "\"reason\" and \"alternatives\" or some text."); |
881 | | |
882 | 0 | wi.message = message; |
883 | 0 | } |
884 | | |
885 | 0 | functionwarn[name] = wi; |
886 | 0 | } else if (functionnodename == "container") { |
887 | 0 | const char* const action_ptr = functionnode->Attribute("action"); |
888 | 0 | Container::Action action = Container::Action::NO_ACTION; |
889 | 0 | if (action_ptr) { |
890 | 0 | std::string actionName = action_ptr; |
891 | 0 | action = Container::actionFrom(actionName); |
892 | 0 | if (action == Container::Action::NO_ACTION) |
893 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); |
894 | 0 | } |
895 | 0 | func.containerAction = action; |
896 | |
|
897 | 0 | const char* const yield_ptr = functionnode->Attribute("yields"); |
898 | 0 | Container::Yield yield = Container::Yield::NO_YIELD; |
899 | 0 | if (yield_ptr) { |
900 | 0 | std::string yieldName = yield_ptr; |
901 | 0 | yield = Container::yieldFrom(yieldName); |
902 | 0 | if (yield == Container::Yield::NO_YIELD) |
903 | 0 | return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); |
904 | 0 | } |
905 | 0 | func.containerYield = yield; |
906 | |
|
907 | 0 | const char* const returnType = functionnode->Attribute("returnType"); |
908 | 0 | if (returnType) |
909 | 0 | func.returnType = returnType; |
910 | 0 | } else |
911 | 0 | unknown_elements.insert(functionnodename); |
912 | 0 | } |
913 | 0 | return Error(ErrorCode::OK); |
914 | 0 | } |
915 | | |
916 | | bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const |
917 | 2.27k | { |
918 | 2.27k | const ArgumentChecks *ac = getarg(ftok, argnr); |
919 | 2.27k | if (!ac || ac->valid.empty()) |
920 | 2.27k | return true; |
921 | 0 | if (ac->valid.find('.') != std::string::npos) |
922 | 0 | return isFloatArgValid(ftok, argnr, argvalue); |
923 | 0 | TokenList tokenList(nullptr); |
924 | 0 | gettokenlistfromvalid(ac->valid, tokenList); |
925 | 0 | for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { |
926 | 0 | if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str())) |
927 | 0 | return true; |
928 | 0 | if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toLongNumber(tok->str()) && argvalue <= MathLib::toLongNumber(tok->strAt(2))) |
929 | 0 | return true; |
930 | 0 | if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toLongNumber(tok->str())) |
931 | 0 | return true; |
932 | 0 | if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toLongNumber(tok->strAt(1))) |
933 | 0 | return true; |
934 | 0 | } |
935 | 0 | return false; |
936 | 0 | } |
937 | | |
938 | | bool Library::isFloatArgValid(const Token *ftok, int argnr, double argvalue) const |
939 | 0 | { |
940 | 0 | const ArgumentChecks *ac = getarg(ftok, argnr); |
941 | 0 | if (!ac || ac->valid.empty()) |
942 | 0 | return true; |
943 | 0 | TokenList tokenList(nullptr); |
944 | 0 | gettokenlistfromvalid(ac->valid, tokenList); |
945 | 0 | for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { |
946 | 0 | if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2))) |
947 | 0 | return true; |
948 | 0 | if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toDoubleNumber(tok->str())) |
949 | 0 | return true; |
950 | 0 | if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toDoubleNumber(tok->strAt(1))) |
951 | 0 | return true; |
952 | 0 | if (Token::Match(tok, "%num%") && MathLib::isFloat(tok->str()) && MathLib::isEqual(tok->str(), MathLib::toString(argvalue))) |
953 | 0 | return true; |
954 | 0 | if (Token::Match(tok, "! %num%") && MathLib::isFloat(tok->next()->str())) |
955 | 0 | return MathLib::isNotEqual(tok->next()->str(), MathLib::toString(argvalue)); |
956 | 0 | } |
957 | 0 | return false; |
958 | 0 | } |
959 | | |
960 | | std::string Library::getFunctionName(const Token *ftok, bool &error) const |
961 | 4.92k | { |
962 | 4.92k | if (!ftok) { |
963 | 0 | error = true; |
964 | 0 | return ""; |
965 | 0 | } |
966 | 4.92k | if (ftok->isName()) { |
967 | 17.1k | for (const Scope *scope = ftok->scope(); scope; scope = scope->nestedIn) { |
968 | 12.1k | if (!scope->isClassOrStruct()) |
969 | 12.1k | continue; |
970 | 0 | const std::vector<Type::BaseInfo> &derivedFrom = scope->definedType->derivedFrom; |
971 | 0 | for (const Type::BaseInfo & baseInfo : derivedFrom) { |
972 | 0 | std::string name; |
973 | 0 | const Token* tok = baseInfo.nameTok; // baseInfo.name still contains template parameters, but is missing namespaces |
974 | 0 | if (tok->str() == "::") |
975 | 0 | tok = tok->next(); |
976 | 0 | while (Token::Match(tok, "%name%|::")) { |
977 | 0 | name += tok->str(); |
978 | 0 | tok = tok->next(); |
979 | 0 | } |
980 | 0 | name += "::" + ftok->str(); |
981 | 0 | if (functions.find(name) != functions.end() && matchArguments(ftok, name)) |
982 | 0 | return name; |
983 | 0 | } |
984 | 0 | } |
985 | 4.92k | return ftok->str(); |
986 | 4.92k | } |
987 | 0 | if (ftok->str() == "::") { |
988 | 0 | if (!ftok->astOperand2()) |
989 | 0 | return getFunctionName(ftok->astOperand1(), error); |
990 | 0 | return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); |
991 | 0 | } |
992 | 0 | if (ftok->str() == "." && ftok->astOperand1()) { |
993 | 0 | const std::string type = astCanonicalType(ftok->astOperand1(), ftok->originalName() == "->"); |
994 | 0 | if (type.empty()) { |
995 | 0 | error = true; |
996 | 0 | return ""; |
997 | 0 | } |
998 | | |
999 | 0 | return type + "::" + getFunctionName(ftok->astOperand2(),error); |
1000 | 0 | } |
1001 | 0 | error = true; |
1002 | 0 | return ""; |
1003 | 0 | } |
1004 | | |
1005 | | std::string Library::getFunctionName(const Token *ftok) const |
1006 | 46.3k | { |
1007 | 46.3k | if (!Token::Match(ftok, "%name% )| (") && (ftok->strAt(-1) != "&" || ftok->previous()->astOperand2())) |
1008 | 37.7k | return ""; |
1009 | | |
1010 | | // Lookup function name using AST.. |
1011 | 8.61k | if (ftok->astParent()) { |
1012 | 4.92k | bool error = false; |
1013 | 4.92k | const Token * tok = ftok->astParent()->isUnaryOp("&") ? ftok->astParent()->astOperand1() : ftok->next()->astOperand1(); |
1014 | 4.92k | const std::string ret = getFunctionName(tok, error); |
1015 | 4.92k | return error ? std::string() : ret; |
1016 | 4.92k | } |
1017 | | |
1018 | | // Lookup function name without using AST.. |
1019 | 3.68k | if (Token::simpleMatch(ftok->previous(), ".")) |
1020 | 0 | return ""; |
1021 | 3.68k | if (!Token::Match(ftok->tokAt(-2), "%name% ::")) |
1022 | 3.68k | return ftok->str(); |
1023 | 0 | std::string ret(ftok->str()); |
1024 | 0 | ftok = ftok->tokAt(-2); |
1025 | 0 | while (Token::Match(ftok, "%name% ::")) { |
1026 | 0 | ret = ftok->str() + "::" + ret; |
1027 | 0 | ftok = ftok->tokAt(-2); |
1028 | 0 | } |
1029 | 0 | return ret; |
1030 | 3.68k | } |
1031 | | |
1032 | | bool Library::isnullargbad(const Token *ftok, int argnr) const |
1033 | 1.38k | { |
1034 | 1.38k | const ArgumentChecks *arg = getarg(ftok, argnr); |
1035 | 1.38k | if (!arg) { |
1036 | | // scan format string argument should not be null |
1037 | 1.38k | const std::string funcname = getFunctionName(ftok); |
1038 | 1.38k | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(funcname); |
1039 | 1.38k | if (it != functions.cend() && it->second.formatstr && it->second.formatstr_scan) |
1040 | 0 | return true; |
1041 | 1.38k | } |
1042 | 1.38k | return arg && arg->notnull; |
1043 | 1.38k | } |
1044 | | |
1045 | | bool Library::isuninitargbad(const Token *ftok, int argnr, int indirect, bool *hasIndirect) const |
1046 | 0 | { |
1047 | 0 | const ArgumentChecks *arg = getarg(ftok, argnr); |
1048 | 0 | if (!arg) { |
1049 | | // non-scan format string argument should not be uninitialized |
1050 | 0 | const std::string funcname = getFunctionName(ftok); |
1051 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(funcname); |
1052 | 0 | if (it != functions.cend() && it->second.formatstr && !it->second.formatstr_scan) |
1053 | 0 | return true; |
1054 | 0 | } |
1055 | 0 | if (hasIndirect && arg && arg->notuninit >= 1) |
1056 | 0 | *hasIndirect = true; |
1057 | 0 | return arg && arg->notuninit >= indirect; |
1058 | 0 | } |
1059 | | |
1060 | | |
1061 | | /** get allocation info for function */ |
1062 | | const Library::AllocFunc* Library::getAllocFuncInfo(const Token *tok) const |
1063 | 2.23k | { |
1064 | 2.23k | const std::string funcname = getFunctionName(tok); |
1065 | 2.23k | return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname); |
1066 | 2.23k | } |
1067 | | |
1068 | | /** get deallocation info for function */ |
1069 | | const Library::AllocFunc* Library::getDeallocFuncInfo(const Token *tok) const |
1070 | 56 | { |
1071 | 56 | const std::string funcname = getFunctionName(tok); |
1072 | 56 | return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname); |
1073 | 56 | } |
1074 | | |
1075 | | /** get reallocation info for function */ |
1076 | | const Library::AllocFunc* Library::getReallocFuncInfo(const Token *tok) const |
1077 | 2.27k | { |
1078 | 2.27k | const std::string funcname = getFunctionName(tok); |
1079 | 2.27k | return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mRealloc, funcname); |
1080 | 2.27k | } |
1081 | | |
1082 | | /** get allocation id for function */ |
1083 | | int Library::getAllocId(const Token *tok, int arg) const |
1084 | 1.46k | { |
1085 | 1.46k | const Library::AllocFunc* af = getAllocFuncInfo(tok); |
1086 | 1.46k | return (af && af->arg == arg) ? af->groupId : 0; |
1087 | 1.46k | } |
1088 | | |
1089 | | /** get deallocation id for function */ |
1090 | | int Library::getDeallocId(const Token *tok, int arg) const |
1091 | 0 | { |
1092 | 0 | const Library::AllocFunc* af = getDeallocFuncInfo(tok); |
1093 | 0 | return (af && af->arg == arg) ? af->groupId : 0; |
1094 | 0 | } |
1095 | | |
1096 | | /** get reallocation id for function */ |
1097 | | int Library::getReallocId(const Token *tok, int arg) const |
1098 | 0 | { |
1099 | 0 | const Library::AllocFunc* af = getReallocFuncInfo(tok); |
1100 | 0 | return (af && af->arg == arg) ? af->groupId : 0; |
1101 | 0 | } |
1102 | | |
1103 | | |
1104 | | const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) const |
1105 | 7.46k | { |
1106 | 7.46k | if (isNotLibraryFunction(ftok)) |
1107 | 7.46k | return nullptr; |
1108 | 0 | const std::unordered_map<std::string, Function>::const_iterator it1 = functions.find(getFunctionName(ftok)); |
1109 | 0 | if (it1 == functions.cend()) |
1110 | 0 | return nullptr; |
1111 | 0 | const std::map<int,ArgumentChecks>::const_iterator it2 = it1->second.argumentChecks.find(argnr); |
1112 | 0 | if (it2 != it1->second.argumentChecks.cend()) |
1113 | 0 | return &it2->second; |
1114 | 0 | const std::map<int,ArgumentChecks>::const_iterator it3 = it1->second.argumentChecks.find(-1); |
1115 | 0 | if (it3 != it1->second.argumentChecks.cend()) |
1116 | 0 | return &it3->second; |
1117 | 0 | return nullptr; |
1118 | 0 | } |
1119 | | |
1120 | | bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const |
1121 | 89 | { |
1122 | 89 | if (unknownFunc) |
1123 | 89 | unknownFunc->clear(); |
1124 | | |
1125 | 89 | if (Token::Match(end->tokAt(-2), "!!{ ; }")) { |
1126 | 3 | const Token *lastTop = end->tokAt(-2)->astTop(); |
1127 | 3 | if (Token::simpleMatch(lastTop, "<<") && |
1128 | 3 | Token::simpleMatch(lastTop->astOperand1(), "(") && |
1129 | 3 | Token::Match(lastTop->astOperand1()->previous(), "%name% (")) |
1130 | 0 | return isnoreturn(lastTop->astOperand1()->previous()); |
1131 | 3 | } |
1132 | | |
1133 | 89 | if (!Token::simpleMatch(end->tokAt(-2), ") ; }")) |
1134 | 89 | return false; |
1135 | | |
1136 | 0 | const Token *funcname = end->linkAt(-2)->previous(); |
1137 | 0 | const Token *start = funcname; |
1138 | 0 | if (Token::Match(funcname->tokAt(-3),"( * %name% )")) { |
1139 | 0 | funcname = funcname->previous(); |
1140 | 0 | start = funcname->tokAt(-3); |
1141 | 0 | } else if (funcname->isName()) { |
1142 | 0 | while (Token::Match(start, "%name%|.|::")) |
1143 | 0 | start = start->previous(); |
1144 | 0 | } else { |
1145 | 0 | return false; |
1146 | 0 | } |
1147 | 0 | if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%name% )| (")) { |
1148 | 0 | if (funcname->isKeyword()) |
1149 | 0 | return false; |
1150 | 0 | if (funcname->str() == "exit") |
1151 | 0 | return true; |
1152 | 0 | if (!isnotnoreturn(funcname)) { |
1153 | 0 | if (unknownFunc && !isnoreturn(funcname)) |
1154 | 0 | *unknownFunc = funcname->str(); |
1155 | 0 | return true; |
1156 | 0 | } |
1157 | 0 | } |
1158 | 0 | return false; |
1159 | 0 | } |
1160 | | |
1161 | | const Library::Container* Library::detectContainerInternal(const Token* const typeStart, DetectContainer detect, bool* isIterator, bool withoutStd) const |
1162 | 119k | { |
1163 | 119k | const Token* firstLinkedTok = nullptr; |
1164 | 252k | for (const Token* tok = typeStart; tok && !tok->varId(); tok = tok->next()) { |
1165 | 159k | if (!tok->link()) |
1166 | 133k | continue; |
1167 | | |
1168 | 26.2k | firstLinkedTok = tok; |
1169 | 26.2k | break; |
1170 | 159k | } |
1171 | | |
1172 | 119k | for (const std::pair<const std::string, Library::Container> & c : containers) { |
1173 | 0 | const Container& container = c.second; |
1174 | 0 | if (container.startPattern.empty()) |
1175 | 0 | continue; |
1176 | | |
1177 | 0 | const int offset = (withoutStd && startsWith(container.startPattern2, "std :: ")) ? 7 : 0; |
1178 | | |
1179 | | // If endPattern is undefined, it will always match, but itEndPattern has to be defined. |
1180 | 0 | if (detect != IteratorOnly && container.endPattern.empty()) { |
1181 | 0 | if (!Token::Match(typeStart, container.startPattern2.c_str() + offset)) |
1182 | 0 | continue; |
1183 | | |
1184 | 0 | if (isIterator) |
1185 | 0 | *isIterator = false; |
1186 | 0 | return &container; |
1187 | 0 | } |
1188 | | |
1189 | 0 | if (!firstLinkedTok) |
1190 | 0 | continue; |
1191 | | |
1192 | 0 | const bool matchedStartPattern = Token::Match(typeStart, container.startPattern2.c_str() + offset); |
1193 | 0 | if (!matchedStartPattern) |
1194 | 0 | continue; |
1195 | | |
1196 | 0 | if (detect != ContainerOnly && Token::Match(firstLinkedTok->link(), container.itEndPattern.c_str())) { |
1197 | 0 | if (isIterator) |
1198 | 0 | *isIterator = true; |
1199 | 0 | return &container; |
1200 | 0 | } |
1201 | 0 | if (detect != IteratorOnly && Token::Match(firstLinkedTok->link(), container.endPattern.c_str())) { |
1202 | 0 | if (isIterator) |
1203 | 0 | *isIterator = false; |
1204 | 0 | return &container; |
1205 | 0 | } |
1206 | 0 | } |
1207 | 119k | return nullptr; |
1208 | 119k | } |
1209 | | |
1210 | | const Library::Container* Library::detectContainer(const Token* typeStart) const |
1211 | 6.80k | { |
1212 | 6.80k | return detectContainerInternal(typeStart, ContainerOnly); |
1213 | 6.80k | } |
1214 | | |
1215 | | const Library::Container* Library::detectIterator(const Token* typeStart) const |
1216 | 0 | { |
1217 | 0 | return detectContainerInternal(typeStart, IteratorOnly); |
1218 | 0 | } |
1219 | | |
1220 | | const Library::Container* Library::detectContainerOrIterator(const Token* typeStart, bool* isIterator, bool withoutStd) const |
1221 | 112k | { |
1222 | 112k | bool res; |
1223 | 112k | const Library::Container* c = detectContainerInternal(typeStart, Both, &res, withoutStd); |
1224 | 112k | if (c && isIterator) |
1225 | 0 | *isIterator = res; |
1226 | 112k | return c; |
1227 | 112k | } |
1228 | | |
1229 | | bool Library::isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback) |
1230 | 444 | { |
1231 | 444 | if (!cond) |
1232 | 0 | return false; |
1233 | 444 | if (cond->str() == "(") { |
1234 | 0 | const Token* tok = cond->astOperand1(); |
1235 | 0 | if (tok && tok->str() == ".") { |
1236 | 0 | if (tok->astOperand1() && tok->astOperand1()->valueType()) { |
1237 | 0 | if (const Library::Container *container = tok->astOperand1()->valueType()->container) { |
1238 | 0 | return tok->astOperand2() && y == container->getYield(tok->astOperand2()->str()); |
1239 | 0 | } |
1240 | 0 | } else if (!fallback.empty()) { |
1241 | 0 | return Token::simpleMatch(cond, "( )") && cond->previous()->str() == fallback; |
1242 | 0 | } |
1243 | 0 | } |
1244 | 0 | } |
1245 | 444 | return false; |
1246 | 444 | } |
1247 | | |
1248 | | // returns true if ftok is not a library function |
1249 | | bool Library::isNotLibraryFunction(const Token *ftok) const |
1250 | 89.1k | { |
1251 | 89.1k | if (ftok->isKeyword() || ftok->isStandardType()) |
1252 | 35.5k | return true; |
1253 | | |
1254 | 53.5k | if (ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) |
1255 | 0 | return true; |
1256 | | |
1257 | | // variables are not library functions. |
1258 | 53.5k | if (ftok->varId()) |
1259 | 13.1k | return true; |
1260 | | |
1261 | 40.4k | return !matchArguments(ftok, getFunctionName(ftok)); |
1262 | 53.5k | } |
1263 | | |
1264 | | bool Library::matchArguments(const Token *ftok, const std::string &functionName) const |
1265 | 40.4k | { |
1266 | 40.4k | if (functionName.empty()) |
1267 | 36.9k | return false; |
1268 | 3.48k | const int callargs = numberOfArgumentsWithoutAst(ftok); |
1269 | 3.48k | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); |
1270 | 3.48k | if (it == functions.cend()) |
1271 | 3.48k | return false; |
1272 | 0 | int args = 0; |
1273 | 0 | int firstOptionalArg = -1; |
1274 | 0 | for (const std::pair<const int, Library::ArgumentChecks> & argCheck : it->second.argumentChecks) { |
1275 | 0 | if (argCheck.first > args) |
1276 | 0 | args = argCheck.first; |
1277 | 0 | if (argCheck.second.optional && (firstOptionalArg == -1 || firstOptionalArg > argCheck.first)) |
1278 | 0 | firstOptionalArg = argCheck.first; |
1279 | |
|
1280 | 0 | if (argCheck.second.formatstr || argCheck.second.variadic) |
1281 | 0 | return args <= callargs; |
1282 | 0 | } |
1283 | 0 | return (firstOptionalArg < 0) ? args == callargs : (callargs >= firstOptionalArg-1 && callargs <= args); |
1284 | 0 | } |
1285 | | |
1286 | | const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const |
1287 | 7.68k | { |
1288 | 7.68k | if (isNotLibraryFunction(ftok)) |
1289 | 7.68k | return nullptr; |
1290 | 0 | const std::map<std::string, WarnInfo>::const_iterator i = functionwarn.find(getFunctionName(ftok)); |
1291 | 0 | if (i == functionwarn.cend()) |
1292 | 0 | return nullptr; |
1293 | 0 | return &i->second; |
1294 | 0 | } |
1295 | | |
1296 | | bool Library::isCompliantValidationExpression(const char* p) |
1297 | 0 | { |
1298 | 0 | if (!p || !*p) |
1299 | 0 | return false; |
1300 | | |
1301 | 0 | bool error = false; |
1302 | 0 | bool range = false; |
1303 | 0 | bool has_dot = false; |
1304 | 0 | bool has_E = false; |
1305 | |
|
1306 | 0 | error = *p == '.'; |
1307 | 0 | for (; *p; p++) { |
1308 | 0 | if (std::isdigit(*p)) { |
1309 | 0 | error |= (*(p + 1) == '-'); |
1310 | 0 | } |
1311 | 0 | else if (*p == ':') { |
1312 | 0 | error |= range | (*(p + 1) == '.'); |
1313 | 0 | range = true; |
1314 | 0 | has_dot = false; |
1315 | 0 | has_E = false; |
1316 | 0 | } |
1317 | 0 | else if ((*p == '-') || (*p == '+')) { |
1318 | 0 | error |= (!std::isdigit(*(p + 1))); |
1319 | 0 | } |
1320 | 0 | else if (*p == ',') { |
1321 | 0 | range = false; |
1322 | 0 | error |= *(p + 1) == '.'; |
1323 | 0 | has_dot = false; |
1324 | 0 | has_E = false; |
1325 | 0 | } else if (*p == '.') { |
1326 | 0 | error |= has_dot | (!std::isdigit(*(p + 1))); |
1327 | 0 | has_dot = true; |
1328 | 0 | } else if (*p == 'E' || *p == 'e') { |
1329 | 0 | error |= has_E; |
1330 | 0 | has_E = true; |
1331 | 0 | } else if (*p == '!') { |
1332 | 0 | error |= !((*(p+1) == '-') || (*(p+1) == '+') || (std::isdigit(*(p + 1)))); |
1333 | 0 | } else |
1334 | 0 | return false; |
1335 | 0 | } |
1336 | 0 | return !error; |
1337 | 0 | } |
1338 | | |
1339 | | bool Library::formatstr_function(const Token* ftok) const |
1340 | 3.37k | { |
1341 | 3.37k | if (isNotLibraryFunction(ftok)) |
1342 | 3.37k | return false; |
1343 | | |
1344 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(getFunctionName(ftok)); |
1345 | 0 | if (it != functions.cend()) |
1346 | 0 | return it->second.formatstr; |
1347 | 0 | return false; |
1348 | 0 | } |
1349 | | |
1350 | | int Library::formatstr_argno(const Token* ftok) const |
1351 | 0 | { |
1352 | 0 | const std::map<int, Library::ArgumentChecks>& argumentChecksFunc = functions.at(getFunctionName(ftok)).argumentChecks; |
1353 | 0 | auto it = std::find_if(argumentChecksFunc.cbegin(), argumentChecksFunc.cend(), [](const std::pair<const int, Library::ArgumentChecks>& a) { |
1354 | 0 | return a.second.formatstr; |
1355 | 0 | }); |
1356 | 0 | return it == argumentChecksFunc.cend() ? -1 : it->first - 1; |
1357 | 0 | } |
1358 | | |
1359 | | bool Library::formatstr_scan(const Token* ftok) const |
1360 | 0 | { |
1361 | 0 | return functions.at(getFunctionName(ftok)).formatstr_scan; |
1362 | 0 | } |
1363 | | |
1364 | | bool Library::formatstr_secure(const Token* ftok) const |
1365 | 0 | { |
1366 | 0 | return functions.at(getFunctionName(ftok)).formatstr_secure; |
1367 | 0 | } |
1368 | | |
1369 | | const Library::NonOverlappingData* Library::getNonOverlappingData(const Token *ftok) const |
1370 | 1.46k | { |
1371 | 1.46k | if (isNotLibraryFunction(ftok)) |
1372 | 1.46k | return nullptr; |
1373 | 0 | const std::unordered_map<std::string, NonOverlappingData>::const_iterator it = mNonOverlappingData.find(getFunctionName(ftok)); |
1374 | 0 | return (it != mNonOverlappingData.cend()) ? &it->second : nullptr; |
1375 | 1.46k | } |
1376 | | |
1377 | | Library::UseRetValType Library::getUseRetValType(const Token *ftok) const |
1378 | 0 | { |
1379 | 0 | if (isNotLibraryFunction(ftok)) { |
1380 | 0 | if (Token::simpleMatch(ftok->astParent(), ".")) { |
1381 | 0 | const Token* contTok = ftok->astParent()->astOperand1(); |
1382 | 0 | using Yield = Library::Container::Yield; |
1383 | 0 | const Yield yield = astContainerYield(contTok); |
1384 | 0 | if (yield == Yield::START_ITERATOR || yield == Yield::END_ITERATOR || yield == Yield::AT_INDEX || |
1385 | 0 | yield == Yield::SIZE || yield == Yield::EMPTY || yield == Yield::BUFFER || yield == Yield::BUFFER_NT || |
1386 | 0 | ((yield == Yield::ITEM || yield == Yield::ITERATOR) && astContainerAction(contTok) == Library::Container::Action::NO_ACTION)) |
1387 | 0 | return Library::UseRetValType::DEFAULT; |
1388 | 0 | } |
1389 | 0 | return Library::UseRetValType::NONE; |
1390 | 0 | } |
1391 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(getFunctionName(ftok)); |
1392 | 0 | if (it != functions.cend()) |
1393 | 0 | return it->second.useretval; |
1394 | 0 | return Library::UseRetValType::NONE; |
1395 | 0 | } |
1396 | | |
1397 | | const std::string& Library::returnValue(const Token *ftok) const |
1398 | 0 | { |
1399 | 0 | if (isNotLibraryFunction(ftok)) |
1400 | 0 | return emptyString; |
1401 | 0 | const std::map<std::string, std::string>::const_iterator it = mReturnValue.find(getFunctionName(ftok)); |
1402 | 0 | return it != mReturnValue.cend() ? it->second : emptyString; |
1403 | 0 | } |
1404 | | |
1405 | | const std::string& Library::returnValueType(const Token *ftok) const |
1406 | 24.3k | { |
1407 | 24.3k | if (isNotLibraryFunction(ftok)) { |
1408 | 24.3k | if (Token::simpleMatch(ftok->astParent(), ".") && ftok->astParent()->astOperand1()) { |
1409 | 0 | const Token* contTok = ftok->astParent()->astOperand1(); |
1410 | 0 | if (contTok->valueType() && contTok->valueType()->container) |
1411 | 0 | return contTok->valueType()->container->getReturnType(ftok->str()); |
1412 | 0 | } |
1413 | 24.3k | return emptyString; |
1414 | 24.3k | } |
1415 | 0 | const std::map<std::string, std::string>::const_iterator it = mReturnValueType.find(getFunctionName(ftok)); |
1416 | 0 | return it != mReturnValueType.cend() ? it->second : emptyString; |
1417 | 24.3k | } |
1418 | | |
1419 | | int Library::returnValueContainer(const Token *ftok) const |
1420 | 206 | { |
1421 | 206 | if (isNotLibraryFunction(ftok)) |
1422 | 206 | return -1; |
1423 | 0 | const std::map<std::string, int>::const_iterator it = mReturnValueContainer.find(getFunctionName(ftok)); |
1424 | 0 | return it != mReturnValueContainer.cend() ? it->second : -1; |
1425 | 206 | } |
1426 | | |
1427 | | std::vector<MathLib::bigint> Library::unknownReturnValues(const Token *ftok) const |
1428 | 0 | { |
1429 | 0 | if (isNotLibraryFunction(ftok)) |
1430 | 0 | return std::vector<MathLib::bigint>(); |
1431 | 0 | const std::map<std::string, std::vector<MathLib::bigint>>::const_iterator it = mUnknownReturnValues.find(getFunctionName(ftok)); |
1432 | 0 | return (it == mUnknownReturnValues.cend()) ? std::vector<MathLib::bigint>() : it->second; |
1433 | 0 | } |
1434 | | |
1435 | | const Library::Function *Library::getFunction(const Token *ftok) const |
1436 | 5.61k | { |
1437 | 5.61k | if (isNotLibraryFunction(ftok)) |
1438 | 5.61k | return nullptr; |
1439 | 0 | const std::unordered_map<std::string, Function>::const_iterator it1 = functions.find(getFunctionName(ftok)); |
1440 | 0 | if (it1 == functions.cend()) |
1441 | 0 | return nullptr; |
1442 | 0 | return &it1->second; |
1443 | 0 | } |
1444 | | |
1445 | | |
1446 | | bool Library::hasminsize(const Token *ftok) const |
1447 | 1.46k | { |
1448 | 1.46k | if (isNotLibraryFunction(ftok)) |
1449 | 1.46k | return false; |
1450 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(getFunctionName(ftok)); |
1451 | 0 | if (it == functions.cend()) |
1452 | 0 | return false; |
1453 | 0 | return std::any_of(it->second.argumentChecks.cbegin(), it->second.argumentChecks.cend(), [](const std::pair<const int, Library::ArgumentChecks>& a) { |
1454 | 0 | return !a.second.minsizes.empty(); |
1455 | 0 | }); |
1456 | 0 | } |
1457 | | |
1458 | | Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const |
1459 | 0 | { |
1460 | 0 | const ArgumentChecks* arg = getarg(ftok, argnr); |
1461 | 0 | if (arg) |
1462 | 0 | return arg->direction; |
1463 | 0 | if (formatstr_function(ftok)) { |
1464 | 0 | const int fs_argno = formatstr_argno(ftok); |
1465 | 0 | if (fs_argno >= 0 && argnr >= fs_argno) { |
1466 | 0 | if (formatstr_scan(ftok)) |
1467 | 0 | return ArgumentChecks::Direction::DIR_OUT; |
1468 | 0 | return ArgumentChecks::Direction::DIR_IN; |
1469 | 0 | } |
1470 | 0 | } |
1471 | 0 | return ArgumentChecks::Direction::DIR_UNKNOWN; |
1472 | 0 | } |
1473 | | |
1474 | | bool Library::ignorefunction(const std::string& functionName) const |
1475 | 0 | { |
1476 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); |
1477 | 0 | if (it != functions.cend()) |
1478 | 0 | return it->second.ignore; |
1479 | 0 | return false; |
1480 | 0 | } |
1481 | | bool Library::isUse(const std::string& functionName) const |
1482 | 0 | { |
1483 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); |
1484 | 0 | if (it != functions.cend()) |
1485 | 0 | return it->second.use; |
1486 | 0 | return false; |
1487 | 0 | } |
1488 | | bool Library::isLeakIgnore(const std::string& functionName) const |
1489 | 0 | { |
1490 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); |
1491 | 0 | if (it != functions.cend()) |
1492 | 0 | return it->second.leakignore; |
1493 | 0 | return false; |
1494 | 0 | } |
1495 | | bool Library::isFunctionConst(const std::string& functionName, bool pure) const |
1496 | 1.46k | { |
1497 | 1.46k | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); |
1498 | 1.46k | if (it != functions.cend()) |
1499 | 0 | return pure ? it->second.ispure : it->second.isconst; |
1500 | 1.46k | return false; |
1501 | 1.46k | } |
1502 | | bool Library::isFunctionConst(const Token *ftok) const |
1503 | 45 | { |
1504 | 45 | if (ftok->function() && ftok->function()->isConst()) |
1505 | 0 | return true; |
1506 | 45 | if (isNotLibraryFunction(ftok)) { |
1507 | 45 | if (Token::simpleMatch(ftok->astParent(), ".")) { |
1508 | 0 | using Yield = Library::Container::Yield; |
1509 | 0 | const Yield yield = astContainerYield(ftok->astParent()->astOperand1()); |
1510 | 0 | if (yield == Yield::EMPTY || yield == Yield::SIZE || yield == Yield::BUFFER_NT) |
1511 | 0 | return true; |
1512 | 0 | } |
1513 | 45 | return false; |
1514 | 45 | } |
1515 | 0 | const std::unordered_map<std::string, Function>::const_iterator it = functions.find(getFunctionName(ftok)); |
1516 | 0 | return (it != functions.cend() && it->second.isconst); |
1517 | 45 | } |
1518 | | |
1519 | | bool Library::isnoreturn(const Token *ftok) const |
1520 | 31.0k | { |
1521 | 31.0k | if (ftok->function() && ftok->function()->isAttributeNoreturn()) |
1522 | 0 | return true; |
1523 | 31.0k | if (isNotLibraryFunction(ftok)) { |
1524 | 31.0k | if (Token::simpleMatch(ftok->astParent(), ".")) { |
1525 | 0 | const Token* contTok = ftok->astParent()->astOperand1(); |
1526 | 0 | if (astContainerAction(contTok) != Library::Container::Action::NO_ACTION || |
1527 | 0 | astContainerYield(contTok) != Library::Container::Yield::NO_YIELD) |
1528 | 0 | return false; |
1529 | 0 | } |
1530 | 31.0k | return false; |
1531 | 31.0k | } |
1532 | 0 | const std::unordered_map<std::string, FalseTrueMaybe>::const_iterator it = mNoReturn.find(getFunctionName(ftok)); |
1533 | 0 | if (it == mNoReturn.end()) |
1534 | 0 | return false; |
1535 | 0 | if (it->second == FalseTrueMaybe::Maybe) |
1536 | 0 | return true; |
1537 | 0 | return it->second == FalseTrueMaybe::True; |
1538 | 0 | } |
1539 | | |
1540 | | bool Library::isnotnoreturn(const Token *ftok) const |
1541 | 0 | { |
1542 | 0 | if (ftok->function() && ftok->function()->isAttributeNoreturn()) |
1543 | 0 | return false; |
1544 | 0 | if (isNotLibraryFunction(ftok)) |
1545 | 0 | return false; |
1546 | 0 | const std::unordered_map<std::string, FalseTrueMaybe>::const_iterator it = mNoReturn.find(getFunctionName(ftok)); |
1547 | 0 | if (it == mNoReturn.end()) |
1548 | 0 | return false; |
1549 | 0 | if (it->second == FalseTrueMaybe::Maybe) |
1550 | 0 | return false; |
1551 | 0 | return it->second == FalseTrueMaybe::False; |
1552 | 0 | } |
1553 | | |
1554 | | bool Library::markupFile(const std::string &path) const |
1555 | 4.08k | { |
1556 | 4.08k | return mMarkupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != mMarkupExtensions.end(); |
1557 | 4.08k | } |
1558 | | |
1559 | | bool Library::processMarkupAfterCode(const std::string &path) const |
1560 | 0 | { |
1561 | 0 | const std::map<std::string, bool>::const_iterator it = mProcessAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); |
1562 | 0 | return (it == mProcessAfterCode.cend() || it->second); |
1563 | 0 | } |
1564 | | |
1565 | | bool Library::reportErrors(const std::string &path) const |
1566 | 1.48k | { |
1567 | 1.48k | const std::map<std::string, bool>::const_iterator it = mReportErrors.find(Path::getFilenameExtensionInLowerCase(path)); |
1568 | 1.48k | return (it == mReportErrors.cend() || it->second); |
1569 | 1.48k | } |
1570 | | |
1571 | | bool Library::isexecutableblock(const std::string &file, const std::string &token) const |
1572 | 92.2k | { |
1573 | 92.2k | const std::unordered_map<std::string, CodeBlock>::const_iterator it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); |
1574 | 92.2k | return (it != mExecutableBlocks.cend() && it->second.isBlock(token)); |
1575 | 92.2k | } |
1576 | | |
1577 | | int Library::blockstartoffset(const std::string &file) const |
1578 | 0 | { |
1579 | 0 | int offset = -1; |
1580 | 0 | const std::unordered_map<std::string, CodeBlock>::const_iterator map_it |
1581 | 0 | = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); |
1582 | |
|
1583 | 0 | if (map_it != mExecutableBlocks.end()) { |
1584 | 0 | offset = map_it->second.offset(); |
1585 | 0 | } |
1586 | 0 | return offset; |
1587 | 0 | } |
1588 | | |
1589 | | const std::string& Library::blockstart(const std::string &file) const |
1590 | 0 | { |
1591 | 0 | const std::unordered_map<std::string, CodeBlock>::const_iterator map_it |
1592 | 0 | = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); |
1593 | |
|
1594 | 0 | if (map_it != mExecutableBlocks.end()) { |
1595 | 0 | return map_it->second.start(); |
1596 | 0 | } |
1597 | 0 | return emptyString; |
1598 | 0 | } |
1599 | | |
1600 | | const std::string& Library::blockend(const std::string &file) const |
1601 | 0 | { |
1602 | 0 | const std::unordered_map<std::string, CodeBlock>::const_iterator map_it |
1603 | 0 | = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); |
1604 | |
|
1605 | 0 | if (map_it != mExecutableBlocks.end()) { |
1606 | 0 | return map_it->second.end(); |
1607 | 0 | } |
1608 | 0 | return emptyString; |
1609 | 0 | } |
1610 | | |
1611 | | bool Library::iskeyword(const std::string &file, const std::string &keyword) const |
1612 | 0 | { |
1613 | 0 | const std::map<std::string, std::set<std::string>>::const_iterator it = |
1614 | 0 | mKeywords.find(Path::getFilenameExtensionInLowerCase(file)); |
1615 | 0 | return (it != mKeywords.end() && it->second.count(keyword)); |
1616 | 0 | } |
1617 | | |
1618 | | bool Library::isimporter(const std::string& file, const std::string &importer) const |
1619 | 0 | { |
1620 | 0 | const std::map<std::string, std::set<std::string>>::const_iterator it = |
1621 | 0 | mImporters.find(Path::getFilenameExtensionInLowerCase(file)); |
1622 | 0 | return (it != mImporters.end() && it->second.count(importer) > 0); |
1623 | 0 | } |
1624 | | |
1625 | | const Token* Library::getContainerFromYield(const Token* tok, Library::Container::Yield yield) const |
1626 | 7.52k | { |
1627 | 7.52k | if (!tok) |
1628 | 0 | return nullptr; |
1629 | 7.52k | if (Token::Match(tok->tokAt(-2), ". %name% (")) { |
1630 | 0 | const Token* containerTok = tok->tokAt(-2)->astOperand1(); |
1631 | 0 | if (!astIsContainer(containerTok)) |
1632 | 0 | return nullptr; |
1633 | 0 | if (containerTok->valueType()->container && |
1634 | 0 | containerTok->valueType()->container->getYield(tok->strAt(-1)) == yield) |
1635 | 0 | return containerTok; |
1636 | 0 | if (yield == Library::Container::Yield::EMPTY && Token::simpleMatch(tok->tokAt(-1), "empty ( )")) |
1637 | 0 | return containerTok; |
1638 | 0 | if (yield == Library::Container::Yield::SIZE && Token::Match(tok->tokAt(-1), "size|length ( )")) |
1639 | 0 | return containerTok; |
1640 | 7.52k | } else if (Token::Match(tok->previous(), "%name% (")) { |
1641 | 5.40k | if (const Library::Function* f = this->getFunction(tok->previous())) { |
1642 | 0 | if (f->containerYield == yield) { |
1643 | 0 | return tok->astOperand2(); |
1644 | 0 | } |
1645 | 0 | } |
1646 | 5.40k | } |
1647 | 7.52k | return nullptr; |
1648 | 7.52k | } |
1649 | | |
1650 | | // cppcheck-suppress unusedFunction |
1651 | | const Token* Library::getContainerFromAction(const Token* tok, Library::Container::Action action) const |
1652 | 0 | { |
1653 | 0 | if (!tok) |
1654 | 0 | return nullptr; |
1655 | 0 | if (Token::Match(tok->tokAt(-2), ". %name% (")) { |
1656 | 0 | const Token* containerTok = tok->tokAt(-2)->astOperand1(); |
1657 | 0 | if (!astIsContainer(containerTok)) |
1658 | 0 | return nullptr; |
1659 | 0 | if (containerTok->valueType()->container && |
1660 | 0 | containerTok->valueType()->container->getAction(tok->strAt(-1)) == action) |
1661 | 0 | return containerTok; |
1662 | 0 | if (Token::simpleMatch(tok->tokAt(-1), "empty ( )")) |
1663 | 0 | return containerTok; |
1664 | 0 | } else if (Token::Match(tok->previous(), "%name% (")) { |
1665 | 0 | if (const Library::Function* f = this->getFunction(tok->previous())) { |
1666 | 0 | if (f->containerAction == action) { |
1667 | 0 | return tok->astOperand2(); |
1668 | 0 | } |
1669 | 0 | } |
1670 | 0 | } |
1671 | 0 | return nullptr; |
1672 | 0 | } |
1673 | | |
1674 | | bool Library::isSmartPointer(const Token* tok) const |
1675 | 36.4k | { |
1676 | 36.4k | return detectSmartPointer(tok); |
1677 | 36.4k | } |
1678 | | |
1679 | | const Library::SmartPointer* Library::detectSmartPointer(const Token* tok, bool withoutStd) const |
1680 | 148k | { |
1681 | 148k | std::string typestr = withoutStd ? "std::" : ""; |
1682 | 380k | while (Token::Match(tok, "%name%|::")) { |
1683 | 232k | typestr += tok->str(); |
1684 | 232k | tok = tok->next(); |
1685 | 232k | } |
1686 | 148k | auto it = smartPointers.find(typestr); |
1687 | 148k | if (it == smartPointers.end()) |
1688 | 148k | return nullptr; |
1689 | 0 | return &it->second; |
1690 | 148k | } |
1691 | | |
1692 | | const Library::Container * getLibraryContainer(const Token * tok) |
1693 | 396k | { |
1694 | 396k | if (!tok) |
1695 | 0 | return nullptr; |
1696 | | // TODO: Support dereferencing iterators |
1697 | | // TODO: Support dereferencing with -> |
1698 | 396k | if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) { |
1699 | 0 | for (const ValueFlow::Value& v:tok->astOperand1()->values()) { |
1700 | 0 | if (!v.isLocalLifetimeValue()) |
1701 | 0 | continue; |
1702 | 0 | if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) |
1703 | 0 | continue; |
1704 | 0 | return getLibraryContainer(v.tokvalue); |
1705 | 0 | } |
1706 | 0 | } |
1707 | 396k | if (!tok->valueType()) |
1708 | 204k | return nullptr; |
1709 | 192k | return tok->valueType()->container; |
1710 | 396k | } |
1711 | | |
1712 | | Library::TypeCheck Library::getTypeCheck(std::string check, std::string typeName) const |
1713 | 0 | { |
1714 | 0 | auto it = mTypeChecks.find(std::pair<std::string, std::string>(std::move(check), std::move(typeName))); |
1715 | 0 | return it == mTypeChecks.end() ? TypeCheck::def : it->second; |
1716 | 0 | } |
1717 | | |
1718 | | bool Library::hasAnyTypeCheck(const std::string& typeName) const |
1719 | 0 | { |
1720 | 0 | return std::any_of(mTypeChecks.begin(), mTypeChecks.end(), [&](const std::pair<std::pair<std::string, std::string>, Library::TypeCheck>& tc) { |
1721 | 0 | return tc.first.second == typeName; |
1722 | 0 | }); |
1723 | 0 | } |
1724 | | |
1725 | | std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue, |
1726 | | const Settings* settings, |
1727 | | std::unordered_map<nonneg int, const Token*>* lookupVarId) |
1728 | 0 | { |
1729 | 0 | std::shared_ptr<TokenList> tokenList = std::make_shared<TokenList>(settings); |
1730 | 0 | { |
1731 | 0 | const std::string code = "return " + returnValue + ";"; |
1732 | 0 | std::istringstream istr(code); |
1733 | 0 | if (!tokenList->createTokens(istr)) |
1734 | 0 | return nullptr; |
1735 | 0 | } |
1736 | | |
1737 | | // combine operators, set links, etc.. |
1738 | 0 | std::stack<Token*> lpar; |
1739 | 0 | for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) { |
1740 | 0 | if (Token::Match(tok2, "[!<>=] =")) { |
1741 | 0 | tok2->str(tok2->str() + "="); |
1742 | 0 | tok2->deleteNext(); |
1743 | 0 | } else if (tok2->str() == "(") |
1744 | 0 | lpar.push(tok2); |
1745 | 0 | else if (tok2->str() == ")") { |
1746 | 0 | if (lpar.empty()) |
1747 | 0 | return nullptr; |
1748 | 0 | Token::createMutualLinks(lpar.top(), tok2); |
1749 | 0 | lpar.pop(); |
1750 | 0 | } |
1751 | 0 | } |
1752 | 0 | if (!lpar.empty()) |
1753 | 0 | return nullptr; |
1754 | | |
1755 | | // set varids |
1756 | 0 | for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) { |
1757 | 0 | if (!startsWith(tok2->str(), "arg")) |
1758 | 0 | continue; |
1759 | 0 | nonneg int const id = strToInt<nonneg int>(tok2->str().c_str() + 3); |
1760 | 0 | tok2->varId(id); |
1761 | 0 | if (lookupVarId) |
1762 | 0 | (*lookupVarId)[id] = tok2; |
1763 | 0 | } |
1764 | | |
1765 | | // Evaluate expression |
1766 | 0 | tokenList->createAst(); |
1767 | 0 | Token* expr = tokenList->front()->astOperand1(); |
1768 | 0 | ValueFlow::valueFlowConstantFoldAST(expr, settings); |
1769 | 0 | return {tokenList, expr}; |
1770 | 0 | } |