diff options
-rw-r--r-- | src/libs/3rdparty/cplusplus/Symbol.cpp | 2 | ||||
-rw-r--r-- | src/libs/3rdparty/cplusplus/Templates.h | 2 | ||||
-rw-r--r-- | src/libs/cplusplus/LookupContext.cpp | 142 | ||||
-rw-r--r-- | src/libs/cplusplus/LookupContext.h | 43 | ||||
-rw-r--r-- | src/libs/cplusplus/ResolveExpression.cpp | 15 | ||||
-rw-r--r-- | src/libs/cplusplus/ResolveExpression.h | 3 | ||||
-rw-r--r-- | src/plugins/cpptools/cppcompletion_test.cpp | 84 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptoolsplugin.h | 2 |
8 files changed, 271 insertions, 22 deletions
diff --git a/src/libs/3rdparty/cplusplus/Symbol.cpp b/src/libs/3rdparty/cplusplus/Symbol.cpp index b6216f002c..cd8ec1ffdd 100644 --- a/src/libs/3rdparty/cplusplus/Symbol.cpp +++ b/src/libs/3rdparty/cplusplus/Symbol.cpp @@ -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), diff --git a/src/libs/3rdparty/cplusplus/Templates.h b/src/libs/3rdparty/cplusplus/Templates.h index 04064a8906..2784f02d7f 100644 --- a/src/libs/3rdparty/cplusplus/Templates.h +++ b/src/libs/3rdparty/cplusplus/Templates.h @@ -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; diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 446aa4ed7e..482c1c1a76 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -260,7 +260,8 @@ ClassOrNamespace *LookupContext::globalNamespace() const return bindings()->globalNamespace(); } -ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope) const +ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope, + ClassOrNamespace* enclosingTemplateInstantiation) const { if (! scope) { return 0; @@ -286,16 +287,17 @@ ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope) cons } } return lookupType(name, scope->enclosingScope()); - } else if (ClassOrNamespace *b = bindings()->lookupType(scope)) { + } else if (ClassOrNamespace *b = bindings()->lookupType(scope, enclosingTemplateInstantiation)) { return b->lookupType(name); } return 0; } -ClassOrNamespace *LookupContext::lookupType(Symbol *symbol) const +ClassOrNamespace *LookupContext::lookupType(Symbol *symbol, + ClassOrNamespace* enclosingTemplateInstantiation) const { - return bindings()->lookupType(symbol); + return bindings()->lookupType(symbol, enclosingTemplateInstantiation); } QList<LookupItem> LookupContext::lookup(const Name *name, Scope *scope) const @@ -587,7 +589,8 @@ void CreateBindings::lookupInScope(const Name *name, Scope *scope, #ifdef DEBUG_LOOKUP Overview oo; - qDebug() << "Found" << id->chars() << "in" << (binding ? oo(binding->_name) : "<null>"); + qDebug() << "Found" << id->chars() << "in" + << (binding ? oo(binding->_name) : QString::fromAscii("<null>")); #endif // DEBUG_LOOKUP LookupItem item; @@ -670,16 +673,23 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name, if (_usings.size() == 1) { ClassOrNamespace *delegate = _usings.first(); - if (ClassOrNamespace *r = delegate->lookupType_helper(name, processed, /*searchInEnclosingScope = */ true, origin)) + if (ClassOrNamespace *r = delegate->lookupType_helper(name, + processed, + /*searchInEnclosingScope = */ true, + origin)) return r; } else { if (debug) - qWarning() << "expected one using declaration. Number of using declarations is:" << _usings.size(); + qWarning() << "expected one using declaration. Number of using declarations is:" + << _usings.size(); } } foreach (ClassOrNamespace *u, usings()) { - if (ClassOrNamespace *r = u->lookupType_helper(name, processed, /*searchInEnclosingScope =*/ false, origin)) + if (ClassOrNamespace *r = u->lookupType_helper(name, + processed, + /*searchInEnclosingScope =*/ false, + origin)) return r; } } @@ -740,6 +750,9 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac // If we are dealling with a template type, more work is required, since we need to // construct all instantiation data. if (templId) { + if (_instantiations.contains(templId)) + return _instantiations[templId]; + _alreadyConsideredTemplates.insert(templId); ClassOrNamespace *instantiation = _factory->allocClassOrNamespace(reference); #ifdef DEBUG_LOOKUP @@ -747,7 +760,6 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac #endif // DEBUG_LOOKUP instantiation->_templateId = templId; instantiation->_instantiationOrigin = origin; - instantiation->_classOrNamespaces = reference->_classOrNamespaces; // The instantiation should have all symbols, enums, and usings from the reference. instantiation->_enums.append(reference->unscopedEnums()); @@ -780,6 +792,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac qDebug()<<"cloned"<<oo(clone->type()); #endif // DEBUG_LOOKUP } + instantiateNestedClasses(reference, cloner, subst, instantiation); } else { instantiation->_symbols.append(reference->symbols()); } @@ -843,10 +856,12 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac instantiation->addUsing(baseBinding); } } else { + instantiation->_classOrNamespaces = reference->_classOrNamespaces; instantiation->_symbols.append(reference->symbols()); } _alreadyConsideredTemplates.clear(templId); + _instantiations[templId] = instantiation; return instantiation; } @@ -881,6 +896,104 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac return reference; } + +void ClassOrNamespace::instantiateNestedClasses(ClassOrNamespace *enclosingTemplateClass, + Clone &cloner, + Subst &subst, + ClassOrNamespace *enclosingTemplateClassInstantiation) +{ + NestedClassInstantiator nestedClassInstantiator(_factory, cloner, subst); + nestedClassInstantiator.instantiate(enclosingTemplateClass, enclosingTemplateClassInstantiation); +} + +void ClassOrNamespace::NestedClassInstantiator::instantiate(ClassOrNamespace *enclosingTemplateClass, + ClassOrNamespace *enclosingTemplateClassInstantiation) +{ + if (_alreadyConsideredNestedClassInstantiations.contains(enclosingTemplateClass)) + return; + _alreadyConsideredNestedClassInstantiations.insert(enclosingTemplateClass); + ClassOrNamespace::Table::const_iterator cit = enclosingTemplateClass->_classOrNamespaces.begin(); + for (; cit != enclosingTemplateClass->_classOrNamespaces.end(); ++cit) { + const Name *nestedName = cit->first; + ClassOrNamespace *nestedClassOrNamespace = cit->second; + ClassOrNamespace *nestedClassOrNamespaceInstantiation = nestedClassOrNamespace; + + if (isInstantiateNestedClassNeeded(nestedClassOrNamespace->_symbols)) { + nestedClassOrNamespaceInstantiation = _factory->allocClassOrNamespace(nestedClassOrNamespace); + nestedClassOrNamespaceInstantiation->_enums.append(nestedClassOrNamespace->unscopedEnums()); + nestedClassOrNamespaceInstantiation->_usings.append(nestedClassOrNamespace->usings()); + nestedClassOrNamespaceInstantiation->_instantiationOrigin = nestedClassOrNamespace; + + foreach (Symbol *s, nestedClassOrNamespace->_symbols) { + Symbol *clone = _cloner.symbol(s, &_subst); + nestedClassOrNamespaceInstantiation->_symbols.append(clone); + } + } + + instantiate(nestedClassOrNamespace, nestedClassOrNamespaceInstantiation); + + enclosingTemplateClassInstantiation->_classOrNamespaces[nestedName] = + nestedClassOrNamespaceInstantiation; + } + _alreadyConsideredNestedClassInstantiations.remove(enclosingTemplateClass); +} + +bool ClassOrNamespace::NestedClassInstantiator::isInstantiateNestedClassNeeded(const QList<Symbol *> &symbols) const +{ + foreach (Symbol *s, symbols) { + if (Class *klass = s->asClass()) { + int memberCount = klass->memberCount(); + for (int i = 0; i < memberCount; ++i) { + Symbol *memberAsSymbol = klass->memberAt(i); + if (Declaration *declaration = memberAsSymbol->asDeclaration()) { + if (containsTemplateType(declaration)) + return true; + } + else if (Function *function = memberAsSymbol->asFunction()) { + if (containsTemplateType(function)) + return true; + } + } + } + } + + return false; +} + +bool ClassOrNamespace::NestedClassInstantiator::containsTemplateType(Declaration *declaration) const +{ + Type *memberType = declaration->type().type(); + NamedType *memberNamedType = findMemberNamedType(memberType); + if (memberNamedType) { + const Name *name = memberNamedType->name(); + if (_subst.contains(name)) { + return true; + } + } + return false; +} + +bool ClassOrNamespace::NestedClassInstantiator::containsTemplateType(Function * /*function*/) const +{ + //TODO: make implementation + return false; +} + +NamedType *ClassOrNamespace::NestedClassInstantiator::findMemberNamedType(Type *memberType) const +{ + if (NamedType *namedType = memberType->asNamedType()) { + return namedType; + } + else if (PointerType *pointerType = memberType->asPointerType()) { + return findMemberNamedType(pointerType->elementType().type()); + } + else if (ReferenceType *referenceType = memberType->asReferenceType()) { + return findMemberNamedType(referenceType->elementType().type()); + } + + return 0; +} + void ClassOrNamespace::flush() { if (! _todo.isEmpty()) { @@ -973,17 +1086,22 @@ ClassOrNamespace *CreateBindings::globalNamespace() const return _globalNamespace; } -ClassOrNamespace *CreateBindings::lookupType(Symbol *symbol) +ClassOrNamespace *CreateBindings::lookupType(Symbol *symbol, ClassOrNamespace* enclosingTemplateInstantiation) { const QList<const Name *> path = LookupContext::path(symbol); - return lookupType(path); + return lookupType(path, enclosingTemplateInstantiation); } -ClassOrNamespace *CreateBindings::lookupType(const QList<const Name *> &path) +ClassOrNamespace *CreateBindings::lookupType(const QList<const Name *> &path, ClassOrNamespace* enclosingTemplateInstantiation) { if (path.isEmpty()) return _globalNamespace; + if (enclosingTemplateInstantiation) { + if (ClassOrNamespace *b = enclosingTemplateInstantiation->lookupType(path.last())) + return b; + } + ClassOrNamespace *b = _globalNamespace->lookupType(path.at(0)); for (int i = 1; b && i < path.size(); ++i) diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index b7c84d4e25..19fec679ea 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -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 diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp index eaa53f738f..a6c3c10141 100644 --- a/src/libs/cplusplus/ResolveExpression.cpp +++ b/src/libs/cplusplus/ResolveExpression.cpp @@ -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; } } diff --git a/src/libs/cplusplus/ResolveExpression.h b/src/libs/cplusplus/ResolveExpression.h index 895a63727e..8a3fbeb072 100644 --- a/src/libs/cplusplus/ResolveExpression.h +++ b/src/libs/cplusplus/ResolveExpression.h @@ -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); diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index 9eb28dca97..3d2abd7095 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -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"))); +} diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index b56ee4aa0d..29f119db76 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -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(); |