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)
_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<LookupItem> &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);
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();
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())
continue;
typedefsResolver.resolve(&retTy, &functionScope, r.binding());
if (PointerType *ptrTy = retTy->asPointerType())
retTy = ptrTy->elementType();
if (! retTy->isPointerType() && ! retTy->isNamedType())
continue;
if (ClassOrNamespace *retBinding = findClass(retTy, functionScope))
return retBinding;
if (PointerType *ptrTy = retTy->asPointerType())
retTy = ptrTy->elementType();
if (scope != functionScope) {
if (ClassOrNamespace *retBinding = findClass(retTy, scope))
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<LookupItem> &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<LookupItem> &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,
......
......@@ -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<Symbol *> &symbols);
void addResults(const QList<LookupItem> &items);
......@@ -126,6 +126,10 @@ protected:
private:
ClassOrNamespace *findClassForTemplateParameterInExpressionScope(
ClassOrNamespace *resultBinding,
const FullySpecifiedType &ty) const;
Scope *_scope;
const LookupContext& _context;
Bind bind;
......
......@@ -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<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:
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();
......
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