From 564c9b2842663062658a0febdcc5787098d871b2 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gorszkowski Date: Mon, 15 Apr 2013 12:50:36 +0200 Subject: [PATCH] C++: fix support for typedef of templated typedefs Fix: * code completion * follow symbols * find usages Task-number: QTCREATORBUG-8375 Change-Id: Ia40273fec3dead76acad4695b852a9e53065d8a7 Reviewed-by: Petar Perisin Reviewed-by: Erik Verbruggen --- src/libs/cplusplus/LookupContext.cpp | 67 +++++++++- src/libs/cplusplus/LookupContext.h | 5 + src/libs/cplusplus/ResolveExpression.cpp | 11 +- src/plugins/cpptools/cppcompletion_test.cpp | 135 ++++++++++++++++++++ src/plugins/cpptools/cpptoolsplugin.h | 5 + 5 files changed, 218 insertions(+), 5 deletions(-) diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index dde50df687f..623a1be8ec9 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -719,10 +719,22 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name, if (name->isNameId() || name->isTemplateNameId() || name->isAnonymousNameId()) { flush(); + if (name->isTemplateNameId()) { + // if it is a base specialization, the 'name' could be an instantiation + QMap::iterator it + = _instantiations.find(name->asTemplateNameId()); + if (it != _instantiations.end()) + return it.value(); + } + foreach (Symbol *s, symbols()) { if (Class *klass = s->asClass()) { if (klass->identifier() && klass->identifier()->isEqualTo(name->identifier())) return this; + + if (ClassOrNamespace *typedefedType + = resolveTypedef(klass, name, searchInEnclosingScope, origin)) + return typedefedType; } } @@ -746,6 +758,9 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name, } foreach (ClassOrNamespace *u, usings()) { + // usings are not instantiated for templates + if (_templateId && u->_templateId) + continue; if (ClassOrNamespace *r = u->lookupType_helper(name, processed, /*searchInEnclosingScope =*/ false, @@ -754,8 +769,21 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name, } } - if (_parent && searchInEnclosingScope) - return _parent->lookupType_helper(name, processed, searchInEnclosingScope, origin); + return lookupType_helper_inParent(name, processed, searchInEnclosingScope, origin); + } + + return 0; +} + +ClassOrNamespace *ClassOrNamespace::lookupType_helper_inParent(const Name *name, QSet *processed, + bool searchInEnclosingScope, ClassOrNamespace *origin) +{ + if (_parent && searchInEnclosingScope) { + // for templates _parent is a base specialization, + // so we should take here rather _parent of this base specialization + ClassOrNamespace *parent = _templateId ? _parent->_parent : _parent; + if (parent) + return parent->lookupType_helper(name, processed, searchInEnclosingScope, origin); } return 0; @@ -795,6 +823,39 @@ ClassOrNamespace *ClassOrNamespace::findSpecializationWithPointer(const Template return 0; } +ClassOrNamespace *ClassOrNamespace::resolveTypedef(Class *klass, const Name *name, + bool searchInEnclosingScope, + ClassOrNamespace *origin) +{ + // it can be a typedef + const unsigned memberClassCount = klass->memberCount(); + for (unsigned i = 0; i < memberClassCount; ++i) { + Symbol *memberClassAsSymbol = klass->memberAt(i); + if (Declaration *declaration = memberClassAsSymbol->asDeclaration()) { + if (declaration->isTypedef() + && name->identifier()->isEqualTo(declaration->name()->identifier())) { + if (NamedType *namedType = declaration->type()->asNamedType()) { + QSet innerProcessed; + const Name *namedTypeName = namedType->name(); + const QualifiedNameId *q = namedTypeName->asQualifiedNameId(); + if (q) { + if (name->isEqualTo(q->base()) && name->isEqualTo(q->name())) + return lookupType_helper_inParent(name, &innerProcessed, + searchInEnclosingScope, + origin); + if ((klass->identifier() + && klass->identifier()->isEqualTo(q->base()->identifier()))) + return this; + } + + return lookupType_helper(namedTypeName, &innerProcessed, true, origin); + } + } + } + } + return 0; +} + ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespace *origin) { Q_ASSERT(name != 0); @@ -959,7 +1020,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac oo.showReturnTypes = true; oo.showTemplateParameters = true; qDebug()<<"cloned"<type()); - if (Class *klass = s->asClass()) { + if (Class *klass = clone->asClass()) { const unsigned klassMemberCount = klass->memberCount(); for (unsigned i = 0; i < klassMemberCount; ++i){ Symbol *klassMemberAsSymbol = klass->memberAt(i); diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index 3dbe525766a..1ad9a27dd07 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -111,6 +111,9 @@ private: ClassOrNamespace *lookupType_helper(const Name *name, QSet *processed, bool searchInEnclosingScope, ClassOrNamespace *origin); + ClassOrNamespace *lookupType_helper_inParent(const Name *name, QSet *processed, + bool searchInEnclosingScope, ClassOrNamespace *origin); + ClassOrNamespace *nestedType(const Name *name, ClassOrNamespace *origin); void instantiateNestedClasses(ClassOrNamespace *enclosingTemplateClass, @@ -120,6 +123,8 @@ private: bool isInstantiateNestedClassNeeded(const QList& symbols, const Subst &subst) const; ClassOrNamespace *findSpecializationWithPointer(const TemplateNameId *templId, const TemplateNameIdTable &specializations); + ClassOrNamespace *resolveTypedef(Class *klass, const Name *name, + bool searchInEnclosingScope, ClassOrNamespace *origin); CreateBindings *_factory; ClassOrNamespace *_parent; diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp index 029bcee00d4..8a72fc1fc2a 100644 --- a/src/libs/cplusplus/ResolveExpression.cpp +++ b/src/libs/cplusplus/ResolveExpression.cpp @@ -690,8 +690,15 @@ bool ResolveExpression::visit(CallAST *ast) } } else if (Function *funTy = ty->asFunctionType()) { - if (maybeValidPrototype(funTy, actualArgumentCount)) - addResult(funTy->returnType().simplified(), scope); + if (maybeValidPrototype(funTy, actualArgumentCount)) { + LookupItem item; + item.setType(funTy->returnType().simplified()); + item.setScope(scope); + // we have to remember a binding because it can be a template instantiation + item.setBinding(result.binding()); + + _results.append(item); + } } else if (Class *classTy = ty->asClassType()) { // Constructor call diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index b8ee95f7c04..b5cd103374a 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -1882,3 +1882,138 @@ void CppToolsPlugin::test_completion_QTCREATORBUG9098() QVERIFY(completions.contains(QLatin1String("c"))); QVERIFY(completions.contains(QLatin1String("B"))); } + +void CppToolsPlugin::test_completion_typedef_of_templated_typedef_QTCREATORBUG8375() +{ + TestData data; + data.srcText = + "struct Foo\n" + "{ void bar(); };\n" + "struct A\n" + "{ typedef Foo AFoo; };\n" + "template \n" + "struct B\n" + "{ typedef typename T::AFoo BFoo; };\n" + "struct C : public B\n" + "{\n" + " void test()\n" + " {\n" + " BFoo foo;\n" + " @\n" + " // padding so we get the scope right\n" + " }\n" + "};\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("foo."); + change.insert(data.pos, txt); + QTextCursor cursor(data.doc); + change.apply(&cursor); + data.pos += txt.length(); + + QStringList completions = getCompletions(data); + + QCOMPARE(completions.size(), 2); + QVERIFY(completions.contains(QLatin1String("Foo"))); + QVERIFY(completions.contains(QLatin1String("bar"))); +} + +void CppToolsPlugin::test_completion_typedef_with_the_same_base_name_and_new_type_name() +{ + TestData data; + data.srcText = + "namespace A\n" + "{\n" + "struct A { int aa; };\n" + "}\n" + "struct S\n" + "{\n" + " typedef A::A A;\n" + " A a;\n" + "};\n" + "void fun()\n" + "{\n" + " S s;\n" + " @\n" + " // padding so we get the scope right\n" + "};\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("s.a."); + change.insert(data.pos, txt); + QTextCursor cursor(data.doc); + change.apply(&cursor); + data.pos += txt.length(); + + QStringList completions = getCompletions(data); + + QCOMPARE(completions.size(), 2); + QVERIFY(completions.contains(QLatin1String("A"))); + QVERIFY(completions.contains(QLatin1String("aa"))); +} + +void CppToolsPlugin::test_completion_qualified_typedef_1() +{ + TestData data; + data.srcText = + "struct S\n" + "{\n" + " typedef S::type type;\n" + "};\n" + "void fun()\n" + "{\n" + " @\n" + " // padding so we get the scope right\n" + "};\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("S::"); + change.insert(data.pos, txt); + QTextCursor cursor(data.doc); + change.apply(&cursor); + data.pos += txt.length(); + + QStringList completions = getCompletions(data); + + QCOMPARE(completions.size(), 2); + QVERIFY(completions.contains(QLatin1String("S"))); + QVERIFY(completions.contains(QLatin1String("type"))); +} + +void CppToolsPlugin::test_completion_qualified_typedef_2() +{ + TestData data; + data.srcText = + "template \n" + "struct S\n" + "{\n" + " typedef S::type type;\n" + "};\n" + "void fun()\n" + "{\n" + " @\n" + " // padding so we get the scope right\n" + "};\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("S::"); + change.insert(data.pos, txt); + QTextCursor cursor(data.doc); + change.apply(&cursor); + data.pos += txt.length(); + + QStringList completions = getCompletions(data); + + QCOMPARE(completions.size(), 2); + QVERIFY(completions.contains(QLatin1String("S"))); + QVERIFY(completions.contains(QLatin1String("type"))); +} + diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index 10975a53013..05607067085 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -124,6 +124,11 @@ private slots: void test_completion_typedef_using_templates2(); void test_completion_namespace_alias_with_many_namespace_declarations(); void test_completion_QTCREATORBUG9098(); + void test_completion_typedef_of_templated_typedef_QTCREATORBUG8375(); + void test_completion_typedef_with_the_same_base_name_and_new_type_name(); + void test_completion_qualified_typedef_1(); + void test_completion_qualified_typedef_2(); + void test_format_pointerdeclaration_in_simpledeclarations(); void test_format_pointerdeclaration_in_simpledeclarations_data();