Commit 6b67f248 authored by Przemyslaw Gorszkowski's avatar Przemyslaw Gorszkowski Committed by Nikolai Kosjar

C++: Fix code completion for operator ->

Fix code completion for operator '->' when
returned value is a pointer of typedef(e.g.:std::auto_ptr).
Included unit test.

Task-number: QTCREATORBUG-629
Change-Id: I36b53a3f80b3c99d52791e18ad1244923c1373f6
Reviewed-by: default avatarErik Verbruggen <erik.verbruggen@digia.com>
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent 0b3eb65f
......@@ -828,43 +828,71 @@ ClassOrNamespace *ResolveExpression::findClass(const FullySpecifiedType &origina
return binding;
}
static void resolveTypedefs(const LookupContext &context,
FullySpecifiedType *type,
Scope **scope, ClassOrNamespace *binding)
class TypedefsResolver
{
public:
TypedefsResolver(const LookupContext &context) : _context(context) {}
void resolve(FullySpecifiedType *type, Scope **scope, ClassOrNamespace *binding)
{
QSet<Symbol *> visited;
while (NamedType *namedTy = (*type)->asNamedType()) {
// check if namedTy->name() resolves to a typedef
QList<LookupItem> namedTypeItems;
if (binding)
namedTypeItems = binding->lookup(namedTy->name());
if (ClassOrNamespace *scopeCon = context.lookupType(*scope))
namedTypeItems += scopeCon->lookup(namedTy->name());
while (NamedType *namedTy = getNamedType(*type)) {
QList<LookupItem> namedTypeItems = getNamedTypeItems(namedTy->name(), *scope, binding);
#ifdef DEBUG_LOOKUP
qDebug() << "-- we have" << namedTypeItems.size() << "candidates";
#endif // DEBUG_LOOKUP
if (!findTypedef(namedTypeItems, type, scope, visited))
break;
}
}
private:
NamedType *getNamedType(FullySpecifiedType& type)
{
NamedType *namedTy = type->asNamedType();
if (! namedTy) {
if (PointerType *pointerTy = type->asPointerType())
namedTy = pointerTy->elementType()->asNamedType();
}
return namedTy;
}
QList<LookupItem> getNamedTypeItems(const Name *name, Scope *scope, ClassOrNamespace *binding)
{
QList<LookupItem> namedTypeItems;
if (binding)
namedTypeItems = binding->lookup(name);
if (ClassOrNamespace *scopeCon = _context.lookupType(scope))
namedTypeItems += scopeCon->lookup(name);
return namedTypeItems;
}
bool findTypedef(const QList<LookupItem>& namedTypeItems, FullySpecifiedType *type,
Scope **scope, QSet<Symbol *>& visited)
{
bool foundTypedef = false;
foreach (const LookupItem &it, namedTypeItems) {
if (it.declaration() && it.declaration()->isTypedef()) {
if (visited.contains(it.declaration()))
Symbol *declaration = it.declaration();
if (declaration && declaration->isTypedef()) {
if (visited.contains(declaration))
break;
visited.insert(it.declaration());
visited.insert(declaration);
// continue working with the typedefed type and scope
*type = it.declaration()->type();
*type = declaration->type();
*scope = it.scope();
foundTypedef = true;
break;
}
}
if (!foundTypedef)
break;
return foundTypedef;
}
}
const LookupContext &_context;
};
ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &baseResults,
int accessOp,
......@@ -875,6 +903,7 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
int i = 0;
Overview oo;
#endif // DEBUG_LOOKUP
TypedefsResolver typedefsResolver(_context);
foreach (const LookupItem &r, baseResults) {
FullySpecifiedType ty = r.type().simplified();
......@@ -885,7 +914,7 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
qDebug()<<"- before typedef resolving we have:"<<oo(ty);
#endif // DEBUG_LOOKUP
resolveTypedefs(_context, &ty, &scope, r.binding());
typedefsResolver.resolve(&ty, &scope, r.binding());
#ifdef DEBUG_LOOKUP
qDebug()<<"- after typedef resolving:"<<oo(ty);
......@@ -913,14 +942,19 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
FullySpecifiedType retTy = instantiatedFunction->returnType().simplified();
resolveTypedefs(_context, &retTy, &functionScope, r.binding());
typedefsResolver.resolve(&retTy, &functionScope, r.binding());
if (! retTy->isPointerType() && ! retTy->isNamedType())
continue;
if (PointerType *ptrTy = retTy->asPointerType()) {
if (ClassOrNamespace *retBinding = findClass(ptrTy->elementType(), functionScope))
if (PointerType *ptrTy = retTy->asPointerType())
retTy = ptrTy->elementType();
if (ClassOrNamespace *retBinding = findClass(retTy, functionScope))
return retBinding;
if (scope != functionScope) {
if (ClassOrNamespace *retBinding = findClass(ptrTy->elementType(), scope))
if (ClassOrNamespace *retBinding = findClass(retTy, scope))
return retBinding;
}
......@@ -928,7 +962,7 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
foreach (Symbol *originSymbol, origin->symbols()) {
Scope *originScope = originSymbol->asScope();
if (originScope && originScope != scope && originScope != functionScope) {
if (ClassOrNamespace *retBinding = findClass(ptrTy->elementType(), originScope))
if (ClassOrNamespace *retBinding = findClass(retTy, originScope))
return retBinding;
}
}
......@@ -936,8 +970,6 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
}
}
}
}
} else if (accessOp == T_DOT) {
if (replacedDotOperator) {
if (PointerType *ptrTy = ty->asPointerType()) {
......
......@@ -409,6 +409,49 @@ void CppToolsPlugin::test_completion_template_6()
QVERIFY(completions.contains(QLatin1String("i")));
}
void CppToolsPlugin::test_completion_template_7()
{
TestData data;
data.srcText = "\n"
"struct Test\n"
"{\n"
" int i;\n"
"};\n"
"\n"
"template<typename T>\n"
"struct TemplateClass\n"
"{\n"
" T* ptr;\n"
"\n"
" typedef T element_type;\n"
" TemplateClass(T* t) : ptr(t) {}\n"
" element_type* operator->()\n"
" {\n"
" return ptr;\n"
" }\n"
"};\n"
"\n"
"TemplateClass<Test> p(new Test);\n"
"@\n"
;
setup(&data);
Utils::ChangeSet change;
QString txt = QLatin1String("p->");
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("Test")));
QVERIFY(completions.contains(QLatin1String("i")));
}
void CppToolsPlugin::test_completion()
{
QFETCH(QByteArray, code);
......
......@@ -97,6 +97,7 @@ private slots:
void test_completion_template_4();
void test_completion_template_5();
void test_completion_template_6();
void test_completion_template_7();
void test_completion_template_as_base();
void test_completion_template_as_base_data();
void test_completion_use_global_identifier_as_base_class();
......
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