Skip to content

Commit 71c2a23

Browse files
committed
Check namespace first
1 parent 7ef5b93 commit 71c2a23

2 files changed

Lines changed: 118 additions & 7 deletions

File tree

‎lib/templatesimplifier.cpp‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,12 +2107,26 @@ void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, c
21072107
// decide if namespace needs to be inserted in or appended to token list
21082108
const bool insert = tokStart != tok;
21092109

2110+
std::string scope = templateDeclaration.scope();
2111+
if (insert) {
2112+
// the qualification that is already there: "C" or "N :: C" for "N :: C :: m"
2113+
std::string existing;
2114+
for (const Token* tok2 = tokStart; tok2 != tok->previous(); tok2 = tok2->next())
2115+
existing += (existing.empty() ? "" : " ") + tok2->str();
2116+
// fully qualified already: nothing to add
2117+
if (scope == existing)
2118+
return;
2119+
// partially qualified: only the missing leading scopes are added
2120+
if (endsWith(scope, " :: " + existing))
2121+
scope.erase(scope.size() - existing.size() - 4);
2122+
}
2123+
21102124
std::string::size_type start = 0;
21112125
std::string::size_type end = 0;
21122126
bool inTemplate = false;
21132127
int level = 0;
2114-
while ((end = templateDeclaration.scope().find(' ', start)) != std::string::npos) {
2115-
std::string token = templateDeclaration.scope().substr(start, end - start);
2128+
while ((end = scope.find(' ', start)) != std::string::npos) {
2129+
std::string token = scope.substr(start, end - start);
21162130
// done if scopes overlap
21172131
if (token == tokStart->str() && tok->strAt(-1) != "::")
21182132
break;
@@ -2139,19 +2153,19 @@ void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, c
21392153
start = end + 1;
21402154
}
21412155
// don't add if it already exists
2142-
std::string token = templateDeclaration.scope().substr(start, end - start);
2156+
std::string token = scope.substr(start, end - start);
21432157
if (token != tokStart->str() || tok->strAt(-1) != "::") {
21442158
if (insert) {
21452159
if (!inTemplate)
2146-
mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope().substr(start));
2160+
mTokenList.back()->tokAt(offset)->insertToken(scope.substr(start));
21472161
else
2148-
mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + templateDeclaration.scope().substr(start));
2162+
mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + scope.substr(start));
21492163
mTokenList.back()->tokAt(offset)->insertToken("::");
21502164
} else {
21512165
if (!inTemplate)
2152-
mTokenList.addtoken(templateDeclaration.scope().substr(start), tok->linenr(), tok->column(), tok->fileIndex());
2166+
mTokenList.addtoken(scope.substr(start), tok->linenr(), tok->column(), tok->fileIndex());
21532167
else
2154-
mTokenList.back()->str(mTokenList.back()->str() + templateDeclaration.scope().substr(start));
2168+
mTokenList.back()->str(mTokenList.back()->str() + scope.substr(start));
21552169
mTokenList.addtoken("::", tok->linenr(), tok->column(), tok->fileIndex());
21562170
}
21572171
}

‎test/testsimplifytemplate.cpp‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ class TestSimplifyTemplate : public TestFixture {
316316
TEST_CASE(templateTypeDeductionFullRebuild); // --template-full-rebuild gives the same result
317317
TEST_CASE(templateTypeDeductionMultiPass); // a pending deduction survives later simplifier passes
318318
TEST_CASE(templateTypeDeductionNested); // calls inside a body created by deduction are deduced too
319+
TEST_CASE(templateTypeDeductionOutOfLine); // member template defined out of line, class in a namespace
319320
TEST_CASE(templateTypeDeductionEnum); // enum arguments deduce the enum, not its underlying type
320321
TEST_CASE(templateTypeDeductionArray); // array arguments: only the decayed dimension is a pointer
321322
TEST_CASE(templateTypeDeductionConstPointer); // "const T *": the parameter provides the pointee const
@@ -7324,6 +7325,102 @@ class TestSimplifyTemplate : public TestFixture {
73247325
}
73257326
}
73267327

