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  logChecker("CheckIO::invalidScanf");
418 
419  const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
420  for (const Scope * scope : symbolDatabase->functionScopes) {
421  for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
422  const Token *formatToken = nullptr;
423  if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
424  formatToken = tok->tokAt(2);
425  else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
426  const Token* nextArg = tok->tokAt(2)->nextArgument();
427  if (nextArg && nextArg->tokType() == Token::eString)
428  formatToken = nextArg;
429  else
430  continue;
431  } else
432  continue;
433 
434  bool format = false;
435 
436  // scan the string backwards, so we do not need to keep states
437  const std::string &formatstr(formatToken->str());
438  for (std::size_t i = 1; i < formatstr.length(); i++) {
439  if (formatstr[i] == '%')
440  format = !format;
441 
442  else if (!format)
443  continue;
444 
445  else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') {
446  format = false;
447  }
448 
449  else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') {
450  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
451  invalidScanfError(tok);
452  format = false;
453  }
454  }
455  }
456  }
457 }
458 
460 {
461  const std::string fname = (tok ? tok->str() : std::string("scanf"));
463  "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" +
464  fname + "() without field width limits can crash with huge input data. Add a field width "
465  "specifier to fix this problem.\n"
466  "\n"
467  "Sample program that can crash:\n"
468  "\n"
469  "#include <stdio.h>\n"
470  "int main()\n"
471  "{\n"
472  " char c[5];\n"
473  " scanf(\"%s\", c);\n"
474  " return 0;\n"
475  "}\n"
476  "\n"
477  "Typing in 5 or more characters may make the program crash. The correct usage "
478  "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the "
479  "terminating null byte.\n"
480  "Source: http://linux.die.net/man/3/scanf\n"
481  "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c",
483 }
484 
485 //---------------------------------------------------------------------------
486 // printf("%u", "xyz"); // Wrong argument type
487 // printf("%u%s", 1); // Too few arguments
488 // printf("", 1); // Too much arguments
489 //---------------------------------------------------------------------------
490 
491 static bool findFormat(nonneg int arg, const Token *firstArg,
492  const Token *&formatStringTok, const Token *&formatArgTok)
493 {
494  const Token* argTok = firstArg;
495 
496  for (int i = 0; i < arg && argTok; ++i)
497  argTok = argTok->nextArgument();
498 
499  if (Token::Match(argTok, "%str% [,)]")) {
500  formatArgTok = argTok->nextArgument();
501  formatStringTok = argTok;
502  return true;
503  }
504  if (Token::Match(argTok, "%var% [,)]") &&
505  argTok->variable() &&
506  Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") &&
507  (argTok->variable()->isPointer() ||
508  (argTok->variable()->dimensions().size() == 1 &&
509  argTok->variable()->dimensionKnown(0) &&
510  argTok->variable()->dimension(0) != 0))) {
511  formatArgTok = argTok->nextArgument();
512  if (!argTok->values().empty()) {
513  const std::list<ValueFlow::Value>::const_iterator value = std::find_if(
514  argTok->values().cbegin(), argTok->values().cend(), std::mem_fn(&ValueFlow::Value::isTokValue));
515  if (value != argTok->values().cend() && value->isTokValue() && value->tokvalue &&
516  value->tokvalue->tokType() == Token::eString) {
517  formatStringTok = value->tokvalue;
518  }
519  }
520  return true;
521  }
522  return false;
523 }
524 
525 // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename
526 static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::")
527 {
528  return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename);
529 }
530 
532 {
533  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
534  const bool isWindows = mSettings->platform.isWindows();
535 
536  logChecker("CheckIO::checkWrongPrintfScanfArguments");
537 
538  for (const Scope * scope : symbolDatabase->functionScopes) {
539  for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
540  if (!tok->isName()) continue;
541 
542  const Token* argListTok = nullptr; // Points to first va_list argument
543  const Token* formatStringTok = nullptr; // Points to format string token
544 
545  bool scan = false;
546  bool scanf_s = false;
547  int formatStringArgNo = -1;
548 
549  if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) {
550  formatStringArgNo = mSettings->library.formatstr_argno(tok);
551  scan = mSettings->library.formatstr_scan(tok);
552  scanf_s = mSettings->library.formatstr_secure(tok);
553  }
554 
555  if (formatStringArgNo >= 0) {
556  // formatstring found in library. Find format string and first argument belonging to format string.
557  if (!findFormat(formatStringArgNo, tok->tokAt(2), formatStringTok, argListTok))
558  continue;
559  } else if (Token::simpleMatch(tok, "swprintf (")) {
560  if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) {
561  // Find third parameter and format string
562  if (!findFormat(1, tok->tokAt(2), formatStringTok, argListTok))
563  continue;
564  } else {
565  // Find fourth parameter and format string
566  if (!findFormat(2, tok->tokAt(2), formatStringTok, argListTok))
567  continue;
568  }
569  } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) {
570  // template <size_t size> int sprintf_s(char (&buffer)[size], const char *format, ...);
571  if (findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) {
572  if (!formatStringTok)
573  continue;
574  }
575  // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);
576  else if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) {
577  if (!formatStringTok)
578  continue;
579  }
580  } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) {
581  // template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...);
582  if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) {
583  if (!formatStringTok)
584  continue;
585  }
586  // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...);
587  else if (findFormat(3, tok->tokAt(2), formatStringTok, argListTok)) {
588  if (!formatStringTok)
589  continue;
590  }
591  } else {
592  continue;
593  }
594 
595  if (!formatStringTok)
596  continue;
597 
598  checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s);
599  }
600  }
601 }
602 
603 void CheckIO::checkFormatString(const Token * const tok,
604  const Token * const formatStringTok,
605  const Token * argListTok,
606  const bool scan,
607  const bool scanf_s)
608 {
609  const bool isWindows = mSettings->platform.isWindows();
610  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
611  const std::string &formatString = formatStringTok->str();
612 
613  // Count format string parameters..
614  int numFormat = 0;
615  int numSecure = 0;
616  bool percent = false;
617  const Token* argListTok2 = argListTok;
618  std::set<int> parameterPositionsUsed;
619  for (std::string::const_iterator i = formatString.cbegin(); i != formatString.cend(); ++i) {
620  if (*i == '%') {
621  percent = !percent;
622  } else if (percent && *i == '[') {
623  while (i != formatString.cend()) {
624  if (*i == ']') {
625  numFormat++;
626  if (argListTok)
627  argListTok = argListTok->nextArgument();
628  percent = false;
629  break;
630  }
631  ++i;
632  }
633  if (scanf_s) {
634  numSecure++;
635  if (argListTok) {
636  argListTok = argListTok->nextArgument();
637  }
638  }
639  if (i == formatString.cend())
640  break;
641  } else if (percent) {
642  percent = false;
643 
644  bool _continue = false;
645  bool skip = false;
646  std::string width;
647  int parameterPosition = 0;
648  bool hasParameterPosition = false;
649  while (i != formatString.cend() && *i != '[' && !std::isalpha((unsigned char)*i)) {
650  if (*i == '*') {
651  skip = true;
652  if (scan)
653  _continue = true;
654  else {
655  numFormat++;
656  if (argListTok)
657  argListTok = argListTok->nextArgument();
658  }
659  } else if (std::isdigit(*i)) {
660  width += *i;
661  } else if (*i == '$') {
662  parameterPosition = strToInt<int>(width);
663  hasParameterPosition = true;
664  width.clear();
665  }
666  ++i;
667  }
668  auto bracketBeg = formatString.cend();
669  if (i != formatString.cend() && *i == '[') {
670  bracketBeg = i;
671  while (i != formatString.cend()) {
672  if (*i == ']')
673  break;
674 
675  ++i;
676  }
677  if (scanf_s && !skip) {
678  numSecure++;
679  if (argListTok) {
680  argListTok = argListTok->nextArgument();
681  }
682  }
683  }
684  if (i == formatString.cend())
685  break;
686  if (_continue)
687  continue;
688 
689  if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
690  ++numFormat;
691 
692  // Handle parameter positions (POSIX extension) - Ticket #4900
693  if (hasParameterPosition) {
694  if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end())
695  parameterPositionsUsed.insert(parameterPosition);
696  else // Parameter already referenced, hence don't consider it a new format
697  --numFormat;
698  }
699 
700  // Perform type checks
701  ArgumentInfo argInfo(argListTok, *mSettings, mTokenizer->isCPP());
702 
703  if ((argInfo.typeToken && !argInfo.isLibraryType(*mSettings)) || *i == ']') {
704  if (scan) {
705  std::string specifier;
706  bool done = false;
707  while (!done) {
708  switch (*i) {
709  case 's':
710  case ']': // charset
711  specifier += (*i == 's' || bracketBeg == formatString.end()) ? std::string{ 's' } : std::string{ bracketBeg, i + 1 };
712  if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
713  if (!width.empty()) {
714  const int numWidth = strToInt<int>(width);
715  if (numWidth != (argInfo.variableInfo->dimension(0) - 1))
716  invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, specifier);
717  }
718  }
719  if (argListTok && argListTok->tokType() != Token::eString && argInfo.typeToken &&
720  argInfo.isKnownType() && argInfo.isArrayOrPointer() &&
721  (!Token::Match(argInfo.typeToken, "char|wchar_t") ||
722  argInfo.typeToken->strAt(-1) == "const")) {
723  if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType()))
724  invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
725  }
726  if (scanf_s && argInfo.typeToken) {
727  numSecure++;
728  if (argListTok) {
729  argListTok = argListTok->nextArgument();
730  }
731  }
732  done = true;
733  break;
734  case 'c':
735  if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
736  if (!width.empty()) {
737  const int numWidth = strToInt<int>(width);
738  if (numWidth > argInfo.variableInfo->dimension(0))
739  invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, std::string(1, *i));
740  }
741  }
742  if (scanf_s) {
743  numSecure++;
744  if (argListTok) {
745  argListTok = argListTok->nextArgument();
746  }
747  }
748  done = true;
749  break;
750  case 'x':
751  case 'X':
752  case 'u':
753  case 'o':
754  specifier += *i;
755  if (argInfo.typeToken->tokType() == Token::eString)
756  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
757  else if (argInfo.isKnownType()) {
758  if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
759  if (argInfo.typeToken->isStandardType() || !argInfo.element)
760  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
761  } else if (!argInfo.typeToken->isUnsigned() ||
762  !argInfo.isArrayOrPointer() ||
763  argInfo.typeToken->strAt(-1) == "const") {
764  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
765  } else {
766  switch (specifier[0]) {
767  case 'h':
768  if (specifier[1] == 'h') {
769  if (argInfo.typeToken->str() != "char")
770  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
771  } else if (argInfo.typeToken->str() != "short")
772  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
773  break;
774  case 'l':
775  if (specifier[1] == 'l') {
776  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
777  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
778  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
779  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
780  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
781  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
782  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
783  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
784  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
785  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
786  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
787  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
788  break;
789  case 'I':
790  if (specifier.find("I64") != std::string::npos) {
791  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
792  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
793  } else if (specifier.find("I32") != std::string::npos) {
794  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
795  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
796  } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
797  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
798  break;
799  case 'j':
800  if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
801  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
802  break;
803  case 'z':
804  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
805  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
806  break;
807  case 't':
808  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
809  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
810  break;
811  case 'L':
812  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
813  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
814  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
815  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
816  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
817  break;
818  default:
819  if (argInfo.typeToken->str() != "int")
820  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
821  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
822  typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
823  typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
824  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
825  break;
826  }
827  }
828  }
829  done = true;
830  break;
831  case 'n':
832  case 'd':
833  case 'i':
834  specifier += *i;
835  if (argInfo.typeToken->tokType() == Token::eString)
836  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
837  else if (argInfo.isKnownType()) {
838  if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
839  if (argInfo.typeToken->isStandardType() || !argInfo.element)
840  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
841  } else if (argInfo.typeToken->isUnsigned() ||
842  !argInfo.isArrayOrPointer() ||
843  argInfo.typeToken->strAt(-1) == "const") {
844  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
845  } else {
846  switch (specifier[0]) {
847  case 'h':
848  if (specifier[1] == 'h') {
849  if (argInfo.typeToken->str() != "char")
850  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
851  } else if (argInfo.typeToken->str() != "short")
852  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
853  break;
854  case 'l':
855  if (specifier[1] == 'l') {
856  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
857  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
858  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
859  typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
860  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
861  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
862  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
863  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
864  typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
865  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
866  break;
867  case 'I':
868  if (specifier.find("I64") != std::string::npos) {
869  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
870  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
871  } else if (specifier.find("I32") != std::string::npos) {
872  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
873  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
874  } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
875  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
876  break;
877  case 'j':
878  if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
879  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
880  break;
881  case 'z':
882  if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
883  (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
884  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
885  break;
886  case 't':
887  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
888  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
889  break;
890  case 'L':
891  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
892  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
893  break;
894  default:
895  if (argInfo.typeToken->str() != "int")
896  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
897  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
898  argInfo.typeToken->originalName() == "intmax_t")
899  invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
900  break;
901  }
902  }
903  }
904  done = true;
905  break;
906  case 'e':
907  case 'E':
908  case 'f':
909  case 'g':
910  case 'G':
911  case 'a':
912  specifier += *i;
913  if (argInfo.typeToken->tokType() == Token::eString)
914  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
915  else if (argInfo.isKnownType()) {
916  if (!Token::Match(argInfo.typeToken, "float|double")) {
917  if (argInfo.typeToken->isStandardType())
918  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
919  } else if (!argInfo.isArrayOrPointer() ||
920  argInfo.typeToken->strAt(-1) == "const") {
921  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
922  } else {
923  switch (specifier[0]) {
924  case 'l':
925  if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong())
926  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
927  break;
928  case 'L':
929  if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong())
930  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
931  break;
932  default:
933  if (argInfo.typeToken->str() != "float")
934  invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
935  break;
936  }
937  }
938  }
939  done = true;
940  break;
941  case 'I':
942  if ((i+1 != formatString.cend() && *(i+1) == '6' &&
943  i+2 != formatString.cend() && *(i+2) == '4') ||
944  (i+1 != formatString.cend() && *(i+1) == '3' &&
945  i+2 != formatString.cend() && *(i+2) == '2')) {
946  specifier += *i++;
947  specifier += *i++;
948  if ((i+1) != formatString.cend() && !isalpha(*(i+1))) {
949  specifier += *i;
950  invalidLengthModifierError(tok, numFormat, specifier);
951  done = true;
952  } else {
953  specifier += *i++;
954  }
955  } else {
956  if ((i+1) != formatString.cend() && !isalpha(*(i+1))) {
957  specifier += *i;
958  invalidLengthModifierError(tok, numFormat, specifier);
959  done = true;
960  } else {
961  specifier += *i++;
962  }
963  }
964  break;
965  case 'h':
966  case 'l':
967  if (i+1 != formatString.cend() && *(i+1) == *i)
968  specifier += *i++;
969  FALLTHROUGH;
970  case 'j':
971  case 'q':
972  case 't':
973  case 'z':
974  case 'L':
975  // Expect an alphabetical character after these specifiers
976  if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
977  specifier += *i;
978  invalidLengthModifierError(tok, numFormat, specifier);
979  done = true;
980  } else {
981  specifier += *i++;
982  }
983  break;
984  default:
985  done = true;
986  break;
987  }
988  }
989  } else if (printWarning) {
990  std::string specifier;
991  bool done = false;
992  while (!done) {
993  if (i == formatString.end()) {
994  break;
995  }
996  switch (*i) {
997  case 's':
998  if (argListTok->tokType() != Token::eString &&
999  argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
1000  if (!Token::Match(argInfo.typeToken, "char|wchar_t")) {
1001  if (!argInfo.element)
1002  invalidPrintfArgTypeError_s(tok, numFormat, &argInfo);
1003  }
1004  }
1005  done = true;
1006  break;
1007  case 'n':
1008  if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString)
1009  invalidPrintfArgTypeError_n(tok, numFormat, &argInfo);
1010  done = true;
1011  break;
1012  case 'c':
1013  case 'x':
1014  case 'X':
1015  case 'o':
1016  specifier += *i;
1017  if (argInfo.typeToken->tokType() == Token::eString)
1018  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1019  else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1020  // use %p on pointers and arrays
1021  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1022  } else if (argInfo.isKnownType()) {
1023  if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) {
1024  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1025  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1026  } else {
1027  switch (specifier[0]) {
1028  case 'h':
1029  if (specifier[1] == 'h') {
1030  if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1031  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1032  } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1033  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1034  break;
1035  case 'l':
1036  if (specifier[1] == 'l') {
1037  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1038  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1039  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1040  argInfo.typeToken->originalName() == "uintmax_t")
1041  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1042  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1043  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1044  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1045  argInfo.typeToken->originalName() == "uintmax_t")
1046  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1047  break;
1048  case 'j':
1049  if (argInfo.typeToken->originalName() != "uintmax_t")
1050  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1051  break;
1052  case 'z':
1053  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1054  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1055  break;
1056  case 't':
1057  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1058  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1059  break;
1060  case 'I':
1061  if (specifier.find("I64") != std::string::npos) {
1062  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1063  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1064  } else if (specifier.find("I32") != std::string::npos) {
1065  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1066  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1067  } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1068  argInfo.typeToken->originalName() == "WPARAM" ||
1069  argInfo.typeToken->originalName() == "UINT_PTR" ||
1070  argInfo.typeToken->originalName() == "LONG_PTR" ||
1071  argInfo.typeToken->originalName() == "LPARAM" ||
1072  argInfo.typeToken->originalName() == "LRESULT"))
1073  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1074  break;
1075  default:
1076  if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int"))
1077  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1078  break;
1079  }
1080  }
1081  }
1082  done = true;
1083  break;
1084  case 'd':
1085  case 'i':
1086  specifier += *i;
1087  if (argInfo.typeToken->tokType() == Token::eString) {
1088  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1089  } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1090  // use %p on pointers and arrays
1091  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1092  } else if (argInfo.isKnownType()) {
1093  if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) {
1094  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1095  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1096  } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) {
1097  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1098  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1099  } else {
1100  switch (specifier[0]) {
1101  case 'h':
1102  if (specifier[1] == 'h') {
1103  if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned()))
1104  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1105  } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned()))
1106  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1107  break;
1108  case 'l':
1109  if (specifier[1] == 'l') {
1110  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1111  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1112  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1113  argInfo.typeToken->originalName() == "intmax_t")
1114  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1115  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1116  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1117  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1118  argInfo.typeToken->originalName() == "intmax_t")
1119  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1120  break;
1121  case 'j':
1122  if (argInfo.typeToken->originalName() != "intmax_t")
1123  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1124  break;
1125  case 't':
1126  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1127  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1128  break;
1129  case 'I':
1130  if (specifier.find("I64") != std::string::npos) {
1131  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1132  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1133  } else if (specifier.find("I32") != std::string::npos) {
1134  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1135  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1136  } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1137  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1138  break;
1139  case 'z':
1140  if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
1141  (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
1142  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1143  break;
1144  case 'L':
1145  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1146  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1147  break;
1148  default:
1149  if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1150  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1151  else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1152  argInfo.typeToken->originalName() == "intmax_t")
1153  invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1154  break;
1155  }
1156  }
1157  }
1158  done = true;
1159  break;
1160  case 'u':
1161  specifier += *i;
1162  if (argInfo.typeToken->tokType() == Token::eString) {
1163  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1164  } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1165  // use %p on pointers and arrays
1166  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1167  } else if (argInfo.isKnownType()) {
1168  if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) {
1169  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1170  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1171  } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) {
1172  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1173  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1174  } else {
1175  switch (specifier[0]) {
1176  case 'h':
1177  if (specifier[1] == 'h') {
1178  if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1179  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1180  } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1181  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1182  break;
1183  case 'l':
1184  if (specifier[1] == 'l') {
1185  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1186  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1187  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1188  argInfo.typeToken->originalName() == "uintmax_t")
1189  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1190  } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1191  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1192  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1193  argInfo.typeToken->originalName() == "uintmax_t")
1194  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1195  break;
1196  case 'j':
1197  if (argInfo.typeToken->originalName() != "uintmax_t")
1198  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1199  break;
1200  case 'z':
1201  if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1202  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1203  break;
1204  case 't':
1205  if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1206  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1207  break;
1208  case 'I':
1209  if (specifier.find("I64") != std::string::npos) {
1210  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1211  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1212  } else if (specifier.find("I32") != std::string::npos) {
1213  if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1214  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1215  } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1216  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1217  break;
1218  case 'L':
1219  if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1220  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1221  break;
1222  default:
1223  if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1224  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1225  else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1226  argInfo.typeToken->originalName() == "intmax_t")
1227  invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1228  break;
1229  }
1230  }
1231  }
1232  done = true;
1233  break;
1234  case 'p':
1235  if (argInfo.typeToken->tokType() == Token::eString)
1236  ; // string literals are passed as pointers to literal start, okay
1237  else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer())
1238  invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
1239  done = true;
1240  break;
1241  case 'e':
1242  case 'E':
1243  case 'f':
1244  case 'g':
1245  case 'G':
1246  specifier += *i;
1247  if (argInfo.typeToken->tokType() == Token::eString)
1248  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1249  else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1250  // use %p on pointers and arrays
1251  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1252  } else if (argInfo.isKnownType()) {
1253  if (!Token::Match(argInfo.typeToken, "float|double")) {
1254  if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1255  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1256  } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
1257  (specifier[0] != 'L' && argInfo.typeToken->isLong()))
1258  invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1259  }
1260  done = true;
1261  break;
1262  case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
1263  case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
1264  // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
1265  if ((i + 1) != formatString.end() && *(i + 1) == *i) {
1266  if ((i + 2) != formatString.end() && !isalpha(*(i + 2))) {
1267  std::string modifier;
1268  modifier += *i;
1269  modifier += *(i + 1);
1270  invalidLengthModifierError(tok, numFormat, modifier);
1271  done = true;
1272  } else {
1273  specifier = *i++;
1274  specifier += *i++;
1275  }
1276  } else {
1277  if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) {
1278  std::string modifier;
1279  modifier += *i;
1280  invalidLengthModifierError(tok, numFormat, modifier);
1281  done = true;
1282  } else {
1283  specifier = *i++;
1284  }
1285  }
1286  }
1287  break;
1288  case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64
1289  if ((*(i+1) == '3' && *(i+2) == '2') ||
1290  (*(i+1) == '6' && *(i+2) == '4')) {
1291  specifier += *i++;
1292  specifier += *i++;
1293  }
1294  FALLTHROUGH;
1295  case 'j': // intmax_t or uintmax_t
1296  case 'z': // size_t
1297  case 't': // ptrdiff_t
1298  case 'L': // long double
1299  // Expect an alphabetical character after these specifiers
1300  if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
1301  specifier += *i;
1302  invalidLengthModifierError(tok, numFormat, specifier);
1303  done = true;
1304  } else {
1305  specifier += *i++;
1306  }
1307  break;
1308  default:
1309  done = true;
1310  break;
1311  }
1312  }
1313  }
1314  }
1315 
1316  if (argListTok)
1317  argListTok = argListTok->nextArgument(); // Find next argument
1318  }
1319  }
1320  }
1321 
1322  // Count printf/scanf parameters..
1323  int numFunction = 0;
1324  while (argListTok2) {
1325  if (Token::Match(argListTok2, "%name% ...")) // bailout for parameter pack
1326  return;
1327  numFunction++;
1328  argListTok2 = argListTok2->nextArgument(); // Find next argument
1329  }
1330 
1331  if (printWarning) {
1332  // Check that all parameter positions reference an actual parameter
1333  for (const int i : parameterPositionsUsed) {
1334  if ((i == 0) || (i > numFormat))
1335  wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat);
1336  }
1337  }
1338 
1339  // Mismatching number of parameters => warning
1340  if ((numFormat + numSecure) != numFunction)
1341  wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction);
1342 }
1343 
1344 // We currently only support string literals, variables, and functions.
1345 /// @todo add non-string literals, and generic expressions
1346 
1347 CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings &settings, bool _isCPP)
1348  : isCPP(_isCPP)
1349 {
1350  if (!arg)
1351  return;
1352 
1353  // Use AST type info
1354  // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type.
1355  if (!Token::Match(arg, "%str% ,|)") && !(arg->variable() && arg->variable()->isArray())) {
1356  const Token *top = arg;
1357  while (top->str() == "(" && !top->isCast())
1358  top = top->next();
1359  while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous())
1360  top = top->astParent();
1361  const ValueType *valuetype = top->argumentType();
1362  if (valuetype && valuetype->type >= ValueType::Type::BOOL) {
1363  typeToken = tempToken = new Token(top);
1364  if (valuetype->pointer && valuetype->constness & 1) {
1365  tempToken->str("const");
1366  tempToken->insertToken("a");
1367  tempToken = tempToken->next();
1368  }
1369  if (valuetype->type == ValueType::BOOL)
1370  tempToken->str("bool");
1371  else if (valuetype->type == ValueType::CHAR)
1372  tempToken->str("char");
1373  else if (valuetype->type == ValueType::SHORT)
1374  tempToken->str("short");
1375  else if (valuetype->type == ValueType::WCHAR_T)
1376  tempToken->str("wchar_t");
1377  else if (valuetype->type == ValueType::INT)
1378  tempToken->str("int");
1379  else if (valuetype->type == ValueType::LONG)
1380  tempToken->str("long");
1381  else if (valuetype->type == ValueType::LONGLONG) {
1382  tempToken->str("long");
1383  tempToken->isLong(true);
1384  } else if (valuetype->type == ValueType::FLOAT)
1385  tempToken->str("float");
1386  else if (valuetype->type == ValueType::DOUBLE)
1387  tempToken->str("double");
1388  else if (valuetype->type == ValueType::LONGDOUBLE) {
1389  tempToken->str("double");
1390  tempToken->isLong(true);
1391  }
1392  if (valuetype->isIntegral()) {
1393  if (valuetype->sign == ValueType::Sign::UNSIGNED)
1394  tempToken->isUnsigned(true);
1395  else if (valuetype->sign == ValueType::Sign::SIGNED)
1396  tempToken->isSigned(true);
1397  }
1398  if (!valuetype->originalTypeName.empty())
1400  for (int p = 0; p < valuetype->pointer; p++)
1401  tempToken->insertToken("*");
1402  tempToken = const_cast<Token*>(typeToken);
1403  if (top->isBinaryOp() && valuetype->pointer == 1 && (valuetype->type == ValueType::CHAR || valuetype->type == ValueType::WCHAR_T))
1405  return;
1406  }
1407  }
1408 
1409 
1410  if (arg->tokType() == Token::eString) {
1411  typeToken = arg;
1412  return;
1413  }
1414  if (arg->str() == "&" || arg->tokType() == Token::eVariable ||
1415  arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") ||
1416  (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") &&
1417  Token::simpleMatch(arg->linkAt(1), "> (") &&
1418  Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) {
1419  if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) {
1420  typeToken = arg->tokAt(2);
1421  while (typeToken->str() == "const" || typeToken->str() == "extern")
1422  typeToken = typeToken->next();
1423  return;
1424  }
1425  if (arg->str() == "&") {
1426  address = true;
1427  arg = arg->next();
1428  }
1429  while (Token::Match(arg, "%type% ::"))
1430  arg = arg->tokAt(2);
1431  if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction))
1432  return;
1433  const Token *varTok = nullptr;
1434  const Token *tok1 = arg->next();
1435  for (; tok1; tok1 = tok1->next()) {
1436  if (tok1->str() == "," || tok1->str() == ")") {
1437  if (tok1->previous()->str() == "]") {
1438  varTok = tok1->linkAt(-1)->previous();
1439  if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) {
1440  const Function * function = varTok->link()->previous()->function();
1441  if (function && function->retType && function->retType->isEnumType()) {
1442  if (function->retType->classScope->enumType)
1443  typeToken = function->retType->classScope->enumType;
1444  else {
1445  tempToken = new Token(tok1);
1446  tempToken->str("int");
1447  typeToken = tempToken;
1448  }
1449  } else if (function && function->retDef) {
1450  typeToken = function->retDef;
1451  while (typeToken->str() == "const" || typeToken->str() == "extern")
1452  typeToken = typeToken->next();
1453  functionInfo = function;
1454  element = true;
1455  }
1456  return;
1457  }
1458  } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) {
1459  const Function * function = tok1->linkAt(-1)->previous()->function();
1460  if (function && function->retType && function->retType->isEnumType()) {
1461  if (function->retType->classScope->enumType)
1462  typeToken = function->retType->classScope->enumType;
1463  else {
1464  tempToken = new Token(tok1);
1465  tempToken->str("int");
1466  typeToken = tempToken;
1467  }
1468  } else if (function && function->retDef) {
1469  typeToken = function->retDef;
1470  while (typeToken->str() == "const" || typeToken->str() == "extern")
1471  typeToken = typeToken->next();
1472  functionInfo = function;
1473  element = false;
1474  }
1475  return;
1476  } else
1477  varTok = tok1->previous();
1478  break;
1479  }
1480  if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[")
1481  tok1 = tok1->link();
1482  else if (tok1->link() && tok1->str() == "<")
1483  tok1 = tok1->link();
1484 
1485  // check for some common well known functions
1486  else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) ||
1487  (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) {
1488  tempToken = new Token(tok1);
1489  if (tok1->next()->str() == "size") {
1490  // size_t is platform dependent
1491  if (settings.platform.sizeof_size_t == 8) {
1492  tempToken->str("long");
1493  if (settings.platform.sizeof_long != 8)
1494  tempToken->isLong(true);
1495  } else if (settings.platform.sizeof_size_t == 4) {
1496  if (settings.platform.sizeof_long == 4) {
1497  tempToken->str("long");
1498  } else {
1499  tempToken->str("int");
1500  }
1501  }
1502 
1503  tempToken->originalName("size_t");
1504  tempToken->isUnsigned(true);
1505  } else if (tok1->next()->str() == "empty") {
1506  tempToken->str("bool");
1507  } else if (tok1->next()->str() == "c_str") {
1508  tempToken->str("const");
1509  tempToken->insertToken("*");
1510  if (typeToken->strAt(2) == "string")
1511  tempToken->insertToken("char");
1512  else
1513  tempToken->insertToken("wchar_t");
1514  }
1515  typeToken = tempToken;
1516  return;
1517  }
1518 
1519  // check for std::vector::at() and std::string::at()
1520  else if (Token::Match(tok1->previous(), "%var% . at (") &&
1521  Token::Match(tok1->linkAt(2), ") [,)]")) {
1522  varTok = tok1->previous();
1523  variableInfo = varTok->variable();
1524 
1525  if (!variableInfo || !isStdVectorOrString()) {
1526  variableInfo = nullptr;
1527  typeToken = nullptr;
1528  }
1529 
1530  return;
1531  } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction))
1532  return;
1533  }
1534 
1535  if (varTok) {
1536  variableInfo = varTok->variable();
1537  element = tok1->previous()->str() == "]";
1538 
1539  // look for std::vector operator [] and use template type as return type
1540  if (variableInfo) {
1541  if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true
1542  element = false; // not really an array element
1543  } else if (variableInfo->isEnumType()) {
1546  else {
1547  tempToken = new Token(tok1);
1548  tempToken->str("int");
1549  typeToken = tempToken;
1550  }
1551  } else
1553  }
1554 
1555  return;
1556  }
1557  }
1558 }
1559 
1561 {
1562  if (tempToken) {
1563  while (tempToken->next())
1564  tempToken->deleteNext();
1565 
1566  delete tempToken;
1567  }
1568 }
1569 
1570 namespace {
1571  const std::set<std::string> stl_vector = { "array", "vector" };
1572  const std::set<std::string> stl_string = { "string", "u16string", "u32string", "wstring" };
1573 }
1574 
1576 {
1577  if (!isCPP)
1578  return false;
1579  if (variableInfo->isStlType(stl_vector)) {
1580  typeToken = variableInfo->typeStartToken()->tokAt(4);
1581  _template = true;
1582  return true;
1583  }
1584  if (variableInfo->isStlType(stl_string)) {
1585  tempToken = new Token(variableInfo->typeStartToken());
1586  if (variableInfo->typeStartToken()->strAt(2) == "string")
1587  tempToken->str("char");
1588  else
1589  tempToken->str("wchar_t");
1590  typeToken = tempToken;
1591  return true;
1592  }
1593  if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) {
1594  const std::vector<Type::BaseInfo>& derivedFrom = variableInfo->type()->derivedFrom;
1595  for (const Type::BaseInfo & i : derivedFrom) {
1596  const Token* nameTok = i.nameTok;
1597  if (Token::Match(nameTok, "std :: vector|array <")) {
1598  typeToken = nameTok->tokAt(4);
1599  _template = true;
1600  return true;
1601  }
1602  if (Token::Match(nameTok, "std :: string|wstring")) {
1603  tempToken = new Token(variableInfo->typeStartToken());
1604  if (nameTok->strAt(2) == "string")
1605  tempToken->str("char");
1606  else
1607  tempToken->str("wchar_t");
1608  typeToken = tempToken;
1609  return true;
1610  }
1611  }
1612  } else if (variableInfo->type()) {
1613  const Scope * classScope = variableInfo->type()->classScope;
1614  if (classScope) {
1615  for (const Function &func : classScope->functionList) {
1616  if (func.name() == "operator[]") {
1617  if (Token::Match(func.retDef, "%type% &")) {
1618  typeToken = func.retDef;
1619  return true;
1620  }
1621  }
1622  }
1623  }
1624  }
1625 
1626  return false;
1627 }
1628 
1629 static const std::set<std::string> stl_container = {
1630  "array", "bitset", "deque", "forward_list",
1631  "hash_map", "hash_multimap", "hash_set",
1632  "list", "map", "multimap", "multiset",
1633  "priority_queue", "queue", "set", "stack",
1634  "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector"
1635 };
1636 
1638 {
1639  if (!isCPP)
1640  return false;
1641  if (tok && tok->variable()) {
1642  const Variable* variable = tok->variable();
1643  if (variable->isStlType(stl_container)) {
1644  typeToken = variable->typeStartToken()->tokAt(4);
1645  return true;
1646  }
1647  if (variable->isStlType(stl_string)) {
1648  typeToken = variable->typeStartToken();
1649  return true;
1650  }
1651  if (variable->type() && !variable->type()->derivedFrom.empty()) {
1652  for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) {
1653  const Token* nameTok = baseInfo.nameTok;
1654  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 <")) {
1655  typeToken = nameTok->tokAt(4);
1656  return true;
1657  }
1658  if (Token::Match(nameTok, "std :: string|wstring")) {
1659  typeToken = nameTok;
1660  return true;
1661  }
1662  }
1663  }
1664  }
1665 
1666  return false;
1667 }
1668 
1670 {
1671  if (address)
1672  return true;
1673  if (variableInfo && !_template)
1674  return variableInfo->isArrayOrPointer();
1675 
1676  const Token *tok = typeToken;
1677  while (Token::Match(tok, "const|struct"))
1678  tok = tok->next();
1679  return tok && tok->strAt(1) == "*";
1680 }
1681 
1683 {
1684  if (variableInfo->type())
1685  return (true);
1686 
1687  const Token* varTypeTok = typeToken;
1688  if (varTypeTok->str() == "std")
1689  varTypeTok = varTypeTok->tokAt(2);
1690 
1691  return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer());
1692 }
1693 
1695 {
1696  if (variableInfo)
1697  return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType());
1698  if (functionInfo)
1699  return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring"));
1700 
1701  return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring");
1702 }
1703 
1705 {
1706  return typeToken && typeToken->isStandardType() && settings.library.podtype(typeToken->str());
1707 }
1708 
1710  const std::string &functionName,
1711  nonneg int numFormat,
1712  nonneg int numFunction)
1713 {
1714  const Severity severity = numFormat > numFunction ? Severity::error : Severity::warning;
1715  if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("wrongPrintfScanfArgNum"))
1716  return;
1717 
1718  std::ostringstream errmsg;
1719  errmsg << functionName
1720  << " format string requires "
1721  << numFormat
1722  << " parameter" << (numFormat != 1 ? "s" : "") << " but "
1723  << (numFormat > numFunction ? "only " : "")
1724  << numFunction
1725  << (numFunction != 1 ? " are" : " is")
1726  << " given.";
1727 
1728  reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal);
1729 }
1730 
1731 void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName,
1732  nonneg int index, nonneg int numFunction)
1733 {
1734  if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("wrongPrintfScanfParameterPositionError"))
1735  return;
1736  std::ostringstream errmsg;
1737  errmsg << functionName << ": ";
1738  if (index == 0) {
1739  errmsg << "parameter positions start at 1, not 0";
1740  } else {
1741  errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given";
1742  }
1743  reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal);
1744 }
1745 
1746 void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1747 {
1748  const Severity severity = getSeverity(argInfo);
1749  if (!mSettings->severity.isEnabled(severity))
1750  return;
1751  std::ostringstream errmsg;
1752  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'";
1753  if (specifier[0] == 's')
1754  errmsg << "char";
1755  else if (specifier[0] == 'S')
1756  errmsg << "wchar_t";
1757  errmsg << " *\' but the argument type is ";
1758  argumentType(errmsg, argInfo);
1759  errmsg << ".";
1760  reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1761 }
1762 void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned)
1763 {
1764  const Severity severity = getSeverity(argInfo);
1765  if (!mSettings->severity.isEnabled(severity))
1766  return;
1767  std::ostringstream errmsg;
1768  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1769  if (specifier[0] == 'h') {
1770  if (specifier[1] == 'h')
1771  errmsg << (isUnsigned ? "unsigned " : "") << "char";
1772  else
1773  errmsg << (isUnsigned ? "unsigned " : "") << "short";
1774  } else if (specifier[0] == 'l') {
1775  if (specifier[1] == 'l')
1776  errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1777  else
1778  errmsg << (isUnsigned ? "unsigned " : "") << "long";
1779  } else if (specifier.find("I32") != std::string::npos) {
1780  errmsg << (isUnsigned ? "unsigned " : "") << "__int32";
1781  } else if (specifier.find("I64") != std::string::npos) {
1782  errmsg << (isUnsigned ? "unsigned " : "") << "__int64";
1783  } else if (specifier[0] == 'I') {
1784  errmsg << (isUnsigned ? "size_t" : "ptrdiff_t");
1785  } else if (specifier[0] == 'j') {
1786  if (isUnsigned)
1787  errmsg << "uintmax_t";
1788  else
1789  errmsg << "intmax_t";
1790  } else if (specifier[0] == 'z') {
1791  if (specifier[1] == 'd' || specifier[1] == 'i')
1792  errmsg << "ssize_t";
1793  else
1794  errmsg << "size_t";
1795  } else if (specifier[0] == 't') {
1796  errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1797  } else if (specifier[0] == 'L') {
1798  errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1799  } else {
1800  errmsg << (isUnsigned ? "unsigned " : "") << "int";
1801  }
1802  errmsg << " *\' but the argument type is ";
1803  argumentType(errmsg, argInfo);
1804  errmsg << ".";
1805  reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal);
1806 }
1807 void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1808 {
1809  const Severity severity = getSeverity(argInfo);
1810  if (!mSettings->severity.isEnabled(severity))
1811  return;
1812  std::ostringstream errmsg;
1813  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1814  if (specifier[0] == 'l' && specifier[1] != 'l')
1815  errmsg << "double";
1816  else if (specifier[0] == 'L')
1817  errmsg << "long double";
1818  else
1819  errmsg << "float";
1820  errmsg << " *\' but the argument type is ";
1821  argumentType(errmsg, argInfo);
1822  errmsg << ".";
1823  reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1824 }
1825 
1826 void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1827 {
1828  const Severity severity = getSeverity(argInfo);
1829  if (!mSettings->severity.isEnabled(severity))
1830  return;
1831  std::ostringstream errmsg;
1832  errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is ";
1833  argumentType(errmsg, argInfo);
1834  errmsg << ".";
1835  reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1836 }
1837 void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1838 {
1839  const Severity severity = getSeverity(argInfo);
1840  if (!mSettings->severity.isEnabled(severity))
1841  return;
1842  std::ostringstream errmsg;
1843  errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is ";
1844  argumentType(errmsg, argInfo);
1845  errmsg << ".";
1846  reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal);
1847 }
1848 void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1849 {
1850  const Severity severity = getSeverity(argInfo);
1851  if (!mSettings->severity.isEnabled(severity))
1852  return;
1853  std::ostringstream errmsg;
1854  errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is ";
1855  argumentType(errmsg, argInfo);
1856  errmsg << ".";
1857  reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, Certainty::normal);
1858 }
1859 static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned)
1860 {
1861  os << "\'";
1862  if (specifier[0] == 'l') {
1863  if (specifier[1] == 'l')
1864  os << (isUnsigned ? "unsigned " : "") << "long long";
1865  else
1866  os << (isUnsigned ? "unsigned " : "") << "long";
1867  } else if (specifier[0] == 'h') {
1868  if (specifier[1] == 'h')
1869  os << (isUnsigned ? "unsigned " : "") << "char";
1870  else
1871  os << (isUnsigned ? "unsigned " : "") << "short";
1872  } else if (specifier.find("I32") != std::string::npos) {
1873  os << (isUnsigned ? "unsigned " : "") << "__int32";
1874  } else if (specifier.find("I64") != std::string::npos) {
1875  os << (isUnsigned ? "unsigned " : "") << "__int64";
1876  } else if (specifier[0] == 'I') {
1877  os << (isUnsigned ? "size_t" : "ptrdiff_t");
1878  } else if (specifier[0] == 'j') {
1879  if (isUnsigned)
1880  os << "uintmax_t";
1881  else
1882  os << "intmax_t";
1883  } else if (specifier[0] == 'z') {
1884  if (specifier[1] == 'd' || specifier[1] == 'i')
1885  os << "ssize_t";
1886  else
1887  os << "size_t";
1888  } else if (specifier[0] == 't') {
1889  os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1890  } else if (specifier[0] == 'L') {
1891  os << (isUnsigned ? "unsigned " : "") << "long long";
1892  } else {
1893  os << (isUnsigned ? "unsigned " : "") << "int";
1894  }
1895  os << "\'";
1896 }
1897 
1898 void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1899 {
1900  const Severity severity = getSeverity(argInfo);
1901  if (!mSettings->severity.isEnabled(severity))
1902  return;
1903  std::ostringstream errmsg;
1904  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1905  printfFormatType(errmsg, specifier, true);
1906  errmsg << " but the argument type is ";
1907  argumentType(errmsg, argInfo);
1908  errmsg << ".";
1909  reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal);
1910 }
1911 
1912 void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1913 {
1914  const Severity severity = getSeverity(argInfo);
1915  if (!mSettings->severity.isEnabled(severity))
1916  return;
1917  std::ostringstream errmsg;
1918  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1919  printfFormatType(errmsg, specifier, false);
1920  errmsg << " but the argument type is ";
1921  argumentType(errmsg, argInfo);
1922  errmsg << ".";
1923  reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal);
1924 }
1925 void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1926 {
1927  const Severity severity = getSeverity(argInfo);
1928  if (!mSettings->severity.isEnabled(severity))
1929  return;
1930  std::ostringstream errmsg;
1931  errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1932  if (specifier[0] == 'L')
1933  errmsg << "long ";
1934  errmsg << "double\' but the argument type is ";
1935  argumentType(errmsg, argInfo);
1936  errmsg << ".";
1937  reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1938 }
1939 
1941 {
1942  return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning;
1943 }
1944 
1945 void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
1946 {
1947  if (argInfo) {
1948  os << "\'";
1949  const Token *type = argInfo->typeToken;
1950  if (type->tokType() == Token::eString) {
1951  if (type->isLong())
1952  os << "const wchar_t *";
1953  else
1954  os << "const char *";
1955  } else {
1956  if (type->originalName().empty()) {
1957  if (type->strAt(-1) == "const")
1958  os << "const ";
1959  while (Token::Match(type, "const|struct")) {
1960  os << type->str() << " ";
1961  type = type->next();
1962  }
1963  while (Token::Match(type, "%any% ::")) {
1964  os << type->str() << "::";
1965  type = type->tokAt(2);
1966  }
1967  os << type->stringify(false, true, false);
1968  if (type->strAt(1) == "*" && !argInfo->element)
1969  os << " *";
1970  else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
1971  os << " *";
1972  else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray())
1973  os << " *";
1974  if (argInfo->address)
1975  os << " *";
1976  } else {
1977  if (type->isUnsigned()) {
1978  if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t")
1979  os << "unsigned ";
1980  }
1981  os << type->originalName();
1982  if (type->strAt(1) == "*" || argInfo->address)
1983  os << " *";
1984  os << " {aka " << type->stringify(false, true, false);
1985  if (type->strAt(1) == "*" || argInfo->address)
1986  os << " *";
1987  os << "}";
1988  }
1989  }
1990  os << "\'";
1991  } else
1992  os << "Unknown";
1993 }
1994 
1995 void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier)
1996 {
1997  if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("invalidLengthModifierError"))
1998  return;
1999  std::ostringstream errmsg;
2000  errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
2001  reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal);
2002 }
2003 
2004 void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier)
2005 {
2006  MathLib::bigint arrlen = 0;
2007  std::string varname;
2008 
2009  if (var) {
2010  arrlen = var->dimension(0);
2011  varname = var->name();
2012  }
2013 
2014  std::ostringstream errmsg;
2015  if (arrlen > width) {
2017  return;
2018  errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer"
2019  << " '" << varname << "[" << arrlen << "]'.";
2020  reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), Certainty::inconclusive);
2021  } else {
2022  errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '"
2023  << varname << "[" << arrlen << "]', use %" << (specifier == "c" ? arrlen : (arrlen - 1)) << specifier << " to prevent overflowing it.";
2024  reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal);
2025  }
2026 }
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3612
static void printfFormatType(std::ostream &os, const std::string &specifier, bool isUnsigned)
Definition: checkio.cpp:1859
static bool findFormat(nonneg int arg, const Token *firstArg, const Token *&formatStringTok, const Token *&formatArgTok)
Definition: checkio.cpp:491
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:1629
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:526
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:1669
bool isKnownType() const
Definition: checkio.cpp:1694
const Function * functionInfo
Definition: checkio.h:93
bool isLibraryType(const Settings &settings) const
Definition: checkio.cpp:1704
bool isStdContainer(const Token *tok)
Definition: checkio.cpp:1637
ArgumentInfo(const Token *arg, const Settings &settings, bool _isCPP)
Definition: checkio.cpp:1347
const Token * typeToken
Definition: checkio.h:92
bool isComplexType() const
Definition: checkio.cpp:1682
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:1898
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:1837
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:1826
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:1995
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:1807
void invalidScanfError(const Token *tok)
Definition: checkio.cpp:459
static void argumentType(std::ostream &os, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1945
void checkFormatString(const Token *const tok, const Token *const formatStringTok, const Token *argListTok, const bool scan, const bool scanf_s)
Definition: checkio.cpp:603
void wrongPrintfScanfPosixParameterPositionError(const Token *tok, const std::string &functionName, nonneg int index, nonneg int numFunction)
Definition: checkio.cpp:1731
static Severity getSeverity(const ArgumentInfo *argInfo)
Definition: checkio.cpp:1940
void invalidScanfFormatWidthError(const Token *tok, nonneg int numFormat, int width, const Variable *var, const std::string &specifier)
Definition: checkio.cpp:2004
void invalidPrintfArgTypeError_sint(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1912
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:531
void invalidScanfArgTypeError_int(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo, bool isUnsigned)
Definition: checkio.cpp:1762
void invalidPrintfArgTypeError_p(const Token *tok, nonneg int numFormat, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1848
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:1925
void invalidScanfArgTypeError_s(const Token *tok, nonneg int numFormat, const std::string &specifier, const ArgumentInfo *argInfo)
Definition: checkio.cpp:1746
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:1709
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:1378
bool formatstr_secure(const Token *ftok) const
Definition: library.cpp:1403
bool isnoreturn(const Token *ftok) const
Definition: library.cpp:1558
int formatstr_argno(const Token *ftok) const
Definition: library.cpp:1389
const PodType * podtype(const std::string &name) const
Definition: library.h:450
bool isFunctionConst(const std::string &functionName, bool pure) const
Definition: library.cpp:1534
bool formatstr_scan(const Token *ftok) const
Definition: library.cpp:1398
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
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:608
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:688
const std::string & originalName() const
Definition: token.h:1193
std::string stringify(const stringifyOptions &options) const
Definition: token.cpp:1280
bool isUnsigned() const
Definition: token.h:424
const std::string & strAt(int index) const
Definition: token.cpp:423
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1093
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:393
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:1105
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1468
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:413
@ 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:223
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:869
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:1437
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:1708