From f51d7a2314fc7174f02cda2b448931fa5eb6721d Mon Sep 17 00:00:00 2001
From: Nikolai Kosjar <nikolai.kosjar@qt.io>
Date: Wed, 5 Jul 2017 12:10:17 +0200
Subject: [PATCH] C++: Fix crash for invalid code

...due to indirect recursion:

  ...
  CPlusPlus::ClassOrNamespace::lookupType        LookupContext.cpp 833  0x7fffd6c954cc
  CPlusPlus::ClassOrNamespace::nestedType        LookupContext.cpp 1364 0x7fffd6c94bc6
  CPlusPlus::ClassOrNamespace::lookupType_helper LookupContext.cpp 955  0x7fffd6c9517f
  CPlusPlus::ClassOrNamespace::lookupType_helper LookupContext.cpp 983  0x7fffd6c952ad
  CPlusPlus::ClassOrNamespace::lookupType        LookupContext.cpp 833  0x7fffd6c954cc
  CPlusPlus::ClassOrNamespace::nestedType        LookupContext.cpp 1364 0x7fffd6c94bc6
  CPlusPlus::ClassOrNamespace::lookupType_helper LookupContext.cpp 955  0x7fffd6c9517f
  CPlusPlus::ClassOrNamespace::lookupType_helper LookupContext.cpp 983  0x7fffd6c952ad
  CPlusPlus::ClassOrNamespace::lookupType        LookupContext.cpp 833  0x7fffd6c954cc
  ...

ClassOrNamespace::lookupType(const Name *) already guards with a list of
entries already processed, but some calls deeper the list is not passed
on and lookupType() starts again with an empty list. Handle that case,
too.

Task-number: QTCREATORBUG-18499
Change-Id: Iab8978f6ac1d0aea16f49b3547415f43de887b07
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
---
 src/libs/cplusplus/LookupContext.cpp | 20 ++++++++++++--------
 src/libs/cplusplus/LookupContext.h   |  3 ++-
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index cb2aead5a35..0e9fccead2d 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -952,7 +952,7 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name,
                     return this;
             }
 
-            if (ClassOrNamespace *e = nestedType(name, origin))
+            if (ClassOrNamespace *e = nestedType(name, processed, origin))
                 return e;
 
             if (_templateId) {
@@ -1074,7 +1074,9 @@ ClassOrNamespace *ClassOrNamespace::findOrCreateNestedAnonymousType(
     }
 }
 
-ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespace *origin)
+ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name,
+                                               QSet<ClassOrNamespace *> *processed,
+                                               ClassOrNamespace *origin)
 {
     Q_ASSERT(name != 0);
     Q_ASSERT(name->isNameId() || name->isTemplateNameId() || name->isAnonymousNameId());
@@ -1184,11 +1186,11 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
             instantiation->_name = templId;
         instantiation->_templateId = templId;
 
-        QSet<ClassOrNamespace *> processed;
+        QSet<ClassOrNamespace *> otherProcessed;
         while (!origin->_symbols.isEmpty() && origin->_symbols[0]->isBlock()) {
-            if (processed.contains(origin))
+            if (otherProcessed.contains(origin))
                 break;
-            processed.insert(origin);
+            otherProcessed.insert(origin);
             origin = origin->parent();
         }
 
@@ -1310,7 +1312,7 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
                         // 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);
+                            baseBinding = nestedType(baseName, processed, origin);
                     } else if (const QualifiedNameId *qBaseName = baseName->asQualifiedNameId()) {
                         // Qualified names in general.
                         // Ex.: template <class T> class A : public B<T>::Type {};
@@ -1361,7 +1363,8 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
         }
 
         if (binding) {
-            ClassOrNamespace * baseBinding = binding->lookupType(baseName);
+            ClassOrNamespace * baseBinding
+                    = binding->lookupType_helper(baseName, processed, true, this);
             if (baseBinding && !knownUsings.contains(baseBinding))
                 reference->addUsing(baseBinding);
         }
@@ -1518,7 +1521,8 @@ ClassOrNamespace *ClassOrNamespace::findOrCreateType(const Name *name, ClassOrNa
         return findOrCreateType(q->base(), origin)->findOrCreateType(q->name(), origin, clazz);
 
     } else if (name->isNameId() || name->isTemplateNameId() || name->isAnonymousNameId()) {
-        ClassOrNamespace *e = nestedType(name, origin);
+        QSet<ClassOrNamespace *> processed;
+        ClassOrNamespace *e = nestedType(name, &processed, origin);
 
         if (! e) {
             e = _factory->allocClassOrNamespace(this);
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index 7273fe57d02..b13df5da29a 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -121,7 +121,8 @@ private:
     ClassOrNamespace *findBlock_helper(Block *block, QSet<ClassOrNamespace *> *processed,
                                        bool searchInEnclosingScope);
 
-    ClassOrNamespace *nestedType(const Name *name, ClassOrNamespace *origin);
+    ClassOrNamespace *nestedType(const Name *name, QSet<ClassOrNamespace *> *processed,
+                                 ClassOrNamespace *origin);
 
     void instantiateNestedClasses(ClassOrNamespace *enclosingTemplateClass,
                                   Clone &cloner,
-- 
GitLab