Cppcheck
checkio.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 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 "checkio.h"
21 
22 #include "astutils.h"
23 #include "errortypes.h"
24 #include "library.h"
25 #include "mathlib.h"
26 #include "platform.h"
27 #include "settings.h"
28 #include "symboldatabase.h"
29 #include "token.h"
30 #include "tokenize.h"
31 #include "utils.h"
32 #include "vfvalue.h"
33 
34 #include <algorithm>
35 #include <cctype>
36 #include <cstdlib>
37 #include <functional>
38 #include <list>
39 #include <map>
40 #include <set>
41 #include <sstream>
42 #include <unordered_set>
43 #include <utility>
44 #include <vector>
45 
46 //---------------------------------------------------------------------------
47 
48 // Register CheckIO..
49 namespace {
50  CheckIO instance;
51 }
52 
53 // CVE ID used:
54 static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer
55 static const CWE CWE398(398U); // Indicator of Poor Code Quality
56 static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime
57 static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments
58 static const CWE CWE686(686U); // Function Call With Incorrect Argument Type
59 static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value
60 static const CWE CWE704(704U); // Incorrect Type Conversion or Cast
61 static const CWE CWE910(910U); // Use of Expired File Descriptor
62 
63 //---------------------------------------------------------------------------
64 // std::cout << std::cout;
65 //---------------------------------------------------------------------------
67 {
68  if (mTokenizer->isC())
69  return;
70 
71  logChecker("CheckIO::checkCoutCerrMisusage"); // c
72 
73  const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
74  for (const Scope * scope : symbolDatabase->functionScopes) {
75  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
76  if (Token::Match(tok, "std :: cout|cerr !!.") && tok->next()->astParent() && tok->next()->astParent()->astOperand1() == tok->next()) {
77  const Token* tok2 = tok->next();
78  while (tok2->astParent() && tok2->astParent()->str() == "<<") {
79  tok2 = tok2->astParent();
80  if (tok2->astOperand2() && Token::Match(tok2->astOperand2()->previous(), "std :: cout|cerr"))
81  coutCerrMisusageError(tok, tok2->astOperand2()->strAt(1));
82  }
83  }
84  }
85  }
86 }
87 
88 void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName)
89 {
90  reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, Certainty::normal);
91 }
92 
93 //---------------------------------------------------------------------------
94 // fflush(stdin) <- fflush only applies to output streams in ANSI C
95 // fread(); fwrite(); <- consecutive read/write statements require repositioning in between
96 // fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
97 // fclose(); fread(); <- Use closed file
98 //---------------------------------------------------------------------------
100 static OpenMode getMode(const std::string& str)
101 {
102  if (str.find('+', 1) != std::string::npos)
103  return OpenMode::RW_MODE;
104  if (str.find('w') != std::string::npos || str.find('a') != std::string::npos)
105  return OpenMode::WRITE_MODE;
106  if (str.find('r') != std::string::npos)
107  return OpenMode::READ_MODE;
108  return OpenMode::UNKNOWN_OM;
109 }
110 
111 namespace {
112  struct Filepointer {
113  OpenMode mode;
114  nonneg int mode_indent{};
115  enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation = Operation::NONE;
116  nonneg int op_indent{};
117  enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
118  AppendMode append_mode = AppendMode::UNKNOWN_AM;
119  std::string filename;
120  explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
121  : mode(mode_) {}
122  };
123 
124  const std::unordered_set<std::string> whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" };
125 }
126 
128 {
129  const bool windows = mSettings->platform.isWindows();
130  const bool printPortability = mSettings->severity.isEnabled(Severity::portability);
131  const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
132 
133  std::map<int, Filepointer> filepointers;
134 
135  logChecker("CheckIO::checkFileUsage");
136 
137  const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
138  for (const Variable* var : symbolDatabase->variableList()) {
139  if (!var || !var->declarationId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *"))
140  continue;
141 
142  if (var->isLocal()) {
143  if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor"
144  filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM)));
145  else
146  filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::CLOSED)));
147  } else {
148  filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM)));
149  // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
150  }
151  }
152 
153  for (const Scope * scope : symbolDatabase->functionScopes) {
154  int indent = 0;
155  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
156  if (Token::Match(tok, "%name% (") && isUnevaluated(tok)) {
157  tok = tok->linkAt(1);
158  continue;
159  }
160  if (tok->str() == "{")
161  indent++;
162  else if (tok->str() == "}") {
163  indent--;
164  for (std::pair<const int, Filepointer>& filepointer : filepointers) {
165  if (indent < filepointer.second.mode_indent) {
166  filepointer.second.mode_indent = 0;
167  filepointer.second.mode = OpenMode::UNKNOWN_OM;
168  }
169  if (indent < filepointer.second.op_indent) {
170  filepointer.second.op_indent = 0;
171  filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
172  }
173  }
174  } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break" || mSettings->library.isnoreturn(tok)) { // Reset upon return, continue or break
175  for (std::pair<const int, Filepointer>& filepointer : filepointers) {
176  filepointer.second.mode_indent = 0;
177  filepointer.second.mode = OpenMode::UNKNOWN_OM;
178  filepointer.second.op_indent = 0;
179  filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
180  }
181  } else if (Token::Match(tok, "%var% =") &&
182  (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" &&
183  (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) {
184  const std::map<int, Filepointer>::iterator i = filepointers.find(tok->varId());
185  if (i != filepointers.end()) {
186  i->second.mode = OpenMode::UNKNOWN_OM;
187  i->second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
188  }
189  } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
190  std::string mode;
191  const Token* fileTok = nullptr;
192  const Token* fileNameTok = nullptr;
193  Filepointer::Operation operation = Filepointer::Operation::NONE;
194 
195  if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" ||
196  (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) &&
197  tok->strAt(-1) == "=") {
198  if (tok->str() != "tmpfile") {
199  const Token* modeTok = tok->tokAt(2)->nextArgument();
200  if (modeTok && modeTok->tokType() == Token::eString)
201  mode = modeTok->strValue();
202  } else
203  mode = "wb+";
204  fileTok = tok->tokAt(-2);
205  operation = Filepointer::Operation::OPEN;
206  if (Token::Match(tok, "fopen ( %str% ,"))
207  fileNameTok = tok->tokAt(2);
208  } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) {
209  const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument();
210  if (modeTok && modeTok->tokType() == Token::eString)
211  mode = modeTok->strValue();
212  fileTok = tok->tokAt(3);
213  operation = Filepointer::Operation::OPEN;
214  } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") ||
215  (windows && tok->str() == "_fseeki64")) {
216  fileTok = tok->tokAt(2);
217  if (printPortability && fileTok && tok->str() == "fflush") {
218  if (fileTok->str() == "stdin")
219  fflushOnInputStreamError(tok, fileTok->str());
220  else {
221  const Filepointer& f = filepointers[fileTok->varId()];
222  if (f.mode == OpenMode::READ_MODE)
223  fflushOnInputStreamError(tok, fileTok->str());
224  }
225  }
226  operation = Filepointer::Operation::POSITIONING;
227  } else if (tok->str() == "fgetc" || tok->str() == "fgetwc" ||
228  tok->str() == "fgets" || tok->str() == "fgetws" || tok->str() == "fread" ||
229  tok->str() == "fscanf" || tok->str() == "fwscanf" || tok->str() == "getc" ||
230  (windows && (tok->str() == "fscanf_s" || tok->str() == "fwscanf_s"))) {
231  if (tok->str().find("scanf") != std::string::npos)
232  fileTok = tok->tokAt(2);
233  else
234  fileTok = tok->linkAt(1)->previous();
235  operation = Filepointer::Operation::READ;
236  } else if (tok->str() == "fputc" || tok->str() == "fputwc" ||
237  tok->str() == "fputs" || tok->str() == "fputws" || tok->str() == "fwrite" ||
238  tok->str() == "fprintf" || tok->str() == "fwprintf" || tok->str() == "putcc" ||
239  (windows && (tok->str() == "fprintf_s" || tok->str() == "fwprintf_s"))) {
240  if (tok->str().find("printf") != std::string::npos)
241  fileTok = tok->tokAt(2);
242  else
243  fileTok = tok->linkAt(1)->previous();
244  operation = Filepointer::Operation::WRITE;
245  } else if (tok->str() == "fclose") {
246  fileTok = tok->tokAt(2);
247  operation = Filepointer::Operation::CLOSE;
248  } else if (whitelist.find(tok->str()) != whitelist.end()) {
249  fileTok = tok->tokAt(2);
250  if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok)
251  fileTok = fileTok->nextArgument();
252  operation = Filepointer::Operation::UNIMPORTANT;
253  } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings->library.isFunctionConst(tok->str(), true)) {
254  const Token* const end2 = tok->linkAt(1);
255  if (scope->functionOf && scope->functionOf->isClassOrStruct() && !scope->function->isStatic() && ((tok->strAt(-1) != "::" && tok->strAt(-1) != ".") || tok->strAt(-2) == "this")) {
256  if (!tok->function() || (tok->function()->nestedIn && tok->function()->nestedIn->isClassOrStruct())) {
257  for (std::pair<const int, Filepointer>& filepointer : filepointers) {
258  const Variable* var = symbolDatabase->getVariableFromVarId(filepointer.first);
259  if (!var || !(var->isLocal() || var->isGlobal() || var->isStatic())) {
260  filepointer.second.mode = OpenMode::UNKNOWN_OM;
261  filepointer.second.mode_indent = 0;
262  filepointer.second.op_indent = indent;
263  filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
264  }
265  }
266  continue;
267  }
268  }
269  for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
270  if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) {
271  fileTok = tok2;
272  operation = Filepointer::Operation::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now
273  break;
274  }
275  }
276  }
277 
278  while (Token::Match(fileTok, "%name% ."))
279  fileTok = fileTok->tokAt(2);
280 
281  if (!fileTok || !fileTok->varId() || fileTok->strAt(1) == "[")
282  continue;
283 
284  if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File
285  filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM)));
286  }
287 
288  Filepointer& f = filepointers[fileTok->varId()];
289 
290  switch (operation) {
291  case Filepointer::Operation::OPEN:
292  if (fileNameTok) {
293  for (std::map<int, Filepointer>::const_iterator it = filepointers.cbegin(); it != filepointers.cend(); ++it) {
294  const Filepointer &fptr = it->second;
295  if (fptr.filename == fileNameTok->str() && (fptr.mode == OpenMode::RW_MODE || fptr.mode == OpenMode::WRITE_MODE))
296  incompatibleFileOpenError(tok, fileNameTok->str());
297  }
298 
299  f.filename = fileNameTok->str();
300  }
301 
302  f.mode = getMode(mode);
303  if (mode.find('a') != std::string::npos) {
304  if (f.mode == OpenMode::RW_MODE)
305  f.append_mode = Filepointer::AppendMode::APPEND_EX;
306  else
307  f.append_mode = Filepointer::AppendMode::APPEND;
308  } else
309  f.append_mode = Filepointer::AppendMode::UNKNOWN_AM;
310  f.mode_indent = indent;
311  break;
312  case Filepointer::Operation::POSITIONING:
313  if (f.mode == OpenMode::CLOSED)
314  useClosedFileError(tok);
315  else if (f.append_mode == Filepointer::AppendMode::APPEND && tok->str() != "fflush" && printWarnings)
317  break;
318  case Filepointer::Operation::READ:
319  if (f.mode == OpenMode::CLOSED)
320  useClosedFileError(tok);
321  else if (f.mode == OpenMode::WRITE_MODE)
323  else if (f.lastOperation == Filepointer::Operation::WRITE)
325  break;
326  case Filepointer::Operation::WRITE:
327  if (f.mode == OpenMode::CLOSED)
328  useClosedFileError(tok);
329  else if (f.mode == OpenMode::READ_MODE)
331  else if (f.lastOperation == Filepointer::Operation::READ)
333  break;
334  case Filepointer::Operation::CLOSE:
335  if (f.mode == OpenMode::CLOSED)
336  useClosedFileError(tok);
337  else
338  f.mode = OpenMode::CLOSED;
339  f.mode_indent = indent;
340  break;
341  case Filepointer::Operation::UNIMPORTANT:
342  if (f.mode == OpenMode::CLOSED)
343  useClosedFileError(tok);
344  break;
345  case Filepointer::Operation::UNKNOWN_OP:
346  f.mode = OpenMode::UNKNOWN_OM;
347  f.mode_indent = 0;
348  break;
349  default:
350  break;
351  }
352  if (operation != Filepointer::Operation::NONE && operation != Filepointer::Operation::UNIMPORTANT) {
353  f.op_indent = indent;
354  f.lastOperation = operation;
355  }
356  }
357  }
358  for (std::pair<const int, Filepointer>& filepointer : filepointers) {
359  filepointer.second.op_indent = 0;
360  filepointer.second.mode = OpenMode::UNKNOWN_OM;
361  filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
362  }
363  }
364 }
365 
366 void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname)
367 {
369  "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, Certainty::normal);
370 }
371 
373 {
375  "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, Certainty::normal);
376 }
377 
379 {
381  "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, Certainty::normal);
382 }
383 
385 {
387  "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, Certainty::normal);
388 }
389 
391 {
393  "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal);
394 }
395 
397 {
399  "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal);
400 }
401 
402 void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename)
403 {
405  "incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal);
406 }
407 
408 
409 //---------------------------------------------------------------------------
410 // scanf without field width limits can crash with huge input data
411 //---------------------------------------------------------------------------
413 {
415  return;
416 
417  const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
418  for (const Scope * scope : symbolDatabase->functionScopes) {
419  for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
420  const Token *formatToken = nullptr;
421  if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
422  formatToken = tok->tokAt(2);
423  else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
424  const Token* nextArg = tok->tokAt(2)->nextArgument();
425  if (nextArg && nextArg->tokType() == Token::eString)
426  formatToken = nextArg;
427  else
428  continue;
429  } else
430  continue;
431 
432  bool format = false;
433 
434  // scan the string backwards, so we do not need to keep states
435  const std::string &formatstr(formatToken->str());
436  for (std::size_t i = 1; i < formatstr.length(); i++) {
437  if (formatstr[i] == '%')
438  format = !format;
439 
440  else if (!format)
441  continue;
442 
443  else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') {
444  format = false;
445  }
446 
447  else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') {
448  if (formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) // #3490 - field width limits are only necessary for string input
449  invalidScanfError(tok);
450  format = false;
451  }
452  }
453  }
454  }
455 }
456 
458 {
459  const std::string fname = (tok ? tok->str() : std::string("scanf"));
461  "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" +
462  fname + "() without field width limits can crash with huge input data. Add a field width "
463  "specifier to fix this problem.\n"
464  "\n"
465  "Sample program that can crash:\n"
466  "\n"
467  "#include <stdio.h>\n"
468  "int main()\n"
469  "{\n"
470  " char c[5];\n"
471  " scanf(\"%s\", c);\n"
472  " return 0;\n"
473  "}\n"
474  "\n"
475  "Typing in 5 or more characters may make the program crash. The correct usage "
476  "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the "
477  "terminating null byte.\n"
478  "Source: http://linux.die.net/man/3/scanf\n"
479  "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c",
481 }
482 
483 //---------------------------------------------------------------------------
484 // printf("%u", "xyz"); // Wrong argument type
485 // printf("%u%s", 1); // Too few arguments
486 // printf("", 1); // Too much arguments
487 //---------------------------------------------------------------------------
488 
489 static bool findFormat(nonneg int arg, const Token *firstArg,
490  const Token *&formatStringTok, const Token *&formatArgTok)
491 {
492  const Token* argTok = firstArg;
493 
494  for (int i = 0; i < arg && argTok; ++i)
495  argTok = argTok->nextArgument();
496 
497  if (Token::Match(argTok, "%str% [,)]")) {
498  formatArgTok = argTok->nextArgument();
499  formatStringTok = argTok;
500  return true;
501  }
502  if (Token::Match(argTok, "%var% [,)]") &&
503  argTok->variable() &&
504  Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") &&
505  (argTok->variable()->isPointer() ||
506  (argTok->variable()->dimensions().size() == 1 &&
507  argTok->variable()->dimensionKnown(0) &&
508  argTok->variable()->dimension(0) != 0))) {
509  formatArgTok = argTok->nextArgument();
510  if (!argTok->values().empty()) {
511  const std::list<ValueFlow::Value>::const_iterator value = std::find_if(
512  argTok->values().cbegin(), argTok->values().cend(), std::mem_fn(&ValueFlow::Value::isTokValue));
513  if (value != argTok->values().cend() && value->isTokValue() && value->tokvalue &&
514  value->tokvalue->tokType() == Token::eString) {
515  formatStringTok = value->tokvalue;
516  }
517  }
518  return true;
519  }
520  return false;
521 }
522 
523 // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename
524 static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::")
525 {
526  return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename);
527 }
528 
530 {
531  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
532  const bool isWindows = mSettings->platform.isWindows();
533 
534  logChecker("CheckIO::checkWrongPrintfScanfArguments");
535 
536  for (const Scope * scope : symbolDatabase->functionScopes) {
537  for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
538  if (!tok->isName()) continue;
539 
540  const Token* argListTok = nullptr; // Points to first va_list argument
541  const Token* formatStringTok = nullptr; // Points to format string token
542 
543  bool scan = false;
544  bool scanf_s = false;
545  int formatStringArgNo = -1;
546 
547  if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) {
548  formatStringArgNo = mSettings->library.formatstr_argno(tok);
549  scan = mSettings->library.formatstr_scan(tok);
550  scanf_s = mSettings->library.formatstr_secure(tok);
551  }
552 
553  if (formatStringArgNo >= 0) {
554  // formatstring found in library. Find format string and first argument belonging to format string.
555  if (!findFormat(formatStringArgNo, tok->tokAt(2), formatStringTok, argListTok))
556  continue;
557  } else if (Token::simpleMatch(tok, "swprintf (")) {
558  if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) {
559  // Find third parameter and format string
560  if (!findFormat(1, tok->tokAt(2), formatStringTok, argListTok))
561  continue;
562  } else {
563  // Find fourth parameter and format string
564  if (!findFormat(2, tok->tokAt(2), formatStringTok, argListTok))
565  continue;
566  }
567  } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) {
568  // template <size_t size> int sprintf_s(char (&buffer)[size], const char *format, ...);
569  if (findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) {
570  if (!formatStringTok)
571  continue;
572  }
573  // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);
574  else if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) {
575  if (!formatStringTok)
576  continue;
577  }
578  } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) {
579  // template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...);
580  if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) {
581  if (!formatStringTok)
582  continue;
583  }
584  // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...);
585  else if (findFormat(3, tok->tokAt(2), formatStringTok, argListTok)) {
586  if (!formatStringTok)
587  continue;
588  }
589  } else {
590  continue;
591  }
592 
593  if (!formatStringTok)
594  continue;
595 
596  checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s);
597  }
598  }
599 }
600 
601 void CheckIO::checkFormatString(const Token * const tok,
602  const Token * const formatStringTok,
603  const Token * argListTok,
604  const bool scan,
605  const bool scanf_s)
606 {
607  const bool isWindows = mSettings->platform.isWindows();
608  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
609  const std::string &formatString = formatStringTok->str();
610 
611  // Count format string parameters..
612  int numFormat = 0;
613  int numSecure = 0;
614  bool percent = false;
615  const Token* argListTok2 = argListTok;
616  std::set<int> parameterPositionsUsed;
617  for (std::string::const_iterator i = formatString.cbegin(); i != formatString.cend(); ++i) {
618  if (*i == '%') {
619  percent = !percent;
620  } else if (percent && *i == '[') {
621  while (i != formatString.cend()) {
622  if (*i == ']') {
623  numFormat++;
624  if (argListTok)
625  argListTok = argListTok->nextArgument();
626  percent = false;
627  break;
628  }
629  ++i;
630  }
631  if (scanf_s) {
632  numSecure++;
633  if (argListTok) {
634  argListTok = argListTok->nextArgument();
635  }
636  }
637  if (i == formatString.cend())
638  break;
639  } else if (percent) {
640  percent = false;
641 
642  bool _continue = false;
643  bool skip = false;
644  std::string width;
645  int parameterPosition = 0;
646  bool hasParameterPosition = false;
647  while (i != formatString.cend() && *i != '[' && !std::isalpha((unsigned char)*i)) {
648  if (*i == '*') {
649  skip = true;
650  if (scan)
651  _continue = true;
652  else {
653  numFormat++;
654  if (argListTok)
655  argListTok = argListTok->nextArgument();
656  }
657  } else if (std::isdigit(*i)) {
658  width += *i;
659  } else if (*i == '$') {
660  parameterPosition = strToInt<int>(width);
661  hasParameterPosition = true;
662  width.clear();
663  }
664  ++i;
665  }
666  auto bracketBeg = formatString.cend();
667  if (i != formatString.cend() && *i == '[') {
668  bracketBeg = i;
669  while (i != formatString.cend()) {
670  if (*i == ']')
671  break;
672 
673  ++i;
674  }
675  if (scanf_s && !skip) {
676  numSecure++;
677  if (argListTok) {
678  argListTok = argListTok->nextArgument();
679  }
680  }
681  }
682  if (i == formatString.cend())
683  break;
684  if (_continue)
685  continue;
686 
687  if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
688  ++numFormat;
689 
690  // Handle parameter positions (POSIX extension) - Ticket #4900
691  if (hasParameterPosition) {
692  if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end())
693  parameterPositionsUsed.insert(parameterPosition);
694  else // Parameter already referenced, hence don't consider it a new format
695  --numFormat;
696  }
697 
698  // Perform type checks
699  ArgumentInfo argInfo(argListTok, *mSettings, mTokenizer->isCPP());
700 
701  if ((argInfo.typeToken && !argInfo.isLibraryType(*mSettings)) || *i == ']') {
702  if (scan) {
703  std::string specifier;
704  bool done = false;
705  while (!done) {
706  switch (*i) {
707  case 's':
708  case ']': // charset
709  specifier += (*i == 's' || bracketBeg == formatString.end()) ? std::string{ 's' } : std::string{ bracketBeg, i + 1 };
710  if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
711  if (!width.empty()) {
712  const int numWidth = strToInt<int>(width);
713  if (numWidth != (argInfo.variableInfo->dimension(0) - 1))
714  invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, specifier);
715  }
716  }
717  if (argListTok && argListTok->tokType() != Token::eString && argInfo.typeToken &&
718  argInfo.isKnownType() && argInfo.isArrayOrPointer() &&
719  (!Token::Match(argInfo.typeToken, "char|wchar_t") ||
720  argInfo.typeToken->strAt(-1) == "const")) {
721  if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType()))
722  invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
723  }
724  if (scanf_s && argInfo.typeToken) {
725  numSecure++;
726  if (argListTok) {
727  argListTok = argListTok->nextArgument();
728  }
729  }
730  done = true;
731  break;
732  case 'c':
733  if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
734  if (!width.empty()) {
735  const int numWidth = strToInt<int>(width);
736  if (numWidth > argInfo.variableInfo->dimension(0))
737  invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, std::string(1, *i));
738  }
739  }
740  if (scanf_s) {
741  numSecure++;
742  if (argListTok) {
743  argListTok = argListTok->nextArgument();
744  }
745  }
746  done = true;
747  break;
748  case 'x':
749  case 'X':
750  case 'u':
751  case 'o':
752  specifier += *i;
753  if (argInfo.typeToken->tokType() == Token::eString)
754  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
755  else if (argInfo.isKnownType()) {
756  if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
757  if (argInfo.typeToken->isStandardType() || !argInfo.element)
758  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
759  } else if (!argInfo.typeToken->isUnsigned() ||
760  !argInfo.isArrayOrPointer() ||
761  argInfo.typeToken->strAt(-1) == "const") {
762  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
763  } else {
764  switch (specifier[0]) {
765  case 'h':
766  if (specifier[1] == 'h') {
767  if (argInfo.typeToken->str() != "char")
768  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
769  } else if (argInfo.typeToken->str() != "short")
770  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
771  break;
772  case 'l':
773  if (specifier[1] == 'l') {
774  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
775  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
776  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
777  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
778  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
779  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
780  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
781  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
782  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
783  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
784  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
785  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
786  break;
787  case 'I':
788  if (specifier.find("I64") != std::string::npos) {
789  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
790  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
791  } else if (specifier.find("I32") != std::string::npos) {
792  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
793  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
794  } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
795  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
796  break;
797  case 'j':
798  if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
799  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
800  break;
801  case 'z':
802  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
803  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
804  break;
805  case 't':
806  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
807  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
808  break;
809  case 'L':
810  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
811  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
812  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
813  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
814  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
815  break;
816  default:
817  if (argInfo.typeToken->str() != "int")
818  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
819  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
820  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
821  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
822  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
823  break;
824  }
825  }
826  }
827  done = true;
828  break;
829  case 'n':
830  case 'd':
831  case 'i':
832  specifier += *i;
833  if (argInfo.typeToken->tokType() == Token::eString)
834  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
835  else if (argInfo.isKnownType()) {
836  if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
837  if (argInfo.typeToken->isStandardType() || !argInfo.element)
838  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
839  } else if (argInfo.typeToken->isUnsigned() ||
840  !argInfo.isArrayOrPointer() ||
841  argInfo.typeToken->strAt(-1) == "const") {
842  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
843  } else {
844  switch (specifier[0]) {
845  case 'h':
846  if (specifier[1] == 'h') {
847  if (argInfo.typeToken->str() != "char")
848  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
849  } else if (argInfo.typeToken->str() != "short")
850  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
851  break;
852  case 'l':
853  if (specifier[1] == 'l') {
854  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
855  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
856  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
857  typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
858  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
859  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
860  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
861  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
862  typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
863  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
864  break;
865  case 'I':
866  if (specifier.find("I64") != std::string::npos) {
867  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
868  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
869  } else if (specifier.find("I32") != std::string::npos) {
870  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
871  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
872  } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
873  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
874  break;
875  case 'j':
876  if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
877  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
878  break;
879  case 'z':
880  if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
881  (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
882  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
883  break;
884  case 't':
885  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
886  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
887  break;
888  case 'L':
889  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
890  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
891  break;
892  default:
893  if (argInfo.typeToken->str() != "int")
894  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
895  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
896  argInfo.typeToken->originalName() == "intmax_t")
897  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
898  break;
899  }
900  }
901  }
902  done = true;
903  break;
904  case 'e':
905  case 'E':
906  case 'f':
907  case 'g':
908  case 'G':
909  case 'a':
910  specifier += *i;
911  if (argInfo.typeToken->tokType() == Token::eString)
912  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
913  else if (argInfo.isKnownType()) {
914  if (!Token::Match(argInfo.typeToken, "float|double")) {
915  if (argInfo.typeToken->isStandardType())
916  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
917  } else if (!argInfo.isArrayOrPointer() ||
918  argInfo.typeToken->strAt(-1) == "const") {
919  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
920  } else {
921  switch (specifier[0]) {
922  case 'l':
923  if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong())
924  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
925  break;
926  case 'L':
927  if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong())
928  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
929  break;
930  default:
931  if (argInfo.typeToken->str() != "float")
932  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
933  break;
934  }
935  }
936  }
937  done = true;
938  break;
939  case 'I':
940  if ((i+1 != formatString.cend() && *(i+1) == '6' &&
941  i+2 != formatString.cend() && *(i+2) == '4') ||
942  (i+1 != formatString.cend() && *(i+1) == '3' &&
943  i+2 != formatString.cend() && *(i+2) == '2')) {
944  specifier += *i++;
945  specifier += *i++;
946  if ((i+1) != formatString.cend() && !isalpha(*(i+1))) {
947  specifier += *i;
948  invalidLengthModifierError(tok, numFormat, specifier);
949  done = true;
950  } else {
951  specifier += *i++;
952  }
953  } else {
954  if ((i+1) != formatString.cend() && !isalpha(*(i+1))) {
955  specifier += *i;
956  invalidLengthModifierError(tok, numFormat, specifier);
957  done = true;
958  } else {
959  specifier += *i++;
960  }
961  }
962  break;
963  case 'h':
964  case 'l':
965  if (i+1 != formatString.cend() && *(i+1) == *i)
966  specifier += *i++;
967  FALLTHROUGH;
968  case 'j':
969  case 'q':
970  case 't':
971  case 'z':
972  case 'L':
973  // Expect an alphabetical character after these specifiers
974  if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
975  specifier += *i;
976  invalidLengthModifierError(tok, numFormat, specifier);
977  done = true;
978  } else {
979  specifier += *i++;
980  }
981  break;
982  default:
983  done = true;
984  break;
985  }
986  }
987  } else if (printWarning) {
988  std::string specifier;
989  bool done = false;
990  while (!done) {
991  if (i == formatString.end()) {
992  break;
993  }
994  switch (*i) {
995  case 's':
996  if (argListTok->tokType() != Token::eString &&
997  argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
998  if (!Token::Match(argInfo.typeToken, "char|wchar_t")) {
999  if (!argInfo.element)
1000  invalidPrintfArgTypeError_s(tok, numFormat, &argInfo);
1001  }
1002  }
1003  done = true;
1004  break;
1005  case 'n':
1006  if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString)
1007  invalidPrintfArgTypeError_n(tok, numFormat, &argInfo);
1008  done = true;
1009  break;
1010  case 'c':
1011  case 'x':
1012  case 'X':
1013  case 'o':
1014  specifier += *i;
1015  if (argInfo.typeToken->tokType() == Token::eString)
1016  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1017  else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1018  // use %p on pointers and arrays
1019  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1020  } else if (argInfo.isKnownType()) {
1021  if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) {
1022  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1023  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1024  } else {
1025  switch (specifier[0]) {
1026  case 'h':
1027  if (specifier[1] == 'h') {
1028  if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1029  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1030  } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1031  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1032  break;
1033  case 'l':
1034  if (specifier[1] == 'l') {
1035  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1036  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1037  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1038  argInfo.typeToken->originalName() == "uintmax_t")
1039  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1040  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1041  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1042  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1043  argInfo.typeToken->originalName() == "uintmax_t")
1044  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1045  break;
1046  case 'j':
1047  if (argInfo.typeToken->originalName() != "uintmax_t")
1048  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1049  break;
1050  case 'z':
1051  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1052  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1053  break;
1054  case 't':
1055  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1056  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1057  break;
1058  case 'I':
1059  if (specifier.find("I64") != std::string::npos) {
1060  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1061  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1062  } else if (specifier.find("I32") != std::string::npos) {
1063  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1064  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1065  } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1066  argInfo.typeToken->originalName() == "WPARAM" ||
1067  argInfo.typeToken->originalName() == "UINT_PTR" ||
1068  argInfo.typeToken->originalName() == "LONG_PTR" ||
1069  argInfo.typeToken->originalName() == "LPARAM" ||
1070  argInfo.typeToken->originalName() == "LRESULT"))
1071  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1072  break;
1073  default:
1074  if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int"))
1075  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1076  break;
1077  }
1078  }
1079  }
1080  done = true;
1081  break;
1082  case 'd':
1083  case 'i':
1084  specifier += *i;
1085  if (argInfo.typeToken->tokType() == Token::eString) {
1086  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1087  } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1088  // use %p on pointers and arrays
1089  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1090  } else if (argInfo.isKnownType()) {
1091  if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) {
1092  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1093  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1094  } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) {
1095  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1096  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1097  } else {
1098  switch (specifier[0]) {
1099  case 'h':
1100  if (specifier[1] == 'h') {
1101  if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned()))
1102  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1103  } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned()))
1104  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1105  break;
1106  case 'l':
1107  if (specifier[1] == 'l') {
1108  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1109  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1110  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1111  argInfo.typeToken->originalName() == "intmax_t")
1112  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1113  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1114  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1115  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1116  argInfo.typeToken->originalName() == "intmax_t")
1117  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1118  break;
1119  case 'j':
1120  if (argInfo.typeToken->originalName() != "intmax_t")
1121  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1122  break;
1123  case 't':
1124  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1125  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1126  break;
1127  case 'I':
1128  if (specifier.find("I64") != std::string::npos) {
1129  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1130  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1131  } else if (specifier.find("I32") != std::string::npos) {
1132  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1133  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1134  } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1135  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1136  break;
1137  case 'z':
1138  if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
1139  (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
1140  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1141  break;
1142  case 'L':
1143  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1144  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1145  break;
1146  default:
1147  if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1148  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1149  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1150  argInfo.typeToken->originalName() == "intmax_t")
1151  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1152  break;
1153  }
1154  }
1155  }
1156  done = true;
1157  break;
1158  case 'u':
1159  specifier += *i;
1160  if (argInfo.typeToken->tokType() == Token::eString) {
1161  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1162  } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1163  // use %p on pointers and arrays
1164  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1165  } else if (argInfo.isKnownType()) {
1166  if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) {
1167  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1168  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1169  } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) {
1170  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1171  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1172  } else {
1173  switch (specifier[0]) {
1174  case 'h':
1175  if (specifier[1] == 'h') {
1176  if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1177  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1178  } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1179  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1180  break;
1181  case 'l':
1182  if (specifier[1] == 'l') {
1183  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1184  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1185  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1186  argInfo.typeToken->originalName() == "uintmax_t")
1187  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1188  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1189  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1190  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1191  argInfo.typeToken->originalName() == "uintmax_t")
1192  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1193  break;
1194  case 'j':
1195  if (argInfo.typeToken->originalName() != "uintmax_t")
1196  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1197  break;
1198  case 'z':
1199  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1200  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1201  break;
1202  case 't':
1203  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1204  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1205  break;
1206  case 'I':
1207  if (specifier.find("I64") != std::string::npos) {
1208  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1209  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1210  } else if (specifier.find("I32") != std::string::npos) {
1211  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1212  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1213  } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1214  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1215  break;
1216  case 'L':
1217  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1218  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1219  break;
1220  default:
1221  if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1222  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1223  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1224  argInfo.typeToken->originalName() == "intmax_t")
1225  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1226  break;
1227  }
1228  }
1229  }
1230  done = true;
1231  break;
1232  case 'p':
1233  if (argInfo.typeToken->tokType() == Token::eString)
1234  ; // string literals are passed as pointers to literal start, okay
1235  else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer())
1236  invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
1237  done = true;
1238  break;
1239  case 'e':
1240  case 'E':
1241  case 'f':
1242  case 'g':
1243  case 'G':
1244  specifier += *i;
1245  if (argInfo.typeToken->tokType() == Token::eString)
1246  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1247  else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1248  // use %p on pointers and arrays
1249  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1250  } else if (argInfo.isKnownType()) {
1251  if (!Token::Match(argInfo.typeToken, "float|double")) {
1252  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1253  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1254  } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
1255  (specifier[0] != 'L' && argInfo.typeToken->isLong()))
1256  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1257  }
1258  done = true;
1259  break;
1260  case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
1261  case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
1262  // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
1263  if ((i + 1) != formatString.end() && *(i + 1) == *i) {
1264  if ((i + 2) != formatString.end() && !isalpha(*(i + 2))) {
1265  std::string modifier;
1266  modifier += *i;
1267  modifier += *(i + 1);
1268  invalidLengthModifierError(tok, numFormat, modifier);
1269  done = true;
1270  } else {
1271  specifier = *i++;
1272  specifier += *i++;
1273  }
1274  } else {
1275  if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) {
1276  std::string modifier;
1277  modifier += *i;
1278  invalidLengthModifierError(tok, numFormat, modifier);
1279  done = true;
1280  } else {
1281  specifier = *i++;
1282  }
1283  }
1284  }
1285  break;
1286  case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64
1287  if ((*(i+1) == '3' && *(i+2) == '2') ||
1288  (*(i+1) == '6' && *(i+2) == '4')) {
1289  specifier += *i++;
1290  specifier += *i++;
1291  }
1292  FALLTHROUGH;
1293  case 'j': // intmax_t or uintmax_t
1294  case 'z': // size_t
1295  case 't': // ptrdiff_t
1296  case 'L': // long double
1297  // Expect an alphabetical character after these specifiers
1298  if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
1299  specifier += *i;
1300  invalidLengthModifierError(tok, numFormat, specifier);
1301  done = true;
1302  } else {
1303  specifier += *i++;
1304  }
1305  break;
1306  default:
1307  done = true;
1308  break;
1309  }
1310  }
1311  }
1312  }
1313 
1314  if (argListTok)
1315  argListTok = argListTok->nextArgument(); // Find next argument
1316  }
1317  }
1318  }
1319 
1320  // Count printf/scanf parameters..
1321  int numFunction = 0;
1322  while (argListTok2) {
1323  if (Token::Match(argListTok2, "%name% ...")) // bailout for parameter pack
1324  return;
1325  numFunction++;
1326  argListTok2 = argListTok2->nextArgument(); // Find next argument
1327  }
1328 
1329  if (printWarning) {
1330  // Check that all parameter positions reference an actual parameter
1331  for (const int i : parameterPositionsUsed) {
1332  if ((i == 0) || (i > numFormat))
1333  wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat);
1334  }
1335  }
1336 
1337  // Mismatching number of parameters => warning
1338  if ((numFormat + numSecure) != numFunction)
1339  wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction);
1340 }
1341 
1342 // We currently only support string literals, variables, and functions.
1343 /// @todo add non-string literals, and generic expressions
1344 
1345 CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings &settings, bool _isCPP)
1346  : isCPP(_isCPP)
1347 {
1348  if (!arg)
1349  return;
1350 
1351  // Use AST type info
1352  // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type.
1353  if (!Token::Match(arg, "%str% ,|)") && !(arg->variable() && arg->variable()->isArray())) {
1354  const Token *top = arg;
1355  while (top->str() == "(" && !top->isCast())
1356  top = top->next();
1357  while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous())
1358  top = top->astParent();
1359  const ValueType *valuetype = top->argumentType();
1360  if (valuetype && valuetype->type >= ValueType::Type::BOOL) {
1361  typeToken = tempToken = new Token(top);
1362  if (valuetype->pointer && valuetype->constness & 1) {
1363  tempToken->str("const");
1364  tempToken->insertToken("a");
1365  tempToken = tempToken->next();
1366  }
1367  if (valuetype->type == ValueType::BOOL)
1368  tempToken->str("bool");
1369  else if (valuetype->type == ValueType::CHAR)
1370  tempToken->str("char");
1371  else if (valuetype->type == ValueType::SHORT)
1372  tempToken->str("short");
1373  else if (valuetype->type == ValueType::WCHAR_T)
1374  tempToken->str("wchar_t");
1375  else if (valuetype->type == ValueType::INT)
1376  tempToken->str("int");
1377  else if (valuetype->type == ValueType::LONG)
1378  tempToken->str("long");
1379  else if (valuetype->type == ValueType::LONGLONG) {
1380  tempToken->str("long");
1381  tempToken->isLong(true);
1382  } else if (valuetype->type == ValueType::FLOAT)
1383  tempToken->str("float");
1384  else if (valuetype->type == ValueType::DOUBLE)
1385  tempToken->str("double");
1386  else if (valuetype->type == ValueType::LONGDOUBLE) {
1387  tempToken->str("double");
1388  tempToken->isLong(true);
1389  }
1390  if (valuetype->isIntegral()) {
1391  if (valuetype->sign == ValueType::Sign::UNSIGNED)
1392  tempToken->isUnsigned(true);
1393  else if (valuetype->sign == ValueType::Sign::SIGNED)
1394  tempToken->isSigned(true);
1395  }
1396  if (!valuetype->originalTypeName.empty())
1398  for (int p = 0; p < valuetype->pointer; p++)
1399  tempToken->insertToken("*");
1400  tempToken = const_cast<Token*>(typeToken);
1401  if (top->isBinaryOp() && valuetype->pointer == 1 && (valuetype->type == ValueType::CHAR || valuetype->type == ValueType::WCHAR_T))
1403  return;
1404  }
1405  }
1406 
1407 
1408  if (arg->tokType() == Token::eString) {
1409  typeToken = arg;
1410  return;
1411  }
1412  if (arg->str() == "&" || arg->tokType() == Token::eVariable ||
1413  arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") ||
1414  (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") &&
1415  Token::simpleMatch(arg->linkAt(1), "> (") &&
1416  Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) {
1417  if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) {
1418  typeToken = arg->tokAt(2);
1419  while (typeToken->str() == "const" || typeToken->str() == "extern")
1420  typeToken = typeToken->next();
1421  return;
1422  }
1423  if (arg->str() == "&") {
1424  address = true;
1425  arg = arg->next();
1426  }
1427  while (Token::Match(arg, "%type% ::"))
1428  arg = arg->tokAt(2);
1429  if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction))
1430  return;
1431  const Token *varTok = nullptr;
1432  const Token *tok1 = arg->next();
1433  for (; tok1; tok1 = tok1->next()) {
1434  if (tok1->str() == "," || tok1->str() == ")") {
1435  if (tok1->previous()->str() == "]") {
1436  varTok = tok1->linkAt(-1)->previous();
1437  if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) {
1438  const Function * function = varTok->link()->previous()->function();
1439  if (function && function->retType && function->retType->isEnumType()) {
1440  if (function->retType->classScope->enumType)
1441  typeToken = function->retType->classScope->enumType;
1442  else {
1443  tempToken = new Token(tok1);
1444  tempToken->str("int");
1445  typeToken = tempToken;
1446  }
1447  } else if (function && function->retDef) {
1448  typeToken = function->retDef;
1449  while (typeToken->str() == "const" || typeToken->str() == "extern")
1450  typeToken = typeToken->next();
1451  functionInfo = function;
1452  element = true;
1453  }
1454  return;
1455  }
1456  } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) {
1457  const Function * function = tok1->linkAt(-1)->previous()->function();
1458  if (function && function->retType && function->retType->isEnumType()) {
1459  if (function->retType->classScope->enumType)
1460  typeToken = function->retType->classScope->enumType;
1461  else {
1462  tempToken = new Token(tok1);
1463  tempToken->str("int");
1464  typeToken = tempToken;
1465  }
1466  } else if (function && function->retDef) {
1467  typeToken = function->retDef;
1468  while (typeToken->str() == "const" || typeToken->str() == "extern")
1469  typeToken = typeToken->next();
1470  functionInfo = function;
1471  element = false;
1472  }
1473  return;
1474  } else
1475  varTok = tok1->previous();
1476  break;
1477  }
1478  if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[")
1479  tok1 = tok1->link();
1480  else if (tok1->link() && tok1->str() == "<")
1481  tok1 = tok1->link();
1482 
1483  // check for some common well known functions
1484  else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) ||
1485  (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) {
1486  tempToken = new Token(tok1);
1487  if (tok1->next()->str() == "size") {
1488  // size_t is platform dependent
1489  if (settings.platform.sizeof_size_t == 8) {
1490  tempToken->str("long");
1491  if (settings.platform.sizeof_long != 8)
1492  tempToken->isLong(true);
1493  } else if (settings.platform.sizeof_size_t == 4) {
1494  if (settings.platform.sizeof_long == 4) {
1495  tempToken->str("long");
1496  } else {
1497  tempToken->str("int");
1498  }
1499  }
1500 
1501  tempToken->originalName("size_t");
1502  tempToken->isUnsigned(true);
1503  } else if (tok1->next()->str() == "empty") {
1504  tempToken->str("bool");
1505  } else if (tok1->next()->str() == "c_str") {
1506  tempToken->str("const");
1507  tempToken->insertToken("*");
1508  if (typeToken->strAt(2) == "string")
1509  tempToken->insertToken("char");
1510  else
1511  tempToken->insertToken("wchar_t");
1512  }
1513  typeToken = tempToken;
1514  return;
1515  }
1516 
1517  // check for std::vector::at() and std::string::at()
1518  else if (Token::Match(tok1->previous(), "%var% . at (") &&
1519  Token::Match(tok1->linkAt(2), ") [,)]")) {
1520  varTok = tok1->previous();
1521  variableInfo = varTok->variable();
1522 
1523  if (!variableInfo || !isStdVectorOrString()) {
1524  variableInfo = nullptr;
1525  typeToken = nullptr;
1526  }
1527 
1528  return;
1529  } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction))
1530  return;
1531  }
1532 
1533  if (varTok) {
1534  variableInfo = varTok->variable();
1535  element = tok1->previous()->str() == "]";
1536 
1537  // look for std::vector operator [] and use template type as return type
1538  if (variableInfo) {
1539  if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true
1540  element = false; // not really an array element
1541  } else if (variableInfo->isEnumType()) {
1544  else {
1545  tempToken = new Token(tok1);
1546  tempToken->str("int");
1547  typeToken = tempToken;
1548  }
1549  } else
1551  }
1552 
1553  return;
1554  }
1555  }
1556 }
1557 
1559 {
1560  if (tempToken) {
1561  while (tempToken->next())
1562  tempToken->deleteNext();
1563 
1564  delete tempToken;
1565  }
1566 }
1567 
1568 namespace {
1569  const std::set<std::string> stl_vector = { "array", "vector" };
1570  const std::set<std::string> stl_string = { "string", "u16string", "u32string", "wstring" };
1571 }
1572 
1574 {
1575  if (!isCPP)
1576  return false;
1577  if (variableInfo->isStlType(stl_vector)) {
1578  typeToken = variableInfo->typeStartToken()->tokAt(4);
1579  _template = true;
1580  return true;
1581  }
1582  if (variableInfo->isStlType(stl_string)) {
1583  tempToken = new Token(variableInfo->typeStartToken());
1584  if (variableInfo->typeStartToken()->strAt(2) == "string")
1585  tempToken->str("char");
1586  else
1587  tempToken->str("wchar_t");
1588  typeToken = tempToken;
1589  return true;
1590  }
1591  if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) {
1592  const std::vector<Type::BaseInfo>& derivedFrom = variableInfo->type()->derivedFrom;
1593  for (const Type::BaseInfo & i : derivedFrom) {
1594  const Token* nameTok = i.nameTok;
1595  if (Token::Match(nameTok, "std :: vector|array <")) {
1596  typeToken = nameTok->tokAt(4);
1597  _template = true;
1598  return true;
1599  }
1600  if (Token::Match(nameTok, "std :: string|wstring")) {
1601  tempToken = new Token(variableInfo->typeStartToken());
1602  if (nameTok->strAt(2) == "string")
1603  tempToken->str("char");
1604  else
1605  tempToken->str("wchar_t");
1606  typeToken = tempToken;
1607  return true;
1608  }
1609  }
1610  } else if (variableInfo->type()) {
1611  const Scope * classScope = variableInfo->type()->classScope;
1612  if (classScope) {
1613  for (const Function &func : classScope->functionList) {
1614  if (func.name() == "operator[]") {
1615  if (Token::Match(func.retDef, "%type% &")) {
1616  typeToken = func.retDef;
1617  return true;
1618  }
1619  }
1620  }
1621  }
1622  }
1623 
1624  return false;
1625 }
1626 
1627 static const std::set<std::string> stl_container = {
1628  "array", "bitset", "deque", "forward_list",
1629  "hash_map", "hash_multimap", "hash_set",
1630  "list", "map", "multimap", "multiset",
1631  "priority_queue", "queue", "set", "stack",
1632  "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector"
1633 };
1634 
1636 {
1637  if (!isCPP)
1638  return false;
1639  if (tok && tok->variable()) {
1640  const Variable* variable = tok->variable();
1641  if (variable->isStlType(stl_container)) {
1642  typeToken = variable->typeStartToken()->tokAt(4);
1643  return true;
1644  }
1645  if (variable->isStlType(stl_string)) {
1646  typeToken = variable->typeStartToken();
1647  return true;
1648  }
1649  if (variable->type() && !variable->type()->derivedFrom.empty()) {
1650  for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) {
1651  const Token* nameTok = baseInfo.nameTok;
1652  if (Token::Match(nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) {
1653  typeToken = nameTok->tokAt(4);
1654  return true;
1655  }
1656  if (Token::Match(nameTok, "std :: string|wstring")) {
1657  typeToken = nameTok;
1658  return true;
1659  }
1660  }
1661  }
1662  }
1663 
1664  return false;
1665 }
1666 
1668 {
1669  if (address)
1670  return true;
1671  if (variableInfo && !_template)
1672  return variableInfo->isArrayOrPointer();
1673 
1674  const Token *tok = typeToken;
1675  while (Token::Match(tok, "const|struct"))
1676  tok = tok->next();
1677  return tok && tok->strAt(1) == "*";
1678 }
1679 
1681 {
1682  if (variableInfo->type())
1683  return (true);
1684 
1685  const Token* varTypeTok = typeToken;
1686  if (varTypeTok->str() == "std")
1687  varTypeTok = varTypeTok->tokAt(2);
1688 
1689  return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer());
1690 }
1691 
1693 {
1694  if (variableInfo)
1695  return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType());
1696  if (functionInfo)
1697  return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring"));
1698 
1699  return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring");
1700 }
1701 
1703 {
1704  return typeToken && typeToken->isStandardType() && settings.library.podtype(typeToken->str());
1705 }
1706 
1708  const std::string &functionName,
1709  nonneg int numFormat,
1710  nonneg int numFunction)
1711 {
1712  const Severity severity = numFormat > numFunction ? Severity::error : Severity::warning;
1714  return;
1715 
1716  std::ostringstream errmsg;
1717  errmsg << functionName
1718  << " format string requires "
1719  << numFormat
1720  << " parameter" << (numFormat != 1 ? "s" : "") << " but "
1721  << (numFormat > numFunction ? "only " : "")
1722  << numFunction
1723  << (numFunction != 1 ? " are" : " is")
1724  << " given.";
1725 
1726  reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal);
1727 }
1728 
1729 void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName,
1730  nonneg int index, nonneg int numFunction)
1731 {
1733  return;
1734  std::ostringstream errmsg;
1735  errmsg << functionName << ": ";
1736  if (index == 0) {
1737  errmsg << "parameter positions start at 1, not 0";
1738  } else {
1739  errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given";
1740  }
1741  reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal);
1742 }
1743 
1744 void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1745 {
1746  const Severity severity = getSeverity(argInfo);
1747  if (!mSettings->severity.isEnabled(severity))
1748  return;
1749  std::ostringstream errmsg;
1750  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'";
1751  if (specifier[0] == 's')
1752  errmsg << "char";
1753  else if (specifier[0] == 'S')
1754  errmsg << "wchar_t";
1755  errmsg << " *\' but the argument type is ";
1756  argumentType(errmsg, argInfo);
1757  errmsg << ".";
1758  reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1759 }
1760 void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned)
1761 {
1762  const Severity severity = getSeverity(argInfo);
1763  if (!mSettings->severity.isEnabled(severity))
1764  return;
1765  std::ostringstream errmsg;
1766  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1767  if (specifier[0] == 'h') {
1768  if (specifier[1] == 'h')
1769  errmsg << (isUnsigned ? "unsigned " : "") << "char";
1770  else
1771  errmsg << (isUnsigned ? "unsigned " : "") << "short";
1772  } else if (specifier[0] == 'l') {
1773  if (specifier[1] == 'l')
1774  errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1775  else
1776  errmsg << (isUnsigned ? "unsigned " : "") << "long";
1777  } else if (specifier.find("I32") != std::string::npos) {
1778  errmsg << (isUnsigned ? "unsigned " : "") << "__int32";
1779  } else if (specifier.find("I64") != std::string::npos) {
1780  errmsg << (isUnsigned ? "unsigned " : "") << "__int64";
1781  } else if (specifier[0] == 'I') {
1782  errmsg << (isUnsigned ? "size_t" : "ptrdiff_t");
1783  } else if (specifier[0] == 'j') {
1784  if (isUnsigned)
1785  errmsg << "uintmax_t";
1786  else
1787  errmsg << "intmax_t";
1788  } else if (specifier[0] == 'z') {
1789  if (specifier[1] == 'd' || specifier[1] == 'i')
1790  errmsg << "ssize_t";
1791  else
1792  errmsg << "size_t";
1793  } else if (specifier[0] == 't') {
1794  errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1795  } else if (specifier[0] == 'L') {
1796  errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1797  } else {
1798  errmsg << (isUnsigned ? "unsigned " : "") << "int";
1799  }
1800  errmsg << " *\' but the argument type is ";
1801  argumentType(errmsg, argInfo);
1802  errmsg << ".";
1803  reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal);
1804 }
1805 void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1806 {
1807  const Severity severity = getSeverity(argInfo);
1808  if (!mSettings->severity.isEnabled(severity))
1809  return;
1810  std::ostringstream errmsg;
1811  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1812  if (specifier[0] == 'l' && specifier[1] != 'l')
1813  errmsg << "double";
1814  else if (specifier[0] == 'L')
1815  errmsg << "long double";
1816  else
1817  errmsg << "float";
1818  errmsg << " *\' but the argument type is ";
1819  argumentType(errmsg, argInfo);
1820  errmsg << ".";
1821  reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1822 }
1823 
1824 void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1825 {
1826  const Severity severity = getSeverity(argInfo);
1827  if (!mSettings->severity.isEnabled(severity))
1828  return;
1829  std::ostringstream errmsg;
1830  errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is ";
1831  argumentType(errmsg, argInfo);
1832  errmsg << ".";
1833  reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1834 }
1835 void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1836 {
1837  const Severity severity = getSeverity(argInfo);
1838  if (!mSettings->severity.isEnabled(severity))
1839  return;
1840  std::ostringstream errmsg;
1841  errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is ";
1842  argumentType(errmsg, argInfo);
1843  errmsg << ".";
1844  reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal);
1845 }
1846 void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1847 {
1848  const Severity severity = getSeverity(argInfo);
1849  if (!mSettings->severity.isEnabled(severity))
1850  return;
1851  std::ostringstream errmsg;
1852  errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is ";
1853  argumentType(errmsg, argInfo);
1854  errmsg << ".";
1855  reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, Certainty::normal);
1856 }
1857 static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned)
1858 {
1859  os << "\'";
1860  if (specifier[0] == 'l') {
1861  if (specifier[1] == 'l')
1862  os << (isUnsigned ? "unsigned " : "") << "long long";
1863  else
1864  os << (isUnsigned ? "unsigned " : "") << "long";
1865  } else if (specifier[0] == 'h') {
1866  if (specifier[1] == 'h')
1867  os << (isUnsigned ? "unsigned " : "") << "char";
1868  else
1869  os << (isUnsigned ? "unsigned " : "") << "short";
1870  } else if (specifier.find("I32") != std::string::npos) {
1871  os << (isUnsigned ? "unsigned " : "") << "__int32";
1872  } else if (specifier.find("I64") != std::string::npos) {
1873  os << (isUnsigned ? "unsigned " : "") << "__int64";
1874  } else if (specifier[0] == 'I') {
1875  os << (isUnsigned ? "size_t" : "ptrdiff_t");
1876  } else if (specifier[0] == 'j') {
1877  if (isUnsigned)
1878  os << "uintmax_t";
1879  else
1880  os << "intmax_t";
1881  } else if (specifier[0] == 'z') {
1882  if (specifier[1] == 'd' || specifier[1] == 'i')
1883  os << "ssize_t";
1884  else
1885  os << "size_t";
1886  } else if (specifier[0] == 't') {
1887  os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1888  } else if (specifier[0] == 'L') {
1889  os << (isUnsigned ? "unsigned " : "") << "long long";
1890  } else {
1891  os << (isUnsigned ? "unsigned " : "") << "int";
1892  }
1893  os << "\'";
1894 }
1895 
1896 void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1897 {
1898  const Severity severity = getSeverity(argInfo);
1899  if (!mSettings->severity.isEnabled(severity))
1900  return;
1901  std::ostringstream errmsg;
1902  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1903  printfFormatType(errmsg, specifier, true);
1904  errmsg << " but the argument type is ";
1905  argumentType(errmsg, argInfo);
1906  errmsg << ".";
1907  reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal);
1908 }
1909 
1910 void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1911 {
1912  const Severity severity = getSeverity(argInfo);
1913  if (!mSettings->severity.isEnabled(severity))
1914  return;
1915  std::ostringstream errmsg;
1916  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1917  printfFormatType(errmsg, specifier, false);
1918  errmsg << " but the argument type is ";
1919  argumentType(errmsg, argInfo);
1920  errmsg << ".";
1921  reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal);
1922 }
1923 void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1924 {
1925  const Severity severity = getSeverity(argInfo);
1926  if (!mSettings->severity.isEnabled(severity))
1927  return;
1928  std::ostringstream errmsg;
1929  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1930  if (specifier[0] == 'L')
1931  errmsg << "long ";
1932  errmsg << "double\' but the argument type is ";
1933  argumentType(errmsg, argInfo);
1934  errmsg << ".";
1935  reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1936 }
1937 
1939 {
1940  return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning;
1941 }
1942 
1943 void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
1944 {
1945  if (argInfo) {
1946  os << "\'";
1947  const Token *type = argInfo->typeToken;
1948  if (type->tokType() == Token::eString) {
1949  if (type->isLong())
1950  os << "const wchar_t *";
1951  else
1952  os << "const char *";
1953  } else {
1954  if (type->originalName().empty()) {
1955  if (type->strAt(-1) == "const")
1956  os << "const ";
1957  while (Token::Match(type, "const|struct")) {
1958  os << type->str() << " ";
1959  type = type->next();
1960  }
1961  while (Token::Match(type, "%any% ::")) {
1962  os << type->str() << "::";
1963  type = type->tokAt(2);
1964  }
1965  os << type->stringify(false, true, false);
1966  if (type->strAt(1) == "*" && !argInfo->element)
1967  os << " *";
1968  else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
1969  os << " *";
1970  else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray())
1971  os << " *";
1972  if (argInfo->address)
1973  os << " *";
1974  } else {
1975  if (type->isUnsigned()) {
1976  if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t")
1977  os << "unsigned ";
1978  }
1979  os << type->originalName();
1980  if (type->strAt(1) == "*" || argInfo->address)
1981  os << " *";
1982  os << " {aka " << type->stringify(false, true, false);
1983  if (type->strAt(1) == "*" || argInfo->address)
1984  os << " *";
1985  os << "}";
1986  }
1987  }
1988  os << "\'";
1989  } else
1990  os << "Unknown";
1991 }
1992 
1993 void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier)
1994 {
1996  return;
1997  std::ostringstream errmsg;
1998  errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
1999  reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal);
2000 }
2001 
2002 void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier)
2003 {
2004  MathLib::bigint arrlen = 0;
2005  std::string varname;
2006 
2007  if (var) {
2008  arrlen = var->dimension(0);
2009  varname = var->name();
2010  }
2011 
2012  std::ostringstream errmsg;
2013  if (arrlen > width) {
2015  return;
2016  errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer"
2017  << " '" << varname << "[" << arrlen << "]'.";
2018  reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), Certainty::inconclusive);
2019  } else {
2020  errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '"
2021  << varname << "[" << arrlen << "]', use %" << (specifier == "c" ? arrlen : (arrlen - 1)) << specifier << " to prevent overflowing it.";
2022  reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal);
2023  }
2024 }
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3591
static void printfFormatType(std::ostream &os, const std::string &specifier, bool isUnsigned)
Definition: checkio.cpp:1857
static bool findFormat(nonneg int arg, const Token *firstArg, const Token *&formatStringTok, const Token *&formatArgTok)
Definition: checkio.cpp:489
static const CWE CWE398(398U)
OpenMode
Definition: checkio.cpp:99
static const CWE CWE704(704U)
static const std::set< std::string > stl_container
Definition: checkio.cpp:1627
static const CWE CWE687(687U)
static const CWE CWE119(119U)
static const CWE CWE910(910U)
static const CWE CWE685(685U)
static bool typesMatch(const std::string &iToTest, const std::string &iTypename, const std::string &iOptionalPrefix="std::")
Definition: checkio.cpp:524
static const CWE CWE686(686U)
static OpenMode getMode(const std::string &str)
Definition: checkio.cpp:100
static const CWE CWE664(664U)
const Variable * variableInfo
Definition: checkio.h:91
bool isArrayOrPointer() const
Definition: checkio.cpp:1667
bool isKnownType() const
Definition: checkio.cpp:1692
const Function * functionInfo
Definition: checkio.h:93
bool isLibraryType(const Settings &settings) const
Definition: checkio.cpp:1702
bool isStdContainer(const Token *tok)
Definition: checkio.cpp:1635
ArgumentInfo(const Token *arg, const Settings &settings, bool _isCPP)
Definition: checkio.cpp:1345
const Token * typeToken
Definition: checkio.h:92
bool isComplexType() const
Definition: checkio.cpp:1680
Check input output operations.
Definition: checkio.h:42
void invalidPrintfArgTypeError_uint(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1896
void invalidScanf()
scanf can crash if width specifiers are not used
Definition: checkio.cpp:412
void invalidPrintfArgTypeError_n(const Token *tok, nonneg int numFormat, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1835
void seekOnAppendedFileError(const Token *tok)
Definition: checkio.cpp:396
void incompatibleFileOpenError(const Token *tok, const std::string &filename)
Definition: checkio.cpp:402
void invalidPrintfArgTypeError_s(const Token *tok, nonneg int numFormat, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1824
void writeReadOnlyFileError(const Token *tok)
Definition: checkio.cpp:384
void readWriteOnlyFileError(const Token *tok)
Definition: checkio.cpp:378
void invalidLengthModifierError(const Token *tok, nonneg int numFormat, const std::string &modifier)
Definition: checkio.cpp:1993
void checkCoutCerrMisusage()
Check for missusage of std::cout
Definition: checkio.cpp:66
void fflushOnInputStreamError(const Token *tok, const std::string &varname)
Definition: checkio.cpp:366
void invalidScanfArgTypeError_float(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1805
void invalidScanfError(const Token *tok)
Definition: checkio.cpp:457
static void argumentType(std::ostream &os, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1943
void checkFormatString(const Token *const tok, const Token *const formatStringTok, const Token *argListTok, const bool scan, const bool scanf_s)
Definition: checkio.cpp:601
void wrongPrintfScanfPosixParameterPositionError(const Token *tok, const std::string &functionName, nonneg int index, nonneg int numFunction)
Definition: checkio.cpp:1729
static Severity getSeverity(const ArgumentInfo *argInfo)
Definition: checkio.cpp:1938
void invalidScanfFormatWidthError(const Token *tok, nonneg int numFormat, int width, const Variable *var, const std::string &specifier)
Definition: checkio.cpp:2002
void invalidPrintfArgTypeError_sint(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1910
void coutCerrMisusageError(const Token *tok, const std::string &streamName)
Definition: checkio.cpp:88
void checkFileUsage()
Check usage of files
Definition: checkio.cpp:127
void checkWrongPrintfScanfArguments()
Checks type and number of arguments given to functions like printf or scanf
Definition: checkio.cpp:529
void invalidScanfArgTypeError_int(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo, bool isUnsigned)
Definition: checkio.cpp:1760
void invalidPrintfArgTypeError_p(const Token *tok, nonneg int numFormat, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1846
void ioWithoutPositioningError(const Token *tok)
Definition: checkio.cpp:372
void invalidPrintfArgTypeError_float(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1923
void invalidScanfArgTypeError_s(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1744
void useClosedFileError(const Token *tok)
Definition: checkio.cpp:390
void wrongPrintfScanfArgumentsError(const Token *tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction)
Definition: checkio.cpp:1707
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
bool isStatic() const
const std::string & name() const
const Token * retDef
function return type token
bool formatstr_function(const Token *ftok) const
Definition: library.cpp:1362
bool formatstr_secure(const Token *ftok) const
Definition: library.cpp:1387
bool isnoreturn(const Token *ftok) const
Definition: library.cpp:1542
int formatstr_argno(const Token *ftok) const
Definition: library.cpp:1373
const PodType * podtype(const std::string &name) const
Definition: library.h:447
bool isFunctionConst(const std::string &functionName, bool pure) const
Definition: library.cpp:1518
bool formatstr_scan(const Token *ftok) const
Definition: library.cpp:1382
long long bigint
Definition: mathlib.h:68
std::size_t sizeof_size_t
Definition: platform.h:101
bool isWindows() const
Returns true if platform type is Windows.
Definition: platform.h:142
std::size_t sizeof_long
Definition: platform.h:95
std::list< Function > functionList
const Token * enumType
ScopeType type
Function * function
function info for this function
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
const Scope * functionOf
scope this function belongs to
bool isClassOrStruct() const
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
Library library
Library.
Definition: settings.h:237
Platform platform
Definition: settings.h:255
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
const Variable * getVariableFromVarId(nonneg int varId) const
const std::vector< const Variable * > & variableList() const
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
const std::string & originalName() const
Definition: token.h:1193
std::string stringify(const stringifyOptions &options) const
Definition: token.cpp:1314
bool isUnsigned() const
Definition: token.h:424
const std::string & strAt(int index) const
Definition: token.cpp:457
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1127
nonneg int varId() const
Definition: token.h:870
bool isSigned() const
Definition: token.h:430
bool isCast() const
Definition: token.h:458
const ValueType * argumentType() const
Definition: token.h:336
const Token * tokAt(int index) const
Definition: token.cpp:427
Token * insertToken(const std::string &tokenStr, const std::string &originalNameStr=emptyString, const std::string &macroNameStr=emptyString, bool prepend=false)
Insert new token after this token.
Definition: token.cpp:1139
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1502
bool isLong() const
Definition: token.h:443
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:447
@ eString
Definition: token.h:162
@ eVariable
Definition: token.h:161
@ eFunction
Definition: token.h:161
Token * previous()
Definition: token.h:862
std::string strValue() const
This can be called only for tokens that are strings, else the assert() is called.
Definition: token.cpp:257
bool isBinaryOp() const
Definition: token.h:410
bool isStandardType() const
Definition: token.h:449
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
Token * next()
Definition: token.h:830
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1197
const Token * nextArgument() const
Definition: token.cpp:903
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
bool isC() const
Is the code C.
Definition: tokenize.h:64
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool isCPP() const
Is the code CPP.
Definition: tokenize.h:69
std::vector< BaseInfo > derivedFrom
const Scope * classScope
bool isTokValue() const
Definition: vfvalue.h:214
Value type.
enum ValueType::Type type
nonneg int constness
bit 0=data, bit 1=*, bit 2=**
bool isIntegral() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
std::string originalTypeName
original type name as written in the source code.
enum ValueType::Sign sign
Information about a member variable.
bool isEnumType() const
Determine whether it's an enumeration type.
bool isStlType() const
Checks if the variable is an STL type ('std::') E.g.
bool isLocal() const
Is variable local.
const Type * type() const
Get Type pointer of known type.
bool isGlobal() const
Is variable global.
const std::string & name() const
Get name string.
MathLib::bigint dimension(nonneg int index_) const
Get array dimension length.
bool isArray() const
Is variable an array.
const Token * typeStartToken() const
Get type start token.
const std::vector< Dimension > & dimensions() const
Get array dimensions.
bool isStatic() const
Is variable static.
#define FALLTHROUGH
Definition: config.h:91
#define nonneg
Definition: config.h:138
Severity
enum class for severity.
Definition: errortypes.h:63
@ warning
Warning.
@ portability
Portability warning.
@ error
Programming error.
static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2)
Definition: token.cpp:1742