summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/cplusplus/LookupContext.cpp133
-rw-r--r--src/libs/cplusplus/LookupContext.h3
-rw-r--r--src/plugins/cpptools/cppcompletion_test.cpp169
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.h2
4 files changed, 294 insertions, 13 deletions
diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index fc5e54c26a..dae8eb6a02 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -32,6 +32,7 @@
#include "ResolveExpression.h"
#include "Overview.h"
#include "DeprecatedGenTemplateInstance.h"
+#include "CppRewriter.h"
#include <CoreTypes.h>
#include <Symbols.h>
@@ -382,9 +383,6 @@ QList<Enum *> ClassOrNamespace::enums() const
QList<Symbol *> ClassOrNamespace::symbols() const
{
- if (_templateId && ! _usings.isEmpty())
- return _usings.first()->symbols(); // ask to the base implementation
-
const_cast<ClassOrNamespace *>(this)->flush();
return _symbols;
}
@@ -608,7 +606,7 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name,
return 0;
}
-ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespace *origin) const
+ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespace *origin)
{
Q_ASSERT(name != 0);
Q_ASSERT(name->isNameId() || name->isTemplateNameId());
@@ -616,21 +614,131 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
const_cast<ClassOrNamespace *>(this)->flush();
Table::const_iterator it = _classOrNamespaces.find(name);
-
if (it == _classOrNamespaces.end())
return 0;
- ClassOrNamespace *c = it->second;
+ ClassOrNamespace *reference = it->second;
+
+ // 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.
+
+ QSet<const Name *> knownBases;
+ foreach (ClassOrNamespace *con, reference->usings()) {
+ foreach (Symbol *s, con->symbols()) {
+ if (Class *c = s->asClass()) {
+ knownBases.insert(c->name());
+ break;
+ }
+ }
+ }
+ Class *referenceClass = 0;
+ QList<const Name *> missingBases;
+ foreach (Symbol *s, reference->symbols()) {
+ if (Class *clazz = s->asClass()) {
+ for (unsigned i = 0; i < clazz->baseClassCount(); ++i) {
+ BaseClass *baseClass = clazz->baseClassAt(i);
+ if (baseClass->name() && !knownBases.contains(baseClass->name()))
+ missingBases.append(baseClass->name());
+ }
+ referenceClass = clazz;
+ break;
+ }
+ }
+
+ if (!referenceClass)
+ return reference;
+
+ // If we are dealling with a template type, more work is required, since we need to
+ // construct all instantiation data.
if (const TemplateNameId *templId = name->asTemplateNameId()) {
- ClassOrNamespace *i = _factory->allocClassOrNamespace(c);
- i->_templateId = templId;
- i->_instantiationOrigin = origin;
- i->_usings.append(c);
- return i;
+ ClassOrNamespace *instantiation = _factory->allocClassOrNamespace(reference);
+ instantiation->_templateId = templId;
+ instantiation->_instantiationOrigin = origin;
+
+ // The instantiation should have all symbols, enums, and usings from the reference.
+ instantiation->_symbols.append(reference->symbols());
+ instantiation->_enums.append(reference->enums());
+ instantiation->_usings.append(reference->usings());
+
+ // It gets a bit complicated if the reference is actually a class template because we
+ // now must worry about dependent names in base classes.
+ if (Template *templ = referenceClass->enclosingTemplate()) {
+ QHash<const Name*, unsigned> templParams;
+ for (unsigned i = 0; i < templ->templateParameterCount(); ++i)
+ templParams.insert(templ->templateParameterAt(i)->name(), i);
+
+ foreach (const Name *baseName, missingBases) {
+ ClassOrNamespace *baseBinding = 0;
+
+ if (const Identifier *nameId = baseName->asNameId()) {
+ // This is the simple case in which a template parameter is itself a base.
+ // Ex.: template <class T> class A : public T {};
+ if (templParams.contains(nameId)) {
+ const FullySpecifiedType &fullType =
+ templId->templateArgumentAt(templParams.value(nameId));
+ if (NamedType *namedType = fullType.type()->asNamedType())
+ baseBinding = lookupType(namedType->name());
+ }
+ } else {
+ SubstitutionMap map;
+ for (unsigned i = 0;
+ i < templ->templateParameterCount() && i < templId->templateArgumentCount();
+ ++i) {
+ map.bind(templ->templateParameterAt(i)->name(),
+ templId->templateArgumentAt(i));
+ }
+ SubstitutionEnvironment env;
+ env.enter(&map);
+
+ baseName = rewriteName(baseName, &env, _control.data());
+
+ if (const TemplateNameId *baseTemplId = baseName->asTemplateNameId()) {
+ // Another template that uses the dependent name.
+ // Ex.: template <class T> class A : public B<T> {};
+ if (baseTemplId->identifier() != templId->identifier())
+ baseBinding = nestedType(baseName, origin);
+ } else if (const QualifiedNameId *qBaseName = baseName->asQualifiedNameId()) {
+ // Qualified names in general.
+ // Ex.: template <class T> class A : public B<T>::Type {};
+ ClassOrNamespace *binding = this;
+ if (const Name *qualification = qBaseName->base())
+ binding = lookupType(qualification);
+ baseName = qBaseName->name();
+
+ if (binding)
+ baseBinding = binding->lookupType(baseName);
+ }
+ }
+
+ if (baseBinding)
+ instantiation->addUsing(baseBinding);
+ }
+ }
+
+ return instantiation;
}
- return c;
+ // Find the missing bases for regular (non-template) types.
+ // Ex.: class A : public B<Some>::Type {};
+ foreach (const Name *baseName, missingBases) {
+ ClassOrNamespace *binding = this;
+ if (const QualifiedNameId *qBaseName = baseName->asQualifiedNameId()) {
+ if (const Name *qualification = qBaseName->base())
+ binding = lookupType(qualification);
+ baseName = qBaseName->name();
+ }
+
+ if (binding) {
+ ClassOrNamespace * baseBinding = binding->lookupType(baseName);
+ if (baseBinding)
+ reference->addUsing(baseBinding);
+ }
+ }
+
+
+ return reference;
}
void ClassOrNamespace::flush()
@@ -761,6 +869,7 @@ QSharedPointer<Control> CreateBindings::control() const
ClassOrNamespace *CreateBindings::allocClassOrNamespace(ClassOrNamespace *parent)
{
ClassOrNamespace *e = new ClassOrNamespace(this, parent);
+ e->_control = control();
_entities.append(e);
return e;
}
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index d9c1da39eb..a2de7f8cad 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -90,7 +90,7 @@ private:
ClassOrNamespace *lookupType_helper(const Name *name, QSet<ClassOrNamespace *> *processed,
bool searchInEnclosingScope, ClassOrNamespace *origin);
- ClassOrNamespace *nestedType(const Name *name, ClassOrNamespace *origin) const;
+ ClassOrNamespace *nestedType(const Name *name, ClassOrNamespace *origin);
private:
struct CompareName: std::binary_function<const Name *, const Name *, bool> {
@@ -106,6 +106,7 @@ private:
Table _classOrNamespaces;
QList<Enum *> _enums;
QList<Symbol *> _todo;
+ QSharedPointer<Control> _control;
// it's an instantiation.
const TemplateNameId *_templateId;
diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp
index 6d4a2641f0..7d9d9f3417 100644
--- a/src/plugins/cpptools/cppcompletion_test.cpp
+++ b/src/plugins/cpptools/cppcompletion_test.cpp
@@ -196,3 +196,172 @@ void CppToolsPlugin::test_completion_template_1()
QVERIFY(!completions.contains("f"));
QVERIFY(!completions.contains("func"));
}
+
+void CppToolsPlugin::test_completion_template_as_base()
+{
+ QFETCH(QByteArray, code);
+ QFETCH(QStringList, expectedCompletions);
+
+ TestData data;
+ data.srcText = code;
+ setup(&data);
+
+ Utils::ChangeSet change;
+ change.insert(data.pos, "c.");
+ QTextCursor cursor(data.doc);
+ change.apply(&cursor);
+ data.pos += 2;
+
+ QStringList actualCompletions = getCompletions(data);
+ actualCompletions.sort();
+ expectedCompletions.sort();
+
+ QCOMPARE(actualCompletions, expectedCompletions);
+}
+
+void CppToolsPlugin::test_completion_template_as_base_data()
+{
+ QTest::addColumn<QByteArray>("code");
+ QTest::addColumn<QStringList>("expectedCompletions");
+
+ QByteArray code;
+ QStringList completions;
+
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "template <class T> class Other : public T { int otherMember; };\n"
+ "\n"
+ "void func() {\n"
+ " Other<Data> c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Other");
+ completions.append("otherMember");
+ QTest::newRow("case: base as template directly") << code << completions;
+
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "template <class T> class Other : public T { int otherMember; };\n"
+ "template <class T> class More : public Other<T> { int moreMember; };\n"
+ "\n"
+ "void func() {\n"
+ " More<Data> c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Other");
+ completions.append("otherMember");
+ completions.append("More");
+ completions.append("moreMember");
+ QTest::newRow("case: base as class template") << code << completions;
+
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "template <class T> class Other : public T { int otherMember; };\n"
+ "template <class T> class More : public ::Other<T> { int moreMember; };\n"
+ "\n"
+ "void func() {\n"
+ " More<Data> c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Other");
+ completions.append("otherMember");
+ completions.append("More");
+ completions.append("moreMember");
+ QTest::newRow("case: base as globally qualified class template") << code << completions;
+
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "namespace NS {\n"
+ "template <class T> class Other : public T { int otherMember; };\n"
+ "}\n"
+ "template <class T> class More : public NS::Other<T> { int moreMember; };\n"
+ "\n"
+ "void func() {\n"
+ " More<Data> c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Other");
+ completions.append("otherMember");
+ completions.append("More");
+ completions.append("moreMember");
+ QTest::newRow("case: base as namespace qualified class template") << code << completions;
+
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "namespace NS {\n"
+ "template <class T> class Delegate { typedef Data<T> Type; };\n"
+ "}\n"
+ "template <class T> class Final : public NS::Delegate<T>::Type { int finalMember; };\n"
+ "\n"
+ "void func() {\n"
+ " Final<Data> c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Final");
+ completions.append("finalMember");
+ QTest::newRow("case: base as nested template name") << code << completions;
+
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "namespace NS {\n"
+ "template <class T> class Delegate { typedef Data<T> Type; };\n"
+ "}\n"
+ "class Final : public NS::Delegate<Data>::Type { int finalMember; };\n"
+ "\n"
+ "void func() {\n"
+ " Final c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Final");
+ completions.append("finalMember");
+ QTest::newRow("case: base as nested template name in non-template") << code << completions;
+
+ completions.clear();
+ code = "\n"
+ "class Data { int dataMember; };\n"
+ "namespace NS {\n"
+ "template <class T> class Other : public T { int otherMember; };\n"
+ "}\n"
+ "class Final : public NS::Other<Data> { int finalMember; };\n"
+ "\n"
+ "void func() {\n"
+ " Final c;\n"
+ " @\n"
+ " // padding so we get the scope right\n"
+ "}";
+ completions.append("Data");
+ completions.append("dataMember");
+ completions.append("Final");
+ completions.append("finalMember");
+ completions.append("Other");
+ completions.append("otherMember");
+ QTest::newRow("case: base as template name in non-template") << code << completions;
+}
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 1c89af6fd3..5a07c0add3 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -92,6 +92,8 @@ private slots:
void test_completion_basic_1();
void test_completion_template_1();
+ void test_completion_template_as_base();
+ void test_completion_template_as_base_data();
#endif
private: