Fixed #4964 (printf format argument check only supports simple variables)
This commit is contained in:
parent
4b1254bc8d
commit
9be2f6b5d4
155
lib/checkio.cpp
155
lib/checkio.cpp
|
@ -554,8 +554,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
switch (*i) {
|
||||
case 's':
|
||||
if (argInfo.variableInfo && argListTok->type() != Token::eString &&
|
||||
argInfo.isKnownType() &&
|
||||
!argInfo.isArrayOrPointer())
|
||||
argInfo.isKnownType() && !argInfo.isArrayOrPointer())
|
||||
invalidPrintfArgTypeError_s(tok, numFormat);
|
||||
done = true;
|
||||
break;
|
||||
|
@ -569,14 +568,18 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
case 'X':
|
||||
case 'o':
|
||||
specifier += *i;
|
||||
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||
if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) &&
|
||||
(!Token::Match(argInfo.typeToken, "bool|short|long|int|char|size_t") ||
|
||||
(specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())))) {
|
||||
if (argInfo.typeToken->type() == Token::eString) {
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
} else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char")) {
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()))) {
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||
} else if ((!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||
(argInfo.element && !argInfo.isArrayOrPointer())) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
done = true;
|
||||
|
@ -584,8 +587,9 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
case 'd':
|
||||
case 'i':
|
||||
specifier += *i;
|
||||
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||
if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
if (argInfo.typeToken->type() == Token::eString) {
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
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 if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
|
@ -606,22 +610,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else {
|
||||
if ((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|int|long")) && !Token::Match(argInfo.typeToken, "char|short")) {
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()))) {
|
||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
case 'u':
|
||||
specifier += *i;
|
||||
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||
if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
if (argInfo.typeToken->type() == Token::eString) {
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
|
||||
if ((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") {
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
|
@ -640,30 +635,12 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||
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 if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()))) {
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if ((specifier[0] == 'z' || (specifier[0] == 'I' && specifier[1] == 'u')) && argInfo.typeToken->originalName() != "size_t") {
|
||||
// use %z or %I on size_t
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
} else if (argInfo.typeToken->originalName() == "size_t" && (specifier[0] != 'z' && !(specifier[0] == 'I' && specifier[1] == 'u'))) {
|
||||
// size_t requires %z or %I
|
||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
case 'p':
|
||||
if (argInfo.functionInfo && argInfo.typeToken->type() == Token::eType && !argInfo.isArrayOrPointer())
|
||||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
|
||||
else if (argInfo.variableInfo && argInfo.typeToken && argInfo.isKnownType() && !argInfo.isArrayOrPointer())
|
||||
invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
|
||||
else if (argInfo.typeToken->type() == Token::eString)
|
||||
else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer())
|
||||
invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
|
||||
done = true;
|
||||
break;
|
||||
|
@ -673,24 +650,20 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
|||
case 'g':
|
||||
case 'G':
|
||||
specifier += *i;
|
||||
if (argInfo.functionInfo) {
|
||||
if (((argInfo.typeToken->isStandardType() || argInfo.functionInfo->retType) && !Token::Match(argInfo.typeToken, "float|double")) ||
|
||||
(!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||
(argInfo.element && !argInfo.isArrayOrPointer()) ||
|
||||
(specifier[0] == 'l' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
|
||||
(specifier[0] != 'l' && argInfo.typeToken->isLong())) {
|
||||
if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
else if (argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.element)) {
|
||||
if (!Token::Match(argInfo.typeToken, "float|double")) {
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
} else if (((specifier[0] == 'l' || specifier[0] == 'L') && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
|
||||
((specifier[0] != 'l' && specifier[0] != 'L') && argInfo.typeToken->isLong())) {
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
} else if (argInfo.variableInfo) {
|
||||
if ((argInfo.isKnownType() && !Token::Match(argInfo.typeToken, "float|double")) ||
|
||||
(!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||
} else if ((!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||
(argInfo.element && !argInfo.isArrayOrPointer())) {
|
||||
// use %p on pointers and arrays
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
}
|
||||
} else if (argInfo.typeToken->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
else if (!Token::Match(argInfo.typeToken, "float|double"))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
|
||||
done = true;
|
||||
break;
|
||||
case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
|
||||
|
@ -788,6 +761,7 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
|||
, typeToken(0)
|
||||
, functionInfo(0)
|
||||
, element(false)
|
||||
, _template(false)
|
||||
, tempToken(0)
|
||||
{
|
||||
if (tok) {
|
||||
|
@ -833,7 +807,8 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
|||
tok1 = tok1->link();
|
||||
|
||||
// check for some common well known functions
|
||||
else if (Token::Match(tok1->previous(), "%var% . size|empty|c_str ( )") && isStdContainer(tok1->previous())) {
|
||||
else if ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( )") && isStdContainer(tok1->previous())) ||
|
||||
(Token::Match(tok1->previous(), "] . size|empty|c_str ( )") && Token::Match(tok1->previous()->link()->previous(), "%var%") && isStdContainer(tok1->previous()->link()->previous()))) {
|
||||
tempToken = new Token(0);
|
||||
tempToken->fileIndex(tok1->fileIndex());
|
||||
tempToken->linenr(tok1->linenr());
|
||||
|
@ -855,7 +830,7 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
|||
} else if (tok1->next()->str() == "c_str") {
|
||||
tempToken->str("const");
|
||||
tempToken->insertToken("*");
|
||||
if (tok1->previous()->variable()->typeStartToken()->strAt(2) == "string")
|
||||
if (typeToken->strAt(2) == "string")
|
||||
tempToken->insertToken("char");
|
||||
else
|
||||
tempToken->insertToken("wchar_t");
|
||||
|
@ -872,7 +847,7 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
|||
|
||||
// look for std::vector operator [] and use template type as return type
|
||||
if (variableInfo) {
|
||||
if (element && isStdVector()) { // isStdVector sets type token if true
|
||||
if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true
|
||||
element = false; // not really an array element
|
||||
} else
|
||||
typeToken = variableInfo->typeStartToken();
|
||||
|
@ -884,15 +859,37 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
|||
}
|
||||
}
|
||||
|
||||
bool CheckIO::ArgumentInfo::isStdVector()
|
||||
bool CheckIO::ArgumentInfo::isStdVectorOrString()
|
||||
{
|
||||
if (Token::Match(variableInfo->typeStartToken(), "std :: vector|array <")) {
|
||||
typeToken = variableInfo->typeStartToken()->tokAt(4);
|
||||
_template = true;
|
||||
return true;
|
||||
} else if (Token::Match(variableInfo->typeStartToken(), "std :: string|wstring")) {
|
||||
tempToken = new Token(0);
|
||||
tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex());
|
||||
tempToken->linenr(variableInfo->typeStartToken()->linenr());
|
||||
if (variableInfo->typeStartToken()->strAt(2) == "string")
|
||||
tempToken->str("char");
|
||||
else
|
||||
tempToken->str("wchar_t");
|
||||
typeToken = tempToken;
|
||||
return true;
|
||||
} else if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) {
|
||||
for (std::size_t i = 0, e = variableInfo->type()->derivedFrom.size(); i != e; ++i) {
|
||||
if (Token::Match(variableInfo->type()->derivedFrom[i].nameTok, "std :: vector|array <")) {
|
||||
typeToken = variableInfo->type()->derivedFrom[i].nameTok->tokAt(4);
|
||||
_template = true;
|
||||
return true;
|
||||
} else if (Token::Match(variableInfo->type()->derivedFrom[i].nameTok, "std :: string|wstring")) {
|
||||
tempToken = new Token(0);
|
||||
tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex());
|
||||
tempToken->linenr(variableInfo->typeStartToken()->linenr());
|
||||
if (variableInfo->type()->derivedFrom[i].nameTok->strAt(2) == "string")
|
||||
tempToken->str("char");
|
||||
else
|
||||
tempToken->str("wchar_t");
|
||||
typeToken = tempToken;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -904,13 +901,19 @@ bool CheckIO::ArgumentInfo::isStdVector()
|
|||
bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok)
|
||||
{
|
||||
if (tok && tok->variable()) {
|
||||
if (Token::Match(tok->variable()->typeStartToken(), "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <") ||
|
||||
Token::Match(tok->variable()->typeStartToken(), "std :: string|wstring")) {
|
||||
if (Token::Match(tok->variable()->typeStartToken(), "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) {
|
||||
typeToken = tok->variable()->typeStartToken()->tokAt(4);
|
||||
return true;
|
||||
} else if (Token::Match(tok->variable()->typeStartToken(), "std :: string|wstring")) {
|
||||
typeToken = tok->variable()->typeStartToken();
|
||||
return true;
|
||||
} else if (tok->variable()->type() && !tok->variable()->type()->derivedFrom.empty()) {
|
||||
for (std::size_t i = 0, e = tok->variable()->type()->derivedFrom.size(); i != e; ++i) {
|
||||
if (Token::Match(tok->variable()->type()->derivedFrom[i].nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <") ||
|
||||
Token::Match(tok->variable()->type()->derivedFrom[i].nameTok, "std :: string|wstring")) {
|
||||
if (Token::Match(tok->variable()->type()->derivedFrom[i].nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) {
|
||||
typeToken = tok->variable()->type()->derivedFrom[i].nameTok->tokAt(4);
|
||||
return true;
|
||||
} else if (Token::Match(tok->variable()->type()->derivedFrom[i].nameTok, "std :: string|wstring")) {
|
||||
typeToken = tok->variable()->type()->derivedFrom[i].nameTok;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -920,6 +923,20 @@ bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CheckIO::ArgumentInfo::isArrayOrPointer() const
|
||||
{
|
||||
if (variableInfo && !_template) {
|
||||
return variableInfo->isArrayOrPointer();
|
||||
} else {
|
||||
const Token *tok = typeToken;
|
||||
while (tok && Token::Match(tok, "const|struct"))
|
||||
tok = tok->next();
|
||||
if (tok && tok->strAt(1) == "*")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckIO::ArgumentInfo::isComplexType() const
|
||||
{
|
||||
if (variableInfo->type())
|
||||
|
@ -944,6 +961,8 @@ bool CheckIO::ArgumentInfo::isKnownType() const
|
|||
return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType());
|
||||
else if (functionInfo)
|
||||
return (typeToken->isStandardType() || functionInfo->retType);
|
||||
else
|
||||
return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1071,15 +1090,15 @@ void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
|
|||
os << "const char *";
|
||||
} else {
|
||||
if (type->originalName().empty()) {
|
||||
if (type->str() == "const") {
|
||||
os << "const ";
|
||||
while (Token::Match(type, "const|struct")) {
|
||||
os << type->str() << " ";
|
||||
type = type->next();
|
||||
}
|
||||
type->stringify(os, false, true);
|
||||
if (type->strAt(1) == "*" ||
|
||||
(argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())) {
|
||||
if (type->strAt(1) == "*" && !(argInfo->functionInfo && argInfo->element))
|
||||
os << " *";
|
||||
else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
|
||||
os << " *";
|
||||
}
|
||||
} else {
|
||||
if ((type->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned())
|
||||
os << "unsigned ";
|
||||
|
|
|
@ -75,22 +75,17 @@ private:
|
|||
~ArgumentInfo() {
|
||||
delete tempToken;
|
||||
}
|
||||
bool isArrayOrPointer() const {
|
||||
if (variableInfo)
|
||||
return variableInfo->isArrayOrPointer();
|
||||
else if (functionInfo)
|
||||
return typeToken->next()->str() == "*";
|
||||
return false;
|
||||
}
|
||||
bool isArrayOrPointer() const;
|
||||
bool isComplexType() const;
|
||||
bool isKnownType() const;
|
||||
bool isStdVector();
|
||||
bool isStdVectorOrString();
|
||||
bool isStdContainer(const Token *tok);
|
||||
|
||||
const Variable *variableInfo;
|
||||
const Token *typeToken;
|
||||
const Function *functionInfo;
|
||||
bool element;
|
||||
bool _template;
|
||||
Token *tempToken;
|
||||
|
||||
private:
|
||||
|
|
|
@ -650,7 +650,9 @@ private:
|
|||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %X in format string (no. 1) requires an integer but the argument type is 'foo'.\n"
|
||||
"[test.cpp:4]: (warning) %c in format string (no. 1) requires an integer but the argument type is 'const char *'.\n"
|
||||
"[test.cpp:5]: (warning) %o in format string (no. 1) requires an integer but the argument type is 'double'.\n", errout.str());
|
||||
"[test.cpp:5]: (warning) %o in format string (no. 1) requires an integer but the argument type is 'double'.\n"
|
||||
"[test.cpp:6]: (warning) %x in format string (no. 1) requires an integer but the argument type is 'int *'.\n"
|
||||
"[test.cpp:8]: (warning) %X in format string (no. 1) requires an integer but the argument type is 'bar *'.\n", errout.str());
|
||||
|
||||
check("class foo {};\n"
|
||||
"void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) {\n"
|
||||
|
@ -1062,7 +1064,7 @@ private:
|
|||
"void f() {\n"
|
||||
" printf(\"%f\", foo()[0]);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires a floating point number but the argument type is 'int *'.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires a floating point number but the argument type is 'int'.\n", errout.str());
|
||||
|
||||
check("struct Base { int length() { } };\n"
|
||||
"struct Derived : public Base { };\n"
|
||||
|
@ -1225,6 +1227,33 @@ private:
|
|||
"[test.cpp:16]: (warning) %ld in format string (no. 8) requires a signed long integer but the argument type is 'ptrdiff_t {aka long}'.\n"
|
||||
"[test.cpp:16]: (warning) %ld in format string (no. 9) requires a signed long integer but the argument type is 'char *'.\n", errout.str());
|
||||
|
||||
check("struct A {};\n"
|
||||
"class B : public std::vector<const int *> {} b;\n"
|
||||
"class C : public std::vector<const struct A *> {} c;\n"
|
||||
"std::string s;\n"
|
||||
"void foo() {\n"
|
||||
" printf(\"%zu %u\", b.size(), b.size());\n"
|
||||
" printf(\"%p %d\", b[0], b[0]);\n"
|
||||
" printf(\"%p %d\", c[0], c[0]);\n"
|
||||
" printf(\"%p %d\", s.c_str(), s.c_str());\n"
|
||||
"}\n", false, false, Settings::Unix64);
|
||||
ASSERT_EQUALS("[test.cpp:6]: (warning) %u in format string (no. 2) requires an unsigned integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||
"[test.cpp:7]: (warning) %d in format string (no. 2) requires a signed integer but the argument type is 'const int *'.\n"
|
||||
"[test.cpp:8]: (warning) %d in format string (no. 2) requires a signed integer but the argument type is 'const struct A *'.\n"
|
||||
"[test.cpp:9]: (warning) %d in format string (no. 2) requires a signed integer but the argument type is 'const char *'.\n", errout.str());
|
||||
|
||||
check("class A : public std::vector<std::string> {} a;\n"
|
||||
"class B : public std::string {} b;\n"
|
||||
"std::string s;\n"
|
||||
"void foo() {\n"
|
||||
" printf(\"%p %d\", a[0].c_str(), a[0].c_str());\n"
|
||||
" printf(\"%c %p\", b[0], b[0]);\n"
|
||||
" printf(\"%c %p\", s[0], s[0]);\n"
|
||||
"}\n", false, false, Settings::Unix64);
|
||||
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires a signed integer but the argument type is 'const char *'.\n"
|
||||
"[test.cpp:6]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n"
|
||||
"[test.cpp:7]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n", errout.str());
|
||||
|
||||
}
|
||||
|
||||
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
|
||||
|
|
Loading…
Reference in New Issue