From 1faae52d06961f2c99b1c24be73c3b4199a6b44e Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Tue, 5 Feb 2019 02:52:23 -0500 Subject: [PATCH] Fixed #8960 ("(debug) Unknown type 'x'." with alias in template class alias) (#1643) * Fixed #8960 ("(debug) Unknown type 'x'." with alias in template class alias) This commit adds non-template type alias support to the template simplifier. Only relatively simple type aliases are supported at this time. More complex types will be added later. --debug-warnings will show unsupported type aliases. Type alias support will be removed from the symbol database in the future. Type alias tests have been removed from the symbol database tests. * Add the changes. * Fix codacy warning. * Fix travis warnings. --- Makefile | 4 + lib/templatesimplifier.cpp | 389 ++++++++++++++++++++++++++++++- lib/templatesimplifier.h | 3 + lib/tokenize.cpp | 6 +- lib/tokenize.h | 1 + test/testfiles.pri | 1 + test/testrunner.vcxproj | 1 + test/testsimplifytemplate.cpp | 18 ++ test/testsimplifyusing.cpp | 417 ++++++++++++++++++++++++++++++++++ test/testsymboldatabase.cpp | 138 ----------- 10 files changed, 834 insertions(+), 144 deletions(-) mode change 100644 => 100755 test/testrunner.vcxproj create mode 100644 test/testsimplifyusing.cpp diff --git a/Makefile b/Makefile index dff3eb329..d0d57c13f 100644 --- a/Makefile +++ b/Makefile @@ -225,6 +225,7 @@ TESTOBJ = test/options.o \ test/testsimplifytemplate.o \ test/testsimplifytokens.o \ test/testsimplifytypedef.o \ + test/testsimplifyusing.o \ test/testsizeof.o \ test/teststl.o \ test/teststring.o \ @@ -625,6 +626,9 @@ test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/platform.h lib/config test/testsimplifytypedef.o: test/testsimplifytypedef.cpp lib/platform.h lib/config.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/templatesimplifier.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytypedef.o test/testsimplifytypedef.cpp +test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/platform.h lib/config.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/templatesimplifier.h lib/tokenize.h lib/tokenlist.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifyusing.o test/testsimplifyusing.cpp + test/testsizeof.o: test/testsizeof.cpp lib/checksizeof.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/templatesimplifier.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsizeof.o test/testsizeof.cpp diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 421f8caf7..c35e82a14 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -146,7 +146,7 @@ TemplateSimplifier::TokenAndName::~TokenAndName() } TemplateSimplifier::TemplateSimplifier(Tokenizer *tokenizer) - : mTokenList(tokenizer->list), mSettings(tokenizer->mSettings), mErrorLogger(tokenizer->mErrorLogger) + : mTokenizer(tokenizer), mTokenList(tokenizer->list), mSettings(tokenizer->mSettings), mErrorLogger(tokenizer->mErrorLogger) { } @@ -520,12 +520,14 @@ namespace { static std::string getScopeName(const std::list &scopeInfo) { std::string ret; - for (const ScopeInfo2 &i : scopeInfo) - ret += (ret.empty() ? "" : " :: ") + i.name; + for (const ScopeInfo2 &i : scopeInfo) { + if (!i.name.empty()) + ret += (ret.empty() ? "" : " :: ") + i.name; + } return ret; } -static void setScopeInfo(Token *tok, std::list *scopeInfo) +static void setScopeInfo(Token *tok, std::list *scopeInfo, bool all = false) { while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd) scopeInfo->pop_back(); @@ -544,6 +546,7 @@ static void setScopeInfo(Token *tok, std::list *scopeInfo) } // check for member function else if (tok->str() == "{") { + bool added = false; Token *tok1 = tok; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); @@ -570,8 +573,12 @@ static void setScopeInfo(Token *tok, std::list *scopeInfo) tok1 = tok1->tokAt(-2); } scopeInfo->emplace_back(scope, tok->link()); + added = true; } } + + if (all && !added) + scopeInfo->emplace_back("", tok->link()); } return; } @@ -2608,6 +2615,375 @@ void TemplateSimplifier::printOut(const std::string & text) const } } +static Token *findSemicolon(Token *tok) +{ + unsigned int level = 0; + + for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) { + if (tok->str() == "{") + ++level; + else if (level > 0 && tok->str() == "}") + --level; + } + + return tok; +} + +bool TemplateSimplifier::simplifyUsing() +{ + bool substitute = false; + std::list scopeList; + bool inTemplateDefinition = false; + const Token *endOfTemplateDefinition = nullptr; + bool isVariable = false; + struct Using { + Using(Token *start, Token *end) : startTok(start), endTok(end) { } + Token *startTok; + Token *endTok; + }; + std::list usingList; + + scopeList.emplace_back("", nullptr); + + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (mErrorLogger && !mTokenList.getFiles().empty()) + mErrorLogger->reportProgress(mTokenList.getFiles()[0], "Tokenize (using)", tok->progressValue()); + + if (mSettings->terminated()) + return substitute; + + if (Token::Match(tok, "{|}|namespace|class|struct|union") || + Token::Match(tok, "using namespace %name% ;|::")) { + setScopeInfo(tok, &scopeList); + } + + if (inTemplateDefinition) { + if (!endOfTemplateDefinition) { + if (isVariable) + endOfTemplateDefinition = findSemicolon(tok); + else if (tok->str() == "{") + endOfTemplateDefinition = tok->link(); + } + if (tok == endOfTemplateDefinition) { + inTemplateDefinition = false; + continue; + } + } + + if (tok->str()=="template") { + if (Token::Match(tok->next(), "< !!>")) + inTemplateDefinition = true; + else + inTemplateDefinition = false; + } + + if (!inTemplateDefinition) { + // look for non-template type aliases + if (tok->strAt(-1) != ">" && + (Token::Match(tok, "using %name% = ::| %name%") || + (Token::Match(tok, "using %name% [ [") && + Token::Match(tok->linkAt(2), "] ] = ::| %name%")))) { + std::list scopeList1; + scopeList1.emplace_back("", nullptr); + std::string name = tok->strAt(1); + const Token *nameToken = tok->next(); + std::string scope = getScopeName(scopeList); + Token *usingStart = tok; + Token *start; + if (tok->strAt(2) == "=") + start = tok->tokAt(3); + else + start = tok->linkAt(2)->tokAt(3); + Token *usingEnd = findSemicolon(start); + if (!usingEnd) + continue; + + // Move struct defined in using out of using. + // using T = struct t { }; => struct t { }; using T = struct t; + // fixme: this doesn't handle attributes + if (Token::Match(start, "struct|union|enum %name%| {")) { + if (start->strAt(1) != "{") { + Token *structEnd = start->linkAt(2); + structEnd->insertToken(";", ""); + mTokenList.copyTokens(structEnd->next(), tok, start->next()); + usingStart = structEnd->tokAt(2); + nameToken = usingStart->next(); + if (usingStart->strAt(2) == "=") + start = usingStart->tokAt(3); + else + start = usingStart->linkAt(2)->tokAt(3); + usingEnd = findSemicolon(start); + tok->deleteThis(); + tok->deleteThis(); + tok->deleteThis(); + tok = usingStart; + } else { + Token *structEnd = start->linkAt(1); + structEnd->insertToken(";", ""); + std::string newName; + if (structEnd->strAt(2) == ";") + newName = name; + else + newName = "Unnamed" + MathLib::toString(mTokenizer->mUnnamedCount++); + mTokenList.copyTokens(structEnd->next(), tok, start); + structEnd->tokAt(5)->insertToken(newName, ""); + start->insertToken(newName, ""); + + usingStart = structEnd->tokAt(2); + nameToken = usingStart->next(); + if (usingStart->strAt(2) == "=") + start = usingStart->tokAt(3); + else + start = usingStart->linkAt(2)->tokAt(3); + usingEnd = findSemicolon(start); + tok->deleteThis(); + tok->deleteThis(); + tok->deleteThis(); + tok = usingStart; + } + } + + // Unfortunately we have to start searching from the beginning + // of the token stream because templates are instantiated at + // the end of the token stream and it may be used before then. + std::string scope1; + bool skip = false; // don't erase type aliases we can't parse + for (Token* tok1 = mTokenList.front(); tok1; tok1 = tok1->next()) { + if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || + Token::Match(tok1, "using namespace %name% ;|::")) { + setScopeInfo(tok1, &scopeList1, true); + scope1 = getScopeName(scopeList1); + continue; + } + if (tok1 && tok1->str() == name) { + // skip this using + if (tok1 == nameToken) { + tok1 = findSemicolon(tok1); + continue; + } + // skip other using with this name + if (tok1->strAt(-1) == "using") { + // fixme: this is wrong + // skip to end of scope + if (scopeList1.back().bodyEnd) + tok1 = scopeList1.back().bodyEnd->previous(); + continue; + } + if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) { + // fixme + continue; + } + + // get qualification + std::string qualification; + const Token* tok2 = tok1; + std::string::size_type index = scope.size(); + std::string::size_type new_index = std::string::npos; + bool match = true; + while (tok2->strAt(-1) == "::") { + std::string last; + if (match && !scope1.empty()) { + new_index = scope1.rfind(' ', index - 1); + if (new_index != std::string::npos) + last = scope1.substr(new_index, index - new_index); + else if (!qualification.empty()) + last.clear(); + else + last = scope1; + } else + match = false; + if (match && tok2->strAt(-2) == last) + index = new_index; + else { + if (!qualification.empty()) + qualification = " :: " + qualification; + qualification = tok2->strAt(-2) + qualification; + } + tok2 = tok2->tokAt(-2); + } + + // todo: check using namespace + std::string fullScope1 = scope1; + if (!scope1.empty() && !qualification.empty()) + fullScope1 += " :: "; + fullScope1 += qualification; + + if (scope == fullScope1) { + // remove the qualification + while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) { + tok1->deletePrevious(); + tok1->deletePrevious(); + } + + Token * arrayStart = nullptr; + + // parse the type + Token *type = start; + if (type->str() == "::") { + type = type->next(); + while (Token::Match(type, "%type% ::")) + type = type->tokAt(2); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type% ::")) { + do { + type = type->tokAt(2); + } while (Token::Match(type, "%type% ::")); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type%")) { + while (Token::Match(type, "const|struct|union|enum %type%") || + (type->next() && type->next()->isStandardType())) + type = type->next(); + + type = type->next(); + + while (Token::Match(type, "%type%") && + (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { + type = type->next(); + } + + bool atEnd = false; + while (!atEnd) { + if (type && type->str() == "::") { + type = type->next(); + } + + if (Token::Match(type, "%type%") && + type->next() && !Token::Match(type->next(), "[|;|,|(")) { + type = type->next(); + } else if (Token::simpleMatch(type, "const (")) { + type = type->next(); + atEnd = true; + } else + atEnd = true; + } + } else + syntaxError(type); + + // check for invalid input + if (!type) + syntaxError(tok1); + + // check for template + if (type->str() == "<") { + type = type->findClosingBracket(); + + while (type && Token::Match(type->next(), ":: %type%")) + type = type->tokAt(2); + + if (!type) { + syntaxError(tok1); + } + + while (Token::Match(type->next(), "const|volatile")) + type = type->next(); + + type = type->next(); + } + + // check for pointers and references + std::list pointers; + while (Token::Match(type, "*|&|&&|const")) { + pointers.push_back(type->str()); + type = type->next(); + } + + // check for array + if (type && type->str() == "[") { + do { + if (!arrayStart) + arrayStart = type; + + bool atEnd = false; + while (!atEnd) { + while (type->next() && !Token::Match(type->next(), ";|,")) { + type = type->next(); + } + + if (!type->next()) + syntaxError(type); // invalid input + else if (type->next()->str() == ";") + atEnd = true; + else if (type->str() == "]") + atEnd = true; + else + type = type->next(); + } + + type = type->next(); + } while (type && type->str() == "["); + } + + Token* after = tok1->next(); + // check if type was parsed + if (type && type == usingEnd) { + // check for array syntax and add type around variable + if (arrayStart) { + if (Token::Match(tok1->next(), "%name%")) { + mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous()); + mTokenList.copyTokens(tok1, start, arrayStart->previous()); + tok1->deleteThis(); + substitute = true; + } + } else { + // just replace simple type aliases + mTokenList.copyTokens(tok1, start, usingEnd->previous()); + tok1->deleteThis(); + substitute = true; + } + } else { + skip = true; + if (mSettings->debugwarnings && mErrorLogger) { + std::string str; + for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { + if (!str.empty()) + str += ' '; + str += tok3->str(); + } + str += " ;"; + std::list callstack(1, usingStart); + mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", + + "Failed to parse \'" + str + "\'. The checking continues anyway.", false)); + } + } + tok1 = after; + } + } + } + if (!skip) + usingList.emplace_back(usingStart, usingEnd); + } + } + } + + // delete all used type alias definitions + for (std::list::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) { + Token *usingStart = it->startTok; + Token *usingEnd = it->endTok; + if (usingStart->previous()) { + if (usingEnd->next()) + Token::eraseTokens(usingStart->previous(), usingEnd->next()); + else { + Token::eraseTokens(usingStart, usingEnd); + usingEnd->deleteThis(); + } + } else { + if (usingEnd->next()) { + Token::eraseTokens(usingStart, usingEnd->next()); + usingStart->deleteThis(); + } else { + Token::eraseTokens(usingStart, usingEnd); + usingStart->deleteThis(); + usingEnd->deleteThis(); + } + } + } + + return substitute; +} + void TemplateSimplifier::simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates) @@ -2619,7 +2995,12 @@ void TemplateSimplifier::simplifyTemplates( // Unfortunately the template simplifier doesn't handle namespaces properly so // the uninstantiated template code in the symbol database can't be removed until #8768 // is fixed. + for (int i = 0; i < 2; ++i) { + // it may take more than one pass to simplify type aliases + while (simplifyUsing()) + ; + if (i) { mTemplateDeclarations.clear(); mTemplateForwardDeclarations.clear(); diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 56580b9c1..eda7eb0cc 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -384,6 +384,9 @@ private: const std::string &indent = " ") const; void printOut(const std::string &text = "") const; + bool simplifyUsing(); + + Tokenizer *mTokenizer; TokenList &mTokenList; const Settings *mSettings; ErrorLogger *mErrorLogger; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 162332815..e26744aeb 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8839,7 +8839,9 @@ void Tokenizer::simplifyStructDecl() } } // check for anonymous enum - else if ((Token::simpleMatch(tok, "enum {") && Token::Match(tok->next()->link(), "} %type%| ,|;|[|(|{")) || + else if ((Token::simpleMatch(tok, "enum {") && + !Token::Match(tok->tokAt(-3), "using %name% =") && + Token::Match(tok->next()->link(), "} %type%| ,|;|[|(|{")) || (Token::Match(tok, "enum : %type% {") && Token::Match(tok->linkAt(3), "} %type%| ,|;|[|(|{"))) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } @@ -8949,7 +8951,7 @@ void Tokenizer::simplifyStructDecl() } // don't remove unnamed anonymous unions from a class, struct or union - if (!(!inFunction && tok1->str() == "union")) { + if (!(!inFunction && tok1->str() == "union") && !Token::Match(tok1->tokAt(-3), "using %name% =")) { skip.pop(); tok1->deleteThis(); if (tok1->next() == tok) { diff --git a/lib/tokenize.h b/lib/tokenize.h index f7bf3a306..60248fe64 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -49,6 +49,7 @@ class CPPCHECKLIB Tokenizer { friend class TestSimplifyTokens; friend class TestSimplifyTypedef; + friend class TestSimplifyUsing; friend class TestTokenizer; friend class SymbolDatabase; friend class TestSimplifyTemplate; diff --git a/test/testfiles.pri b/test/testfiles.pri index c6eecd214..5d771fc5f 100644 --- a/test/testfiles.pri +++ b/test/testfiles.pri @@ -42,6 +42,7 @@ SOURCES += $${BASEPATH}/test64bit.cpp \ $${BASEPATH}/testsimplifytemplate.cpp \ $${BASEPATH}/testsimplifytokens.cpp \ $${BASEPATH}/testsimplifytypedef.cpp \ + $${BASEPATH}/testsimplifyusing.cpp \ $${BASEPATH}/testsizeof.cpp \ $${BASEPATH}/teststl.cpp \ $${BASEPATH}/teststring.cpp \ diff --git a/test/testrunner.vcxproj b/test/testrunner.vcxproj old mode 100644 new mode 100755 index 4f9f4bfa0..cd7e68406 --- a/test/testrunner.vcxproj +++ b/test/testrunner.vcxproj @@ -69,6 +69,7 @@ + diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index aceb3f4a0..ddf4a9f9f 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -136,6 +136,7 @@ private: TEST_CASE(template96); // #7854 TEST_CASE(template97); TEST_CASE(template98); // #8959 + TEST_CASE(template99); // #8960 TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) @@ -2083,6 +2084,23 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template99() { // #8960 + const char code[] = "template \n" + "class Base {\n" + "public:\n" + " using ArrayType = std::vector>;\n" + "};\n" + "using A = Base;\n" + "static A::ArrayType array;\n"; + const char exp[] = "class Base ; " + "static std :: vector < Base > array ; " + "class Base { " + "public: " + "} ;"; + + ASSERT_EQUALS(exp, tok(code)); + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp new file mode 100644 index 000000000..3c1bd5664 --- /dev/null +++ b/test/testsimplifyusing.cpp @@ -0,0 +1,417 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2018 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "platform.h" +#include "settings.h" +#include "testsuite.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" + +struct InternalError; + + +class TestSimplifyUsing : public TestFixture { +public: + TestSimplifyUsing() : TestFixture("TestSimplifyUsing") { + } + + +private: + Settings settings0; + Settings settings1; + Settings settings2; + + void run() OVERRIDE { + settings0.addEnabled("style"); + settings2.addEnabled("style"); + + TEST_CASE(simplifyUsing1); + TEST_CASE(simplifyUsing2); + TEST_CASE(simplifyUsing3); + TEST_CASE(simplifyUsing4); + TEST_CASE(simplifyUsing5); + TEST_CASE(simplifyUsing6); + TEST_CASE(simplifyUsing7); + TEST_CASE(simplifyUsing8); + TEST_CASE(simplifyUsing9); + TEST_CASE(simplifyUsing10); + TEST_CASE(simplifyUsing11); + TEST_CASE(simplifyUsing12); + TEST_CASE(simplifyUsing13); + TEST_CASE(simplifyUsing14); + TEST_CASE(simplifyUsing15); + TEST_CASE(simplifyUsing16); + } + + std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { + errout.str(""); + + settings0.inconclusive = true; + settings0.debugwarnings = debugwarnings; + settings0.platform(type); + Tokenizer tokenizer(&settings0, this); + + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + if (simplify) + tokenizer.simplifyTokenList2(); + + return tokenizer.tokens()->stringifyList(0, !simplify); + } + + void simplifyUsing1() { + const char code[] = "class A\n" + "{\n" + "public:\n" + " using duplicate = wchar_t;\n" + " void foo() {}\n" + "};\n" + "using duplicate = A;\n" + "int main()\n" + "{\n" + " duplicate a;\n" + " a.foo();\n" + " A::duplicate c = 0;\n" + "}"; + + const char expected[] = + "class A " + "{ " + "public: " + "" + "void foo ( ) { } " + "} ; " + "int main ( ) " + "{ " + "A a ; " + "a . foo ( ) ; " + "wchar_t c ; c = 0 ; " + "}"; + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing2() { + const char code[] = "class A;\n" + "using duplicate = A;\n" + "class A\n" + "{\n" + "public:\n" + "using duplicate = wchar_t;\n" + "duplicate foo() { wchar_t b; return b; }\n" + "};"; + + const char expected[] = + "class A ; " + "class A " + "{ " + "public: " + "" + "wchar_t foo ( ) { wchar_t b ; return b ; } " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void simplifyUsing3() { + const char code[] = "class A {};\n" + "using duplicate = A;\n" + "wchar_t foo()\n" + "{\n" + "using duplicate = wchar_t;\n" + "duplicate b;\n" + "return b;\n" + "}\n" + "int main()\n" + "{\n" + "duplicate b;\n" + "}"; + + const char expected[] = + "class A { } ; " + "wchar_t foo ( ) " + "{ " + "" + "wchar_t b ; " + "return b ; " + "} " + "int main ( ) " + "{ " + "A b ; " + "}"; + ASSERT_EQUALS(expected, tok(code)); + } + + void simplifyUsing4() { + const char code[] = "using s32 = int;\n" + "using u32 = unsigned int;\n" + "void f()\n" + "{\n" + " s32 ivar = -2;\n" + " u32 uvar = 2;\n" + " return uvar / ivar;\n" + "}"; + + const char expected[] = + "void f ( ) " + "{ " + "int ivar ; ivar = -2 ; " + "unsigned int uvar ; uvar = 2 ; " + "return uvar / ivar ; " + "}"; + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing5() { + const char code[] = + "using YY_BUFFER_STATE = struct yy_buffer_state *;\n" + "void f()\n" + "{\n" + " YY_BUFFER_STATE state;\n" + "}"; + + const char expected[] = + "void f ( ) " + "{ " + "struct yy_buffer_state * state ; " + "}"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing6() { + const char code[] = + "namespace VL {\n" + " using float_t = float;\n" + " inline VL::float_t fast_atan2(VL::float_t y, VL::float_t x){}\n" + "}"; + + const char expected[] = + "namespace VL { " + "" + "float fast_atan2 ( float y , float x ) { } " + "}"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing7() { + const char code[] = "using abc = int; " + "Fred :: abc f ;"; + const char expected[] = "Fred :: abc f ;"; + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing8() { + const char code[] = "using INT = int;\n" + "using UINT = unsigned int;\n" + "using PINT = int *;\n" + "using PUINT = unsigned int *;\n" + "using RINT = int &;\n" + "using RUINT = unsigned int &;\n" + "using RCINT = const int &;\n" + "using RCUINT = const unsigned int &;\n" + "INT ti;\n" + "UINT tui;\n" + "PINT tpi;\n" + "PUINT tpui;\n" + "RINT tri;\n" + "RUINT trui;\n" + "RCINT trci;\n" + "RCUINT trcui;"; + + const char expected[] = + "int ti ; " + "unsigned int tui ; " + "int * tpi ; " + "unsigned int * tpui ; " + "int & tri ; " + "unsigned int & trui ; " + "const int & trci ; " + "const unsigned int & trcui ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing9() { + const char code[] = "using S = struct s;\n" + "using PS = S *;\n" + "using T = struct t { int a; };\n" + "using TP = T *;\n" + "using U = struct { int a; };\n" + "using V = U *;\n" + "using W = struct { int a; } *;\n" + "S s;\n" + "PS ps;\n" + "T t;\n" + "TP tp;\n" + "U u;\n" + "V v;\n" + "W w;"; + + const char expected[] = + "struct t { int a ; } ; " + "struct U { int a ; } ; " + "struct Unnamed0 { int a ; } ; " + "struct s s ; " + "struct s * ps ; " + "struct t t ; " + "struct t * tp ; " + "struct U u ; " + "struct U * v ; " + "struct Unnamed0 * w ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing10() { + const char code[] = "using S = union s;\n" + "using PS = S *;\n" + "using T = union t { int a; float b ; };\n" + "using TP = T *;\n" + "using U = union { int a; float b; };\n" + "using V = U *;\n" + "using W = union { int a; float b; } *;\n" + "S s;\n" + "PS ps;\n" + "T t;\n" + "TP tp;\n" + "U u;\n" + "V v;\n" + "W w;"; + + const char expected[] = + "union t { int a ; float b ; } ; " + "union U { int a ; float b ; } ; " + "union Unnamed0 { int a ; float b ; } ; " + "union s s ; " + "union s * ps ; " + "union t t ; " + "union t * tp ; " + "union U u ; " + "union U * v ; " + "union Unnamed0 * w ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing11() { + const char code[] = "using abc = enum { a = 0 , b = 1 , c = 2 };\n" + "using XYZ = enum xyz { x = 0 , y = 1 , z = 2 };\n" + "abc e1;\n" + "XYZ e2;"; + + const char expected[] = "enum abc { a = 0 , b = 1 , c = 2 } ; " + "enum xyz { x = 0 , y = 1 , z = 2 } ; " + "enum abc e1 ; " + "enum xyz e2 ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing12() { + const char code[] = "using V1 = vector;\n" + "using V2 = std::vector;\n" + "using V3 = std::vector >;\n" + "using IntListIterator = std::list::iterator;\n" + "V1 v1;\n" + "V2 v2;\n" + "V3 v3;\n" + "IntListIterator iter;"; + + const char expected[] = "vector < int > v1 ; " + "std :: vector < int > v2 ; " + "std :: vector < std :: vector < int > > v3 ; " + "std :: list < int > :: iterator iter ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing13() { + const char code[] = "using Func = std::pair;\n" + "using CallQueue = std::vector;\n" + "int main() {\n" + " CallQueue q;\n" + "}"; + + const char expected[] = "int main ( ) { " + "std :: vector < std :: pair < int ( * ) ( void * ) , void * > > q ; " + "}"; + + ASSERT_EQUALS(expected, tok(code, false)); + ASSERT_EQUALS("", errout.str()); + } + + void simplifyUsing14() { + const char code[] = "template struct E" + "{" + " using v = E0)?(N-1):0>;" + " using val = typename add::val;" + " FP_M(val);" + "};" + "template struct E " + "{" + " using nal = typename D<1>::val;" + " FP_M(val);" + "};"; + + tok(code, true, Settings::Native, false); + ASSERT_EQUALS("", errout.str()); + } + + void simplifyUsing15() { + { + const char code[] = "using frame = char [10];\n" + "frame f;"; + + const char expected[] = "char f [ 10 ] ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + { + const char code[] = "using frame = unsigned char [10];\n" + "frame f;"; + + const char expected[] = "unsigned char f [ 10 ] ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + } + + void simplifyUsing16() { + const char code[] = "using MOT8 = char;\n" + "using CHFOO = MOT8 [4096];\n" + "using STRFOO = struct {\n" + " CHFOO freem;\n" + "};\n" + "STRFOO s;"; + + const char expected[] = "struct STRFOO { " + "char freem [ 4096 ] ; " + "} ; " + "struct STRFOO s ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + ASSERT_EQUALS("", errout.str()); + } + +}; + +REGISTER_TEST(TestSimplifyUsing) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 78f2d4706..395702349 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -382,12 +382,6 @@ private: TEST_CASE(auto10); // #8020 TEST_CASE(unionWithConstructor); - - TEST_CASE(using1); - TEST_CASE(using2); // #8331 (segmentation fault) - TEST_CASE(using3); // #8343 (segmentation fault) - TEST_CASE(using4); // #8952 (unknown type) - TEST_CASE(using5); // #8950 (couldn't resolve all user defined types) } void array() { @@ -6806,138 +6800,6 @@ private: f = Token::findsimplematch(tokenizer.tokens(), "Fred ( float"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); } - - void using1() { - const Standards::cppstd_t original_std = settings1.standards.cpp; - settings1.standards.cpp = Standards::CPP11; - GET_SYMBOL_DB("using INT = int;\n" - "using PINT = INT *;\n" - "using PCINT = const PINT;\n" - "INT i;\n" - "PINT pi;\n" - "PCINT pci;"); - settings1.standards.cpp = original_std; - const Token *tok = Token::findsimplematch(tokenizer.tokens(), "INT i ;"); - - ASSERT(db && tok && tok->next() && tok->next()->valueType()); - if (db && tok && tok->next() && tok->next()->valueType()) { - tok = tok->next(); - ASSERT_EQUALS(0, tok->valueType()->constness); - ASSERT_EQUALS(0, tok->valueType()->pointer); - ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); - ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); - } - - tok = Token::findsimplematch(tokenizer.tokens(), "PINT pi ;"); - - ASSERT(db && tok && tok->next() && tok->next()->valueType()); - if (db && tok && tok->next() && tok->next()->valueType()) { - tok = tok->next(); - ASSERT_EQUALS(0, tok->valueType()->constness); - ASSERT_EQUALS(1, tok->valueType()->pointer); - ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); - ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); - } - - tok = Token::findsimplematch(tokenizer.tokens(), "PCINT pci ;"); - - ASSERT(db && tok && tok->next() && tok->next()->valueType()); - if (db && tok && tok->next() && tok->next()->valueType()) { - tok = tok->next(); - ASSERT_EQUALS(1, tok->valueType()->constness); - ASSERT_EQUALS(1, tok->valueType()->pointer); - ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); - ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); - } - } - - void using2() { // #8331 (segmentation fault) - const Standards::cppstd_t original_std = settings1.standards.cpp; - settings1.standards.cpp = Standards::CPP11; - - { - GET_SYMBOL_DB("using pboolean = pboolean;\n" - "pboolean b;"); - const Token *tok = Token::findsimplematch(tokenizer.tokens(), "b ;"); - - ASSERT(db && tok && !tok->valueType()); - } - - { - GET_SYMBOL_DB("using pboolean = bool;\n" - "using pboolean = pboolean;\n" - "pboolean b;"); - const Token *tok = Token::findsimplematch(tokenizer.tokens(), "b ;"); - - ASSERT(db && tok && tok->valueType()); - if (db && tok && tok->valueType()) { - ASSERT_EQUALS(0, tok->valueType()->constness); - ASSERT_EQUALS(0, tok->valueType()->pointer); - ASSERT_EQUALS(ValueType::UNKNOWN_SIGN, tok->valueType()->sign); - ASSERT_EQUALS(ValueType::BOOL, tok->valueType()->type); - } - } - - settings1.standards.cpp = original_std; - } - - void using3() { // #8343 (segmentation fault) - const Standards::cppstd_t original_std = settings1.standards.cpp; - settings1.standards.cpp = Standards::CPP11; - GET_SYMBOL_DB("template \n" - "using vector = typename MemoryModel::template vector;\n" - "vector m_bits;"); - settings1.standards.cpp = original_std; - - ASSERT(db != nullptr); - ASSERT_EQUALS("", errout.str()); - } - - void using4() { // #8952 (unknown type) - const Standards::cppstd_t original_std = settings1.standards.cpp; - settings1.standards.cpp = Standards::CPP11; - settings1.debugwarnings = true; - GET_SYMBOL_DB("class A {\n" - "public:\n" - " enum Type { Null };\n" - "};\n" - "using V = A;\n" - "V::Type value;"); - settings1.standards.cpp = original_std; - settings1.debugwarnings = false; - - vartok = Token::findsimplematch(tokenizer.tokens(), "value"); - ASSERT(db && vartok && vartok->valueType()); - if (db && vartok && vartok->valueType()) { - ASSERT_EQUALS(0, vartok->valueType()->constness); - ASSERT_EQUALS(0, vartok->valueType()->pointer); - ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); - ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); - const Variable *var = vartok->variable(); - ASSERT(var && var->type() && var->type()->isEnumType()); - } - } - - void using5() { // #8950 (couldn't resolve all user defined types) - const Standards::cppstd_t original_std = settings1.standards.cpp; - settings1.standards.cpp = Standards::CPP11; - settings1.debugwarnings = true; - GET_SYMBOL_DB("class A {\n" - "public:\n" - " using MapType = std::map;\n" - "private:\n" - " MapType m;\n" - "};"); - settings1.standards.cpp = original_std; - settings1.debugwarnings = false; - - ASSERT_EQUALS("", errout.str()); - ASSERT(db && db->scopeList.size() == 2); // global + class - if (db && db->scopeList.size() == 2) { - const Scope *scope = &db->scopeList.back(); - ASSERT(scope->type == Scope::eClass && scope->definedType && scope->definedType->needInitialization == Type::False); - } - } }; REGISTER_TEST(TestSymbolDatabase)