Fixed #5104 (False positive: Invalid argument in printf and scanf for vector template)
This commit is contained in:
parent
e70f0a601f
commit
94187c41c2
|
@ -643,7 +643,8 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
argInfo.isKnownType() && argInfo.isArrayOrPointer() &&
|
||||
(!Token::Match(argInfo.typeToken, "char|wchar_t") ||
|
||||
argInfo.typeToken->strAt(-1) == "const")) {
|
||||
invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
|
||||
if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType()))
|
||||
invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
if (scanf_s) {
|
||||
numSecure++;
|
||||
|
@ -669,9 +670,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
else if (argInfo.isKnownType()) {
|
||||
if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
|
||||
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
|
||||
if (argInfo.typeToken->isStandardType() || !argInfo.element)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
} else if (!argInfo.isArrayOrPointer() ||
|
||||
argInfo.typeToken->strAt(-1) == "const") {
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
else {
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'h':
|
||||
if (specifier[1] == 'h') {
|
||||
|
@ -745,9 +750,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
|
||||
else if (argInfo.isKnownType()) {
|
||||
if (argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
|
||||
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
|
||||
if (argInfo.typeToken->isStandardType() || !argInfo.element)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
|
||||
} else if (argInfo.typeToken->isUnsigned() ||
|
||||
!argInfo.isArrayOrPointer() ||
|
||||
argInfo.typeToken->strAt(-1) == "const") {
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
|
||||
else {
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'h':
|
||||
if (specifier[1] == 'h') {
|
||||
|
@ -812,9 +822,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
else if (argInfo.isKnownType()) {
|
||||
if (!argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
|
||||
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
|
||||
if (argInfo.typeToken->isStandardType() || !argInfo.element)
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
} else if (!argInfo.typeToken->isUnsigned() ||
|
||||
!argInfo.isArrayOrPointer() ||
|
||||
argInfo.typeToken->strAt(-1) == "const") {
|
||||
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
|
||||
else {
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'h':
|
||||
if (specifier[1] == 'h') {
|
||||
|
@ -884,9 +899,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
else if (argInfo.isKnownType()) {
|
||||
if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
|
||||
if (!Token::Match(argInfo.typeToken, "float|double")) {
|
||||
if (argInfo.typeToken->isStandardType())
|
||||
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
} else if (!argInfo.isArrayOrPointer() ||
|
||||
argInfo.typeToken->strAt(-1) == "const") {
|
||||
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
else {
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'l':
|
||||
if (specifier[1] == 'l') {
|
||||
|
@ -963,8 +982,12 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
switch (*i) {
|
||||
case 's':
|
||||
if (argInfo.variableInfo && argListTok->type() != Token::eString &&
|
||||
argInfo.isKnownType() && !argInfo.isArrayOrPointer())
|
||||
invalidPrintfArgTypeError_s(tok, numFormat);
|
||||
argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
if (!Token::Match(argInfo.typeToken, "char|wchar_t")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_s(tok, numFormat, &argInfo);
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
case 'n':
|
||||
|
@ -983,9 +1006,10 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.isArrayOrPointer() && !argInfo.element) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
} else if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t"))
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
else {
|
||||
} else if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'l':
|
||||
if (specifier[1] == 'l') {
|
||||
|
@ -1039,9 +1063,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.isArrayOrPointer() && !argInfo.element) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|long|int")) && !Token::Match(argInfo.typeToken, "char|short"))
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
else {
|
||||
} else if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'l':
|
||||
if (specifier[1] == 'l') {
|
||||
|
@ -1097,9 +1125,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.isArrayOrPointer() && !argInfo.element) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool")
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
else {
|
||||
} else if (!argInfo.typeToken->isUnsigned() && argInfo.typeToken->str() != "bool") {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else {
|
||||
switch (specifier[0]) {
|
||||
case 'l':
|
||||
if (specifier[1] == 'l') {
|
||||
|
@ -1165,12 +1197,12 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
if (argInfo.isArrayOrPointer() && !argInfo.element) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
} else if (!Token::Match(argInfo.typeToken, "float|double"))
|
||||
} else if (!Token::Match(argInfo.typeToken, "float|double")) {
|
||||
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
|
||||
(specifier[0] != 'L' && argInfo.typeToken->isLong()))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
|
||||
(specifier[0] != 'L' && argInfo.typeToken->isLong()))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
|
||||
} else if (argInfo.isArrayOrPointer() && !argInfo.element) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
|
@ -1600,10 +1632,12 @@ void CheckIO::invalidScanfArgTypeError_float(const Token* tok, unsigned int numF
|
|||
reportError(tok, Severity::warning, "invalidScanfArgType_float", errmsg.str());
|
||||
}
|
||||
|
||||
void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
|
||||
void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list.";
|
||||
errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is ";
|
||||
argumentType(errmsg, argInfo);
|
||||
errmsg << ".";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo)
|
||||
|
@ -1716,11 +1750,17 @@ void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
|
|||
os << type->str() << " ";
|
||||
type = type->next();
|
||||
}
|
||||
while (Token::Match(type, "%any% ::")) {
|
||||
os << type->str() << "::";
|
||||
type = type->tokAt(2);
|
||||
}
|
||||
type->stringify(os, false, true);
|
||||
if (type->strAt(1) == "*" && !argInfo->element)
|
||||
os << " *";
|
||||
else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
|
||||
os << " *";
|
||||
else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray())
|
||||
os << " *";
|
||||
if (argInfo->address)
|
||||
os << " *";
|
||||
} else {
|
||||
|
|
|
@ -111,7 +111,7 @@ private:
|
|||
void invalidScanfArgTypeError_s(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
|
||||
void invalidScanfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned);
|
||||
void invalidScanfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
|
||||
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
|
||||
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
|
||||
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
|
||||
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
|
||||
|
@ -136,7 +136,7 @@ private:
|
|||
c.invalidScanfArgTypeError_s(0, 1, "s", NULL);
|
||||
c.invalidScanfArgTypeError_int(0, 1, "d", NULL, false);
|
||||
c.invalidScanfArgTypeError_float(0, 1, "f", NULL);
|
||||
c.invalidPrintfArgTypeError_s(0, 1);
|
||||
c.invalidPrintfArgTypeError_s(0, 1, NULL);
|
||||
c.invalidPrintfArgTypeError_n(0, 1, NULL);
|
||||
c.invalidPrintfArgTypeError_p(0, 1, NULL);
|
||||
c.invalidPrintfArgTypeError_int(0, 1, "X", NULL);
|
||||
|
|
|
@ -1305,6 +1305,20 @@ private:
|
|||
"[test.cpp:23]: (warning) %n in format string (no. 23) requires 'int *' but the argument type is 'const wchar_t *'.\n"
|
||||
"[test.cpp:23]: (warning) %n in format string (no. 24) requires 'int *' but the argument type is 'const int *'.\n", errout.str());
|
||||
|
||||
check("void g() {\n" // #5104
|
||||
" myvector<int> v1(1);\n"
|
||||
" scanf(\"%d\n\",&v1[0]);\n"
|
||||
" myvector<unsigned int> v2(1);\n"
|
||||
" scanf(\"%u\n\",&v2[0]);\n"
|
||||
" myvector<unsigned int> v3(1);\n"
|
||||
" scanf(\"%x\n\",&v3[0]);\n"
|
||||
" myvector<double> v4(1);\n"
|
||||
" scanf(\"%lf\n\",&v4[0]);\n"
|
||||
" myvector<char *> v5(1);\n"
|
||||
" scanf(\"%10s\n\",v5[0]);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
}
|
||||
|
||||
void testPrintfArgument() {
|
||||
|
@ -1371,9 +1385,9 @@ private:
|
|||
" 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"
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:5]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n"
|
||||
"[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str());
|
||||
|
||||
check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n"
|
||||
|
@ -1387,7 +1401,7 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int *'.\n"
|
||||
"[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int'.\n"
|
||||
"[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std'.\n"
|
||||
"[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std::string'.\n"
|
||||
"[test.cpp:7]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout.str());
|
||||
|
||||
check("class foo {};\n"
|
||||
|
@ -2059,6 +2073,24 @@ private:
|
|||
"[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'long'.\n"
|
||||
"[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'long'.\n", errout.str());
|
||||
|
||||
check("void f() {\n" // #5104
|
||||
" myvector<unsigned short> v1(1,0);\n"
|
||||
" printf(\"%d\n\",v1[0]);\n"
|
||||
" myvector<int> v2(1,0);\n"
|
||||
" printf(\"%d\n\",v2[0]);\n"
|
||||
" myvector<unsigned int> v3(1,0);\n"
|
||||
" printf(\"%u\n\",v3[0]);\n"
|
||||
" myvector<unsigned int> v4(1,0);\n"
|
||||
" printf(\"%x\n\",v4[0]);\n"
|
||||
" myvector<double> v5(1,0);\n"
|
||||
" printf(\"%f\n\",v5[0]);\n"
|
||||
" myvector<bool> v6(1,0);\n"
|
||||
" printf(\"%u\n\",v6[0]);\n"
|
||||
" myvector<char *> v7(1,0);\n"
|
||||
" printf(\"%s\n\",v7[0]);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
}
|
||||
|
||||
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
|
||||
|
@ -2441,17 +2473,17 @@ private:
|
|||
" printf(format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n"
|
||||
" printf(format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n"
|
||||
"}\n", false, false, Settings::Win32A);
|
||||
ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:6]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n"
|
||||
"[test.cpp:7]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
"[test.cpp:7]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:7]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n"
|
||||
"[test.cpp:9]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
"[test.cpp:9]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:9]: (warning) sprintf format string requires 5 parameters but 6 are given.\n"
|
||||
"[test.cpp:10]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
"[test.cpp:10]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:10]: (warning) sprintf format string requires 5 parameters but 6 are given.\n"
|
||||
"[test.cpp:12]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
"[test.cpp:12]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:12]: (warning) printf format string requires 5 parameters but 6 are given.\n"
|
||||
"[test.cpp:13]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
|
||||
"[test.cpp:13]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
|
||||
"[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n", errout.str());
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue