Run simplifyPlatformTypes on library return types (#1672)

Add a call to simplifyPlatformTypes() in
SymbolDatabase::setValueTypeInTokenList() to simplify return types of
library configured functions. This fixes the FN in #8141. Regression
tests are added, both for the original issue and another FN in the comments.

In order to do that, move simplifyPlatformTypes() to TokenList from Tokenizer.
This is a pure refactoring and does not change any behaviour. The code was
literally copy-pasted from one file to another and in two places
'list.front()' was changed to 'front()'.

When adding the call to simplifyPlatformTypes(), the original type of
v.size() where v is a container is changed from 'size_t' to 'std::size_t'.
Tests are updated accordingly. It can be noted that if v is declared as
'class fred : public std::vector<int> {} v', the original type of 'v.size()'
is still 'size_t' and not 'std::size_t'.
This commit is contained in:
rikardfalkeborn 2019-02-15 13:29:52 +01:00 committed by Daniel Marjamäki
parent fc84227668
commit dc4e7cef88
7 changed files with 158 additions and 142 deletions

View File

@ -5417,6 +5417,7 @@ void SymbolDatabase::setValueTypeInTokenList()
if (tokenList.createTokens(istr)) {
ValueType vt;
assert(tokenList.front());
tokenList.simplifyPlatformTypes();
tokenList.simplifyStdType();
if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings)) {
setValueType(tok, vt);

View File

@ -3867,7 +3867,7 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
// convert platform dependent types to standard types
// 32 bits: size_t -> unsigned long
// 64 bits: size_t -> unsigned long long
simplifyPlatformTypes();
list.simplifyPlatformTypes();
// collapse compound standard types into a single token
// unsigned long long int => long (with _isUnsigned=true,_isLong=true)
@ -5972,119 +5972,6 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
}
}
void Tokenizer::simplifyPlatformTypes()
{
const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11;
enum { isLongLong, isLong, isInt } type;
/** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */
if (mSettings->sizeof_size_t == mSettings->sizeof_long)
type = isLong;
else if (mSettings->sizeof_size_t == mSettings->sizeof_long_long)
type = isLongLong;
else if (mSettings->sizeof_size_t == mSettings->sizeof_int)
type = isInt;
else
return;
for (Token *tok = list.front(); tok; tok = tok->next()) {
// pre-check to reduce unneeded match calls
if (!Token::Match(tok, "std| ::| %type%"))
continue;
bool isUnsigned;
if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) {
if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=")
continue;
isUnsigned = true;
} else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) {
if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=")
continue;
isUnsigned = false;
} else
continue;
bool inStd = false;
if (tok->str() == "::") {
tok->deleteThis();
} else if (tok->str() == "std") {
if (tok->next()->str() != "::")
continue;
inStd = true;
tok->deleteNext();
tok->deleteThis();
}
if (inStd)
tok->originalName("std::" + tok->str());
else
tok->originalName(tok->str());
if (isUnsigned)
tok->isUnsigned(true);
switch (type) {
case isLongLong:
tok->isLong(true);
tok->str("long");
break;
case isLong:
tok->str("long");
break;
case isInt:
tok->str("int");
break;
}
}
const std::string platform_type(mSettings->platformString());
for (Token *tok = list.front(); tok; tok = tok->next()) {
if (tok->tokType() != Token::eType && tok->tokType() != Token::eName)
continue;
const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type);
if (platformtype) {
// check for namespace
if (tok->strAt(-1) == "::") {
const Token * tok1 = tok->tokAt(-2);
// skip when non-global namespace defined
if (tok1 && tok1->tokType() == Token::eName)
continue;
tok = tok->previous();
tok->deleteThis();
}
Token *typeToken;
if (platformtype->_const_ptr) {
tok->str("const");
tok->insertToken("*");
tok->insertToken(platformtype->mType);
typeToken = tok;
} else if (platformtype->_pointer) {
tok->str(platformtype->mType);
typeToken = tok;
tok->insertToken("*");
} else if (platformtype->_ptr_ptr) {
tok->str(platformtype->mType);
typeToken = tok;
tok->insertToken("*");
tok->insertToken("*");
} else {
tok->originalName(tok->str());
tok->str(platformtype->mType);
typeToken = tok;
}
if (platformtype->_signed)
typeToken->isSigned(true);
if (platformtype->_unsigned)
typeToken->isUnsigned(true);
if (platformtype->_long)
typeToken->isLong(true);
}
}
}
void Tokenizer::simplifyStaticConst()
{
// This function will simplify the token list so that the qualifiers "extern", "static"

View File

@ -285,13 +285,6 @@ public:
void simplifyInitVar();
Token * initVar(Token * tok);
/**
* Convert platform dependent types to standard types.
* 32 bits: size_t -> unsigned long
* 64 bits: size_t -> unsigned long long
*/
void simplifyPlatformTypes();
/**
* Simplify easy constant '?:' operation
* Example: 0 ? (2/0) : 0 => 0

View File

@ -1278,6 +1278,119 @@ bool TokenList::validateToken(const Token* tok) const
return false;
}
void TokenList::simplifyPlatformTypes()
{
const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11;
enum { isLongLong, isLong, isInt } type;
/** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */
if (mSettings->sizeof_size_t == mSettings->sizeof_long)
type = isLong;
else if (mSettings->sizeof_size_t == mSettings->sizeof_long_long)
type = isLongLong;
else if (mSettings->sizeof_size_t == mSettings->sizeof_int)
type = isInt;
else
return;
for (Token *tok = front(); tok; tok = tok->next()) {
// pre-check to reduce unneeded match calls
if (!Token::Match(tok, "std| ::| %type%"))
continue;
bool isUnsigned;
if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) {
if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=")
continue;
isUnsigned = true;
} else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) {
if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=")
continue;
isUnsigned = false;
} else
continue;
bool inStd = false;
if (tok->str() == "::") {
tok->deleteThis();
} else if (tok->str() == "std") {
if (tok->next()->str() != "::")
continue;
inStd = true;
tok->deleteNext();
tok->deleteThis();
}
if (inStd)
tok->originalName("std::" + tok->str());
else
tok->originalName(tok->str());
if (isUnsigned)
tok->isUnsigned(true);
switch (type) {
case isLongLong:
tok->isLong(true);
tok->str("long");
break;
case isLong:
tok->str("long");
break;
case isInt:
tok->str("int");
break;
}
}
const std::string platform_type(mSettings->platformString());
for (Token *tok = front(); tok; tok = tok->next()) {
if (tok->tokType() != Token::eType && tok->tokType() != Token::eName)
continue;
const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type);
if (platformtype) {
// check for namespace
if (tok->strAt(-1) == "::") {
const Token * tok1 = tok->tokAt(-2);
// skip when non-global namespace defined
if (tok1 && tok1->tokType() == Token::eName)
continue;
tok = tok->previous();
tok->deleteThis();
}
Token *typeToken;
if (platformtype->_const_ptr) {
tok->str("const");
tok->insertToken("*");
tok->insertToken(platformtype->mType);
typeToken = tok;
} else if (platformtype->_pointer) {
tok->str(platformtype->mType);
typeToken = tok;
tok->insertToken("*");
} else if (platformtype->_ptr_ptr) {
tok->str(platformtype->mType);
typeToken = tok;
tok->insertToken("*");
tok->insertToken("*");
} else {
tok->originalName(tok->str());
tok->str(platformtype->mType);
typeToken = tok;
}
if (platformtype->_signed)
typeToken->isSigned(true);
if (platformtype->_unsigned)
typeToken->isUnsigned(true);
if (platformtype->_long)
typeToken->isLong(true);
}
}
}
void TokenList::simplifyStdType()
{
for (Token *tok = front(); tok; tok = tok->next()) {

View File

@ -169,6 +169,13 @@ public:
*/
bool validateToken(const Token* tok) const;
/**
* Convert platform dependent types to standard types.
* 32 bits: size_t -> unsigned long
* 64 bits: size_t -> unsigned long long
*/
void simplifyPlatformTypes();
/**
* Collapse compound standard types into a single token.
* unsigned long long int => long _isUnsigned=true,_isLong=true

View File

@ -2867,10 +2867,10 @@ private:
" 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, true, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str());
check("std::vector<int> v;\n"
"std::string s;\n"
@ -2878,10 +2878,10 @@ private:
" 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, true, Settings::Win64);
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long long}'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n", errout.str());
check("std::vector<int> v;\n"
"std::string s;\n"
@ -2889,10 +2889,10 @@ private:
" 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, true, Settings::Unix32);
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str());
check("std::vector<int> v;\n"
"std::string s;\n"
@ -2900,10 +2900,10 @@ private:
" 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, true, Settings::Unix64);
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str());
check("class Fred : public std::vector<int> {} v;\n"
"std::string s;\n"
@ -2913,8 +2913,8 @@ private:
"}\n", false, true, Settings::Unix64);
ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
"[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str());
check("class Fred : public std::vector<int> {} v;\n"
"void foo() {\n"
@ -2946,11 +2946,11 @@ private:
ASSERT_EQUALS("[test.cpp:8]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n"
"[test.cpp:8]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char *'.\n"
"[test.cpp:8]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'char *'.\n"
"[test.cpp:9]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:9]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:9]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:10]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:10]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:10]: (portability) %lu in format string (no. 2) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:11]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n"
"[test.cpp:11]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'std::size_t {aka unsigned long}'.\n"
"[test.cpp:11]: (portability) %llu in format string (no. 2) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str());
check("bool b; bool bf();\n"
@ -4686,6 +4686,12 @@ private:
" printf(\"%ld%lld\", atol(s), atoll(s));\n"
"}");
ASSERT_EQUALS("", errout.str());
// 8141
check("void f(int i) {\n"
" printf(\"%f\", imaxabs(i));\n"
"}\n", false, true, Settings::Unix64);
ASSERT_EQUALS("[test.cpp:2]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'intmax_t {aka signed long}'.\n", errout.str());
}
void testPrintfTypeAlias1() {

View File

@ -55,6 +55,7 @@ private:
TEST_CASE(zeroDiv9);
TEST_CASE(zeroDiv10);
TEST_CASE(zeroDiv11);
TEST_CASE(zeroDiv12);
TEST_CASE(zeroDivCond); // division by zero / useless condition
@ -531,6 +532,14 @@ private:
ASSERT_EQUALS("", errout.str());
}
void zeroDiv12() {
// #8141
check("intmax_t f() {\n"
" return 1 / imaxabs(0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout.str());
}
void zeroDivCond() {
check("void f(unsigned int x) {\n"
" int y = 17 / x;\n"