7328+
void templateTypeDeductionOutOfLine()
7329+
{ // the out of line definition is already qualified with the class and the
7330+
// namespace - the instantiation must not get the qualification a second time,
7331+
// otherwise the body is not a member function body and the members used in it
7332+
// get no variable id
7333+
{
7334+
const char code[] = "namespace N {\n"
7335+
"struct C {\n"
7336+
" template<class T> T m(T t);\n"
7337+
" int x;\n"
7338+
" int use(int i) { return m(i); }\n"
7339+
"};\n"
7340+
"}\n"
7341+
"template<class T> T N::C::m(T t) { return t + x; }";
7342+
// the incremental setVarId() gives the new tokens ids above the existing ones
7343+
const char exp[] = "namespace N { "
7344+
"struct C { "
7345+
"int m<int> ( int t@5 ) ; "
7346+
"int x@2 ; "
7347+
"int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7348+
"} ; "
7349+
"} "
7350+
"int N :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }";
7351+
ASSERT_EQUALS(exp, tokVarId(code));
7352+
const char expFullRebuild[] = "namespace N { "
7353+
"struct C { "
7354+
"int m<int> ( int t@1 ) ; "
7355+
"int x@2 ; "
7356+
"int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7357+
"} ; "
7358+
"} "
7359+
"int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }";
7360+
ASSERT_EQUALS(expFullRebuild, tokVarId(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7361+
}
7362+
{ // explicit template arguments: same expansion
7363+
const char code[] = "namespace N {\n"
7364+
"struct C {\n"
7365+
" template<class T> T m(T t);\n"
7366+
" int x;\n"
7367+
" int use(int i) { return m<int>(i); }\n"
7368+
"};\n"
7369+
"}\n"
7370+
"template<class T> T N::C::m(T t) { return t + x; }";
7371+
const char exp[] = "namespace N { "
7372+
"struct C { "
7373+
"int m<int> ( int t@1 ) ; "
7374+
"int x@2 ; "
7375+
"int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7376+
"} ; "
7377+
"} "
7378+
"int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }";
7379+
ASSERT_EQUALS(exp, tokVarId(code));
7380+
}
7381+
{ // the definition is qualified with the class only: the namespace is added
7382+
// (the instantiation is appended after the namespace, like all instantiations)
7383+
const char code[] = "namespace N {\n"
7384+
"struct C {\n"
7385+
" template<class T> T m(T t);\n"
7386+
" int x;\n"
7387+
" int use(int i) { return m(i); }\n"
7388+
"};\n"
7389+
"template<class T> T C::m(T t) { return t + x; }\n"
7390+
"}";
7391+
const char exp[] = "namespace N { "
7392+
"struct C { "
7393+
"int m<int> ( int t@1 ) ; "
7394+
"int x@2 ; "
7395+
"int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7396+
"} ; "
7397+
"} "
7398+
"int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }";
7399+
ASSERT_EQUALS(exp, tokVarId(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7400+
ASSERT(tokVarId(code).find("int N :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }") != std::string::npos);
7401+
}
7402+
{ // three levels of nesting
7403+
const char code[] = "namespace A { namespace B {\n"
7404+
"struct C {\n"
7405+
" template<class T> T m(T t);\n"
7406+
" int x;\n"
7407+
" int use(int i) { return m(i); }\n"
7408+
"};\n"
7409+
"} }\n"
7410+
"template<class T> T A::B::C::m(T t) { return t + x; }";
7411+
const char exp[] = "namespace A { namespace B { "
7412+
"struct C { "
7413+
"int m<int> ( int t@1 ) ; "
7414+
"int x@2 ; "
7415+
"int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7416+
"} ; "
7417+
"} } "
7418+
"int A :: B :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }";
7419+
ASSERT_EQUALS(exp, tokVarId(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7420+
ASSERT(tokVarId(code).find("int A :: B :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }") != std::string::npos);
7421+
}
7422+
}
7423+
73277424
void templateTypeDeductionEnum()
73287425
{
73297426
{ // an enum variable has the ValueType of the underlying integer type - the enum

0 commit comments

Comments
 (0)