Fixed #4189 (Improve check (printf('%l') not detected))
This commit is contained in:
parent
f0f216390e
commit
0e100f7563
|
@ -560,6 +560,35 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
else if (argListTok->type() == Token::eString)
|
else if (argListTok->type() == Token::eString)
|
||||||
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
||||||
break;
|
break;
|
||||||
|
case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
|
||||||
|
case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
|
||||||
|
// If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
|
||||||
|
if (i != formatString.end() && *(i+1) == *i) {
|
||||||
|
if (i+1 != formatString.end() && !isalpha(*(i+2))) {
|
||||||
|
std::string modifier;
|
||||||
|
modifier += *i;
|
||||||
|
modifier += *(i+1);
|
||||||
|
invalidLengthModifierError(tok, numFormat, modifier);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (i != formatString.end() && !isalpha(*(i+1))) {
|
||||||
|
std::string modifier;
|
||||||
|
modifier += *i;
|
||||||
|
invalidLengthModifierError(tok, numFormat, modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'j': // intmax_t or uintmax_t
|
||||||
|
case 'z': // size_t
|
||||||
|
case 't': // ptrdiff_t
|
||||||
|
case 'L': // long double
|
||||||
|
// Expect an alphabetical character after these specifiers
|
||||||
|
if (i != formatString.end() && !isalpha(*(i+1))) {
|
||||||
|
std::string modifier;
|
||||||
|
modifier += *i;
|
||||||
|
invalidLengthModifierError(tok, numFormat, modifier);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -654,6 +683,13 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int num
|
||||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list.";
|
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());
|
reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
|
||||||
}
|
}
|
||||||
|
void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier)
|
||||||
|
{
|
||||||
|
std::ostringstream errmsg;
|
||||||
|
errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
|
||||||
|
reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str());
|
||||||
|
}
|
||||||
|
|
||||||
void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var)
|
void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var)
|
||||||
{
|
{
|
||||||
std::ostringstream errmsg;
|
std::ostringstream errmsg;
|
||||||
|
|
|
@ -90,6 +90,7 @@ private:
|
||||||
void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c);
|
void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c);
|
||||||
void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c);
|
void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c);
|
||||||
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
||||||
|
void invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier);
|
||||||
void invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var);
|
void invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
|
|
|
@ -640,6 +640,48 @@ private:
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an integer given in the argument list.\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: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());
|
"[test.cpp:5]: (warning) %p in format string (no. 1) requires an integer given in the argument list.\n", "", errout.str());
|
||||||
|
|
||||||
|
// Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7)
|
||||||
|
// False positive tests
|
||||||
|
check("void foo(signed char sc, unsigned char uc, short int si, unsigned short int usi) {\n"
|
||||||
|
" printf(\"%hhx %hhd\", sc, uc);\n"
|
||||||
|
" printf(\"%hd %hi\", si, usi);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) {\n"
|
||||||
|
" printf(\"%llo %llx\", lli, ulli);\n"
|
||||||
|
" printf(\"%ld %lu\", li, uli);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld) {\n"
|
||||||
|
" printf(\"%jd %jo\", im, uim);\n"
|
||||||
|
" printf(\"%zx\", s);\n"
|
||||||
|
" printf(\"%ti\", p);\n"
|
||||||
|
" printf(\"%La\", ld);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// False negative test
|
||||||
|
check("void foo(unsigned int i) {\n"
|
||||||
|
" printf(\"%h\", i);\n"
|
||||||
|
" printf(\"%hh\", i);\n"
|
||||||
|
" printf(\"%l\", i);\n"
|
||||||
|
" printf(\"%ll\", i);\n"
|
||||||
|
" printf(\"%j\", i);\n"
|
||||||
|
" printf(\"%z\", i);\n"
|
||||||
|
" printf(\"%t\", i);\n"
|
||||||
|
" printf(\"%L\", i);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
|
||||||
|
"[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue