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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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<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();
|
||||
|
Reference in New Issue
Block a user