From 376f77952e9c579043a26fcd6be33baed9fd1610 Mon Sep 17 00:00:00 2001
From: Przemyslaw Gorszkowski <pgorszkowski@gmail.com>
Date: Sun, 24 Nov 2013 21:02:26 +0100
Subject: [PATCH] C++: fix support for nested anonymous classes

A member of nested anonymous class should be visible as a member of
enclosing class(if there is no declaration of this nested anonymous
class).

Fix:
* marking
* find usage
* follow symbol
* completion

Task-number: QTCREATORBUG-10876
Task-number: QTCREATORBUG-11170
Change-Id: If5b4d198e9075f2a8aa899ae59190f2c05f7b1ff
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Przemyslaw Gorszkowski <pgorszkowski@gmail.com>
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
---
 src/libs/cplusplus/LookupContext.cpp          | 15 ++++-
 src/libs/cplusplus/LookupContext.h            |  4 +-
 src/plugins/cpptools/cppcompletion_test.cpp   | 63 ++++++++++++++++++-
 src/plugins/cpptools/cppcompletionassist.cpp  | 51 ++++++++++-----
 src/plugins/cpptools/cppcompletionassist.h    |  1 +
 .../checksymbols/tst_checksymbols.cpp         |  2 +
 6 files changed, 117 insertions(+), 19 deletions(-)

diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp
index ecf54a5f1a3..0d600993c68 100644
--- a/src/libs/cplusplus/LookupContext.cpp
+++ b/src/libs/cplusplus/LookupContext.cpp
@@ -680,6 +680,15 @@ void ClassOrNamespace::lookup_helper(const Name *name, ClassOrNamespace *binding
 
         foreach (ClassOrNamespace *u, binding->usings())
             lookup_helper(name, u, result, processed, binding->_templateId);
+
+        Anonymouses::const_iterator cit = binding->_anonymouses.begin();
+        Anonymouses::const_iterator citEnd = binding->_anonymouses.end();
+        for (; cit != citEnd; ++cit) {
+            const AnonymousNameId *anonymousNameId = cit.key();
+            ClassOrNamespace *a = cit.value();
+            if (!binding->_declaredAnonymouses.contains(anonymousNameId))
+                lookup_helper(name, a, result, processed, binding->_templateId);
+        }
     }
 }
 
@@ -1578,8 +1587,12 @@ bool CreateBindings::visit(Declaration *decl)
                 }
             }
         }
+    } else if (Class *clazz = decl->type()->asClassType()) {
+        if (const Name *name = clazz->name()) {
+            if (const AnonymousNameId *anonymousNameId = name->asAnonymousNameId())
+                _currentClassOrNamespace->_declaredAnonymouses.insert(anonymousNameId);
+        }
     }
-
     return false;
 }
 
diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h
index 84c9d470069..11c7bc6268a 100644
--- a/src/libs/cplusplus/LookupContext.h
+++ b/src/libs/cplusplus/LookupContext.h
@@ -93,6 +93,7 @@ public:
 private:
     typedef std::map<const Name *, ClassOrNamespace *, Name::Compare> Table;
     typedef std::map<const TemplateNameId *, ClassOrNamespace *, TemplateNameId::Compare> TemplateNameIdTable;
+    typedef QHash<const AnonymousNameId *, ClassOrNamespace *> Anonymouses;
 
     /// \internal
     void flush();
@@ -138,7 +139,8 @@ private:
     QSharedPointer<Control> _control;
     TemplateNameIdTable _specializations;
     QMap<const TemplateNameId *, ClassOrNamespace *> _instantiations;
-    QHash<const AnonymousNameId *, ClassOrNamespace *> _anonymouses;
+    Anonymouses _anonymouses;
+    QSet<const AnonymousNameId *> _declaredAnonymouses;
 
     QHash<Internal::FullyQualifiedName, Symbol *> *_scopeLookupCache;
 
diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp
index 7b78ac901a5..eb657f783b1 100644
--- a/src/plugins/cpptools/cppcompletion_test.cpp
+++ b/src/plugins/cpptools/cppcompletion_test.cpp
@@ -1425,7 +1425,9 @@ void CppToolsPlugin::test_completion_data()
             "   @\n"
             "}\n"
         ) << _("s.") << (QStringList()
-            << QLatin1String("S"));
+            << QLatin1String("S")
+            << QLatin1String("i")
+            << QLatin1String("c"));
 
     QTest::newRow("instantiate_template_function") << _(
             "template <typename T>\n"
@@ -1439,6 +1441,65 @@ void CppToolsPlugin::test_completion_data()
             << QLatin1String("A")
             << QLatin1String("a"));
 
