forked from qt-creator/qt-creator
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: Nikolai Kosjar <nikolai.kosjar@digia.com>
This commit is contained in:
committed by
Nikolai Kosjar
parent
62af817175
commit
bfbf93e64f
@@ -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)
|
|||||||
|
|
||||||
} 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
|
|||||||
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)
|
||||||
FullySpecifiedType retTy = instantiatedFunction->returnType().simplified();
|
|
||||||
|
|
||||||
typedefsResolver.resolve(&retTy, &functionScope, r.binding());
|
|
||||||
|
|
||||||
if (! retTy->isPointerType() && ! retTy->isNamedType())
|
|
||||||
continue;
|
continue;
|
||||||
|
Scope *functionScope = overload->enclosingScope();
|
||||||
|
|
||||||
if (PointerType *ptrTy = retTy->asPointerType())
|
if (overload->type()->isFunctionType()) {
|
||||||
retTy = ptrTy->elementType();
|
FullySpecifiedType overloadTy
|
||||||
|
= instantiate(binding->templateId(), overload);
|
||||||
|
Function *instantiatedFunction = overloadTy->asFunctionType();
|
||||||
|
Q_ASSERT(instantiatedFunction != 0);
|
||||||
|
|
||||||
if (ClassOrNamespace *retBinding = findClass(retTy, functionScope))
|
FullySpecifiedType retTy
|
||||||
return retBinding;
|
= instantiatedFunction->returnType().simplified();
|
||||||
|
|
||||||
if (scope != functionScope) {
|
typedefsResolver.resolve(&retTy, &functionScope, r.binding());
|
||||||
if (ClassOrNamespace *retBinding = findClass(retTy, scope))
|
|
||||||
|
if (! retTy->isPointerType() && ! retTy->isNamedType())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (PointerType *ptrTy = retTy->asPointerType())
|
||||||
|
retTy = ptrTy->elementType();
|
||||||
|
|
||||||
|
if (ClassOrNamespace *retBinding = findClass(retTy, functionScope))
|
||||||
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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
|||||||
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:
|
|||||||
|
|
||||||
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:
|
|||||||
|
|
||||||
|
|
||||||
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()
|
|||||||
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:
|
|||||||
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();
|
||||||
|
Reference in New Issue
Block a user