From fbb756cdcc3976a3afb8ffec5477b19ec77914c9 Mon Sep 17 00:00:00 2001
From: Przemyslaw Gorszkowski <pgorszkowski@gmail.com>
Date: Sun, 30 Sep 2012 23:19:53 +0200
Subject: [PATCH] Fix crashes when cyclic inheritance

Task-number: QTCREATORBUG-7933

Change-Id: I98469a092ff3ff0acc69800e9aade4ebb268332a
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
---
 src/libs/cplusplus/LookupContext.cpp        |  15 ++-
 src/libs/cplusplus/LookupContext.h          |  30 +++++
 src/plugins/cpptools/cppcompletion_test.cpp | 120 ++++++++++++++++++++
 src/plugins/cpptools/cpptoolsplugin.h       |   2 +
 4 files changed, 166 insertions(+), 1 deletion(-)

diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index e474287d199..3e675644075 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -708,11 +708,22 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
     if (!referenceClass)
         return reference;
 
+    const TemplateNameId *templId = name->asTemplateNameId();
+    if (_alreadyConsideredClasses.contains(referenceClass) ||
+            (templId &&
+            _alreadyConsideredTemplates.contains(templId))) {
+            return reference;
+    }
+
+    if (!name->isTemplateNameId())
+        _alreadyConsideredClasses.insert(referenceClass);
+
     QSet<ClassOrNamespace *> knownUsings = reference->usings().toSet();
 
     // 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()) {
+    if (templId) {
+        _alreadyConsideredTemplates.insert(templId);
         ClassOrNamespace *instantiation = _factory->allocClassOrNamespace(reference);
         instantiation->_templateId = templId;
         instantiation->_instantiationOrigin = origin;
@@ -786,6 +797,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
             }
         }
 
+        _alreadyConsideredTemplates.clear(templId);
         return instantiation;
     }
 
@@ -828,6 +840,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
         }
     }
 
+    _alreadyConsideredClasses.clear(referenceClass);
     return reference;
 }
 
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index fef317fd614..6db8b1187de 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -45,6 +45,33 @@
 namespace CPlusPlus {
 
 class CreateBindings;
+class Class;
+template<typename T>
+class AlreadyConsideredClassContainer
+{
+public:
+    AlreadyConsideredClassContainer() : _class(0) {}
+    void insert(const T *item)
+    {
+        if (_container.isEmpty())
+            _class = item;
+        _container.insert(item);
+    }
+    bool contains(const T *item)
+    {
+        return _container.contains(item);
+    }
+
+    void clear(const T *item)
+    {
+        if (_class != item)
+            _container.clear();
+    }
+
+private:
+    QSet<const T *> _container;
+    const T * _class;
+};
 
 class CPLUSPLUS_EXPORT ClassOrNamespace
 {
@@ -112,6 +139,9 @@ private:
     const TemplateNameId *_templateId;
     ClassOrNamespace *_instantiationOrigin;
 
+    AlreadyConsideredClassContainer<Class> _alreadyConsideredClasses;
+    AlreadyConsideredClassContainer<TemplateNameId> _alreadyConsideredTemplates;
+
     friend class CreateBindings;
 };
 
diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp
index 08e14bd67a6..8f0b5ecbe3b 100644
--- a/src/plugins/cpptools/cppcompletion_test.cpp
+++ b/src/plugins/cpptools/cppcompletion_test.cpp
@@ -672,3 +672,123 @@ void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_dat
     completions.clear();
 
 }
+
+
+void CppToolsPlugin::test_completion_cyclic_inheritance()
+{
+    test_completion();
+}
+
+void CppToolsPlugin::test_completion_cyclic_inheritance_data()
+{
+    QTest::addColumn<QByteArray>("code");
+    QTest::addColumn<QStringList>("expectedCompletions");
+
+    QByteArray code;
+    QStringList completions;
+
+    code = "\n"
+            "struct B;\n"
+            "struct A : B { int _a; };\n"
+            "struct B : A { int _b; };\n"
+            "\n"
+            "A c;\n"
+            "@\n"
+            ;
+    completions.append("A");
+    completions.append("_a");
+    completions.append("B");
+    completions.append("_b");
+    QTest::newRow("case: direct cyclic inheritance") << code << completions;
+
+    completions.clear();
+    code = "\n"
+            "struct C;\n"
+            "struct A : C { int _a; };\n"
+            "struct B : A { int _b; };\n"
+            "struct C : B { int _c; };\n"
+            "\n"
+            "A c;\n"
+            "@\n"
+            ;
+    completions.append("A");
+    completions.append("_a");
+    completions.append("B");
+    completions.append("_b");
+    completions.append("C");
+    completions.append("_c");
+    QTest::newRow("case: indirect cyclic inheritance") << code << completions;
+
+    completions.clear();
+    code = "\n"
+            "struct B;\n"
+            "struct A : B { int _a; };\n"
+            "struct C { int _c; };\n"
+            "struct B : C, A { int _b; };\n"
+            "\n"
+            "A c;\n"
+            "@\n"
+            ;
+    completions.append("A");
+    completions.append("_a");
+    completions.append("B");
+    completions.append("_b");
+    completions.append("C");
+    completions.append("_c");
+    QTest::newRow("case: indirect cyclic inheritance") << code << completions;
+
+    completions.clear();
+    code = "\n"
+            "template< typename T > struct C;\n"
+            "template< typename T, typename S > struct D : C< S >\n"
+            "{\n"
+            "   T _d_t;\n"
+            "   S _d_s;\n"
+            "};\n"
+            "template< typename T > struct C : D< T, int >\n"
+            "{\n"
+            "   T _c_t;\n"
+            "};\n"
+            "\n"
+            "D<int, float> c;\n"
+            "@\n"
+            ;
+    completions.append("D");
+    completions.append("_d_t");
+    completions.append("_d_s");
+    completions.append("C");
+    completions.append("_c_t");
+    QTest::newRow("case: direct cyclic inheritance with templates")
+            << code << completions;
+
+    completions.clear();
+    code = "\n"
+            "template< typename T > struct C;\n"
+            "template< typename T, typename S > struct D : C< S >\n"
+            "{\n"
+            "   T _d_t;\n"
+            "   S _d_s;\n"
+            "};\n"
+            "template< typename T > struct B : D< T, int >\n"
+            "{\n"
+            "   T _b_t;\n"
+            "};\n"
+            "template< typename T > struct C : B<T>\n"
+            "{\n"
+            "   T _c_t;\n"
+            "};\n"
+            "\n"
+            "D<int, float> c;\n"
+            "@\n"
+            ;
+    completions.append("D");
+    completions.append("_d_t");
+    completions.append("_d_s");
+    completions.append("C");
+    completions.append("_c_t");
+    completions.append("B");
+    completions.append("_b_t");
+    QTest::newRow("case: indirect cyclic inheritance with templates")
+            << code << completions;
+
+}
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 554a1877832..d84319db03b 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -100,6 +100,8 @@ private slots:
     void test_completion_use_global_identifier_as_base_class_data();
     void test_completion_base_class_has_name_the_same_as_derived();
     void test_completion_base_class_has_name_the_same_as_derived_data();
+    void test_completion_cyclic_inheritance();
+    void test_completion_cyclic_inheritance_data();
 
 private:
     void test_completion();
-- 
GitLab