Commit bfbf93e6 authored by Przemyslaw Gorszkowski's avatar Przemyslaw Gorszkowski Committed by Nikolai Kosjar

C++: fix auto completion for template parameters

Fix auto completion for the case when template parameter should be
found somewhere of scope of template instantiation declaration.
Example:
struct A
{
    void foo();
    struct B
    {
        int b;
    };
};

template<typename T>
struct Template
{
    T* get() { return 0; }
    T t;
};

void A::foo()
{
    Template<B> templ;
    templ.get()->//no autocompletion
    templ.t.//no autocompletion
}

Task-number: QTCREATORBUG-8852
Task-number: QTCREATORBUG-9169
Change-Id: I56b40776e66740f995ae6fc5d69e3c50139a3af2
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent 62af8171
...@@ -142,11 +142,13 @@ void ResolveExpression::addResults(const QList<LookupItem> &items) ...@@ -142,11 +142,13 @@ void ResolveExpression::addResults(const QList<LookupItem> &items)
_results += items; _results += items;
} }
void ResolveExpression::addResult(const FullySpecifiedType &ty, Scope *scope) void ResolveExpression::addResult(const FullySpecifiedType &ty, Scope *scope,
ClassOrNamespace *binding)
{ {
LookupItem item; LookupItem item;
item.setType(ty); item.setType(ty);
item.setScope(scope); item.setScope(scope);
item.setBinding(binding);
_results.append(item); _results.append(item);
} }
...@@ -713,7 +715,7 @@ bool ResolveExpression::visit(CallAST *ast) ...@@ -713,7 +715,7 @@ bool ResolveExpression::visit(CallAST *ast)
} else if (Function *funTy = ty->asFunctionType()) { } else if (Function *funTy = ty->asFunctionType()) {
if (maybeValidPrototype(funTy, actualArgumentCount)) if (maybeValidPrototype(funTy, actualArgumentCount))
addResult(funTy->returnType().simplified(), scope); addResult(funTy->returnType().simplified(), scope, result.binding());
} else if (Class *classTy = ty->asClassType()) { } else if (Class *classTy = ty->asClassType()) {
// Constructor call // Constructor call
...@@ -1014,53 +1016,77 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas ...@@ -1014,53 +1016,77 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
FullySpecifiedType type = ptrTy->elementType(); FullySpecifiedType type = ptrTy->elementType();
if (! ty->isPointerType()) if (! ty->isPointerType())
type = ty; type = ty;
if (ClassOrNamespace *binding
= findClassForTemplateParameterInExpressionScope(r.binding(),
type)) {
return binding;
}
if (ClassOrNamespace *binding = findClass(type, scope)) if (ClassOrNamespace *binding = findClass(type, scope))
return binding; return binding;
} else if (PointerType *ptrTy = ty->asPointerType()) { } else if (PointerType *ptrTy = ty->asPointerType()) {
FullySpecifiedType type = ptrTy->elementType(); FullySpecifiedType type = ptrTy->elementType();
if (ClassOrNamespace *binding
= findClassForTemplateParameterInExpressionScope(r.binding(),
type)) {
return binding;
}
if (ClassOrNamespace *binding = findClass(type, scope)) if (ClassOrNamespace *binding = findClass(type, scope))
return binding; return binding;
} else if (ClassOrNamespace *binding = findClass(ty, scope)) { } else {
// lookup for overloads of operator-> ClassOrNamespace *binding
= findClassForTemplateParameterInExpressionScope(r.binding(),
ty);
if (! binding)
binding = findClass(ty, scope);
const OperatorNameId *arrowOp = control()->operatorNameId(OperatorNameId::ArrowOp); if (binding){
foreach (const LookupItem &r, binding->find(arrowOp)) { // lookup for overloads of operator->
Symbol *overload = r.declaration();
if (! overload)
continue;
Scope *functionScope = overload->enclosingScope();
if (overload->type()->isFunctionType()) { const OperatorNameId *arrowOp
FullySpecifiedType overloadTy = instantiate(binding->templateId(), overload); = control()->operatorNameId(OperatorNameId::ArrowOp);
Function *instantiatedFunction = overloadTy->asFunctionType(); foreach (const LookupItem &r, binding->find(arrowOp)) {
Q_ASSERT(instantiatedFunction != 0); Symbol *overload = r.declaration();
if (! overload)
continue;
Scope *functionScope = overload->enclosingScope();
FullySpecifiedType retTy = instantiatedFunction->returnType().simplified(); if (overload->type()->isFunctionType()) {
FullySpecifiedType overloadTy
= instantiate(binding->templateId(), overload);
Function *instantiatedFunction = overloadTy->asFunctionType();
Q_ASSERT(instantiatedFunction != 0);
typedefsResolver.resolve(&retTy, &functionScope, r.binding()); FullySpecifiedType retTy
= instantiatedFunction->returnType().simplified();
if (! retTy->isPointerType() && ! retTy->isNamedType()) typedefsResolver.resolve(&retTy, &functionScope, r.binding());
continue;
if (PointerType *ptrTy = retTy->asPointerType()) if (! retTy->isPointerType() && ! retTy->isNamedType())
retTy = ptrTy->elementType(); continue;
if (ClassOrNamespace *retBinding = findClass(retTy, functionScope)) if (PointerType *ptrTy = retTy->asPointerType())
return retBinding; retTy = ptrTy->elementType();
if (scope != functionScope) { if (ClassOrNamespace *retBinding = findClass(retTy, functionScope))
if (ClassOrNamespace *retBinding = findClass(retTy, scope))
return retBinding; return retBinding;
}
if (ClassOrNamespace *origin = binding->instantiationOrigin()) { if (scope != functionScope) {
foreach (Symbol *originSymbol, origin->symbols()) { if (ClassOrNamespace *retBinding = findClass(retTy, scope))
Scope *originScope = originSymbol->asScope(); return retBinding;
if (originScope && originScope != scope && originScope != functionScope) { }
if (ClassOrNamespace *retBinding = findClass(retTy, originScope))
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<LookupItem> &bas ...@@ -1078,6 +1104,12 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
} }
} }
if (ClassOrNamespace *binding
= findClassForTemplateParameterInExpressionScope(r.binding(),
ty)) {
return binding;
}
ClassOrNamespace *enclosingTemplateInstantiation = 0; ClassOrNamespace *enclosingTemplateInstantiation = 0;
if (ClassOrNamespace *binding = r.binding()) { if (ClassOrNamespace *binding = r.binding()) {
if (binding->instantiationOrigin()) if (binding->instantiationOrigin())
...@@ -1092,6 +1124,24 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas ...@@ -1092,6 +1124,24 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
return 0; 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 FullySpecifiedType ResolveExpression::instantiate(const Name *className, Symbol *candidate) const
{ {
return DeprecatedGenTemplateInstance::instantiate(className, candidate, return DeprecatedGenTemplateInstance::instantiate(className, candidate,
......
...@@ -71,7 +71,7 @@ protected: ...@@ -71,7 +71,7 @@ protected:
void thisObject(); void thisObject();
void addResult(const FullySpecifiedType &ty, Scope *scope); void addResult(const FullySpecifiedType &ty, Scope *scope, ClassOrNamespace *binding = 0);
void addResults(const QList<Symbol *> &symbols); void addResults(const QList<Symbol *> &symbols);
void addResults(const QList<LookupItem> &items); void addResults(const QList<LookupItem> &items);
...@@ -126,6 +126,10 @@ protected: ...@@ -126,6 +126,10 @@ protected:
private: private:
ClassOrNamespace *findClassForTemplateParameterInExpressionScope(
ClassOrNamespace *resultBinding,
const FullySpecifiedType &ty) const;
Scope *_scope; Scope *_scope;
const LookupContext& _context; const LookupContext& _context;
Bind bind; Bind bind;
......
...@@ -3027,3 +3027,211 @@ void CppToolsPlugin::test_completion_local_type_and_member_6() ...@@ -3027,3 +3027,211 @@ void CppToolsPlugin::test_completion_local_type_and_member_6()
QVERIFY(completions.contains(QLatin1String("OtherType"))); QVERIFY(completions.contains(QLatin1String("OtherType")));
QVERIFY(completions.contains(QLatin1String("otherTypeMember"))); 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<typename T>\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<B> 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<typename T>\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<B> 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 <typename T>\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<Foo> 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 <typename T>\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<Foo> 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 <typename T>\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<Foo> 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")));
}
...@@ -156,6 +156,12 @@ private slots: ...@@ -156,6 +156,12 @@ private slots:
void test_completion_enum_inside_block_inside_function_QTCREATORBUG5456(); void test_completion_enum_inside_block_inside_function_QTCREATORBUG5456();
void test_completion_enum_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 //lambda
void test_completion_lambdaCalls_1(); void test_completion_lambdaCalls_1();
void test_completion_lambdaCalls_2(); void test_completion_lambdaCalls_2();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment