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)