Cppcheck
checkio.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 //---------------------------------------------------------------------------
00020 #include "checkio.h"
00021 
00022 #include "tokenize.h"
00023 #include "token.h"
00024 #include "errorlogger.h"
00025 #include "symboldatabase.h"
00026 
00027 #include <cctype>
00028 #include <cstdlib>
00029 
00030 //---------------------------------------------------------------------------
00031 
00032 // Register CheckIO..
00033 namespace {
00034     CheckIO instance;
00035 }
00036 
00037 
00038 //---------------------------------------------------------------------------
00039 //    std::cout << std::cout;
00040 //---------------------------------------------------------------------------
00041 void CheckIO::checkCoutCerrMisusage()
00042 {
00043     const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
00044     std::size_t functions = symbolDatabase->functionScopes.size();
00045     for (std::size_t i = 0; i < functions; ++i) {
00046         const Scope * scope = symbolDatabase->functionScopes[i];
00047         bool firstCout = false;
00048         for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
00049             if (tok->str() == "(")
00050                 tok = tok->link();
00051 
00052             if (Token::Match(tok, "std :: cout|cerr")) {
00053                 if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") {
00054                     coutCerrMisusageError(tok, tok->strAt(2));
00055                     firstCout = false;
00056                 } else if (tok->strAt(3) == "<<")
00057                     firstCout = true;
00058             } else if (firstCout && tok->str() == ";")
00059                 firstCout = false;
00060         }
00061     }
00062 }
00063 
00064 void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName)
00065 {
00066     reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.");
00067 }
00068 
00069 //---------------------------------------------------------------------------
00070 // fflush(stdin) <- fflush only applies to output streams in ANSI C
00071 // fread(); fwrite(); <- consecutive read/write statements require repositioning in between
00072 // fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
00073 // fclose(); fread(); <- Use closed file
00074 //---------------------------------------------------------------------------
00075 enum OpenMode {CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN};
00076 static OpenMode getMode(const std::string& str)
00077 {
00078     if (str.find('+', 1) != std::string::npos)
00079         return RW_MODE;
00080     else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos)
00081         return WRITE_MODE;
00082     else if (str.find('r') != std::string::npos)
00083         return READ_MODE;
00084     return UNKNOWN;
00085 }
00086 
00087 struct Filepointer {
00088     OpenMode mode;
00089     unsigned int mode_indent;
00090     enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation;
00091     unsigned int op_indent;
00092     Filepointer(OpenMode mode_ = UNKNOWN)
00093         : mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0) {
00094     }
00095 };
00096 
00097 void CheckIO::checkFileUsage()
00098 {
00099     static const char* _whitelist[] = {
00100         "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc"
00101     };
00102     static const std::set<std::string> whitelist(_whitelist, _whitelist + sizeof(_whitelist)/sizeof(*_whitelist));
00103 
00104     std::map<unsigned int, Filepointer> filepointers;
00105 
00106     const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
00107     std::size_t varListSize = symbolDatabase->getVariableListSize();
00108     for (std::size_t i = 1; i < varListSize; ++i) {
00109         const Variable* var = symbolDatabase->getVariableFromVarId(i);
00110         if (!var || !var->varId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *"))
00111             continue;
00112 
00113         if (var->isLocal()) {
00114             if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor"
00115                 filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN)));
00116             else
00117                 filepointers.insert(std::make_pair(var->varId(), Filepointer(CLOSED)));
00118         } else {
00119             filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN)));
00120             // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
00121         }
00122     }
00123 
00124     std::size_t functions = symbolDatabase->functionScopes.size();
00125     for (std::size_t j = 0; j < functions; ++j) {
00126         const Scope * scope = symbolDatabase->functionScopes[j];
00127         unsigned int indent = 0;
00128         for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
00129             if (tok->str() == "{")
00130                 indent++;
00131             else if (tok->str() == "}") {
00132                 indent--;
00133                 for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
00134                     if (indent < i->second.mode_indent) {
00135                         i->second.mode_indent = 0;
00136                         i->second.mode = UNKNOWN;
00137                     }
00138                     if (indent < i->second.op_indent) {
00139                         i->second.op_indent = 0;
00140                         i->second.lastOperation = Filepointer::UNKNOWN_OP;
00141                     }
00142                 }
00143             } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break") { // Reset upon return, continue or break
00144                 for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
00145                     i->second.mode_indent = 0;
00146                     i->second.mode = UNKNOWN;
00147                     i->second.op_indent = 0;
00148                     i->second.lastOperation = Filepointer::UNKNOWN_OP;
00149                 }
00150             } else if (tok->varId() && Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile")) {
00151                 std::map<unsigned int, Filepointer>::iterator i = filepointers.find(tok->varId());
00152                 if (i != filepointers.end()) {
00153                     i->second.mode = UNKNOWN;
00154                     i->second.lastOperation = Filepointer::UNKNOWN_OP;
00155                 }
00156             } else if (Token::Match(tok, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
00157                 std::string mode;
00158                 const Token* fileTok = 0;
00159                 Filepointer::Operation operation = Filepointer::NONE;
00160 
00161                 if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile") && tok->strAt(-1) == "=") {
00162                     if (tok->str() != "tmpfile") {
00163                         const Token* modeTok = tok->tokAt(2)->nextArgument();
00164                         if (modeTok && modeTok->type() == Token::eString)
00165                             mode = modeTok->strValue();
00166                     } else
00167                         mode = "wb+";
00168                     fileTok = tok->tokAt(-2);
00169                     operation = Filepointer::OPEN;
00170                 } else if (tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") {
00171                     if (Token::simpleMatch(tok, "fflush ( stdin )"))
00172                         fflushOnInputStreamError(tok, tok->strAt(2));
00173                     else {
00174                         fileTok = tok->tokAt(2);
00175                         operation = Filepointer::POSITIONING;
00176                     }
00177                 } else if (tok->str() == "fgetc" || tok->str() == "fgets" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "getc") {
00178                     if (tok->str() == "fscanf")
00179                         fileTok = tok->tokAt(2);
00180                     else
00181                         fileTok = tok->linkAt(1)->previous();
00182                     operation = Filepointer::READ;
00183                 } else if (tok->str() == "fputc" || tok->str() == "fputs" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "putcc") {
00184                     if (tok->str() == "fprintf")
00185                         fileTok = tok->tokAt(2);
00186                     else
00187                         fileTok = tok->linkAt(1)->previous();
00188                     operation = Filepointer::WRITE;
00189                 } else if (tok->str() == "fclose") {
00190                     fileTok = tok->tokAt(2);
00191                     operation = Filepointer::CLOSE;
00192                 } else if (whitelist.find(tok->str()) != whitelist.end()) {
00193                     fileTok = tok->tokAt(2);
00194                     if (tok->str() == "ungetc" && fileTok)
00195                         fileTok = fileTok->nextArgument();
00196                     operation = Filepointer::UNIMPORTANT;
00197                 } else if (!Token::Match(tok, "if|for|while|catch|switch")) {
00198                     const Token* const end2 = tok->linkAt(1);
00199                     for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
00200                         if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) {
00201                             fileTok = tok2;
00202                             operation = Filepointer::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now
00203                             break;
00204                         }
00205                     }
00206                 }
00207 
00208                 while (Token::Match(fileTok, "%var% ."))
00209                     fileTok = fileTok->tokAt(2);
00210 
00211                 if (!fileTok || !fileTok->varId())
00212                     continue;
00213 
00214                 if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File
00215                     filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN)));
00216                 }
00217                 Filepointer& f = filepointers[fileTok->varId()];
00218 
00219                 switch (operation) {
00220                 case Filepointer::OPEN:
00221                     f.mode = getMode(mode);
00222                     f.mode_indent = indent;
00223                     break;
00224                 case Filepointer::POSITIONING:
00225                     if (f.mode == CLOSED)
00226                         useClosedFileError(tok);
00227                     break;
00228                 case Filepointer::READ:
00229                     if (f.mode == CLOSED)
00230                         useClosedFileError(tok);
00231                     else if (f.mode == WRITE_MODE)
00232                         readWriteOnlyFileError(tok);
00233                     else if (f.lastOperation == Filepointer::WRITE)
00234                         ioWithoutPositioningError(tok);
00235                     break;
00236                 case Filepointer::WRITE:
00237                     if (f.mode == CLOSED)
00238                         useClosedFileError(tok);
00239                     else if (f.mode == READ_MODE)
00240                         writeReadOnlyFileError(tok);
00241                     else if (f.lastOperation == Filepointer::READ)
00242                         ioWithoutPositioningError(tok);
00243                     break;
00244                 case Filepointer::CLOSE:
00245                     if (f.mode == CLOSED)
00246                         useClosedFileError(tok);
00247                     else
00248                         f.mode = CLOSED;
00249                     f.mode_indent = indent;
00250                     break;
00251                 case Filepointer::UNIMPORTANT:
00252                     if (f.mode == CLOSED)
00253                         useClosedFileError(tok);
00254                     break;
00255                 case Filepointer::UNKNOWN_OP:
00256                     f.mode = UNKNOWN;
00257                     f.mode_indent = 0;
00258                     break;
00259                 default:
00260                     break;
00261                 }
00262                 if (operation != Filepointer::NONE && operation != Filepointer::UNIMPORTANT) {
00263                     f.op_indent = indent;
00264                     f.lastOperation = operation;
00265                 }
00266             }
00267         }
00268         for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
00269             i->second.op_indent = 0;
00270             i->second.mode = UNKNOWN;
00271             i->second.lastOperation = Filepointer::UNKNOWN_OP;
00272         }
00273     }
00274 }
00275 
00276 void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname)
00277 {
00278     reportError(tok, Severity::error,
00279                 "fflushOnInputStream", "fflush() called on input stream '" + varname + "' results in undefined behaviour.");
00280 }
00281 
00282 void CheckIO::ioWithoutPositioningError(const Token *tok)
00283 {
00284     reportError(tok, Severity::error,
00285                 "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.");
00286 }
00287 
00288 void CheckIO::readWriteOnlyFileError(const Token *tok)
00289 {
00290 
00291     reportError(tok, Severity::error,
00292                 "readWriteOnlyFile", "Read operation on a file that was opened only for writing.");
00293 }
00294 
00295 void CheckIO::writeReadOnlyFileError(const Token *tok)
00296 {
00297     reportError(tok, Severity::error,
00298                 "writeReadOnlyFile", "Write operation on a file that was opened only for reading.");
00299 }
00300 
00301 void CheckIO::useClosedFileError(const Token *tok)
00302 {
00303     reportError(tok, Severity::error,
00304                 "useClosedFile", "Used file that is not opened.");
00305 }
00306 
00307 
00308 //---------------------------------------------------------------------------
00309 // scanf without field width limits can crash with huge input data
00310 //---------------------------------------------------------------------------
00311 void CheckIO::invalidScanf()
00312 {
00313     if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability"))
00314         return;
00315 
00316     const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
00317     std::size_t functions = symbolDatabase->functionScopes.size();
00318     for (std::size_t j = 0; j < functions; ++j) {
00319         const Scope * scope = symbolDatabase->functionScopes[j];
00320         for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
00321             const Token *formatToken = 0;
00322             if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
00323                 formatToken = tok->tokAt(2);
00324             else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
00325                 const Token* nextArg = tok->tokAt(2)->nextArgument();
00326                 if (nextArg && nextArg->type() == Token::eString)
00327                     formatToken = nextArg;
00328                 else
00329                     continue;
00330             } else
00331                 continue;
00332 
00333             bool format = false;
00334 
00335             // scan the string backwards, so we dont need to keep states
00336             const std::string &formatstr(formatToken->str());
00337             for (unsigned int i = 1; i < formatstr.length(); i++) {
00338                 if (formatstr[i] == '%')
00339                     format = !format;
00340 
00341                 else if (!format)
00342                     continue;
00343 
00344                 else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') {
00345                     format = false;
00346                 }
00347 
00348                 else if (std::isalpha(formatstr[i]) || formatstr[i] == '[') {
00349                     if ((formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) && _settings->isEnabled("warning"))  // #3490 - field width limits are only necessary for string input
00350                         invalidScanfError(tok, false);
00351                     else if (formatstr[i] != 'n' && formatstr[i] != 'c' && _settings->platformType != Settings::Win32A && _settings->platformType != Settings::Win32W && _settings->platformType != Settings::Win64 && _settings->isEnabled("portability"))
00352                         invalidScanfError(tok, true); // Warn about libc bug in versions prior to 2.13-25
00353                     format = false;
00354                 }
00355             }
00356         }
00357     }
00358 }
00359 
00360 void CheckIO::invalidScanfError(const Token *tok, bool portability)
00361 {
00362     if (portability)
00363         reportError(tok, Severity::portability,
00364                     "invalidscanf", "scanf without field width limits can crash with huge input data on some versions of libc.\n"
00365                     "scanf without field width limits can crash with huge input data on libc versions older than 2.13-25. Add a field "
00366                     "width specifier to fix this problem:\n"
00367                     "    %i => %3i\n"
00368                     "\n"
00369                     "Sample program that can crash:\n"
00370                     "\n"
00371                     "#include <stdio.h>\n"
00372                     "int main()\n"
00373                     "{\n"
00374                     "    int a;\n"
00375                     "    scanf(\"%i\", &a);\n"
00376                     "    return 0;\n"
00377                     "}\n"
00378                     "\n"
00379                     "To make it crash:\n"
00380                     "perl -e 'print \"5\"x2100000' | ./a.out");
00381     else
00382         reportError(tok, Severity::warning,
00383                     "invalidscanf", "scanf without field width limits can crash with huge input data.\n"
00384                     "scanf without field width limits can crash with huge input data. Add a field width "
00385                     "specifier to fix this problem:\n"
00386                     "    %s => %20s\n"
00387                     "\n"
00388                     "Sample program that can crash:\n"
00389                     "\n"
00390                     "#include <stdio.h>\n"
00391                     "int main()\n"
00392                     "{\n"
00393                     "    char c[5];\n"
00394                     "    scanf(\"%s\", c);\n"
00395                     "    return 0;\n"
00396                     "}\n"
00397                     "\n"
00398                     "To make it crash, type in more than 5 characters.");
00399 }
00400 
00401 //---------------------------------------------------------------------------
00402 //    printf("%u", "xyz"); // Wrong argument type
00403 //    printf("%u%s", 1); // Too few arguments
00404 //    printf("", 1); // Too much arguments
00405 //---------------------------------------------------------------------------
00406 static bool isComplexType(const Variable* var, const Token* varTypeTok)
00407 {
00408     if (var->type())
00409         return(true);
00410 
00411     static std::set<std::string> knownTypes;
00412     if (knownTypes.empty()) {
00413         knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type
00414         knownTypes.insert("string");
00415         knownTypes.insert("wstring");
00416     }
00417 
00418     if (varTypeTok->str() == "std")
00419         varTypeTok = varTypeTok->tokAt(2);
00420     return((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !var->isPointer() && !var->isArray());
00421 }
00422 
00423 static bool isKnownType(const Variable* var, const Token* varTypeTok)
00424 {
00425     return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok));
00426 }
00427 
00428 void CheckIO::checkWrongPrintfScanfArguments()
00429 {
00430     const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
00431     bool warning = _settings->isEnabled("warning");
00432 
00433     std::size_t functions = symbolDatabase->functionScopes.size();
00434     for (std::size_t j = 0; j < functions; ++j) {
00435         const Scope * scope = symbolDatabase->functionScopes[j];
00436         for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
00437             if (!tok->isName()) continue;
00438 
00439             const Token* argListTok = 0; // Points to first va_list argument
00440             std::string formatString;
00441 
00442             if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%")) {
00443                 formatString = tok->strAt(2);
00444                 if (tok->strAt(3) == ",") {
00445                     argListTok = tok->tokAt(4);
00446                 } else if (tok->strAt(3) == ")") {
00447                     argListTok = 0;
00448                 } else {
00449                     continue;
00450                 }
00451             } else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%")) {
00452                 const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string)
00453                 if (Token::Match(formatStringTok, "%str% ,")) {
00454                     argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args)
00455                     formatString = formatStringTok->str();
00456                 } else if (Token::Match(formatStringTok, "%str% )")) {
00457                     argListTok = 0; // Find third parameter (first argument of va_args)
00458                     formatString = formatStringTok->str();
00459                 } else {
00460                     continue;
00461                 }
00462             } else if (Token::Match(tok, "snprintf|fnprintf|swprintf (")) {
00463                 const Token* formatStringTok = tok->tokAt(2);
00464                 for (int i = 0; i < 2 && formatStringTok; i++) {
00465                     formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string)
00466                 }
00467                 if (Token::Match(formatStringTok, "%str% ,")) {
00468                     argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
00469                     formatString = formatStringTok->str();
00470                 } else if (Token::Match(formatStringTok, "%str% )")) {
00471                     argListTok = 0; // Find fourth parameter (first argument of va_args)
00472                     formatString = formatStringTok->str();
00473                 } else {
00474                     continue;
00475                 }
00476             } else {
00477                 continue;
00478             }
00479 
00480             // Count format string parameters..
00481             bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf");
00482             unsigned int numFormat = 0;
00483             bool percent = false;
00484             const Token* argListTok2 = argListTok;
00485             for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) {
00486                 if (*i == '%') {
00487                     percent = !percent;
00488                 } else if (percent && *i == '[') {
00489                     while (i != formatString.end()) {
00490                         if (*i == ']') {
00491                             numFormat++;
00492                             if (argListTok)
00493                                 argListTok = argListTok->nextArgument();
00494                             percent = false;
00495                             break;
00496                         }
00497                         ++i;
00498                     }
00499                     if (i == formatString.end())
00500                         break;
00501                 } else if (percent) {
00502                     percent = false;
00503 
00504                     bool _continue = false;
00505                     std::string width;
00506                     while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
00507                         if (*i == '*') {
00508                             if (scan)
00509                                 _continue = true;
00510                             else {
00511                                 numFormat++;
00512                                 if (argListTok)
00513                                     argListTok = argListTok->nextArgument();
00514                             }
00515                         } else if (std::isdigit(*i))
00516                             width += *i;
00517                         ++i;
00518                     }
00519                     if (i == formatString.end())
00520                         break;
00521                     if (_continue)
00522                         continue;
00523 
00524                     if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
00525                         numFormat++;
00526 
00527                         // Perform type checks
00528                         if (argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern.
00529                             const Variable *variableInfo = argListTok->variable();
00530                             const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
00531                             if (varTypeTok && varTypeTok->str() == "static")
00532                                 varTypeTok = varTypeTok->next();
00533 
00534                             if (scan && varTypeTok) {
00535                                 if (warning && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const"))
00536                                     invalidScanfArgTypeError(tok, tok->str(), numFormat);
00537 
00538                                 if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1) && variableInfo->dimensions()[0].known) {
00539                                     if (!width.empty()) {
00540                                         int numWidth = std::atoi(width.c_str());
00541                                         if (numWidth != (variableInfo->dimension(0) - 1))
00542                                             invalidScanfFormatWidthError(tok, numFormat, numWidth, variableInfo);
00543                                     }
00544                                 }
00545                             } else if (!scan && warning) {
00546                                 switch (*i) {
00547                                 case 's':
00548                                     if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
00549                                         invalidPrintfArgTypeError_s(tok, numFormat);
00550                                     break;
00551                                 case 'n':
00552                                     if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
00553                                         invalidPrintfArgTypeError_n(tok, numFormat);
00554                                     break;
00555                                 case 'c':
00556                                 case 'x':
00557                                 case 'X':
00558                                 case 'o':
00559                                     if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
00560                                         invalidPrintfArgTypeError_int(tok, numFormat, *i);
00561                                     else if (argListTok->type() == Token::eString)
00562                                         invalidPrintfArgTypeError_int(tok, numFormat, *i);
00563                                     break;
00564                                 case 'd':
00565                                 case 'i':
00566                                     if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
00567                                         if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char")
00568                                             invalidPrintfArgTypeError_sint(tok, numFormat, *i);
00569                                     } else if (argListTok->type() == Token::eString)
00570                                         invalidPrintfArgTypeError_sint(tok, numFormat, *i);
00571                                     break;
00572                                 case 'u':
00573                                     if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
00574                                         if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool")
00575                                             invalidPrintfArgTypeError_uint(tok, numFormat, *i);
00576                                     } else if (argListTok->type() == Token::eString)
00577                                         invalidPrintfArgTypeError_uint(tok, numFormat, *i);
00578                                     break;
00579                                 case 'p':
00580                                     if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
00581                                         invalidPrintfArgTypeError_p(tok, numFormat);
00582                                     else if (argListTok->type() == Token::eString)
00583                                         invalidPrintfArgTypeError_p(tok, numFormat);
00584                                     break;
00585                                 case 'e':
00586                                 case 'E':
00587                                 case 'f':
00588                                 case 'g':
00589                                 case 'G':
00590                                     if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray()))
00591                                         invalidPrintfArgTypeError_float(tok, numFormat, *i);
00592                                     else if (argListTok->type() == Token::eString)
00593                                         invalidPrintfArgTypeError_float(tok, numFormat, *i);
00594                                     break;
00595                                 case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
00596                                 case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
00597                                     // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
00598                                     if (i != formatString.end() && *(i+1) == *i) {
00599                                         if (i+1 != formatString.end() && !isalpha(*(i+2))) {
00600                                             std::string modifier;
00601                                             modifier += *i;
00602                                             modifier += *(i+1);
00603                                             invalidLengthModifierError(tok, numFormat, modifier);
00604                                         }
00605                                     } else {
00606                                         if (i != formatString.end() && !isalpha(*(i+1))) {
00607                                             std::string modifier;
00608                                             modifier += *i;
00609                                             invalidLengthModifierError(tok, numFormat, modifier);
00610                                         }
00611                                     }
00612                                     break;
00613                                 case 'j': // intmax_t or uintmax_t
00614                                 case 'z': // size_t
00615                                 case 't': // ptrdiff_t
00616                                 case 'L': // long double
00617                                     // Expect an alphabetical character after these specifiers
00618                                     if (i != formatString.end() && !isalpha(*(i+1))) {
00619                                         std::string modifier;
00620                                         modifier += *i;
00621                                         invalidLengthModifierError(tok, numFormat, modifier);
00622                                     }
00623                                     break;
00624                                 default:
00625                                     break;
00626                                 }
00627                             }
00628                         }
00629 
00630                         if (argListTok)
00631                             argListTok = argListTok->nextArgument(); // Find next argument
00632                     }
00633                 }
00634             }
00635 
00636             // Count printf/scanf parameters..
00637             unsigned int numFunction = 0;
00638             while (argListTok2) {
00639                 numFunction++;
00640                 argListTok2 = argListTok2->nextArgument(); // Find next argument
00641             }
00642 
00643             // Mismatching number of parameters => warning
00644             if (numFormat != numFunction)
00645                 wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction);
00646         }
00647     }
00648 }
00649 
00650 void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
00651         const std::string &functionName,
00652         unsigned int numFormat,
00653         unsigned int numFunction)
00654 {
00655     Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning;
00656     if (severity != Severity::error && !_settings->isEnabled("style"))
00657         return;
00658 
00659     std::ostringstream errmsg;
00660     errmsg << functionName
00661            << " format string has "
00662            << numFormat
00663            << " parameters but "
00664            << (numFormat > numFunction ? "only " : "")
00665            << numFunction
00666            << " are given.";
00667 
00668     reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str());
00669 }
00670 
00671 void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat)
00672 {
00673     std::ostringstream errmsg;
00674     errmsg << functionName << " argument no. " << numFormat << ": requires a non-const pointer or array as argument.";
00675     reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str());
00676 }
00677 void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
00678 {
00679     std::ostringstream errmsg;
00680     errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list.";
00681     reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
00682 }
00683 void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat)
00684 {
00685     std::ostringstream errmsg;
00686     errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list.";
00687     reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str());
00688 }
00689 void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat)
00690 {
00691     std::ostringstream errmsg;
00692     errmsg << "%p in format string (no. " << numFormat << ") requires an address given in the argument list.";
00693     reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str());
00694 }
00695 void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c)
00696 {
00697     std::ostringstream errmsg;
00698     errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list.";
00699     reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str());
00700 }
00701 void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c)
00702 {
00703     std::ostringstream errmsg;
00704     errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list.";
00705     reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str());
00706 }
00707 void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c)
00708 {
00709     std::ostringstream errmsg;
00710     errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list.";
00711     reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str());
00712 }
00713 void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c)
00714 {
00715     std::ostringstream errmsg;
00716     errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list.";
00717     reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
00718 }
00719 void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier)
00720 {
00721     std::ostringstream errmsg;
00722     errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
00723     reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str());
00724 }
00725 
00726 void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var)
00727 {
00728     std::ostringstream errmsg;
00729     Severity::SeverityType severity = Severity::warning;
00730     bool inconclusive = false;
00731 
00732     if (var) {
00733         if (var->dimension(0) > width) {
00734             if (!_settings->inconclusive)
00735                 return;
00736             inconclusive = true;
00737             errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer"
00738                    << " '" << var->name() << "[" << var->dimension(0) << "]'.";
00739         } else {
00740             errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '"
00741                    << var->name() << "[" << var->dimension(0) << "]', use %" << (var->dimension(0) - 1) << "s to prevent overflowing it.";
00742             severity = Severity::error;
00743         }
00744 
00745     } else
00746         errmsg << "Width " << width << " given in format string (no. " << numFormat << ") doesn't match destination buffer.";
00747 
00748     if (severity == Severity::error || _settings->isEnabled("style"))
00749         reportError(tok, severity, "invalidScanfFormatWidth", errmsg.str(), inconclusive);
00750 }