From 97d3d9ac0946f3be0e036b70401352e3ddaeb8fc Mon Sep 17 00:00:00 2001
From: Orgad Shaneh <orgad.shaneh@audiocodes.com>
Date: Sun, 7 Jun 2015 11:47:50 +0300
Subject: [PATCH] C++: Support default template argument lookup for
 specialization

This fixes std::vector, although it doesn't really resolve numeric
template arguments. It just picks the first specialization.

Use-case:
class Foo {};
template<class T1 = Foo> class Temp;
template<> class Temp<Foo> { int var; };
void func()
{
    Temp<> t;
    t.var; // var not highlighted
}

Task-number: QTCREATORBUG-8922
Change-Id: I593515beb3a6d901b6088db8bc1b8e16c39083d3
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
---
 src/libs/cplusplus/LookupContext.cpp        | 94 ++++++++++++---------
 src/libs/cplusplus/LookupContext.h          |  7 +-
 src/plugins/cpptools/cppcompletion_test.cpp | 13 +++
 3 files changed, 74 insertions(+), 40 deletions(-)

diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index 031afb4004b..38093f8b47b 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -569,7 +569,7 @@ private:
 
     LookupScopePrivate *nestedType(const Name *name, LookupScopePrivate *origin);
 
-    LookupScopePrivate *findSpecialization(const TemplateNameId *templId,
+    LookupScopePrivate *findSpecialization(const Template *baseTemplate, const TemplateNameId *templId,
                                            const TemplateNameIdTable &specializations,
                                            LookupScopePrivate *origin);
 
@@ -1201,28 +1201,25 @@ static bool matchTypes(const FullySpecifiedType &instantiation,
 }
 
 LookupScopePrivate *LookupScopePrivate::findSpecialization(
+        const Template *baseTemplate,
         const TemplateNameId *templId,
         const TemplateNameIdTable &specializations,
         LookupScopePrivate *origin)
 {
+    Clone cloner(_factory->control().data());
     for (TemplateNameIdTable::const_iterator cit = specializations.begin();
          cit != specializations.end(); ++cit) {
         const TemplateNameId *specializationNameId = cit->first;
         const unsigned specializationTemplateArgumentCount
                 = specializationNameId->templateArgumentCount();
-        const unsigned initializationTemplateArgumentCount = templId->templateArgumentCount();
-        // for now it works only when we have the same number of arguments in specialization
-        // and initialization(in future it should be more clever)
-        if (specializationTemplateArgumentCount != initializationTemplateArgumentCount)
-            continue;
+        Subst subst(_factory->control().data());
         bool match = true;
-        for (unsigned i = 0; i < initializationTemplateArgumentCount && match; ++i) {
+        for (unsigned i = 0; i < specializationTemplateArgumentCount && match; ++i) {
             const FullySpecifiedType &specializationTemplateArgument
                     = specializationNameId->templateArgumentAt(i);
-            FullySpecifiedType initializationTemplateArgument = templId->templateArgumentAt(i);
-            TypeResolver typeResolver(*_factory);
-            Scope *scope = 0;
-            typeResolver.resolve(&initializationTemplateArgument, &scope, origin ? origin->q : 0);
+            FullySpecifiedType initializationTemplateArgument =
+                    _factory->resolveTemplateArgument(cloner, subst, origin ? origin->q : 0,
+                                                      baseTemplate, templId, i);
             // specialization and initialization argument have to be a pointer
             // additionally type of pointer argument of specialization has to be namedType
             if (findTemplateArgument(dereference(specializationTemplateArgument), cit->second)) {
@@ -1340,8 +1337,17 @@ LookupScopePrivate *LookupScopePrivate::nestedType(const Name *name, LookupScope
                 // we found full specialization
                 reference = cit->second;
             } else {
+                Template *baseTemplate = 0;
+                foreach (Symbol *s, reference->_symbols) {
+                    if (Class *clazz = s->asClass())
+                        baseTemplate = clazz->enclosingTemplate();
+                    else if (ForwardClassDeclaration *forward = s->asForwardClassDeclaration())
+                        baseTemplate = forward->enclosingTemplate();
+                    if (baseTemplate)
+                        break;
+                }
                 if (LookupScopePrivate *specialization =
-                        findSpecialization(templId, specializations, origin)) {
+                        findSpecialization(baseTemplate, templId, specializations, origin)) {
                     reference = specialization;
                     if (Q_UNLIKELY(debug)) {
                         Overview oo;
@@ -2088,40 +2094,50 @@ bool CreateBindings::visit(ObjCMethod *)
     return false;
 }
 
-void CreateBindings::initializeSubst(Clone &cloner,
-                                     Subst &subst,
-                                     LookupScope *origin,
-                                     Template *specialization,
-                                     const TemplateNameId *instantiation)
+FullySpecifiedType CreateBindings::resolveTemplateArgument(Clone &cloner,
+                                                           Subst &subst,
+                                                           LookupScope *origin,
+                                                           const Template *specialization,
+                                                           const TemplateNameId *instantiation,
+                                                           unsigned index)
 {
-    const unsigned argumentCountOfInitialization = instantiation->templateArgumentCount();
-    const unsigned argumentCountOfSpecialization = specialization->templateParameterCount();
+    FullySpecifiedType ty;
+
+    const TypenameArgument *tParam
+            = specialization->templateParameterAt(index)->asTypenameArgument();
+    if (!tParam)
+        return ty;
 
+    if (index < instantiation->templateArgumentCount())
+        ty = instantiation->templateArgumentAt(index);
+    else
+        ty = cloner.type(tParam->type(), &subst);
+
+    TypeResolver typeResolver(*this);
+    Scope *resolveScope = specialization->enclosingScope();
+    typeResolver.resolve(&ty, &resolveScope, origin);
     const TemplateNameId *templSpecId = specialization->name()->asTemplateNameId();
     const unsigned templSpecArgumentCount = templSpecId ? templSpecId->templateArgumentCount() : 0;
-    for (unsigned i = 0; i < argumentCountOfSpecialization; ++i) {
-        const TypenameArgument *tParam
-                = specialization->templateParameterAt(i)->asTypenameArgument();
-        if (!tParam)
-            continue;
-        const Name *name = tParam->name();
-        if (!name)
-            continue;
+    if (index < templSpecArgumentCount && templSpecId->templateArgumentAt(index)->isPointerType()) {
+        if (PointerType *pointerType = ty->asPointerType())
+            ty = pointerType->elementType();
+    }
 
-        FullySpecifiedType ty = (i < argumentCountOfInitialization) ?
-                    instantiation->templateArgumentAt(i):
-                    cloner.type(tParam->type(), &subst);
+    if (const Name *name = tParam->name())
+        subst.bind(cloner.name(name, &subst), ty);
+    return ty;
+}
 
-        TypeResolver typeResolver(*this);
-        Scope *resolveScope = specialization->enclosingScope();
-        typeResolver.resolve(&ty, &resolveScope, origin);
-        if (i < templSpecArgumentCount && templSpecId->templateArgumentAt(i)->isPointerType()) {
-            if (PointerType *pointerType = ty->asPointerType())
-                ty = pointerType->elementType();
-        }
+void CreateBindings::initializeSubst(Clone &cloner,
+                                     Subst &subst,
+                                     LookupScope *origin,
+                                     const Template *specialization,
+                                     const TemplateNameId *instantiation)
+{
+    const unsigned argumentCountOfSpecialization = specialization->templateParameterCount();
 
-        subst.bind(cloner.name(name, &subst), ty);
-    }
+    for (unsigned i = 0; i < argumentCountOfSpecialization; ++i)
+        resolveTemplateArgument(cloner, subst, origin, specialization, instantiation, i);
 }
 
 } // namespace CPlusPlus
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index f68ff829057..0308388a3d5 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -150,8 +150,13 @@ public:
     /// \internal
     LookupScope *allocLookupScope(LookupScope *parent, const Name *name);
 
+    FullySpecifiedType resolveTemplateArgument(Clone &cloner, Subst &subst,
+                                               LookupScope *origin,
+                                               const Template *specialization,
+                                               const TemplateNameId *instantiation,
+                                               unsigned index);
     void initializeSubst(Clone &cloner, Subst &subst, LookupScope *origin,
-                         Template *specialization, const TemplateNameId *instantiation);
+                         const Template *specialization, const TemplateNameId *instantiation);
 
 protected:
     using SymbolVisitor::visit;
diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp
index 563a6b9a1ce..3bf0904c652 100644
--- a/src/plugins/cpptools/cppcompletion_test.cpp
+++ b/src/plugins/cpptools/cppcompletion_test.cpp
@@ -2689,6 +2689,19 @@ void CppToolsPlugin::test_completion_data()
         << QLatin1String("f")
         << QLatin1String("and_type"));
 
+    QTest::newRow("specialization_with_default_value") << _(
+            "class Foo {};\n"
+            "template<class T1 = Foo> class Temp;\n"
+            "template<> class Temp<Foo> { int var; };\n"
+            "void func()\n"
+            "{\n"
+            "    Temp<> t;\n"
+            "    @\n"
+            "}\n"
+     ) << _("t.") << (QStringList()
+        << QLatin1String("var")
+        << QLatin1String("Temp"));
+
     QTest::newRow("auto_declaration_in_if_condition") << _(
             "struct Foo { int bar; };\n"
             "void fun() {\n"
-- 
GitLab