CheckIO: Improved handling of function return type
This commit is contained in:
parent
6819f5ed18
commit
0dddd424a4
285
lib/checkio.cpp
285
lib/checkio.cpp
|
@ -403,28 +403,6 @@ void CheckIO::invalidScanfError(const Token *tok, bool portability)
|
||||||
// printf("%u%s", 1); // Too few arguments
|
// printf("%u%s", 1); // Too few arguments
|
||||||
// printf("", 1); // Too much arguments
|
// printf("", 1); // Too much arguments
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
static bool isComplexType(const Variable* var, const Token* varTypeTok)
|
|
||||||
{
|
|
||||||
if (var->type())
|
|
||||||
return (true);
|
|
||||||
|
|
||||||
static std::set<std::string> knownTypes;
|
|
||||||
if (knownTypes.empty()) {
|
|
||||||
knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type
|
|
||||||
knownTypes.insert("string");
|
|
||||||
knownTypes.insert("wstring");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (varTypeTok->str() == "std")
|
|
||||||
varTypeTok = varTypeTok->tokAt(2);
|
|
||||||
return ((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !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()
|
void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
{
|
{
|
||||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
@ -555,24 +533,18 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform type checks
|
// Perform type checks
|
||||||
const Variable *variableInfo;
|
ArgumentInfo argInfo(argListTok, _settings);
|
||||||
const Token *varTypeTok;
|
|
||||||
const Function *functionInfo;
|
|
||||||
bool element;
|
|
||||||
|
|
||||||
if (getArgumentInfo(argListTok, &variableInfo, &varTypeTok, &functionInfo, element)) {
|
if (argInfo.typeToken) {
|
||||||
if (varTypeTok->str() == "static")
|
if (scan) {
|
||||||
varTypeTok = varTypeTok->next();
|
if (warning && ((!argInfo.variableInfo->isPointer() && !argInfo.variableInfo->isArray()) || argInfo.typeToken->strAt(-1) == "const"))
|
||||||
|
|
||||||
if (scan && varTypeTok) {
|
|
||||||
if (warning && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const"))
|
|
||||||
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
||||||
|
|
||||||
if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1) && variableInfo->dimensions()[0].known) {
|
if (*i == 's' && argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
|
||||||
if (!width.empty()) {
|
if (!width.empty()) {
|
||||||
int numWidth = std::atoi(width.c_str());
|
int numWidth = std::atoi(width.c_str());
|
||||||
if (numWidth != (variableInfo->dimension(0) - 1))
|
if (numWidth != (argInfo.variableInfo->dimension(0) - 1))
|
||||||
invalidScanfFormatWidthError(tok, numFormat, numWidth, variableInfo);
|
invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!scan && warning) {
|
} else if (!scan && warning) {
|
||||||
|
@ -581,12 +553,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
while (!done) {
|
while (!done) {
|
||||||
switch (*i) {
|
switch (*i) {
|
||||||
case 's':
|
case 's':
|
||||||
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
|
if (argInfo.variableInfo && argListTok->type() != Token::eString &&
|
||||||
|
argInfo.isKnownType() &&
|
||||||
|
!argInfo.isArrayOrPointer())
|
||||||
invalidPrintfArgTypeError_s(tok, numFormat);
|
invalidPrintfArgTypeError_s(tok, numFormat);
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
if ((variableInfo && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
|
if ((argInfo.variableInfo && argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->type() == Token::eString)
|
||||||
invalidPrintfArgTypeError_n(tok, numFormat);
|
invalidPrintfArgTypeError_n(tok, numFormat);
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
@ -595,69 +569,62 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
case 'X':
|
case 'X':
|
||||||
case 'o':
|
case 'o':
|
||||||
specifier += *i;
|
specifier += *i;
|
||||||
if (functionInfo && ((varTypeTok->isStandardType() || functionInfo->retType) && varTypeTok->next()->str() != "*")) {
|
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||||
if (!Token::Match(varTypeTok, "bool|short|long|int|char|size_t") ||
|
if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) &&
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
(!Token::Match(argInfo.typeToken, "bool|short|long|int|char|size_t") ||
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
(specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok);
|
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())))) {
|
||||||
|
invalidPrintfArgTypeError_int(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
}
|
}
|
||||||
} else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
|
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||||
if (!Token::Match(varTypeTok, "bool|short|long|int|char|size_t") ||
|
invalidPrintfArgTypeError_int(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
|
||||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok);
|
|
||||||
}
|
|
||||||
} else if (varTypeTok->type() == Token::eString) {
|
|
||||||
invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok);
|
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'i':
|
case 'i':
|
||||||
specifier += *i;
|
specifier += *i;
|
||||||
if (functionInfo && (varTypeTok->isStandardType() || functionInfo->retType) && varTypeTok->next()->str() != "*") {
|
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||||
if (((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") ||
|
if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) &&
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
(((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|long|int")) && argInfo.typeToken->str() != "char") ||
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
(specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok);
|
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())))) {
|
||||||
|
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
}
|
}
|
||||||
} else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
|
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||||
if (((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") ||
|
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
} else if (argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|int|long")) {
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok);
|
|
||||||
}
|
|
||||||
} else if (varTypeTok->type() == Token::eString) {
|
|
||||||
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok);
|
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
specifier += *i;
|
specifier += *i;
|
||||||
if (functionInfo && ((varTypeTok->isStandardType() || functionInfo->retType) || varTypeTok->next()->str() != "*")) {
|
if (argInfo.functionInfo || argInfo.variableInfo) {
|
||||||
if (((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") ||
|
if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) &&
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
(((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") ||
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
(specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) ||
|
||||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok);
|
(specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())))) {
|
||||||
|
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
}
|
}
|
||||||
} else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
|
} else if (argInfo.typeToken->type() == Token::eString) {
|
||||||
if (((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") ||
|
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
(specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) ||
|
} else {
|
||||||
(specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) {
|
if (((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") ||
|
||||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok);
|
(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.typeToken);
|
||||||
}
|
}
|
||||||
} else if (varTypeTok->type() == Token::eString) {
|
|
||||||
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok);
|
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (functionInfo && varTypeTok->type() == Token::eType && varTypeTok->next()->str() != "*")
|
if (argInfo.functionInfo && argInfo.typeToken->type() == Token::eType && !argInfo.isArrayOrPointer())
|
||||||
invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok);
|
invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken);
|
||||||
else if (variableInfo && varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray())
|
else if (argInfo.variableInfo && argInfo.typeToken && argInfo.isKnownType() && !argInfo.isArrayOrPointer())
|
||||||
invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok);
|
invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken);
|
||||||
else if (varTypeTok->type() == Token::eString)
|
else if (argInfo.typeToken->type() == Token::eString)
|
||||||
invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok);
|
invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken);
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
|
@ -666,18 +633,24 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
case 'g':
|
case 'g':
|
||||||
case 'G':
|
case 'G':
|
||||||
specifier += *i;
|
specifier += *i;
|
||||||
if (functionInfo && (((varTypeTok->isStandardType() || functionInfo->retType) && !Token::Match(varTypeTok, "float|double")) ||
|
if (argInfo.functionInfo) {
|
||||||
(!element && Token::simpleMatch(varTypeTok->next(), "*")) ||
|
if (((argInfo.typeToken->isStandardType() || argInfo.functionInfo->retType) && !Token::Match(argInfo.typeToken, "float|double")) ||
|
||||||
(element && !Token::simpleMatch(varTypeTok->next(), "*")) ||
|
(!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||||
(specifier[0] == 'l' && (!varTypeTok->isLong() || varTypeTok->str() != "double")) ||
|
(argInfo.element && !argInfo.isArrayOrPointer()) ||
|
||||||
(specifier[0] != 'l' && varTypeTok->isLong())))
|
(specifier[0] == 'l' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
|
||||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok);
|
(specifier[0] != 'l' && argInfo.typeToken->isLong())) {
|
||||||
else if (variableInfo && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) ||
|
invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
(!element && variableInfo->isArrayOrPointer()) ||
|
}
|
||||||
(element && !variableInfo->isArrayOrPointer())))
|
} else if (argInfo.variableInfo) {
|
||||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok);
|
if ((argInfo.isKnownType() && !Token::Match(argInfo.typeToken, "float|double")) ||
|
||||||
else if (varTypeTok->type() == Token::eString)
|
(!argInfo.element && argInfo.isArrayOrPointer()) ||
|
||||||
invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok);
|
(argInfo.element && !argInfo.isArrayOrPointer())) {
|
||||||
|
invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
|
}
|
||||||
|
} else if (argInfo.typeToken->type() == Token::eString)
|
||||||
|
invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
|
else if (!Token::Match(argInfo.typeToken, "float|double"))
|
||||||
|
invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken);
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
|
case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
|
||||||
|
@ -770,20 +743,22 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
// We currently only support string literals, variables, and functions.
|
// We currently only support string literals, variables, and functions.
|
||||||
/// @todo add non-string literals, and generic expressions
|
/// @todo add non-string literals, and generic expressions
|
||||||
|
|
||||||
bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Token **typeTok, const Function **func, bool &element) const
|
CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings)
|
||||||
|
: variableInfo(0)
|
||||||
|
, typeToken(0)
|
||||||
|
, functionInfo(0)
|
||||||
|
, element(false)
|
||||||
|
, tempToken(0)
|
||||||
{
|
{
|
||||||
if (tok) {
|
if (tok) {
|
||||||
if (tok->type() == Token::eString) {
|
if (tok->type() == Token::eString) {
|
||||||
*var = 0;
|
typeToken = tok;
|
||||||
*typeTok = tok;
|
return;
|
||||||
*func = 0;
|
|
||||||
element = false;
|
|
||||||
return true;
|
|
||||||
} else if (tok->type() == Token::eVariable || tok->type() == Token::eFunction || Token::Match(tok, "%type% ::")) {
|
} else if (tok->type() == Token::eVariable || tok->type() == Token::eFunction || Token::Match(tok, "%type% ::")) {
|
||||||
while (Token::Match(tok, "%type% ::"))
|
while (Token::Match(tok, "%type% ::"))
|
||||||
tok = tok->tokAt(2);
|
tok = tok->tokAt(2);
|
||||||
if (!tok || !(tok->type() == Token::eVariable || tok->type() == Token::eFunction))
|
if (!tok || !(tok->type() == Token::eVariable || tok->type() == Token::eFunction))
|
||||||
return false;
|
return;
|
||||||
const Token *varTok = 0;
|
const Token *varTok = 0;
|
||||||
const Token *tok1 = tok->next();
|
const Token *tok1 = tok->next();
|
||||||
for (; tok1; tok1 = tok1->next()) {
|
for (; tok1; tok1 = tok1->next()) {
|
||||||
|
@ -793,24 +768,22 @@ bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Tok
|
||||||
if (varTok->str() == ")" && varTok->link()->previous()->type() == Token::eFunction) {
|
if (varTok->str() == ")" && varTok->link()->previous()->type() == Token::eFunction) {
|
||||||
const Function * function = varTok->link()->previous()->function();
|
const Function * function = varTok->link()->previous()->function();
|
||||||
if (function && function->retDef) {
|
if (function && function->retDef) {
|
||||||
*var = 0;
|
typeToken = function->retDef;
|
||||||
*typeTok = function->retDef;
|
functionInfo = function;
|
||||||
*func = function;
|
|
||||||
element = true;
|
element = true;
|
||||||
return true;
|
return;
|
||||||
} else
|
} else
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
} else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->type() == Token::eFunction) {
|
} else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->type() == Token::eFunction) {
|
||||||
const Function * function = tok1->linkAt(-1)->previous()->function();
|
const Function * function = tok1->linkAt(-1)->previous()->function();
|
||||||
if (function && function->retDef) {
|
if (function && function->retDef) {
|
||||||
*var = 0;
|
typeToken = function->retDef;
|
||||||
*typeTok = function->retDef;
|
functionInfo = function;
|
||||||
*func = function;
|
|
||||||
element = false;
|
element = false;
|
||||||
return true;
|
return;
|
||||||
} else
|
} else
|
||||||
return false;
|
return;
|
||||||
} else
|
} else
|
||||||
varTok = tok1->previous();
|
varTok = tok1->previous();
|
||||||
break;
|
break;
|
||||||
|
@ -818,30 +791,86 @@ bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Tok
|
||||||
tok1 = tok1->link();
|
tok1 = tok1->link();
|
||||||
else if (tok1->str() == "<" && tok1->link())
|
else if (tok1->str() == "<" && tok1->link())
|
||||||
tok1 = tok1->link();
|
tok1 = tok1->link();
|
||||||
else if (!(tok1->str() == "." || tok1->type() == Token::eVariable || tok1->type() == Token::eFunction))
|
|
||||||
return false;
|
// check for some common well known functions
|
||||||
|
else if (Token::Match(tok1->previous(), "%var% . size|empty|c_str ( )") && tok1->previous()->variable() &&
|
||||||
|
(Token::Match(tok1->previous()->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(tok1->previous()->variable()->typeStartToken(), "std :: string|wstring"))) {
|
||||||
|
tempToken = new Token(0);
|
||||||
|
tempToken->fileIndex(tok1->fileIndex());
|
||||||
|
tempToken->linenr(tok1->linenr());
|
||||||
|
if (tok1->next()->str() == "size") {
|
||||||
|
// size_t is platform dependent
|
||||||
|
if (settings->sizeof_size_t == 8) {
|
||||||
|
tempToken->str("long");
|
||||||
|
if (settings->sizeof_long != 8)
|
||||||
|
tempToken->isLong(true);
|
||||||
|
} else if (settings->sizeof_size_t == 4 && settings->sizeof_long == 4)
|
||||||
|
tempToken->str("long");
|
||||||
|
else if (settings->sizeof_size_t == 4)
|
||||||
|
tempToken->str("int");
|
||||||
|
|
||||||
|
tempToken->originalName("size_t");
|
||||||
|
tempToken->isUnsigned(true);
|
||||||
|
} else if (tok1->next()->str() == "empty") {
|
||||||
|
tempToken->str("bool");
|
||||||
|
} else if (tok1->next()->str() == "c_str") {
|
||||||
|
tempToken->str("const");
|
||||||
|
tempToken->insertToken("*");
|
||||||
|
if (tok1->previous()->variable()->typeStartToken()->strAt(2) == "string")
|
||||||
|
tempToken->insertToken("char");
|
||||||
|
else
|
||||||
|
tempToken->insertToken("wchar_t");
|
||||||
|
}
|
||||||
|
typeToken = tempToken;
|
||||||
|
return;
|
||||||
|
} else if (!(tok1->str() == "." || tok1->type() == Token::eVariable || tok1->type() == Token::eFunction))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (varTok) {
|
if (varTok) {
|
||||||
const Variable *variableInfo = varTok->variable();
|
variableInfo = varTok->variable();
|
||||||
*var = variableInfo;
|
|
||||||
element = tok1->previous()->str() == "]";
|
element = tok1->previous()->str() == "]";
|
||||||
*func = 0;
|
|
||||||
|
|
||||||
// look for std::vector operator [] and use template type as return type
|
// look for std::vector operator [] and use template type as return type
|
||||||
if (variableInfo) {
|
if (variableInfo) {
|
||||||
if (element && Token::Match(variableInfo->typeStartToken(), "std :: vector|array <")) {
|
if (element && Token::Match(variableInfo->typeStartToken(), "std :: vector|array <")) {
|
||||||
*typeTok = variableInfo->typeStartToken()->tokAt(4);
|
typeToken = variableInfo->typeStartToken()->tokAt(4);
|
||||||
element = false; // not really an array element
|
element = false; // not really an array element
|
||||||
} else
|
} else
|
||||||
*typeTok = variableInfo->typeStartToken();
|
typeToken = variableInfo->typeStartToken();
|
||||||
} else
|
}
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckIO::ArgumentInfo::isComplexType() const
|
||||||
|
{
|
||||||
|
if (variableInfo->type())
|
||||||
|
return (true);
|
||||||
|
|
||||||
|
static std::set<std::string> knownTypes;
|
||||||
|
if (knownTypes.empty()) {
|
||||||
|
knownTypes.insert("string");
|
||||||
|
knownTypes.insert("wstring");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* varTypeTok = typeToken;
|
||||||
|
if (varTypeTok->str() == "std")
|
||||||
|
varTypeTok = varTypeTok->tokAt(2);
|
||||||
|
|
||||||
|
return ((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckIO::ArgumentInfo::isKnownType() const
|
||||||
|
{
|
||||||
|
if (variableInfo)
|
||||||
|
return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType());
|
||||||
|
else if (functionInfo)
|
||||||
|
return (typeToken->isStandardType() || functionInfo->retType);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -936,6 +965,10 @@ void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFo
|
||||||
errmsg << "const char *";
|
errmsg << "const char *";
|
||||||
} else {
|
} else {
|
||||||
if (type->originalName().empty()) {
|
if (type->originalName().empty()) {
|
||||||
|
if (type->str() == "const") {
|
||||||
|
errmsg << "const ";
|
||||||
|
type = type->next();
|
||||||
|
}
|
||||||
type->stringify(errmsg, false, true);
|
type->stringify(errmsg, false, true);
|
||||||
if (type->strAt(1) == "*")
|
if (type->strAt(1) == "*")
|
||||||
errmsg << " *";
|
errmsg << " *";
|
||||||
|
@ -972,6 +1005,10 @@ void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numF
|
||||||
errmsg << "const char *";
|
errmsg << "const char *";
|
||||||
} else {
|
} else {
|
||||||
if (type->originalName().empty()) {
|
if (type->originalName().empty()) {
|
||||||
|
if (type->str() == "const") {
|
||||||
|
errmsg << "const ";
|
||||||
|
type = type->next();
|
||||||
|
}
|
||||||
type->stringify(errmsg, false, true);
|
type->stringify(errmsg, false, true);
|
||||||
if (type->strAt(1) == "*")
|
if (type->strAt(1) == "*")
|
||||||
errmsg << " *";
|
errmsg << " *";
|
||||||
|
@ -1008,6 +1045,10 @@ void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numF
|
||||||
errmsg << "const char *";
|
errmsg << "const char *";
|
||||||
} else {
|
} else {
|
||||||
if (type->originalName().empty()) {
|
if (type->originalName().empty()) {
|
||||||
|
if (type->str() == "const") {
|
||||||
|
errmsg << "const ";
|
||||||
|
type = type->next();
|
||||||
|
}
|
||||||
type->stringify(errmsg, false, true);
|
type->stringify(errmsg, false, true);
|
||||||
if (type->strAt(1) == "*")
|
if (type->strAt(1) == "*")
|
||||||
errmsg << " *";
|
errmsg << " *";
|
||||||
|
@ -1038,6 +1079,10 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int num
|
||||||
errmsg << "const char *";
|
errmsg << "const char *";
|
||||||
} else {
|
} else {
|
||||||
if (type->originalName().empty()) {
|
if (type->originalName().empty()) {
|
||||||
|
if (type->str() == "const") {
|
||||||
|
errmsg << "const ";
|
||||||
|
type = type->next();
|
||||||
|
}
|
||||||
type->stringify(errmsg, false, true);
|
type->stringify(errmsg, false, true);
|
||||||
if (type->strAt(1) == "*")
|
if (type->strAt(1) == "*")
|
||||||
errmsg << " *";
|
errmsg << " *";
|
||||||
|
|
|
@ -23,8 +23,7 @@
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "symboldatabase.h"
|
||||||
class Variable;
|
|
||||||
|
|
||||||
/// @addtogroup Checks
|
/// @addtogroup Checks
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -70,7 +69,31 @@ public:
|
||||||
void checkWrongPrintfScanfArguments();
|
void checkWrongPrintfScanfArguments();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getArgumentInfo(const Token *tok, const Variable **var, const Token **typeTok, const Function **func, bool &element) const;
|
class ArgumentInfo {
|
||||||
|
public:
|
||||||
|
ArgumentInfo(const Token *arg, const Settings *settings);
|
||||||
|
~ArgumentInfo() {
|
||||||
|
delete tempToken;
|
||||||
|
}
|
||||||
|
bool isArrayOrPointer() const {
|
||||||
|
if (variableInfo)
|
||||||
|
return variableInfo->isArrayOrPointer();
|
||||||
|
else if (functionInfo)
|
||||||
|
return typeToken->next()->str() == "*";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool isComplexType() const;
|
||||||
|
bool isKnownType() const;
|
||||||
|
|
||||||
|
const Variable *variableInfo;
|
||||||
|
const Token *typeToken;
|
||||||
|
const Function *functionInfo;
|
||||||
|
bool element;
|
||||||
|
Token *tempToken;
|
||||||
|
private:
|
||||||
|
ArgumentInfo(const ArgumentInfo &); // not implemented
|
||||||
|
ArgumentInfo operator = (const ArgumentInfo &); // not implemented
|
||||||
|
};
|
||||||
|
|
||||||
// Reporting errors..
|
// Reporting errors..
|
||||||
void coutCerrMisusageError(const Token* tok, const std::string& streamName);
|
void coutCerrMisusageError(const Token* tok, const std::string& streamName);
|
||||||
|
|
|
@ -1081,6 +1081,68 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<int> v;\n"
|
||||||
|
"std::string s;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n"
|
||||||
|
"}\n", false, false, Settings::Win32A);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<int> v;\n"
|
||||||
|
"std::string s;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n"
|
||||||
|
"}\n", false, false, Settings::Win64);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long long}'.\n"
|
||||||
|
"[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long long}'.\n", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<int> v;\n"
|
||||||
|
"std::string s;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n"
|
||||||
|
"}\n", false, false, Settings::Unix32);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
|
||||||
|
|
||||||
|
check("std::vector<int> v;\n"
|
||||||
|
"std::string s;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n"
|
||||||
|
"}\n", false, false, Settings::Unix64);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
|
||||||
|
|
||||||
|
check("class Fred : public std::vector<int> {} v;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n"
|
||||||
|
" printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n"
|
||||||
|
"}\n", false, false, Settings::Unix64);
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n"
|
||||||
|
"[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", "", errout.str());
|
||||||
|
|
||||||
|
check("std::string s;\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" printf(\"%s %p %u %d %f\", s.c_str(), s.c_str(), s.c_str(), s.c_str(), s.c_str());\n"
|
||||||
|
"}\n", false, false, Settings::Unix64);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 3) requires an unsigned integer but the argument type is 'const char *'.\n"
|
||||||
|
"[test.cpp:3]: (warning) %d in format string (no. 4) requires a signed integer but the argument type is 'const char *'.\n"
|
||||||
|
"[test.cpp:3]: (warning) %f in format string (no. 5) requires a floating point number but the argument type is 'const char *'.\n", errout.str());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
|
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
|
||||||
|
|
Loading…
Reference in New Issue