diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp index 4b204664587..20a562562e9 100644 --- a/src/libs/cplusplus/ResolveExpression.cpp +++ b/src/libs/cplusplus/ResolveExpression.cpp @@ -142,11 +142,13 @@ void ResolveExpression::addResults(const QList &items) _results += items; } -void ResolveExpression::addResult(const FullySpecifiedType &ty, Scope *scope) +void ResolveExpression::addResult(const FullySpecifiedType &ty, Scope *scope, + ClassOrNamespace *binding) { LookupItem item; item.setType(ty); item.setScope(scope); + item.setBinding(binding); _results.append(item); } @@ -713,7 +715,7 @@ bool ResolveExpression::visit(CallAST *ast) } else if (Function *funTy = ty->asFunctionType()) { if (maybeValidPrototype(funTy, actualArgumentCount)) - addResult(funTy->returnType().simplified(), scope); + addResult(funTy->returnType().simplified(), scope, result.binding()); } else if (Class *classTy = ty->asClassType()) { // Constructor call @@ -1014,53 +1016,77 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList &bas FullySpecifiedType type = ptrTy->elementType(); if (! ty->isPointerType()) type = ty; + + if (ClassOrNamespace *binding + = findClassForTemplateParameterInExpressionScope(r.binding(), + type)) { + return binding; + } if (ClassOrNamespace *binding = findClass(type, scope)) return binding; } else if (PointerType *ptrTy = ty->asPointerType()) { FullySpecifiedType type = ptrTy->elementType(); + if (ClassOrNamespace *binding + = findClassForTemplateParameterInExpressionScope(r.binding(), + type)) { + return binding; + } if (ClassOrNamespace *binding = findClass(type, scope)) return binding; - } else if (ClassOrNamespace *binding = findClass(ty, scope)) { - // lookup for overloads of operator-> + } else { + ClassOrNamespace *binding + = findClassForTemplateParameterInExpressionScope(r.binding(), + ty); + if (! binding) + binding = findClass(ty, scope); - const OperatorNameId *arrowOp = control()->operatorNameId(OperatorNameId::ArrowOp); - foreach (const LookupItem &r, binding->find(arrowOp)) { - Symbol *overload = r.declaration(); - if (! overload) - continue; - Scope *functionScope = overload->enclosingScope(); + if (binding){ + // lookup for overloads of operator-> - if (overload->type()->isFunctionType()) { - FullySpecifiedType overloadTy = instantiate(binding->templateId(), overload); - Function *instantiatedFunction = overloadTy->asFunctionType(); - Q_ASSERT(instantiatedFunction != 0); - - FullySpecifiedType retTy = instantiatedFunction->returnType().simplified(); - - typedefsResolver.resolve(&retTy, &functionScope, r.binding()); - - if (! retTy->isPointerType() && ! retTy->isNamedType()) + const OperatorNameId *arrowOp + = control()->operatorNameId(OperatorNameId::ArrowOp); + foreach (const LookupItem &r, binding->find(arrowOp)) { + Symbol *overload = r.declaration(); + if (! overload) continue; + Scope *functionScope = overload->enclosingScope(); - if (PointerType *ptrTy = retTy->asPointerType()) - retTy = ptrTy->elementType(); + if (overload->type()->isFunctionType()) { + FullySpecifiedType overloadTy + = instantiate(binding->templateId(), overload); + Function *instantiatedFunction = overloadTy->asFunctionType(); + Q_ASSERT(instantiatedFunction != 0); - if (ClassOrNamespace *retBinding = findClass(retTy, functionScope)) - return retBinding; + FullySpecifiedType retTy + = instantiatedFunction->returnType().simplified(); - if (scope != functionScope) { - if (ClassOrNamespace *retBinding = findClass(retTy, scope)) + typedefsResolver.resolve(&retTy, &functionScope, r.binding()); + + if (! retTy->isPointerType() && ! retTy->isNamedType()) + continue; + + if (PointerType *ptrTy = retTy->asPointerType()) + retTy = ptrTy->elementType(); + + if (ClassOrNamespace *retBinding = findClass(retTy, functionScope)) return retBinding; - } - if (ClassOrNamespace *origin = binding->instantiationOrigin()) { - foreach (Symbol *originSymbol, origin->symbols()) { - Scope *originScope = originSymbol->asScope(); - if (originScope && originScope != scope && originScope != functionScope) { - if (ClassOrNamespace *retBinding = findClass(retTy, originScope)) - return retBinding; + if (scope != functionScope) { + if (ClassOrNamespace *retBinding = findClass(retTy, scope)) + return retBinding; + } + + if (ClassOrNamespace *origin = binding->instantiationOrigin()) { + foreach (Symbol *originSymbol, origin->symbols()) { + Scope *originScope = originSymbol->asScope(); + if (originScope && originScope != scope + && originScope != functionScope) { + if (ClassOrNamespace *retBinding + = findClass(retTy, originScope)) + return retBinding; + } } } } @@ -1078,6 +1104,12 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList &bas } } + if (ClassOrNamespace *binding + = findClassForTemplateParameterInExpressionScope(r.binding(), + ty)) { + return binding; + } + ClassOrNamespace *enclosingTemplateInstantiation = 0; if (ClassOrNamespace *binding = r.binding()) { if (binding->instantiationOrigin()) @@ -1092,6 +1124,24 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList &bas return 0; } +ClassOrNamespace *ResolveExpression::findClassForTemplateParameterInExpressionScope( + ClassOrNamespace *resultBinding, + const FullySpecifiedType &ty) const +{ + if (resultBinding && resultBinding->instantiationOrigin()) { + if (ClassOrNamespace *origin = resultBinding->instantiationOrigin()) { + foreach (Symbol *originSymbol, origin->symbols()) { + if (Scope *originScope = originSymbol->asScope()) { + if (ClassOrNamespace *retBinding = findClass(ty, originScope)) + return retBinding; + } + } + } + } + + return 0; +} + FullySpecifiedType ResolveExpression::instantiate(const Name *className, Symbol *candidate) const { return DeprecatedGenTemplateInstance::instantiate(className, candidate, diff --git a/src/libs/cplusplus/ResolveExpression.h b/src/libs/cplusplus/ResolveExpression.h index b261dc39a28..79f843107cd 100644 --- a/src/libs/cplusplus/ResolveExpression.h +++ b/src/libs/cplusplus/ResolveExpression.h @@ -71,7 +71,7 @@ protected: void thisObject(); - void addResult(const FullySpecifiedType &ty, Scope *scope); + void addResult(const FullySpecifiedType &ty, Scope *scope, ClassOrNamespace *binding = 0); void addResults(const QList &symbols); void addResults(const QList &items); @@ -126,6 +126,10 @@ protected: private: + ClassOrNamespace *findClassForTemplateParameterInExpressionScope( + ClassOrNamespace *resultBinding, + const FullySpecifiedType &ty) const; + Scope *_scope; const LookupContext& _context; Bind bind; diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index f1229a38d3f..45f7dd1dc3c 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -3027,3 +3027,211 @@ void CppToolsPlugin::test_completion_local_type_and_member_6() QVERIFY(completions.contains(QLatin1String("OtherType"))); QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); } + +void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_1() +{ + TestData data; + data.srcText = + "struct A\n" + "{\n" + " void foo();\n" + " struct B\n" + " {\n" + " int b;\n" + " };\n" + "};\n" + "template\n" + "struct Template\n" + "{\n" + " T* get();\n" + "};\n" + "namespace foo\n" + "{\n" + " struct B\n" + " {\n" + " int foo_b;\n" + " };\n" + "}\n" + "using namespace foo;\n" + "void A::foo()\n" + "{\n" + " Template templ;\n" + " @\n" + " // padding so we get the scope right\n" + "}\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("templ.get()->"); + 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("B"))); + QVERIFY(completions.contains(QLatin1String("b"))); +} + +void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_2() +{ + TestData data; + data.srcText = + "struct A\n" + "{\n" + " void foo();\n" + " struct B\n" + " {\n" + " int b;\n" + " };\n" + "};\n" + "template\n" + "struct Template\n" + "{\n" + " T t;\n" + "};\n" + "namespace foo\n" + "{\n" + " struct B\n" + " {\n" + " int foo_b;\n" + " };\n" + "}\n" + "using namespace foo;\n" + "void A::foo()\n" + "{\n" + " Template templ;\n" + " @\n" + " // padding so we get the scope right\n" + "}\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("templ.t."); + 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("B"))); + QVERIFY(completions.contains(QLatin1String("b"))); +} + +void CppToolsPlugin::test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_1() +{ + TestData data; + data.srcText = + "template \n" + "struct QList\n" + "{\n" + " T at(int i) const;\n" + "};\n" + "namespace ns\n" + "{\n" + " struct Foo { int bar; };\n" + " void foo()\n" + " {\n" + " QList list;\n" + " @\n" + " // padding so we get the scope right\n" + " }\n" + "}\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("list.at(0)."); + 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_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_2() +{ + TestData data; + data.srcText = + "template \n" + "struct QList\n" + "{\n" + " T at(int i) const;\n" + "};\n" + "namespace ns\n" + "{\n" + " struct Foo { int bar; };\n" + " namespace nested\n" + " {\n" + " void foo()\n" + " {\n" + " QList list;\n" + " @\n" + " // padding so we get the scope right\n" + " }\n" + " }\n" + "}\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("list.at(0)."); + 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_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_3() +{ + TestData data; + data.srcText = + "template \n" + "struct QList\n" + "{\n" + " T at(int i) const;\n" + "};\n" + "namespace ns\n" + "{\n" + " struct Foo { int bar; };\n" + "}\n" + "void foo()\n" + "{\n" + " using namespace ns;\n" + " QList list;\n" + " @\n" + " // padding so we get the scope right\n" + "}\n" + ; + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("list.at(0)."); + 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"))); +} diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index 09028beb6ba..7b6ae5aad05 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -156,6 +156,12 @@ private slots: void test_completion_enum_inside_block_inside_function_QTCREATORBUG5456(); void test_completion_enum_inside_function_QTCREATORBUG5456(); + void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_1(); + void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG9169_2(); + void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_1(); + void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_2(); + void test_completion_template_parameter_defined_inside_scope_of_declaration_QTCREATORBUG8852_3(); + //lambda void test_completion_lambdaCalls_1(); void test_completion_lambdaCalls_2();