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