Commit b1199ef0 authored by Przemyslaw Gorszkowski's avatar Przemyslaw Gorszkowski Committed by hjk

C++: Fix code completion for nested classes

Fix code completion for nested classes when enclosing is
template class.
Unit tests

Task-number: QTCREATORBUG-8245 (only standalone)
Change-Id: Ib31ad4b799db927b56debd4dc3e7403404c1839d
Reviewed-by: default avatarhjk <qthjk@ovi.com>
parent 17748280
......@@ -105,7 +105,7 @@ Symbol::Symbol(TranslationUnit *translationUnit, unsigned sourceLocation, const
Symbol::Symbol(Clone *clone, Subst *subst, Symbol *original)
: _name(clone->name(original->_name, subst)),
_scope(0),
_scope(original->_scope),
_next(0),
_fileId(clone->control()->stringLiteral(original->fileName(), original->fileNameLength())),
_sourceLocation(original->_sourceLocation),
......
......@@ -54,6 +54,8 @@ public:
FullySpecifiedType &operator[](const Name *name) { return _map[name]; }
bool contains(const Name *name) const { return _map.find(name) != _map.end(); }
private:
Control *_control;
Subst *_previous;
......
This diff is collapsed.
......@@ -41,6 +41,7 @@
#include <QSet>
#include <map>
#include <functional>
#include <QMap>
namespace CPlusPlus {
......@@ -92,8 +93,15 @@ private:
ClassOrNamespace *nestedType(const Name *name, ClassOrNamespace *origin);
void instantiateNestedClasses(ClassOrNamespace *enclosingTemplateClass,
Clone &cloner,
Subst &subst,
ClassOrNamespace *enclosingTemplateClassInstantiation);
bool isInstantiateNestedClassNeeded(const QList<Symbol *>& symbols, const Subst &subst) const;
private:
typedef std::map<const Name *, ClassOrNamespace *, Name::Compare> Table;
CreateBindings *_factory;
ClassOrNamespace *_parent;
QList<Symbol *> _symbols;
......@@ -102,6 +110,7 @@ private:
QList<Enum *> _enums;
QList<Symbol *> _todo;
QSharedPointer<Control> _control;
QMap<const Name *, ClassOrNamespace *> _instantiations;
// it's an instantiation.
const TemplateNameId *_templateId;
......@@ -110,6 +119,28 @@ private:
AlreadyConsideredClassContainer<Class> _alreadyConsideredClasses;
AlreadyConsideredClassContainer<TemplateNameId> _alreadyConsideredTemplates;
class NestedClassInstantiator
{
public:
NestedClassInstantiator(CreateBindings *factory, Clone &cloner, Subst &subst)
: _factory(factory)
, _cloner(cloner)
, _subst(subst)
{}
void instantiate(ClassOrNamespace *enclosingTemplateClass,
ClassOrNamespace *enclosingTemplateClassInstantiation);
private:
bool isInstantiateNestedClassNeeded(const QList<Symbol *> &symbols) const;
bool containsTemplateType(Declaration *declaration) const;
bool containsTemplateType(Function *function) const;
NamedType *findMemberNamedType(Type *memberType) const;
QSet<ClassOrNamespace *> _alreadyConsideredNestedClassInstantiations;
CreateBindings *_factory;
Clone &_cloner;
Subst &_subst;
};
#ifdef DEBUG_LOOKUP
public:
const Name *_name;
......@@ -130,8 +161,10 @@ public:
ClassOrNamespace *globalNamespace() const;
/// Finds the binding associated to the given symbol.
ClassOrNamespace *lookupType(Symbol *symbol);
ClassOrNamespace *lookupType(const QList<const Name *> &path);
ClassOrNamespace *lookupType(Symbol *symbol,
ClassOrNamespace* enclosingTemplateInstantiation = 0);
ClassOrNamespace *lookupType(const QList<const Name *> &path,
ClassOrNamespace* enclosingTemplateInstantiation = 0);
/// Returns the Control that must be used to create temporary symbols.
/// \internal
......@@ -228,8 +261,10 @@ public:
ClassOrNamespace *globalNamespace() const;
QList<LookupItem> lookup(const Name *name, Scope *scope) const;
ClassOrNamespace *lookupType(const Name *name, Scope *scope) const;
ClassOrNamespace *lookupType(Symbol *symbol) const;
ClassOrNamespace *lookupType(const Name *name, Scope *scope,
ClassOrNamespace* enclosingTemplateInstantiation = 0) const;
ClassOrNamespace *lookupType(Symbol *symbol,
ClassOrNamespace* enclosingTemplateInstantiation = 0) const;
ClassOrNamespace *lookupParent(Symbol *symbol) const;
/// \internal
......
......@@ -811,16 +811,17 @@ bool ResolveExpression::visit(MemberAccessAST *ast)
return false;
}
ClassOrNamespace *ResolveExpression::findClass(const FullySpecifiedType &originalTy, Scope *scope) const
ClassOrNamespace *ResolveExpression::findClass(const FullySpecifiedType &originalTy, Scope *scope,
ClassOrNamespace* enclosingTemplateInstantiation) const
{
FullySpecifiedType ty = originalTy.simplified();
ClassOrNamespace *binding = 0;
if (Class *klass = ty->asClassType())
binding = _context.lookupType(klass);
binding = _context.lookupType(klass, enclosingTemplateInstantiation);
else if (NamedType *namedTy = ty->asNamedType())
binding = _context.lookupType(namedTy->name(), scope);
binding = _context.lookupType(namedTy->name(), scope, enclosingTemplateInstantiation);
else if (Function *funTy = ty->asFunctionType())
return findClass(funTy->returnType(), scope);
......@@ -979,7 +980,13 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
}
}
if (ClassOrNamespace *binding = findClass(ty, scope))
ClassOrNamespace *enclosingTemplateInstantiation = 0;
if (ClassOrNamespace *binding = r.binding()) {
if (binding->instantiationOrigin())
enclosingTemplateInstantiation = binding;
}
if (ClassOrNamespace *binding = findClass(ty, scope, enclosingTemplateInstantiation))
return binding;
}
}
......
......@@ -56,7 +56,8 @@ public:
const LookupContext &context() const;
protected:
ClassOrNamespace *findClass(const FullySpecifiedType &ty, Scope *scope) const;
ClassOrNamespace *findClass(const FullySpecifiedType &ty, Scope *scope,
ClassOrNamespace* enclosingTemplateInstantiation = 0) const;
QList<LookupItem> expression(ExpressionAST *ast);
......
......@@ -1176,3 +1176,87 @@ void CppToolsPlugin::test_completion_enclosing_template_class_data()
QTest::newRow("case: nested template class with enclosing template class")
<< code << completions;
}
void CppToolsPlugin::test_completion_instantiate_nested_class_when_enclosing_is_template()
{
TestData data;
data.srcText = "\n"
"struct Foo \n"
"{\n"
" int foo_i;\n"
"};\n"
"\n"
"template <typename T>\n"
"struct Enclosing\n"
"{\n"
" struct Nested\n"
" {\n"
" T nested_t;\n"
" } nested;\n"
"\n"
" T enclosing_t;\n"
"};\n"
"\n"
"Enclosing<Foo> enclosing;\n"
"@\n"
;
setup(&data);
Utils::ChangeSet change;
QString txt = QLatin1String("enclosing.nested.nested_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("Foo")));
QVERIFY(completions.contains(QLatin1String("foo_i")));
}
void CppToolsPlugin::test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template()
{
TestData data;
data.srcText = "\n"
"struct Foo \n"
"{\n"
" int foo_i;\n"
"};\n"
"\n"
"template <typename T>\n"
"struct Enclosing\n"
"{\n"
" struct Nested\n"
" {\n"
" T nested_t;\n"
" struct NestedNested\n"
" {\n"
" T nestedNested_t;\n"
" } nestedNested;\n"
" } nested;\n"
"\n"
" T enclosing_t;\n"
"};\n"
"\n"
"Enclosing<Foo> enclosing;\n"
"@\n"
;
setup(&data);
Utils::ChangeSet change;
QString txt = QLatin1String("enclosing.nested.nestedNested.nestedNested_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("Foo")));
QVERIFY(completions.contains(QLatin1String("foo_i")));
}
......@@ -110,6 +110,8 @@ private slots:
void test_completion_cyclic_inheritance_data();
void test_completion_enclosing_template_class();
void test_completion_enclosing_template_class_data();
void test_completion_instantiate_nested_class_when_enclosing_is_template();
void test_completion_instantiate_nested_of_nested_class_when_enclosing_is_template();
private:
void test_completion();
......
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