diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 3919650bf..02ffbb134 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -3690,7 +3690,7 @@ void TemplateSimplifier::simplifyTemplates( if (mTemplateDeclarations.empty() && mTemplateForwardDeclarations.empty()) return; - if (passCount != 0 && mSettings->debugtemplate && mSettings->debugnormal) { + if (mSettings->debugtemplate && mSettings->debugnormal) { std::string title("Template Simplifier pass " + std::to_string(passCount + 1)); mTokenList.front()->printOut(title.c_str(), mTokenList.getFiles()); } diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 0ef905e48..e0ae1dc36 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1702,6 +1702,7 @@ namespace { const Token * const bodyStart; const Token * const bodyEnd; std::set usingNamespaces; + std::set recordTypes; }; std::string getScopeName(const std::list &scopeInfo) @@ -1778,12 +1779,16 @@ namespace { return; } + const bool record = Token::Match(tok, "class|struct|union %name%"); tok = tok->next(); std::string classname = tok->str(); while (Token::Match(tok, "%name% :: %name%")) { tok = tok->tokAt(2); classname += " :: " + tok->str(); } + // add record type to scope info + if (record) + scopeInfo->back().recordTypes.insert(classname); tok = tok->next(); if (tok && tok->str() == ":") { while (tok && !Token::Match(tok, ";|{")) @@ -1968,6 +1973,11 @@ bool Tokenizer::simplifyUsing() // skip template declarations if (Token::Match(tok, "template < !!>")) { + // add template record type to scope info + const Token *end = tok->next()->findClosingBracket(); + if (end && Token::Match(end->next(), "class|struct|union %name%")) + scopeList.back().recordTypes.insert(end->strAt(2)); + Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); if (endToken) tok = endToken; @@ -2088,7 +2098,9 @@ bool Tokenizer::simplifyUsing() // remove the qualification std::string fullScope = scope; + std::string removed; while (tok1->strAt(-1) == "::") { + removed = (tok1->strAt(-2) + " :: ") + removed; if (fullScope == tok1->strAt(-2)) { tok1->deletePrevious(); tok1->deletePrevious(); @@ -2220,6 +2232,32 @@ bool Tokenizer::simplifyUsing() substitute = true; } } else { + // add some qualification back if needed + std::string removed1 = removed; + std::string::size_type idx = removed1.rfind(" ::"); + if (idx != std::string::npos) + removed1.resize(idx); + if (removed1 == scope && !removed1.empty()) { + for (std::list::const_reverse_iterator it = scopeList.crbegin(); it != scopeList.crend(); ++it) { + if (it->recordTypes.find(start->str()) != it->recordTypes.end()) { + std::string::size_type spaceIdx = 0; + std::string::size_type startIdx = 0; + while ((spaceIdx = removed1.find(" ", startIdx)) != std::string::npos) { + tok1->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx).c_str()); + startIdx = spaceIdx + 1; + } + tok1->previous()->insertToken(removed1.substr(startIdx).c_str()); + tok1->previous()->insertToken("::"); + break; + } + idx = removed1.rfind(" ::"); + if (idx == std::string::npos) + break; + + removed1.resize(idx); + } + } + // just replace simple type aliases TokenList::copyTokens(tok1, start, usingEnd->previous()); tok1->deleteThis(); diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 2bd5f203c..722eacd6b 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -207,7 +207,7 @@ private: TEST_CASE(template162); TEST_CASE(template163); // #9685 syntax error TEST_CASE(template164); // #9394 - TEST_CASE(template162); // #10032 syntax error + TEST_CASE(template165); // #10032 syntax error 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) diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 58eb8cfb9..c1fbc8252 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -76,6 +76,7 @@ private: TEST_CASE(simplifyUsing9518); TEST_CASE(simplifyUsing9757); TEST_CASE(simplifyUsing10008); + TEST_CASE(simplifyUsing10054); } std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { @@ -663,6 +664,205 @@ private: "}"; ASSERT_EQUALS(exp, tok(code, false)); } + + void simplifyUsing10054() { // debug: Executable scope 'x' with unknown function. + { + // original example: using "namespace external::ns1;" but redundant qualification + const char code[] = "namespace external {\n" + " namespace ns1 {\n" + " template struct B { };\n" + " using V = B;\n" + " }\n" + "}\n" + "namespace ns {\n" + " struct A {\n" + " void f(external::ns1::V);\n" + " };\n" + "}\n" + "using namespace external::ns1;\n" + "namespace ns {\n" + " void A::f(external::ns1::V) {}\n" + "}"; + const char exp[] = "namespace external { " + "namespace ns1 { " + "struct B<1> ; " + "} " + "} " + "namespace ns { " + "struct A { " + "void f ( external :: ns1 :: B<1> ) ; " + "} ; " + "} " + "using namespace external :: ns1 ; " + "namespace ns { " + "void A :: f ( external :: ns1 :: B<1> ) { } " + "} " + "struct external :: ns1 :: B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + // no using "namespace external::ns1;" + const char code[] = "namespace external {\n" + " namespace ns1 {\n" + " template struct B { };\n" + " using V = B;\n" + " }\n" + "}\n" + "namespace ns {\n" + " struct A {\n" + " void f(external::ns1::V);\n" + " };\n" + "}\n" + "namespace ns {\n" + " void A::f(external::ns1::V) {}\n" + "}"; + const char exp[] = "namespace external { " + "namespace ns1 { " + "struct B<1> ; " + "} " + "} " + "namespace ns { " + "struct A { " + "void f ( external :: ns1 :: B<1> ) ; " + "} ; " + "} " + "namespace ns { " + "void A :: f ( external :: ns1 :: B<1> ) { } " + "} " + "struct external :: ns1 :: B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + // using "namespace external::ns1;" without redundant qualification + const char code[] = "namespace external {\n" + " namespace ns1 {\n" + " template struct B { };\n" + " using V = B;\n" + " }\n" + "}\n" + "namespace ns {\n" + " struct A {\n" + " void f(external::ns1::V);\n" + " };\n" + "}\n" + "using namespace external::ns1;\n" + "namespace ns {\n" + " void A::f(V) {}\n" + "}"; + const char exp[] = "namespace external { " + "namespace ns1 { " + "struct B<1> ; " + "} " + "} " + "namespace ns { " + "struct A { " + "void f ( external :: ns1 :: B<1> ) ; " + "} ; " + "} " + "using namespace external :: ns1 ; " + "namespace ns { " + "void A :: f ( external :: ns1 :: B<1> ) { } " + "} " + "struct external :: ns1 :: B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + // using "namespace external::ns1;" without redundant qualification on declaration and definition + const char code[] = "namespace external {\n" + " namespace ns1 {\n" + " template struct B { };\n" + " using V = B;\n" + " }\n" + "}\n" + "using namespace external::ns1;\n" + "namespace ns {\n" + " struct A {\n" + " void f(V);\n" + " };\n" + "}\n" + "namespace ns {\n" + " void A::f(V) {}\n" + "}"; + const char exp[] = "namespace external { " + "namespace ns1 { " + "struct B<1> ; " + "} " + "} " + "using namespace external :: ns1 ; " + "namespace ns { " + "struct A { " + "void f ( external :: ns1 :: B<1> ) ; " + "} ; " + "} " + "namespace ns { " + "void A :: f ( external :: ns1 :: B<1> ) { } " + "} " + "struct external :: ns1 :: B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + const char code[] = "namespace external {\n" + " template struct B { };\n" + " namespace ns1 {\n" + " using V = B;\n" + " }\n" + "}\n" + "namespace ns {\n" + " struct A {\n" + " void f(external::ns1::V);\n" + " };\n" + "}\n" + "namespace ns {\n" + " void A::f(external::ns1::V) {}\n" + "}"; + const char exp[] = "namespace external { " + "struct B<1> ; " + "} " + "namespace ns { " + "struct A { " + "void f ( external :: B<1> ) ; " + "} ; " + "} " + "namespace ns { " + "void A :: f ( external :: B<1> ) { } " + "} " + "struct external :: B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + { + const char code[] = "template struct B { };\n" + "namespace external {\n" + " namespace ns1 {\n" + " using V = B;\n" + " }\n" + "}\n" + "namespace ns {\n" + " struct A {\n" + " void f(external::ns1::V);\n" + " };\n" + "}\n" + "namespace ns {\n" + " void A::f(external::ns1::V) {}\n" + "}"; + const char exp[] = "struct B<1> ; " + "namespace ns { " + "struct A { " + "void f ( B<1> ) ; " + "} ; " + "} " + "namespace ns { " + "void A :: f ( B<1> ) { } " + "} " + "struct B<1> { } ;"; + ASSERT_EQUALS(exp, tok(code, true)); + ASSERT_EQUALS("", errout.str()); + } + } }; REGISTER_TEST(TestSimplifyUsing)