LCOV - code coverage report
Current view: top level - lib - checkio.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1353 1433 94.4 %
Date: 2024-04-28 12:00:40 Functions: 42 42 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14