From b81eafe0dc46fc0f54f34c12b375fb651bbab81b Mon Sep 17 00:00:00 2001 From: PKEuS Date: Sun, 20 May 2012 02:57:07 -0700 Subject: [PATCH] Splitted CheckIO from CheckOther. --- lib/checkio.cpp | 402 +++++++++++++++++++++++++++++++++++++++++++++ lib/checkio.h | 115 +++++++++++++ lib/checkother.cpp | 367 ----------------------------------------- lib/checkother.h | 48 +----- test/testother.cpp | 290 -------------------------------- 5 files changed, 518 insertions(+), 704 deletions(-) create mode 100644 lib/checkio.cpp create mode 100644 lib/checkio.h diff --git a/lib/checkio.cpp b/lib/checkio.cpp new file mode 100644 index 000000000..0f0bc69df --- /dev/null +++ b/lib/checkio.cpp @@ -0,0 +1,402 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#include "checkio.h" + +#include "tokenize.h" +#include "token.h" +#include "errorlogger.h" +#include "symboldatabase.h" + +#include + +//--------------------------------------------------------------------------- + +// Register CheckIO.. +namespace { + CheckIO instance; +} + + +//--------------------------------------------------------------------------- +// std::cout << std::cout; +//--------------------------------------------------------------------------- +void CheckIO::checkCoutCerrMisusage() +{ + bool firstCout = false; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() == "(") + tok = tok->link(); + + if (Token::Match(tok, "std :: cout|cerr")) { + if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") { + coutCerrMisusageError(tok, tok->strAt(2)); + firstCout = false; + } else if (tok->strAt(3) == "<<") + firstCout = true; + } else if (firstCout && tok->str() == ";") + firstCout = false; + } +} + +void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) +{ + reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'."); +} + +//--------------------------------------------------------------------------- +// fflush(stdin) <- fflush only applies to output streams in ANSI C +//--------------------------------------------------------------------------- +void CheckIO::checkFflushOnInputStream() +{ + const Token *tok = _tokenizer->tokens(); + while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) { + fflushOnInputStreamError(tok, tok->strAt(2)); + tok = tok->tokAt(4); + } +} + +void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::error, + "fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour"); +} + +//--------------------------------------------------------------------------- +// scanf without field width limits can crash with huge input data +//--------------------------------------------------------------------------- +void CheckIO::invalidScanf() +{ + if (!_settings->isEnabled("style")) + return; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + const Token *formatToken = 0; + if (Token::Match(tok, "scanf|vscanf ( %str% ,")) + formatToken = tok->tokAt(2); + else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { + const Token* nextArg = tok->tokAt(2)->nextArgument(); + if (nextArg && nextArg->type() == Token::eString) + formatToken = nextArg; + else + continue; + } else + continue; + + bool format = false; + + // scan the string backwards, so we dont need to keep states + const std::string &formatstr(formatToken->str()); + for (unsigned int i = 1; i < formatstr.length(); i++) { + if (formatstr[i] == '%') + format = !format; + + else if (!format) + continue; + + else if (std::isdigit(formatstr[i])) { + format = false; + } + + else if (std::isalpha(formatstr[i])) { + if (formatstr[i] != 'c') // #3490 - field width limits are not necessary for %c + invalidScanfError(tok); + format = false; + } + } + } +} + +void CheckIO::invalidScanfError(const Token *tok) +{ + reportError(tok, Severity::warning, + "invalidscanf", "scanf without field width limits can crash with huge input data\n" + "scanf without field width limits can crash with huge input data. To fix this error " + "message add a field width specifier:\n" + " %s => %20s\n" + " %i => %3i\n" + "\n" + "Sample program that can crash:\n" + "\n" + "#include \n" + "int main()\n" + "{\n" + " int a;\n" + " scanf(\"%i\", &a);\n" + " return 0;\n" + "}\n" + "\n" + "To make it crash:\n" + "perl -e 'print \"5\"x2100000' | ./a.out"); +} + +//--------------------------------------------------------------------------- +// printf("%u", "xyz"); // Wrong argument type +// printf("%u%s", 1); // Too few arguments +// printf("", 1); // Too much arguments +//--------------------------------------------------------------------------- +static bool isComplexType(const Variable* var, const Token* varTypeTok) +{ + if (var->type()) + return(true); + + static std::set knownTypes; + if (knownTypes.empty()) { + knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type + knownTypes.insert("string"); + } + + if (varTypeTok->str() == "std") + varTypeTok = varTypeTok->tokAt(2); + return(knownTypes.find(varTypeTok->str()) != knownTypes.end() && !var->isPointer() && !var->isArray()); +} + +static bool isKnownType(const Variable* var, const Token* varTypeTok) +{ + return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok)); +} + +void CheckIO::checkWrongPrintfScanfArguments() +{ + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->isName()) continue; + + const Token* argListTok = 0; // Points to first va_list argument + std::string formatString; + + if (Token::Match(tok, "printf|scanf ( %str%")) { + formatString = tok->strAt(2); + if (tok->strAt(3) == ",") { + argListTok = tok->tokAt(4); + } else if (tok->strAt(3) == ")") { + argListTok = 0; + } else { + continue; + } + } else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf ( %any%")) { + const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string) + if (Token::Match(formatStringTok, "%str% ,")) { + argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args) + formatString = formatStringTok->str(); + } else if (Token::Match(formatStringTok, "%str% )")) { + argListTok = 0; // Find third parameter (first argument of va_args) + formatString = formatStringTok->str(); + } else { + continue; + } + } else if (Token::Match(tok, "snprintf|fnprintf (")) { + const Token* formatStringTok = tok->tokAt(2); + for (int i = 0; i < 2 && formatStringTok; i++) { + formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string) + } + if (Token::Match(formatStringTok, "%str% ,")) { + argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args) + formatString = formatStringTok->str(); + } else if (Token::Match(formatStringTok, "%str% )")) { + argListTok = 0; // Find fourth parameter (first argument of va_args) + formatString = formatStringTok->str(); + } else { + continue; + } + } else { + continue; + } + + // Count format string parameters.. + bool scan = Token::Match(tok, "sscanf|fscanf|scanf"); + unsigned int numFormat = 0; + bool percent = false; + const Token* argListTok2 = argListTok; + for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) { + if (*i == '%') { + percent = !percent; + } else if (percent && *i == '[') { + while (i != formatString.end()) { + if (*i == ']') { + numFormat++; + if (argListTok) + argListTok = argListTok->nextArgument(); + percent = false; + break; + } + ++i; + } + if (i == formatString.end()) + break; + } else if (percent) { + percent = false; + + bool _continue = false; + while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { + if (*i == '*') { + if (scan) + _continue = true; + else { + numFormat++; + if (argListTok) + argListTok = argListTok->nextArgument(); + } + } + ++i; + } + if (i == formatString.end()) + break; + if (_continue) + continue; + + if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. + numFormat++; + + // Perform type checks + if (_settings->isEnabled("style") && argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern. + const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId()); + const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL; + if (varTypeTok && varTypeTok->str() == "static") + varTypeTok = varTypeTok->next(); + + if (scan && varTypeTok) { + if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const") + invalidScanfArgTypeError(tok, tok->str(), numFormat); + } else if (!scan) { + switch (*i) { + case 's': + if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray())) + invalidPrintfArgTypeError_s(tok, numFormat); + break; + case 'n': + if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")) || argListTok->type() == Token::eString) + invalidPrintfArgTypeError_n(tok, numFormat); + break; + case 'c': + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + if (varTypeTok && varTypeTok->str() == "const") + varTypeTok = varTypeTok->next(); + if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| bool|short|long|int|char|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray())) + invalidPrintfArgTypeError_int(tok, numFormat, *i); + else if (argListTok->type() == Token::eString) + invalidPrintfArgTypeError_int(tok, numFormat, *i); + break; + case 'p': + if (varTypeTok && varTypeTok->str() == "const") + varTypeTok = varTypeTok->next(); + if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| short|long|int|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray()) + invalidPrintfArgTypeError_p(tok, numFormat); + else if (argListTok->type() == Token::eString) + invalidPrintfArgTypeError_p(tok, numFormat); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (varTypeTok && varTypeTok->str() == "const") + varTypeTok = varTypeTok->next(); + if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) + invalidPrintfArgTypeError_float(tok, numFormat, *i); + else if (argListTok->type() == Token::eString) + invalidPrintfArgTypeError_float(tok, numFormat, *i); + break; + default: + break; + } + } + } + + if (argListTok) + argListTok = argListTok->nextArgument(); // Find next argument + } + } + } + + // Count printf/scanf parameters.. + unsigned int numFunction = 0; + while (argListTok2) { + numFunction++; + argListTok2 = argListTok2->nextArgument(); // Find next argument + } + + // Mismatching number of parameters => warning + if (numFormat != numFunction) + wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction); + } +} + +void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, + const std::string &functionName, + unsigned int numFormat, + unsigned int numFunction) +{ + Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; + if (severity != Severity::error && !_settings->isEnabled("style")) + return; + + std::ostringstream errmsg; + errmsg << functionName + << " format string has " + << numFormat + << " parameters but " + << (numFormat > numFunction ? "only " : "") + << numFunction + << " are given"; + + reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str()); +} + +void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat) +{ + std::ostringstream errmsg; + errmsg << functionName << " argument no. " << numFormat << ": requires non-const pointers or arrays as arguments"; + reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat) +{ + std::ostringstream errmsg; + errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat) +{ + std::ostringstream errmsg; + errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat) +{ + std::ostringstream errmsg; + errmsg << "%p in format string (no. " << numFormat << ") requires an integer or pointer given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c) +{ + std::ostringstream errmsg; + errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c) +{ + std::ostringstream errmsg; + errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); +} diff --git a/lib/checkio.h b/lib/checkio.h new file mode 100644 index 000000000..f0cd7194b --- /dev/null +++ b/lib/checkio.h @@ -0,0 +1,115 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef CheckIOH +#define CheckIOH +//--------------------------------------------------------------------------- + +#include "check.h" + +/// @addtogroup Checks +/// @{ + + +/** @brief %Check input output operations. */ +class CheckIO : public Check { +public: + /** @brief This constructor is used when registering CheckIO */ + CheckIO() : Check(myName()) + { } + + /** @brief This constructor is used when running checks. */ + CheckIO(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) + { } + + /** @brief Run checks on the normal token list */ + void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { + CheckIO checkIO(tokenizer, settings, errorLogger); + + checkIO.checkWrongPrintfScanfArguments(); + } + + /** @brief Run checks on the simplified token list */ + void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { + CheckIO checkIO(tokenizer, settings, errorLogger); + + checkIO.checkCoutCerrMisusage(); + checkIO.checkFflushOnInputStream(); + checkIO.invalidScanf(); + } + + /** @brief %Check for missusage of std::cout */ + void checkCoutCerrMisusage(); + + /** @brief %Check for using fflush() on an input stream*/ + void checkFflushOnInputStream(); + + /** @brief scanf can crash if width specifiers are not used */ + void invalidScanf(); + + /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ + void checkWrongPrintfScanfArguments(); + +private: + // Reporting errors.. + void coutCerrMisusageError(const Token* tok, const std::string& streamName); + void fflushOnInputStreamError(const Token *tok, const std::string &varname); + void invalidScanfError(const Token *tok); + void wrongPrintfScanfArgumentsError(const Token* tok, + const std::string &function, + unsigned int numFormat, + unsigned int numFunction); + void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat); + void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat); + void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); + void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat); + void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c); + void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckIO c(0, settings, errorLogger); + + c.coutCerrMisusageError(0, "cout"); + c.fflushOnInputStreamError(0, "stdin"); + c.invalidScanfError(0); + c.wrongPrintfScanfArgumentsError(0,"printf",3,2); + c.invalidScanfArgTypeError(0, "scanf", 1); + c.invalidPrintfArgTypeError_s(0, 1); + c.invalidPrintfArgTypeError_n(0, 1); + c.invalidPrintfArgTypeError_p(0, 1); + c.invalidPrintfArgTypeError_int(0, 1, 'u'); + c.invalidPrintfArgTypeError_float(0, 1, 'f'); + } + + std::string myName() const { + return "IO"; + } + + std::string classInfo() const { + return "Check input/output operations.\n"; + "* Bad usage of the function 'sprintf' (overlapping data)\n" + "* Using fflush() on an input stream\n" + "* Invalid usage of output stream. For example: std::cout << std::cout;'\n" + "* Wrong number of arguments given to 'printf' or 'scanf;'\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 9ccd98970..6d9c6002e 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -22,7 +22,6 @@ #include "mathlib.h" #include "symboldatabase.h" -#include #include // fabs() #include #include // find_if() @@ -423,24 +422,6 @@ void CheckOther::invalidPointerCastError(const Token* tok, const std::string& fr reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation"); } -//--------------------------------------------------------------------------- -// fflush(stdin) <- fflush only applies to output streams in ANSI C -//--------------------------------------------------------------------------- -void CheckOther::checkFflushOnInputStream() -{ - const Token *tok = _tokenizer->tokens(); - while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) { - fflushOnInputStreamError(tok, tok->strAt(2)); - tok = tok->tokAt(4); - } -} - -void CheckOther::fflushOnInputStreamError(const Token *tok, const std::string &varname) -{ - reportError(tok, Severity::error, - "fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour"); -} - //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkSizeofForNumericParameter() @@ -859,32 +840,6 @@ void CheckOther::switchCaseFallThrough(const Token *tok) "switchCaseFallThrough", "Switch falls through case without comment"); } -//--------------------------------------------------------------------------- -// std::cout << std::cout; -//--------------------------------------------------------------------------- -void CheckOther::checkCoutCerrMisusage() -{ - bool firstCout = false; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (tok->str() == "(") - tok = tok->link(); - - if (Token::Match(tok, "std :: cout|cerr")) { - if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") { - coutCerrMisusageError(tok, tok->strAt(2)); - firstCout = false; - } else if (tok->strAt(3) == "<<") - firstCout = true; - } else if (firstCout && tok->str() == ";") - firstCout = false; - } -} - -void CheckOther::coutCerrMisusageError(const Token* tok, const std::string& streamName) -{ - reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'."); -} - //--------------------------------------------------------------------------- // int x = 1; // x = x; // <- redundant assignment to self @@ -1330,328 +1285,6 @@ void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string "to sprintf() or snprintf(), the results are undefined.'"); } -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -void CheckOther::invalidScanf() -{ - if (!_settings->isEnabled("style")) - return; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - const Token *formatToken = 0; - if (Token::Match(tok, "scanf|vscanf ( %str% ,")) - formatToken = tok->tokAt(2); - else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { - const Token* nextArg = tok->tokAt(2)->nextArgument(); - if (nextArg && nextArg->type() == Token::eString) - formatToken = nextArg; - else - continue; - } else - continue; - - bool format = false; - - // scan the string backwards, so we dont need to keep states - const std::string &formatstr(formatToken->str()); - for (unsigned int i = 1; i < formatstr.length(); i++) { - if (formatstr[i] == '%') - format = !format; - - else if (!format) - continue; - - else if (std::isdigit(formatstr[i])) { - format = false; - } - - else if (std::isalpha(formatstr[i])) { - if (formatstr[i] != 'c') // #3490 - field width limits are not necessary for %c - invalidScanfError(tok); - format = false; - } - } - } -} - -void CheckOther::invalidScanfError(const Token *tok) -{ - reportError(tok, Severity::warning, - "invalidscanf", "scanf without field width limits can crash with huge input data\n" - "scanf without field width limits can crash with huge input data. To fix this error " - "message add a field width specifier:\n" - " %s => %20s\n" - " %i => %3i\n" - "\n" - "Sample program that can crash:\n" - "\n" - "#include \n" - "int main()\n" - "{\n" - " int a;\n" - " scanf(\"%i\", &a);\n" - " return 0;\n" - "}\n" - "\n" - "To make it crash:\n" - "perl -e 'print \"5\"x2100000' | ./a.out"); -} - -//--------------------------------------------------------------------------- -// printf("%u", "xyz"); // Wrong argument type -// printf("%u%s", 1); // Too few arguments -// printf("", 1); // Too much arguments -//--------------------------------------------------------------------------- -static bool isComplexType(const Variable* var, const Token* varTypeTok) -{ - if (var->type()) - return(true); - - static std::set knownTypes; - if (knownTypes.empty()) { - knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type - knownTypes.insert("string"); - } - - if (varTypeTok->str() == "std") - varTypeTok = varTypeTok->tokAt(2); - return(knownTypes.find(varTypeTok->str()) != knownTypes.end() && !var->isPointer() && !var->isArray()); -} - -static bool isKnownType(const Variable* var, const Token* varTypeTok) -{ - return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok)); -} - -void CheckOther::checkWrongPrintfScanfArguments() -{ - const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (!tok->isName()) continue; - - const Token* argListTok = 0; // Points to first va_list argument - std::string formatString; - - if (Token::Match(tok, "printf|scanf ( %str%")) { - formatString = tok->strAt(2); - if (tok->strAt(3) == ",") { - argListTok = tok->tokAt(4); - } else if (tok->strAt(3) == ")") { - argListTok = 0; - } else { - continue; - } - } else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf ( %any%")) { - const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string) - if (Token::Match(formatStringTok, "%str% ,")) { - argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args) - formatString = formatStringTok->str(); - } else if (Token::Match(formatStringTok, "%str% )")) { - argListTok = 0; // Find third parameter (first argument of va_args) - formatString = formatStringTok->str(); - } else { - continue; - } - } else if (Token::Match(tok, "snprintf|fnprintf (")) { - const Token* formatStringTok = tok->tokAt(2); - for (int i = 0; i < 2 && formatStringTok; i++) { - formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string) - } - if (Token::Match(formatStringTok, "%str% ,")) { - argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args) - formatString = formatStringTok->str(); - } else if (Token::Match(formatStringTok, "%str% )")) { - argListTok = 0; // Find fourth parameter (first argument of va_args) - formatString = formatStringTok->str(); - } else { - continue; - } - } else { - continue; - } - - // Count format string parameters.. - bool scan = Token::Match(tok, "sscanf|fscanf|scanf"); - unsigned int numFormat = 0; - bool percent = false; - const Token* argListTok2 = argListTok; - for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) { - if (*i == '%') { - percent = !percent; - } else if (percent && *i == '[') { - while (i != formatString.end()) { - if (*i == ']') { - numFormat++; - if (argListTok) - argListTok = argListTok->nextArgument(); - percent = false; - break; - } - ++i; - } - if (i == formatString.end()) - break; - } else if (percent) { - percent = false; - - bool _continue = false; - while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { - if (*i == '*') { - if (scan) - _continue = true; - else { - numFormat++; - if (argListTok) - argListTok = argListTok->nextArgument(); - } - } - ++i; - } - if (i == formatString.end()) - break; - if (_continue) - continue; - - if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. - numFormat++; - - // Perform type checks - if (_settings->isEnabled("style") && argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern. - const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId()); - const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL; - if (varTypeTok && varTypeTok->str() == "static") - varTypeTok = varTypeTok->next(); - - if (scan && varTypeTok) { - if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const") - invalidScanfArgTypeError(tok, tok->str(), numFormat); - } else if (!scan) { - switch (*i) { - case 's': - if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray())) - invalidPrintfArgTypeError_s(tok, numFormat); - break; - case 'n': - if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")) || argListTok->type() == Token::eString) - invalidPrintfArgTypeError_n(tok, numFormat); - break; - case 'c': - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); - if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| bool|short|long|int|char|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray())) - invalidPrintfArgTypeError_int(tok, numFormat, *i); - else if (argListTok->type() == Token::eString) - invalidPrintfArgTypeError_int(tok, numFormat, *i); - break; - case 'p': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| short|long|int|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray()) - invalidPrintfArgTypeError_p(tok, numFormat); - else if (argListTok->type() == Token::eString) - invalidPrintfArgTypeError_p(tok, numFormat); - break; - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); - if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) - invalidPrintfArgTypeError_float(tok, numFormat, *i); - else if (argListTok->type() == Token::eString) - invalidPrintfArgTypeError_float(tok, numFormat, *i); - break; - default: - break; - } - } - } - - if (argListTok) - argListTok = argListTok->nextArgument(); // Find next argument - } - } - } - - // Count printf/scanf parameters.. - unsigned int numFunction = 0; - while (argListTok2) { - numFunction++; - argListTok2 = argListTok2->nextArgument(); // Find next argument - } - - // Mismatching number of parameters => warning - if (numFormat != numFunction) - wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction); - } -} - -void CheckOther::wrongPrintfScanfArgumentsError(const Token* tok, - const std::string &functionName, - unsigned int numFormat, - unsigned int numFunction) -{ - Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; - if (severity != Severity::error && !_settings->isEnabled("style")) - return; - - std::ostringstream errmsg; - errmsg << functionName - << " format string has " - << numFormat - << " parameters but " - << (numFormat > numFunction ? "only " : "") - << numFunction - << " are given"; - - reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str()); -} - -void CheckOther::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat) -{ - std::ostringstream errmsg; - errmsg << functionName << " argument no. " << numFormat << ": requires non-const pointers or arrays as arguments"; - reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str()); -} -void CheckOther::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat) -{ - std::ostringstream errmsg; - errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list"; - reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str()); -} -void CheckOther::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat) -{ - std::ostringstream errmsg; - errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list"; - reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); -} -void CheckOther::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat) -{ - std::ostringstream errmsg; - errmsg << "%p in format string (no. " << numFormat << ") requires an integer or pointer given in the argument list"; - reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); -} -void CheckOther::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c) -{ - std::ostringstream errmsg; - errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list"; - reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); -} -void CheckOther::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c) -{ - std::ostringstream errmsg; - errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list"; - reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); -} - //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- diff --git a/lib/checkother.h b/lib/checkother.h index 917194e26..74c61ae7f 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -68,7 +68,6 @@ public: checkOther.checkDuplicateExpression(); checkOther.checkUnreachableCode(); checkOther.checkSuspiciousSemicolon(); - checkOther.checkWrongPrintfScanfArguments(); checkOther.checkVariableScope(); checkOther.clarifyCondition(); // not simplified because ifAssign checkOther.checkComparisonOfBoolExpressionWithInt(); @@ -88,10 +87,7 @@ public: checkOther.checkZeroDivision(); checkOther.checkMathFunctions(); checkOther.checkCCTypeFunctions(); - checkOther.checkFflushOnInputStream(); - checkOther.invalidScanf(); - checkOther.checkCoutCerrMisusage(); checkOther.checkIncorrectLogicOperator(); checkOther.checkMisusedScopedObject(); checkOther.checkMemsetZeroBytes(); @@ -157,30 +153,18 @@ public: void lookupVar(const Token *tok1, const std::string &varname); - /** @brief %Check for using fflush() on an input stream*/ - void checkFflushOnInputStream(); - /** @brief %Check for 'sizeof sizeof ..' */ void sizeofsizeof(); /** @brief %Check for calculations inside sizeof */ void sizeofCalculation(); - /** @brief scanf can crash if width specifiers are not used */ - void invalidScanf(); - - /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ - void checkWrongPrintfScanfArguments(); - /** @brief %Check for assigning to the same variable twice in a switch statement*/ void checkRedundantAssignmentInSwitch(); /** @brief %Check for switch case fall through without comment */ void checkSwitchCaseFallThrough(); - /** @brief %Check for missusage of std::cout */ - void checkCoutCerrMisusage(); - /** @brief %Check for assigning a variable to itself*/ void checkSelfAssignment(); @@ -256,17 +240,6 @@ private: void clarifyConditionError(const Token *tok, bool assign, bool boolop); void sizeofsizeofError(const Token *tok); void sizeofCalculationError(const Token *tok, bool inconclusive); - void invalidScanfError(const Token *tok); - void wrongPrintfScanfArgumentsError(const Token* tok, - const std::string &function, - unsigned int numFormat, - unsigned int numFunction); - void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat); - void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat); - void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); - void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat); - void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c); - void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c); void cstyleCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive); void dangerousUsageStrtolError(const Token *tok); @@ -279,10 +252,8 @@ private: void variableScopeError(const Token *tok, const std::string &varname); void strPlusCharError(const Token *tok); void zerodivError(const Token *tok); - void coutCerrMisusageError(const Token* tok, const std::string& streamName); void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1); void cctypefunctionCallError(const Token *tok, const std::string &functionName, const std::string &value); - void fflushOnInputStreamError(const Token *tok, const std::string &varname); void redundantAssignmentInSwitchError(const Token *tok, const std::string &varname); void redundantStrcpyInSwitchError(const Token *tok, const std::string &varname); void switchCaseFallThrough(const Token *tok); @@ -325,12 +296,10 @@ private: c.udivError(0, false); c.zerodivError(0); c.mathfunctionCallError(0); - c.fflushOnInputStreamError(0, "stdin"); c.misusedScopeObjectError(NULL, "varname"); c.sizeofForArrayParameterError(0); c.sizeofForPointerError(0, "varname"); c.sizeofForNumericParameterError(0); - c.coutCerrMisusageError(0, "cout"); c.doubleFreeError(0, "varname"); c.invalidPointerCastError(0, "float", "double", false); @@ -349,7 +318,6 @@ private: c.switchCaseFallThrough(0); c.selfAssignmentError(0, "varname"); c.assignmentInAssertError(0, "varname"); - c.invalidScanfError(0); c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true); c.redundantConditionError(0, "If x > 10 the condition x > 11 is always true."); c.memsetZeroBytesError(0, "varname"); @@ -371,13 +339,6 @@ private: c.bitwiseOnBooleanError(0, "varname", "&&"); c.comparisonOfBoolExpressionWithIntError(0, true); c.SuspiciousSemicolonError(0); - c.wrongPrintfScanfArgumentsError(0,"printf",3,2); - c.invalidScanfArgTypeError(0, "scanf", 1); - c.invalidPrintfArgTypeError_s(0, 1); - c.invalidPrintfArgTypeError_n(0, 1); - c.invalidPrintfArgTypeError_p(0, 1); - c.invalidPrintfArgTypeError_int(0, 1, 'u'); - c.invalidPrintfArgTypeError_float(0, 1, 'f'); c.cctypefunctionCallError(0, "funname", "value"); c.moduloAlwaysTrueFalseError(0, "1"); } @@ -391,17 +352,13 @@ private: // error "* Assigning bool value to pointer (converting bool value to address)\n" - "* [[OverlappingData|bad usage of the function 'sprintf' (overlapping data)]]\n" "* division with zero\n" - "* using fflush() on an input stream\n" "* scoped object destroyed immediately after construction\n" "* assignment in an assert statement\n" "* sizeof for array given as function argument\n" "* sizeof for numeric given as function argument\n" "* using sizeof(pointer) instead of the size of pointed data\n" "* incorrect length arguments for 'substr' and 'strncmp'\n" - "* invalid usage of output stream. For example: std::cout << std::cout;'\n" - "* wrong number of arguments given to 'printf' or 'scanf;'\n" "* double free() or double closedir()\n" // style @@ -437,10 +394,7 @@ private: "* using bool in bitwise expression\n" "* Suspicious use of ; at the end of 'if/for/while' statement.\n" "* incorrect usage of functions from ctype library.\n" - "* Comparisons of modulo results that are always true/false.\n" - - // optimisations - "* optimisation: detect post increment/decrement\n"; + "* Comparisons of modulo results that are always true/false.\n"; } void checkExpressionRange(const std::list &constFunctions, diff --git a/test/testother.cpp b/test/testother.cpp index bef1867fd..733b8622f 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -74,8 +74,6 @@ private: TEST_CASE(mathfunctionCall1); TEST_CASE(cctypefunctionCall); - TEST_CASE(fflushOnInputStreamTest); - TEST_CASE(sizeofsizeof); TEST_CASE(sizeofCalculation); @@ -83,17 +81,7 @@ private: TEST_CASE(switchFallThroughCase); TEST_CASE(unreachableCode); - TEST_CASE(coutCerrMisusage); - TEST_CASE(selfAssignment); - TEST_CASE(testScanf1); - TEST_CASE(testScanf2); - TEST_CASE(testScanf3); - TEST_CASE(testScanf4); - - TEST_CASE(testScanfArgument); - TEST_CASE(testPrintfArgument); - TEST_CASE(trac1132); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction1); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction2); @@ -1274,19 +1262,6 @@ private: ASSERT_EQUALS("[test.cpp:2]: (error) Passing value -10000 to isgraph() cause undefined behavior, which may lead to a crash\n", errout.str()); } - void fflushOnInputStreamTest() { - check("void foo()\n" - "{\n" - " fflush(stdin);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:3]: (error) fflush() called on input stream \"stdin\" may result in undefined behaviour\n", errout.str()); - - check("void foo()\n" - "{\n" - " fflush(stdout);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } void sizeofsizeof() { check("void foo()\n" @@ -2136,232 +2111,6 @@ private: ASSERT_EQUALS("", errout.str()); } - void testScanf1() { - check("#include \n" - "int main(int argc, char **argv)\n" - "{\n" - " int a, b;\n" - " FILE *file = fopen(\"test\", \"r\");\n" - " b = fscanf(file, \"aa %ds\", &a);\n" - " c = scanf(\"aa %ds\", &a);\n" - " b = fscanf(file, \"aa%%ds\", &a);\n" - " fclose(file);\n" - " return b;\n" - "}"); - ASSERT_EQUALS("[test.cpp:8]: (warning) fscanf format string has 0 parameters but 1 are given\n" - "[test.cpp:6]: (warning) scanf without field width limits can crash with huge input data\n" - "[test.cpp:7]: (warning) scanf without field width limits can crash with huge input data\n", errout.str()); - } - - void testScanf2() { - check("#include \n" - "int main(int argc, char **argv)\n" - "{\n" - " int a, b;\n" - " FILE *file = fopen(\"test\", \"r\");\n" - " b = fscanf(file, \"aa%%%ds\", &a);\n" - " c = scanf(\"aa %%%ds\", &a);\n" - " b = fscanf(file, \"aa%%ds\", &a);\n" - " fclose(file);\n" - " return b;\n" - "}"); - ASSERT_EQUALS("[test.cpp:8]: (warning) fscanf format string has 0 parameters but 1 are given\n" - "[test.cpp:6]: (warning) scanf without field width limits can crash with huge input data\n" - "[test.cpp:7]: (warning) scanf without field width limits can crash with huge input data\n", errout.str()); - } - - void testScanf3() { - check("#include \n" - "int main(int argc, char **argv)\n" - "{\n" - " char a[32];\n" - " int b, c;\n" - " FILE *file = fopen(\"test\", \"r\");\n" - " c = fscanf(file, \"%[^ ] %d\n\", a, &b);\n" - " fclose(file);\n" - " return c;\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - check("#include \n" - "int main(int argc, char **argv)\n" - "{\n" - " char a[32];\n" - " int b;\n" - " FILE *file = fopen(\"test\", \"r\");\n" - " b = fscanf(file, \"%[^ \n\", a);\n" - " fclose(file);\n" - " return b;\n" - "}"); - ASSERT_EQUALS("[test.cpp:7]: (warning) fscanf format string has 0 parameters but 1 are given\n", errout.str()); - } - - void testScanf4() { - check("void f() {\n" - " char c;\n" - " scanf(\"%c\", &c);\n" - "}"); - ASSERT_EQUALS("", errout.str()); - } - - void testScanfArgument() { - check("void foo() {\n" - " scanf(\"%1d\", &foo);\n" - " sscanf(bar, \"%1d\", &foo);\n" - " scanf(\"%1u%1u\", &foo, bar());\n" - " scanf(\"%*1x %1x %29s\", &count, KeyName);\n" // #3373 - " fscanf(f, \"%7ms\", &ref);\n" // #3461 - " sscanf(ip_port, \"%*[^:]:%d\", &port);\n" // #3468 - "}"); - ASSERT_EQUALS("", errout.str()); - - check("void foo() {\n" - " scanf(\"\", &foo);\n" - " scanf(\"%1d\", &foo, &bar);\n" - " fscanf(bar, \"%1d\", &foo, &bar);\n" - " scanf(\"%*1x %1x %29s\", &count, KeyName, foo);\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string has 0 parameters but 1 are given\n" - "[test.cpp:3]: (warning) scanf format string has 1 parameters but 2 are given\n" - "[test.cpp:4]: (warning) fscanf format string has 1 parameters but 2 are given\n" - "[test.cpp:5]: (warning) scanf format string has 2 parameters but 3 are given\n", errout.str()); - - check("void foo() {\n" - " scanf(\"%1d\");\n" - " scanf(\"%1u%1u\", bar());\n" - " sscanf(bar, \"%1d%1d\", &foo);\n" - " scanf(\"%*1x %1x %29s\", &count);\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string has 1 parameters but only 0 are given\n" - "[test.cpp:3]: (error) scanf format string has 2 parameters but only 1 are given\n" - "[test.cpp:4]: (error) sscanf format string has 2 parameters but only 1 are given\n" - "[test.cpp:5]: (error) scanf format string has 2 parameters but only 1 are given\n", errout.str()); - } - - void testPrintfArgument() { - check("void foo() {\n" - " printf(\"%u\");\n" - " printf(\"%u%s\", 123);\n" - " printf(\"%u%s%d\", 0, bar());\n" - " printf(\"%u%%%s%d\", 0, bar());\n" - " printf(\"%udfd%%dfa%s%d\", 0, bar());\n" - " fprintf(stderr,\"%u%s\");\n" - " snprintf(str,10,\"%u%s\");\n" - " sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364 - " snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655 - "}"); - ASSERT_EQUALS("[test.cpp:2]: (error) printf format string has 1 parameters but only 0 are given\n" - "[test.cpp:3]: (error) printf format string has 2 parameters but only 1 are given\n" - "[test.cpp:4]: (error) printf format string has 3 parameters but only 2 are given\n" - "[test.cpp:5]: (error) printf format string has 3 parameters but only 2 are given\n" - "[test.cpp:6]: (error) printf format string has 3 parameters but only 2 are given\n" - "[test.cpp:7]: (error) fprintf format string has 2 parameters but only 0 are given\n" - "[test.cpp:8]: (error) snprintf format string has 2 parameters but only 0 are given\n" - "[test.cpp:9]: (error) sprintf format string has 3 parameters but only 2 are given\n" - "[test.cpp:10]: (error) snprintf format string has 2 parameters but only 1 are given\n", errout.str()); - - check("void foo(char *str) {\n" - " printf(\"\", 0);\n" - " printf(\"%u\", 123, bar());\n" - " printf(\"%u%s\", 0, bar(), 43123);\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string has 0 parameters but 1 are given\n" - "[test.cpp:3]: (warning) printf format string has 1 parameters but 2 are given\n" - "[test.cpp:4]: (warning) printf format string has 2 parameters but 3 are given\n", errout.str()); - - check("void foo(char *str) {\n" - " printf(\"%u\", 0);\n" - " printf(\"%u%s\", 123, bar());\n" - " printf(\"%u%s%d\", 0, bar(), 43123);\n" - " printf(\"%u%%%s%d\", 0, bar(), 43123);\n" - " printf(\"%udfd%%dfa%s%d\", 0, bar(), 43123);\n" - " printf(\"%\"PRId64\"\n\", 123);\n" - " fprintf(stderr,\"%\"PRId64\"\n\", 123);\n" - " snprintf(str,10,\"%\"PRId64\"\n\", 123);\n" - " fprintf(stderr, \"error: %m\n\");\n" // #3339 - " printf(\"string: %.*s\n\", len, string);\n" // #3311 - " fprintf(stderr, \"%*cText.\n\", indent, ' ');\n" // #3313 - " sprintf(string1, \"%*\", 32);\n" // #3364 - "}"); - ASSERT_EQUALS("", errout.str()); - - check("void foo(char* s, const char* s2, std::string s3, int i) {\n" - " printf(\"%s%s\", s, s2);\n" - " printf(\"%s\", i);\n" - " printf(\"%i%s\", i, i);\n" - " printf(\"%s\", s3);\n" - " printf(\"%s\", \"s4\");\n" - " printf(\"%u\", s);\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires a char* given in the argument list\n" - "[test.cpp:4]: (warning) %s in format string (no. 2) requires a char* given in the argument list\n" - "[test.cpp:5]: (warning) %s in format string (no. 1) requires a char* given in the argument list\n", errout.str()); - - check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n" - " printf(\"%n\", cpi);\n" - " printf(\"%n\", ci);\n" - " printf(\"%n\", i);\n" - " printf(\"%n\", pi);\n" - " printf(\"%n\", s);\n" - " printf(\"%n\", \"s4\");\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n" - "[test.cpp:3]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n" - "[test.cpp:4]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n" - "[test.cpp:6]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n" - "[test.cpp:7]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n", errout.str()); - - check("class foo {};\n" - "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" - " printf(\"%i\", f);\n" - " printf(\"%c\", \"s4\");\n" - " printf(\"%o\", d);\n" - " printf(\"%i\", cpi);\n" - " printf(\"%u\", b);\n" - " printf(\"%u\", bp);\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires an integer given in the argument list\n" - "[test.cpp:4]: (warning) %c in format string (no. 1) requires an integer given in the argument list\n" - "[test.cpp:5]: (warning) %o in format string (no. 1) requires an integer given in the argument list\n", errout.str()); - - check("class foo {};\n" - "void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n" - " printf(\"%p\", f);\n" - " printf(\"%p\", c);\n" - " printf(\"%p\", bp);\n" - " printf(\"%p\", cpi);\n" - " printf(\"%p\", b);\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (warning) %p in format string (no. 1) requires an integer or pointer given in the argument list\n" - "[test.cpp:4]: (warning) %p in format string (no. 1) requires an integer or pointer given in the argument list\n", errout.str()); - - check("class foo {};\n" - "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" - " printf(\"%e\", f);\n" - " printf(\"%E\", \"s4\");\n" - " printf(\"%f\", cpi);\n" - " printf(\"%G\", bp);\n" - " printf(\"%f\", d);\n" - " printf(\"%f\", b);\n" - " printf(\"%f\", (float)cpi);\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires a floating point number given in the argument list\n" - "[test.cpp:4]: (warning) %E in format string (no. 1) requires a floating point number given in the argument list\n" - "[test.cpp:5]: (warning) %f in format string (no. 1) requires a floating point number given in the argument list\n" - "[test.cpp:6]: (warning) %G in format string (no. 1) requires a floating point number given in the argument list\n", errout.str()); - - check("class foo;\n" - "void foo(foo f) {\n" - " printf(\"%u\", f);\n" - " printf(\"%f\", f);\n" - " printf(\"%p\", f);\n" - "}"); - TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an integer given in the argument list\n" - "[test.cpp:4]: (warning) %f in format string (no. 1) requires an integer given in the argument list\n" - "[test.cpp:5]: (warning) %p in format string (no. 1) requires an integer given in the argument list\n", "", errout.str()); - - } - void trac1132() { std::istringstream code("#include \n" "class Lock\n" @@ -5063,45 +4812,6 @@ private: ); ASSERT_EQUALS("", errout.str()); } - - void coutCerrMisusage() { - check( - "void foo() {\n" - " std::cout << std::cout;\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); - - check( - "void foo() {\n" - " std::cout << \"xyz\" << std::cout;\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); - - check( - "void foo(int i) {\n" - " std::cout << i << std::cerr;\n" - "}"); - ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cerr'.\n", errout.str()); - - check( - "void foo() {\n" - " std::cout << \"xyz\";\n" - " std::cout << \"xyz\";\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - check( - "void foo() {\n" - " std::cout << std::cout.good();\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - check( - "void foo() {\n" - " MACRO(std::cout <<, << std::cout)\n" - "}"); - ASSERT_EQUALS("", errout.str()); - } }; REGISTER_TEST(TestOther)