From dc4e7cef88052ea41df1569a4da5193913a716e7 Mon Sep 17 00:00:00 2001 From: rikardfalkeborn Date: Fri, 15 Feb 2019 13:29:52 +0100 Subject: [PATCH] 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 {} v', the original type of 'v.size()' is still 'size_t' and not 'std::size_t'. --- lib/symboldatabase.cpp | 1 + lib/tokenize.cpp | 115 +---------------------------------------- lib/tokenize.h | 7 --- lib/tokenlist.cpp | 113 ++++++++++++++++++++++++++++++++++++++++ lib/tokenlist.h | 7 +++ test/testio.cpp | 48 +++++++++-------- test/testother.cpp | 9 ++++ 7 files changed, 158 insertions(+), 142 deletions(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 0e1b223bb..83aa6f82c 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -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); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 9d728b3d5..4ab0bd1c1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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" diff --git a/lib/tokenize.h b/lib/tokenize.h index 6fb1b3708..2a9872ed2 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -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 diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index d68fc47fd..c257fc2ce 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -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()) { diff --git a/lib/tokenlist.h b/lib/tokenlist.h index f0baabdb7..7a2546976 100644 --- a/lib/tokenlist.h +++ b/lib/tokenlist.h @@ -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 diff --git a/test/testio.cpp b/test/testio.cpp index 1a2e28235..7d145ba86 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -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 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 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 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 {} 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 {} 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() { diff --git a/test/testother.cpp b/test/testother.cpp index f201501a9..0874fa13d 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -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"