diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp index 98310f9ee8bd737afdca8a6de360cc1420a7d1b6..b21b167655a3ccb96ac8694c2357d619a4922374 100644 --- a/src/libs/3rdparty/cplusplus/Bind.cpp +++ b/src/libs/3rdparty/cplusplus/Bind.cpp @@ -2639,10 +2639,14 @@ bool Bind::visit(TemplateIdAST *ast) } const Identifier *id = identifier(ast->identifier_token); + const int tokenKindBeforeIdentifier(translationUnit()->tokenKind(ast->identifier_token - 1)); + const bool isSpecialization = (tokenKindBeforeIdentifier == T_CLASS || + tokenKindBeforeIdentifier == T_STRUCT); if (templateArguments.empty()) - _name = control()->templateNameId(id); + _name = control()->templateNameId(id, isSpecialization); else - _name = control()->templateNameId(id, &templateArguments[0], templateArguments.size()); + _name = control()->templateNameId(id, isSpecialization, &templateArguments[0], + templateArguments.size()); ast->name = _name; return false; diff --git a/src/libs/3rdparty/cplusplus/Control.cpp b/src/libs/3rdparty/cplusplus/Control.cpp index ba709de0527a6aa5b164ff99d3e7e93ed3fd7af5..8f6136534ffdc8220bdd3bfac87ac7dbc577f79a 100644 --- a/src/libs/3rdparty/cplusplus/Control.cpp +++ b/src/libs/3rdparty/cplusplus/Control.cpp @@ -131,9 +131,18 @@ template <> struct Compare<TemplateNameId> const Identifier *id = name.identifier(); const Identifier *otherId = otherName.identifier(); - if (id == otherId) - return std::lexicographical_compare(name.firstTemplateArgument(), name.lastTemplateArgument(), - otherName.firstTemplateArgument(), otherName.lastTemplateArgument()); + if (id == otherId) { + // we have to differentiate TemplateNameId with respect to specialization or + // instantiation + if (name.isSpecialization() == otherName.isSpecialization()) { + return std::lexicographical_compare(name.firstTemplateArgument(), + name.lastTemplateArgument(), + otherName.firstTemplateArgument(), + otherName.lastTemplateArgument()); + } else { + return name.isSpecialization(); + } + } return id < otherId; } @@ -211,9 +220,10 @@ public: } template <typename _Iterator> - const TemplateNameId *findOrInsertTemplateNameId(const Identifier *id, _Iterator first, _Iterator last) + const TemplateNameId *findOrInsertTemplateNameId(const Identifier *id, bool isSpecialization, + _Iterator first, _Iterator last) { - return templateNameIds.intern(TemplateNameId(id, first, last)); + return templateNameIds.intern(TemplateNameId(id, isSpecialization, first, last)); } const DestructorNameId *findOrInsertDestructorNameId(const Name *name) @@ -598,10 +608,11 @@ const NumericLiteral *Control::numericLiteral(const char *chars) } const TemplateNameId *Control::templateNameId(const Identifier *id, + bool isSpecialization, const FullySpecifiedType *const args, unsigned argv) { - return d->findOrInsertTemplateNameId(id, args, args + argv); + return d->findOrInsertTemplateNameId(id, isSpecialization, args, args + argv); } const DestructorNameId *Control::destructorNameId(const Name *name) diff --git a/src/libs/3rdparty/cplusplus/Control.h b/src/libs/3rdparty/cplusplus/Control.h index 4132d5f3cfaa695ad2e67b70d341b66e55a37170..cef4df40f75ba0d3cd459845c17d9c2bad91beb8 100644 --- a/src/libs/3rdparty/cplusplus/Control.h +++ b/src/libs/3rdparty/cplusplus/Control.h @@ -51,6 +51,7 @@ public: /// Returns the canonical template name id. const TemplateNameId *templateNameId(const Identifier *id, + bool isSpecialization, const FullySpecifiedType *const args = 0, unsigned argc = 0); diff --git a/src/libs/3rdparty/cplusplus/Names.cpp b/src/libs/3rdparty/cplusplus/Names.cpp index 9024b65a914a4f517cb618ff6678f6304045373e..4c5cb7db7949ba18551aa69f233db9a10c65c289 100644 --- a/src/libs/3rdparty/cplusplus/Names.cpp +++ b/src/libs/3rdparty/cplusplus/Names.cpp @@ -128,6 +128,27 @@ bool TemplateNameId::isEqualTo(const Name *other) const return true; } +bool TemplateNameId::Compare::operator()(const TemplateNameId *name, + const TemplateNameId *other) const +{ + const Identifier *id = name->identifier(); + const Identifier *otherId = other->identifier(); + + if (id == otherId) { + // we have to differentiate TemplateNameId with respect to specialization or instantiation + if (name->isSpecialization() == other->isSpecialization()) { + return std::lexicographical_compare(name->firstTemplateArgument(), + name->lastTemplateArgument(), + other->firstTemplateArgument(), + other->lastTemplateArgument()); + } else { + return name->isSpecialization(); + } + } + + return id < otherId; +} + OperatorNameId::OperatorNameId(Kind kind) : _kind(kind) { } diff --git a/src/libs/3rdparty/cplusplus/Names.h b/src/libs/3rdparty/cplusplus/Names.h index 70c782591f5264939af57a7e9bc5328598a9e03d..e600ec1dafee8abd8d1190b999a55167469c3bc8 100644 --- a/src/libs/3rdparty/cplusplus/Names.h +++ b/src/libs/3rdparty/cplusplus/Names.h @@ -80,8 +80,11 @@ class CPLUSPLUS_EXPORT TemplateNameId: public Name { public: template <typename _Iterator> - TemplateNameId(const Identifier *identifier, _Iterator first, _Iterator last) - : _identifier(identifier), _templateArguments(first, last) {} + TemplateNameId(const Identifier *identifier, bool isSpecialization, _Iterator first, + _Iterator last) + : _identifier(identifier) + , _templateArguments(first, last) + , _isSpecialization(isSpecialization) {} virtual ~TemplateNameId(); @@ -100,6 +103,15 @@ public: TemplateArgumentIterator firstTemplateArgument() const { return _templateArguments.begin(); } TemplateArgumentIterator lastTemplateArgument() const { return _templateArguments.end(); } + bool isSpecialization() const { return _isSpecialization; } + // this is temporary solution needed in ClassOrNamespace::nestedType + // when we try to find correct specialization for instantiation + void setIsSpecialization(bool isSpecialization) { _isSpecialization = isSpecialization; } + + // Comparator needed to distinguish between two different TemplateNameId(e.g.:used in std::map) + struct Compare: std::binary_function<const TemplateNameId *, const TemplateNameId *, bool> { + bool operator()(const TemplateNameId *name, const TemplateNameId *other) const; + }; protected: virtual void accept0(NameVisitor *visitor) const; @@ -107,6 +119,8 @@ protected: private: const Identifier *_identifier; std::vector<FullySpecifiedType> _templateArguments; + // now TemplateNameId can be a specialization or an instantiation + bool _isSpecialization; }; class CPLUSPLUS_EXPORT OperatorNameId: public Name diff --git a/src/libs/3rdparty/cplusplus/Templates.cpp b/src/libs/3rdparty/cplusplus/Templates.cpp index fd1ee873d9ea33f793c47720709603a0b166956d..c81ed51b09c483f750dbc56f75044cb0ff435995 100644 --- a/src/libs/3rdparty/cplusplus/Templates.cpp +++ b/src/libs/3rdparty/cplusplus/Templates.cpp @@ -414,9 +414,10 @@ void CloneName::visit(const TemplateNameId *name) for (unsigned i = 0; i < args.size(); ++i) args[i] = _clone->type(name->templateArgumentAt(i), _subst); if (args.empty()) - _name = _control->templateNameId(_clone->identifier(name->identifier())); + _name = _control->templateNameId(_clone->identifier(name->identifier()), name->isSpecialization()); else - _name = _control->templateNameId(_clone->identifier(name->identifier()), &args[0], args.size()); + _name = _control->templateNameId(_clone->identifier(name->identifier()), name->isSpecialization(), + &args[0], args.size()); } void CloneName::visit(const DestructorNameId *name) @@ -528,7 +529,8 @@ FullySpecifiedType Subst::apply(const Name *name) const const NamedType *name = apply(q->base())->asNamedType(); const NamedType *unqualified = apply(q->name())->asNamedType(); if (name && name->name()->identifier() != 0 && unqualified) - return control()->namedType(control()->qualifiedNameId(name->name()->identifier(), unqualified->name())); + return control()->namedType(control()->qualifiedNameId(name->name()->identifier(), + unqualified->name())); } } diff --git a/src/libs/cplusplus/CppRewriter.cpp b/src/libs/cplusplus/CppRewriter.cpp index 2dee6ddc9434a35ebe0603649f2639b8c7c41903..15d3b9d156fe723055281ca14bfecf3c347ff266 100644 --- a/src/libs/cplusplus/CppRewriter.cpp +++ b/src/libs/cplusplus/CppRewriter.cpp @@ -263,7 +263,8 @@ public: QVarLengthArray<FullySpecifiedType, 8> args(name->templateArgumentCount()); for (unsigned i = 0; i < name->templateArgumentCount(); ++i) args[i] = rewrite->rewriteType(name->templateArgumentAt(i)); - temps.append(control()->templateNameId(identifier(name->identifier()), args.data(), args.size())); + temps.append(control()->templateNameId(identifier(name->identifier()), name->isSpecialization(), + args.data(), args.size())); } virtual void visit(const DestructorNameId *name) diff --git a/src/libs/cplusplus/DeprecatedGenTemplateInstance.cpp b/src/libs/cplusplus/DeprecatedGenTemplateInstance.cpp index eb27a6bf555cf5a52a2c3fff5a61501773bd761d..83a833cc38932300ef0a6c113ade03dce39fe323 100644 --- a/src/libs/cplusplus/DeprecatedGenTemplateInstance.cpp +++ b/src/libs/cplusplus/DeprecatedGenTemplateInstance.cpp @@ -253,6 +253,7 @@ private: } const TemplateNameId *templId = control()->templateNameId(name->identifier(), + name->isSpecialization(), arguments.data(), arguments.size()); _type = control()->namedType(templId); @@ -269,13 +270,15 @@ private: } else if (const TemplateNameId *templId = name->asTemplateNameId()) { QVarLengthArray<FullySpecifiedType, 8> arguments(templId->templateArgumentCount()); - for (unsigned templateArgIndex = 0; templateArgIndex < templId->templateArgumentCount(); ++templateArgIndex) { + for (unsigned templateArgIndex = 0; templateArgIndex < templId->templateArgumentCount(); + ++templateArgIndex) { FullySpecifiedType argTy = templId->templateArgumentAt(templateArgIndex); arguments[templateArgIndex] = q->apply(argTy); } const Identifier *id = control()->identifier(templId->identifier()->chars(), templId->identifier()->size()); - return control()->templateNameId(id, arguments.data(), arguments.size()); + return control()->templateNameId(id, templId->isSpecialization(), arguments.data(), + arguments.size()); } else if (const QualifiedNameId *qq = name->asQualifiedNameId()) { const Name *base = instantiate(qq->base()); diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index b73c22b6571c6721feb46d459450d5ded03fc230..32e2fef6ddc8968913069584ec062fe68a2b636c 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -716,6 +716,42 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac ClassOrNamespace *reference = it->second; + const TemplateNameId *templId = name->asTemplateNameId(); + if (templId) { + // if it is a TemplateNameId it could be a specialization(full or partial) or + // instantiation of one of the specialization(reference->_specialization) or + // base class(reference) + if (templId->isSpecialization()) { + // if it is a specialization we try to find or create new one and + // add to base class(reference) + TemplateNameIdTable::const_iterator cit = reference->_specializations.find(templId); + if (cit != reference->_specializations.end()) { + return cit->second; + } else { + ClassOrNamespace *newSpecialization = _factory->allocClassOrNamespace(reference); +#ifdef DEBUG_LOOKUP + newSpecialization->_name = templId; +#endif // DEBUG_LOOKUP + reference->_specializations[templId] = newSpecialization; + return newSpecialization; + } + } else { + TemplateNameId *nonConstTemplId = const_cast<TemplateNameId *>(templId); + // make this instantiation looks like specialization which help to find + // full specialization for this instantiation + nonConstTemplId->setIsSpecialization(true); + TemplateNameIdTable::const_iterator cit = reference->_specializations.find(templId); + if (cit != reference->_specializations.end()) { + // we found full specialization + reference = cit->second; + } else { + // TODO: find the best specialization(probably partial) for this instantiation + } + // let's instantiation be instantiation + nonConstTemplId->setIsSpecialization(false); + } + } + // The reference binding might still be missing some of its base classes in the case they // are templates. We need to collect them now. First, we track the bases which are already // part of the binding so we can identify the missings ones later. @@ -737,7 +773,6 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac if (!referenceClass) return reference; - const TemplateNameId *templId = name->asTemplateNameId(); if ((! templId && _alreadyConsideredClasses.contains(referenceClass)) || (templId && _alreadyConsideredTemplates.contains(templId))) { @@ -752,9 +787,6 @@ 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 @@ -863,7 +895,6 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac } _alreadyConsideredTemplates.clear(templId); - _instantiations[templId] = instantiation; return instantiation; } diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index 8144cbfa84d615785a16a8c8b8e684c9d46b8964..d5f6a9acc3e2c013b6dbd6cc7fd4f3eccc092479 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -101,6 +101,7 @@ private: private: typedef std::map<const Name *, ClassOrNamespace *, Name::Compare> Table; + typedef std::map<const TemplateNameId *, ClassOrNamespace *, TemplateNameId::Compare> TemplateNameIdTable; CreateBindings *_factory; ClassOrNamespace *_parent; @@ -110,7 +111,7 @@ private: QList<Enum *> _enums; QList<Symbol *> _todo; QSharedPointer<Control> _control; - QMap<const Name *, ClassOrNamespace *> _instantiations; + TemplateNameIdTable _specializations; // it's an instantiation. const TemplateNameId *_templateId; diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index 80471cd8dfb89429e139900c837db8bac6f3b298..63325d521ff1dc4d6b61f2b6a87b4ce89fe93f91 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -564,6 +564,42 @@ void CppToolsPlugin::test_completion_type_of_pointer_is_typedef() QVERIFY(completions.contains(QLatin1String("foo"))); } +void CppToolsPlugin::test_completion_instantiate_full_specialization() +{ + TestData data; + data.srcText = "\n" + "template<typename T>\n" + "struct Template\n" + "{\n" + " int templateT_i;\n" + "};\n" + "\n" + "template<>\n" + "struct Template<char>\n" + "{\n" + " int templateChar_i;\n" + "};\n" + "\n" + "Template<char> templateChar;\n" + "@\n" + ; + + setup(&data); + + Utils::ChangeSet change; + QString txt = QLatin1String("templateChar."); + 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("Template"))); + QVERIFY(completions.contains(QLatin1String("templateChar_i"))); +} + void CppToolsPlugin::test_completion() { QFETCH(QByteArray, code); diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index 012d03112cf7f8e49ae4c20efea0f8797a7ede8a..ba6c47a548da37158f7a51e484ab61a9c8611529 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -100,6 +100,7 @@ private slots: void test_completion_template_6(); void test_completion_template_7(); void test_completion_type_of_pointer_is_typedef(); + void test_completion_instantiate_full_specialization(); void test_completion_template_as_base(); void test_completion_template_as_base_data(); void test_completion_use_global_identifier_as_base_class(); diff --git a/tests/auto/cplusplus/semantic/tst_semantic.cpp b/tests/auto/cplusplus/semantic/tst_semantic.cpp index 9644621cf7361e7670f8b0c526978b837aaa944e..cf789c554226439a385db03b50fcb372e66aa3f7 100644 --- a/tests/auto/cplusplus/semantic/tst_semantic.cpp +++ b/tests/auto/cplusplus/semantic/tst_semantic.cpp @@ -481,7 +481,7 @@ void tst_Semantic::template_instance_1() QVERIFY(decl); FullySpecifiedType templArgs[] = { control->integerType(IntegerType::Int) }; - const Name *templId = control->templateNameId(control->identifier("QList"), templArgs, 1); + const Name *templId = control->templateNameId(control->identifier("QList"), false, templArgs, 1); FullySpecifiedType genTy = DeprecatedGenTemplateInstance::instantiate(templId, decl, control);