Commit 2b95d81c authored by Erik Verbruggen's avatar Erik Verbruggen

C++: Fix typedef resolving when templates are involved.

Task-number: QTCREATORBUG-7978
Change-Id: I27e6ebe56e410d509580a1e00f8986642cacc5ba
Reviewed-by: default avatarDavid Schulz <david.schulz@digia.com>
Reviewed-by: default avatarEike Ziller <eike.ziller@digia.com>
Reviewed-by: default avatarChristian Stenger <christian.stenger@digia.com>
parent fa655dd4
......@@ -263,11 +263,23 @@ ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope) cons
return 0;
} else if (Block *block = scope->asBlock()) {
for (unsigned i = 0; i < block->memberCount(); ++i) {
if (UsingNamespaceDirective *u = block->memberAt(i)->asUsingNamespaceDirective()) {
Symbol *m = block->memberAt(i);
if (UsingNamespaceDirective *u = m->asUsingNamespaceDirective()) {
if (ClassOrNamespace *uu = lookupType(u->name(), scope->enclosingNamespace())) {
if (ClassOrNamespace *r = uu->lookupType(name))
return r;
}
} else if (Declaration *d = m->asDeclaration()) {
if (d->name() && d->name()->isEqualTo(name->asNameId())) {
if (d->isTypedef() && d->type()) {
#ifdef DEBUG_LOOKUP
Overview oo;
qDebug() << "Looks like" << oo(name) << "is a typedef for" << oo(d->type());
#endif // DEBUG_LOOKUP
if (const NamedType *namedTy = d->type()->asNamedType())
return lookupType(namedTy->name(), scope);
}
}
}
}
return lookupType(name, scope->enclosingScope());
......@@ -380,6 +392,9 @@ ClassOrNamespace *LookupContext::lookupParent(Symbol *symbol) const
ClassOrNamespace::ClassOrNamespace(CreateBindings *factory, ClassOrNamespace *parent)
: _factory(factory), _parent(parent), _templateId(0), _instantiationOrigin(0)
#ifdef DEBUG_LOOKUP
, _name(0)
#endif // DEBUG_LOOKUP
{
}
......@@ -574,6 +589,11 @@ void CreateBindings::lookupInScope(const Name *name, Scope *scope,
else if (s->name()->isQualifiedNameId())
continue; // skip qualified ids.
#ifdef DEBUG_LOOKUP
Overview oo;
qDebug() << "Found" << id->chars() << "in" << (binding ? oo(binding->_name) : "<null>");
#endif // DEBUG_LOOKUP
LookupItem item;
item.setDeclaration(s);
item.setBinding(binding);
......@@ -616,6 +636,11 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name,
bool searchInEnclosingScope,
ClassOrNamespace *origin)
{
#ifdef DEBUG_LOOKUP
Overview oo;
qDebug() << "Looking up" << oo(name) << "in" << oo(_name);
#endif // DEBUG_LOOKUP
if (const QualifiedNameId *q = name->asQualifiedNameId()) {
QSet<ClassOrNamespace *> innerProcessed;
......@@ -721,6 +746,9 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
if (templId) {
_alreadyConsideredTemplates.insert(templId);
ClassOrNamespace *instantiation = _factory->allocClassOrNamespace(reference);
#ifdef DEBUG_LOOKUP
instantiation->_name = templId;
#endif // DEBUG_LOOKUP
instantiation->_templateId = templId;
instantiation->_instantiationOrigin = origin;
......@@ -734,6 +762,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
const unsigned argumentCount = templId->templateArgumentCount();
if (_factory->expandTemplates()) {
Clone cloner(_control.data());
Subst subst(_control.data());
for (unsigned i = 0, ei = std::min(argumentCount, templ->templateParameterCount()); i < ei; ++i) {
const TypenameArgument *tParam = templ->templateParameterAt(i)->asTypenameArgument();
......@@ -743,12 +772,16 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
if (!name)
continue;
const FullySpecifiedType &ty = templId->templateArgumentAt(i);
subst.bind(name, ty);
subst.bind(cloner.name(name, &subst), ty);
}
Clone cloner(_control.data());
foreach (Symbol *s, reference->symbols()) {
instantiation->_symbols.append(cloner.symbol(s, &subst));
Symbol *clone = cloner.symbol(s, &subst);
instantiation->_symbols.append(clone);
#ifdef DEBUG_LOOKUP
Overview oo;oo.setShowFunctionSignatures(true);oo.setShowReturnTypes(true);oo.setShowTemplateParameters(true);
qDebug()<<"cloned"<<oo(clone->type());
#endif // DEBUG_LOOKUP
}
} else {
instantiation->_symbols.append(reference->symbols());
......@@ -917,6 +950,9 @@ ClassOrNamespace *ClassOrNamespace::findOrCreateType(const Name *name, ClassOrNa
if (! e) {
e = _factory->allocClassOrNamespace(this);
#ifdef DEBUG_LOOKUP
e->_name = name;
#endif // DEBUG_LOOKUP
_classOrNamespaces[name] = e;
}
......
......@@ -136,6 +136,10 @@ private:
AlreadyConsideredClassContainer<Class> _alreadyConsideredClasses;
AlreadyConsideredClassContainer<TemplateNameId> _alreadyConsideredTemplates;
#ifdef DEBUG_LOOKUP
const Name *_name;
#endif // DEBUG_LOOKUP
friend class CreateBindings;
};
......
......@@ -830,16 +830,22 @@ ClassOrNamespace *ResolveExpression::findClass(const FullySpecifiedType &origina
static void resolveTypedefs(const LookupContext &context,
FullySpecifiedType *type,
Scope **scope)
Scope **scope, ClassOrNamespace *binding)
{
QSet<Symbol *> visited;
while (NamedType *namedTy = (*type)->asNamedType()) {
ClassOrNamespace *scopeCoN = context.lookupType(*scope);
if (!scopeCoN)
break;
// check if namedTy->name() resolves to a typedef
QList<LookupItem> namedTypeItems = scopeCoN->lookup(namedTy->name());
QList<LookupItem> namedTypeItems;
if (binding)
namedTypeItems = binding->lookup(namedTy->name());
if (ClassOrNamespace *scopeCon = context.lookupType(*scope))
namedTypeItems += scopeCon->lookup(namedTy->name());
#ifdef DEBUG_LOOKUP
qDebug() << "-- we have" << namedTypeItems.size() << "candidates";
#endif // DEBUG_LOOKUP
bool foundTypedef = false;
foreach (const LookupItem &it, namedTypeItems) {
if (it.declaration() && it.declaration()->isTypedef()) {
......@@ -864,11 +870,26 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
int accessOp,
bool *replacedDotOperator) const
{
#ifdef DEBUG_LOOKUP
qDebug() << "In ResolveExpression::baseExpression with" << baseResults.size() << "results...";
int i = 0;
Overview oo;
#endif // DEBUG_LOOKUP
foreach (const LookupItem &r, baseResults) {
FullySpecifiedType ty = r.type().simplified();
Scope *scope = r.scope();
resolveTypedefs(_context, &ty, &scope);
#ifdef DEBUG_LOOKUP
qDebug("trying result #%d", ++i);
qDebug()<<"- before typedef resolving we have:"<<oo(ty);
#endif // DEBUG_LOOKUP
resolveTypedefs(_context, &ty, &scope, r.binding());
#ifdef DEBUG_LOOKUP
qDebug()<<"- after typedef resolving:"<<oo(ty);
#endif // DEBUG_LOOKUP
if (accessOp == T_ARROW) {
if (PointerType *ptrTy = ty->asPointerType()) {
......@@ -892,7 +913,7 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
FullySpecifiedType retTy = instantiatedFunction->returnType().simplified();
resolveTypedefs(_context, &retTy, &functionScope);
resolveTypedefs(_context, &retTy, &functionScope, r.binding());
if (PointerType *ptrTy = retTy->asPointerType()) {
if (ClassOrNamespace *retBinding = findClass(ptrTy->elementType(), functionScope))
......
......@@ -2,6 +2,7 @@ TEMPLATE = lib
TARGET = CPlusPlus
DEFINES += NDEBUG
#DEFINES += DEBUG_LOOKUP
unix:QMAKE_CXXFLAGS_DEBUG += -O2
include(../../qtcreatorlibrary.pri)
......
......@@ -296,6 +296,76 @@ void CppToolsPlugin::test_completion_template_3()
QVERIFY(completions.contains("Tupple"));
QVERIFY(completions.contains("a"));
QVERIFY(completions.contains("b"));
}
void CppToolsPlugin::test_completion_template_4()
{
TestData data;
data.srcText = "\n"
"template <class T>\n"
"struct List\n"
"{\n"
" typedef T U;\n"
" U u;\n"
"};\n"
"\n"
"struct Tupple { int a; int b; };\n"
"\n"
"void func() {\n"
" List<Tupple> l;\n"
" @\n"
" // padding so we get the scope right\n"
"}";
setup(&data);
Utils::ChangeSet change;
QString txt = QLatin1String("l.u.");
change.insert(data.pos, txt);
QTextCursor cursor(data.doc);
change.apply(&cursor);
data.pos += txt.length();
QStringList completions = getCompletions(data);
QCOMPARE(completions.size(), 3);
QVERIFY(completions.contains("Tupple"));
QVERIFY(completions.contains("a"));
QVERIFY(completions.contains("b"));
}
void CppToolsPlugin::test_completion_template_5()
{
TestData data;
data.srcText = "\n"
"template <class T>\n"
"struct List\n"
"{\n"
" T u;\n"
"};\n"
"\n"
"struct Tupple { int a; int b; };\n"
"\n"
"void func() {\n"
" typedef List<Tupple> LT;\n"
" LT l;"
" @\n"
" // padding so we get the scope right\n"
"}";
setup(&data);
Utils::ChangeSet change;
QString txt = QLatin1String("l.u.");
change.insert(data.pos, txt);
QTextCursor cursor(data.doc);
change.apply(&cursor);
data.pos += txt.length();
QStringList completions = getCompletions(data);
QCOMPARE(completions.size(), 3);
QVERIFY(completions.contains("Tupple"));
QVERIFY(completions.contains("a"));
QVERIFY(completions.contains("b"));
}
......
......@@ -74,7 +74,6 @@ public:
private slots:
void switchHeaderSource();
#ifdef WITH_TESTS
// codegen tests
......@@ -95,6 +94,8 @@ private slots:
void test_completion_template_1();
void test_completion_template_2();
void test_completion_template_3();
void test_completion_template_4();
void test_completion_template_5();
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