From 997ab425ce5cb0797101acadcfe9217ea1aa6813 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh <orgad.shaneh@audiocodes.com> Date: Fri, 5 Jun 2015 17:08:44 +0300 Subject: [PATCH] C++: Improve accuracy in findSpecialization * If a template type is specialized as a pointer, accept only pointers (of any type) * Same for references and arrays * Only if the specialized type is not part of the template, match it against the input. Fixes resolving of partial specialization with pointers. Use-cases: // 1 struct b {}; struct a : b {}; template<class X, class Y> struct s { float f; }; template<class X> struct s<X, b*> { int i; }; template<class X> struct s<X, a*> { char j; }; void f() { s<int, a*> var; var.j; // j not highlighted } // 2 template <typename T> struct Temp { T variable; }; template <typename T> struct Temp<T &> { T reference; }; void func() { Temp<int&> templ; templ.reference; // reference not highlighted } // 3 class false_type {}; class true_type {}; template<class T1, class T2> class and_type { false_type f; }; template<> class and_type<true_type, true_type> { true_type t; }; void func2() { and_type<true_type, false_type> a; a.f; // f not highlighted } Task-number: QTCREATORBUG-14036 Change-Id: Idee5e3f41d15c0772318d3837cbcd442cb80293a Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com> --- src/libs/cplusplus/LookupContext.cpp | 87 +++++++++++++-------- src/plugins/cpptools/cppcompletion_test.cpp | 61 ++++++++++++--- 2 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 574f7f6359a..82e9186feb0 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -1144,9 +1144,29 @@ LookupScope *LookupScopePrivate::lookupType_helper( return 0; } -static LookupScopePrivate *findSpecializationWithMatchingTemplateArgument( - const Name *argumentName, LookupScopePrivate *reference) +static const NamedType *dereference(const FullySpecifiedType &type) +{ + FullySpecifiedType ty = type; + forever { + if (PointerType *pointer = ty->asPointerType()) + ty = pointer->elementType(); + else if (ReferenceType *reference = ty->asReferenceType()) + ty = reference->elementType(); + else if (ArrayType *array = ty->asArrayType()) + ty = array->elementType(); + else if (const NamedType *namedType = ty->asNamedType()) + return namedType; + else + break; + } + return 0; +} + +static bool findTemplateArgument(const NamedType *namedType, LookupScopePrivate *reference) { + if (!namedType) + return false; + const Name *argumentName = namedType->name(); foreach (Symbol *s, reference->_symbols) { if (Class *clazz = s->asClass()) { if (Template *templateSpecialization = clazz->enclosingTemplate()) { @@ -1157,14 +1177,28 @@ static LookupScopePrivate *findSpecializationWithMatchingTemplateArgument( = templateSpecialization->templateParameterAt(i)->asTypenameArgument()) { if (const Name *name = tParam->name()) { if (compareName(name, argumentName)) - return reference; + return true; } } } } } } - return 0; + return false; +} + +static bool matchTypes(const FullySpecifiedType &instantiation, + const FullySpecifiedType &specialization) +{ + if (specialization.match(instantiation)) + return true; + if (const NamedType *specName = specialization->asNamedType()) { + if (const NamedType *initName = instantiation->asNamedType()) { + if (specName->name()->identifier()->match(initName->name()->identifier())) + return true; + } + } + return false; } LookupScopePrivate *LookupScopePrivate::findSpecialization( @@ -1172,7 +1206,6 @@ LookupScopePrivate *LookupScopePrivate::findSpecialization( const TemplateNameIdTable &specializations, LookupScopePrivate *origin) { - // we go through all specialization and try to find that one with template argument as pointer for (TemplateNameIdTable::const_iterator cit = specializations.begin(); cit != specializations.end(); ++cit) { const TemplateNameId *specializationNameId = cit->first; @@ -1183,45 +1216,31 @@ LookupScopePrivate *LookupScopePrivate::findSpecialization( // and initialization(in future it should be more clever) if (specializationTemplateArgumentCount != initializationTemplateArgumentCount) continue; - for (unsigned i = 0; i < initializationTemplateArgumentCount; ++i) { + bool match = true; + for (unsigned i = 0; i < initializationTemplateArgumentCount && 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); - PointerType *specPointer = specializationTemplateArgument.type()->asPointerType(); // specialization and initialization argument have to be a pointer // additionally type of pointer argument of specialization has to be namedType - if (specPointer && initializationTemplateArgument.type()->isPointerType() - && specPointer->elementType().type()->isNamedType()) { - return cit->second; - } - - ArrayType *specArray = specializationTemplateArgument.type()->asArrayType(); - if (specArray && initializationTemplateArgument.type()->isArrayType()) { - if (const NamedType *argumentNamedType - = specArray->elementType().type()->asNamedType()) { - if (const Name *argumentName = argumentNamedType->name()) { - if (LookupScopePrivate *reference - = findSpecializationWithMatchingTemplateArgument( - argumentName, cit->second)) { - return reference; - } - } - } - } - - if (specializationTemplateArgument == initializationTemplateArgument) - return cit->second; - - if (const NamedType *specName = specializationTemplateArgument->asNamedType()) { - if (const NamedType *initName = initializationTemplateArgument->asNamedType()) { - if (specName->name()->identifier() == initName->name()->identifier()) - return cit->second; - } + if (findTemplateArgument(dereference(specializationTemplateArgument), cit->second)) { + if (specializationTemplateArgument->isPointerType()) + match = initializationTemplateArgument->isPointerType(); + else if (specializationTemplateArgument->isReferenceType()) + match = initializationTemplateArgument->isReferenceType(); + else if (specializationTemplateArgument->isArrayType()) + match = initializationTemplateArgument->isArrayType(); + // Do not try exact match (typename T != class T {};) + } else { + // Real type specialization + match = matchTypes(initializationTemplateArgument, specializationTemplateArgument); } } + if (match) + return cit->second; } return 0; diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index d8bd4101e23..563a6b9a1ce 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -1306,22 +1306,29 @@ void CppToolsPlugin::test_completion_data() << QLatin1String("Template1")); QTest::newRow("template_specialization_with_pointer") << _( - "template <typename T>\n" - "struct Template\n" - "{\n" - " T variable;\n" - "};\n" - "template <typename T>\n" - "struct Template<T *>\n" + "template <typename T> struct Temp { T variable; };\n" + "template <typename T> struct Temp<T *> { T *pointer; };\n" + "void func()\n" "{\n" - " T *pointer;\n" - "};\n" - "Template<int*> templ;\n" - "@\n" + " Temp<int*> templ;\n" + " @\n" + "}" ) << _("templ.") << (QStringList() - << QLatin1String("Template") + << QLatin1String("Temp") << QLatin1String("pointer")); + QTest::newRow("template_specialization_with_reference") << _( + "template <typename T> struct Temp { T variable; };\n" + "template <typename T> struct Temp<T &> { T reference; };\n" + "void func()\n" + "{\n" + " Temp<int&> templ;\n" + " @\n" + "}" + ) << _("templ.") << (QStringList() + << QLatin1String("Temp") + << QLatin1String("reference")); + QTest::newRow("typedef_using_templates1") << _( "namespace NS1\n" "{\n" @@ -2637,6 +2644,22 @@ void CppToolsPlugin::test_completion_data() << QLatin1String("i") << QLatin1String("s")); + QTest::newRow("partial_specialization_with_pointer") << _( + "struct b {};\n" + "struct a : b {};\n" + "template<class X, class Y> struct s { float f; };\n" + "template<class X> struct s<X, b*> { int i; };\n" + "template<class X> struct s<X, a*> { char j; };\n" + "\n" + "void f()\n" + "{\n" + " s<int, a*> var;\n" + " @\n" + "}\n" + ) << _("var.") << (QStringList() + << QLatin1String("j") + << QLatin1String("s")); + QTest::newRow("partial_specialization_templated_argument") << _( "template<class T> struct t {};\n" "\n" @@ -2652,6 +2675,20 @@ void CppToolsPlugin::test_completion_data() << QLatin1String("i") << QLatin1String("s")); + QTest::newRow("specialization_multiple_arguments") << _( + "class false_type {};\n" + "class true_type {};\n" + "template<class T1, class T2> class and_type { false_type f; };\n" + "template<> class and_type<true_type, true_type> { true_type t; };\n" + "void func()\n" + "{\n" + " and_type<true_type, false_type> a;\n" + " @;\n" + "}\n" + ) << _("a.") << (QStringList() + << QLatin1String("f") + << QLatin1String("and_type")); + QTest::newRow("auto_declaration_in_if_condition") << _( "struct Foo { int bar; };\n" "void fun() {\n" -- GitLab