+    QTest::newRow("nested_anonymous_class_QTCREATORBUG10876_1") << _(
+            "struct EnclosingStruct\n"
+            "{\n"
+            "   int memberOfEnclosingStruct;\n"
+            "   struct\n"
+            "   {\n"
+            "       int memberNestedAnonymousClass;\n"
+            "   };\n"
+            "   void fun()\n"
+            "   {\n"
+            "       @\n"
+            "   }\n"
+            "};\n"
+        ) << _("member") << (QStringList()
+            << QLatin1String("memberNestedAnonymousClass")
+            << QLatin1String("memberOfEnclosingStruct"));
+
+    QTest::newRow("nested_anonymous_class_QTCREATORBUG10876_2") << _(
+            "struct EnclosingStruct\n"
+            "{\n"
+            "   int memberOfEnclosingStruct;\n"
+            "   struct\n"
+            "   {\n"
+            "       int memberOfNestedAnonymousClass;\n"
+            "       struct\n"
+            "       {\n"
+            "           int memberOfNestedOfNestedAnonymousClass;\n"
+            "       };\n"
+            "   };\n"
+            "   void fun()\n"
+            "   {\n"
+            "       @\n"
+            "   }\n"
+            "};\n"
+        ) << _("member") << (QStringList()
+            << QLatin1String("memberOfNestedAnonymousClass")
+            << QLatin1String("memberOfNestedOfNestedAnonymousClass")
+            << QLatin1String("memberOfEnclosingStruct"));
+
+    QTest::newRow("nested_anonymous_class_QTCREATORBUG10876_3") << _(
+            "struct EnclosingStruct\n"
+            "{\n"
+            "   int memberOfEnclosingStruct;\n"
+            "   struct\n"
+            "   {\n"
+            "       int memberOfNestedAnonymousClass;\n"
+            "       struct\n"
+            "       {\n"
+            "           int memberOfNestedOfNestedAnonymousClass;\n"
+            "       } nestedOfNestedAnonymousClass;\n"
+            "   };\n"
+            "   void fun()\n"
+            "   {\n"
+            "       @\n"
+            "   }\n"
+            "};\n"
+        ) << _("nestedOfNestedAnonymousClass.") << (QStringList()
+            << QLatin1String("memberOfNestedOfNestedAnonymousClass"));
+
     QTest::newRow("crash_cloning_template_class_QTCREATORBUG9329") << _(
             "struct A {};\n"
             "template <typename T>\n"
diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp
index c65c0f22ef5..d0eb4af1035 100644
--- a/src/plugins/cpptools/cppcompletionassist.cpp
+++ b/src/plugins/cpptools/cppcompletionassist.cpp
@@ -1554,25 +1554,44 @@ void CppCompletionAssistProcessor::completeClass(CPlusPlus::ClassOrNamespace *b,
             if (staticLookup)
                 addCompletionItem(scope, InjectedClassNameOrder); // add a completion item for the injected class name.
 
-            for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
-                Symbol *member = *it;
-                if (member->isFriend()
-                        || member->isQtPropertyDeclaration()
-                        || member->isQtEnum()) {
-                    continue;
-                } else if (!staticLookup && (member->isTypedef() ||
-                                            member->isEnum()    ||
-                                            member->isClass())) {
-                    continue;
-                }
+            addClassMembersToCompletion(scope, staticLookup);
+        }
+    }
+}
 
-                if (member->isPublic())
-                    addCompletionItem(member, PublicClassMemberOrder);
-                else
-                    addCompletionItem(member);
-            }
+void CppCompletionAssistProcessor::addClassMembersToCompletion(Scope *scope, bool staticLookup)
+{
+    if (!scope)
+        return;
+
+    std::set<Class *> nestedAnonymouses;
+
+    for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
+        Symbol *member = *it;
+        if (member->isFriend()
+                || member->isQtPropertyDeclaration()
+                || member->isQtEnum()) {
+            continue;
+        } else if (!staticLookup && (member->isTypedef() ||
+                                    member->isEnum()    ||
+                                    member->isClass())) {
+            continue;
+        } else if (member->isClass() && member->name()->isAnonymousNameId()) {
+            nestedAnonymouses.insert(member->asClass());
+        } else if (member->isDeclaration()) {
+            Class *declTypeAsClass = member->asDeclaration()->type()->asClassType();
+            if (declTypeAsClass && declTypeAsClass->name()->isAnonymousNameId())
+                nestedAnonymouses.erase(declTypeAsClass);
         }
+
+        if (member->isPublic())
+            addCompletionItem(member, PublicClassMemberOrder);
+        else
+            addCompletionItem(member);
     }
+    std::set<Class *>::const_iterator citEnd = nestedAnonymouses.end();
+    for (std::set<Class *>::const_iterator cit = nestedAnonymouses.begin(); cit != citEnd; ++cit)
+        addClassMembersToCompletion(*cit, staticLookup);
 }
 
 bool CppCompletionAssistProcessor::completeQtMethod(const QList<CPlusPlus::LookupItem> &results, bool wantSignals)
diff --git a/src/plugins/cpptools/cppcompletionassist.h b/src/plugins/cpptools/cppcompletionassist.h
index 0e0268b74b6..ae2e45a23e5 100644
--- a/src/plugins/cpptools/cppcompletionassist.h
+++ b/src/plugins/cpptools/cppcompletionassist.h
@@ -136,6 +136,7 @@ private:
     bool completeScope(const QList<CPlusPlus::LookupItem> &results);
     void completeNamespace(CPlusPlus::ClassOrNamespace *binding);
     void completeClass(CPlusPlus::ClassOrNamespace *b, bool staticLookup = true);
+    void addClassMembersToCompletion(CPlusPlus::Scope *scope, bool staticLookup);
     bool completeQtMethod(const QList<CPlusPlus::LookupItem> &results, bool wantSignals);
     bool completeSignal(const QList<CPlusPlus::LookupItem> &results)
     { return completeQtMethod(results, true); }
diff --git a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp
index efad13c5c8b..4e113da9de9 100644
--- a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp
+++ b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp
@@ -1539,7 +1539,9 @@ void tst_CheckSymbols::test_checksymbols_AnonymousClass_insideNamespace()
             << Use(13, 11, 3, CppHighlightingSupport::TypeUse)
             << Use(15, 27, 4, CppHighlightingSupport::FieldUse)
             << Use(16, 10, 4, CppHighlightingSupport::FunctionUse)
+            << Use(16, 19, 4, CppHighlightingSupport::FieldUse)
             << Use(18, 8, 4, CppHighlightingSupport::FunctionUse)
+            << Use(20, 10, 4, CppHighlightingSupport::FieldUse)
             ;
 
     TestData::check(source, expectedUses);
-- 
GitLab