From 7e9913f0f540e196ca7e53217d2edf19a9e8ef29 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gorszkowski Date: Sat, 22 Sep 2012 14:24:43 +0200 Subject: [PATCH] Fix crashes when typing code Problem was with cyclic recurrence. To solve it we need to check if derived class is different class than its base class. Keep completion corrected. Include some unit tests when base class has the same name as derived. Task-number: QTCREATORBUG-7887 Change-Id: I7973c0b06e3b62d2da3d06048f4327d18a0b8011 Reviewed-by: hjk --- src/libs/cplusplus/LookupContext.cpp | 23 +- src/plugins/cpptools/cppcompletion_test.cpp | 275 +++++++++++++++++++- src/plugins/cpptools/cpptoolsplugin.h | 8 + 3 files changed, 304 insertions(+), 2 deletions(-) diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 8edd3b30ca6..e474287d199 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -789,15 +789,37 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac return instantiation; } + if (allBases.isEmpty() || allBases.size() == knownUsings.size()) + return reference; + + QList fullyQualifiedNameForReferenceClass = + LookupContext::fullyQualifiedName(referenceClass); // Find the missing bases for regular (non-template) types. // Ex.: class A : public B::Type {}; foreach (const Name *baseName, allBases) { ClassOrNamespace *binding = this; if (const QualifiedNameId *qBaseName = baseName->asQualifiedNameId()) { + QList fullyQualifiedNameForBaseClass; + addNames(baseName, &fullyQualifiedNameForBaseClass); + if (compareFullyQualifiedName(fullyQualifiedNameForReferenceClass, + fullyQualifiedNameForBaseClass)) { + continue; + } + if (const Name *qualification = qBaseName->base()) binding = lookupType(qualification); + else if (binding->parent() != 0) + //if this is global identifier we take global namespace + //Ex: class A{}; namespace NS { class A: public ::A{}; } + binding = binding->globalNamespace(); + else + //if we are in the global scope + continue; baseName = qBaseName->name(); } + else if (compareName(name, baseName)) { + continue; + } if (binding) { ClassOrNamespace * baseBinding = binding->lookupType(baseName); @@ -806,7 +828,6 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac } } - return reference; } diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index a6681bb3981..08e14bd67a6 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -231,7 +231,7 @@ void CppToolsPlugin::test_completion_template_1() QVERIFY(!completions.contains("func")); } -void CppToolsPlugin::test_completion_template_as_base() +void CppToolsPlugin::test_completion() { QFETCH(QByteArray, code); QFETCH(QStringList, expectedCompletions); @@ -253,6 +253,11 @@ void CppToolsPlugin::test_completion_template_as_base() QCOMPARE(actualCompletions, expectedCompletions); } +void CppToolsPlugin::test_completion_template_as_base() +{ + test_completion(); +} + void CppToolsPlugin::test_completion_template_as_base_data() { QTest::addColumn("code"); @@ -399,3 +404,271 @@ void CppToolsPlugin::test_completion_template_as_base_data() completions.append("otherMember"); QTest::newRow("case: base as template name in non-template") << code << completions; } + +void CppToolsPlugin::test_completion_use_global_identifier_as_base_class() +{ + test_completion(); +} + +void CppToolsPlugin::test_completion_use_global_identifier_as_base_class_data() +{ + QTest::addColumn("code"); + QTest::addColumn("expectedCompletions"); + + QByteArray code; + QStringList completions; + + code = "\n" + "struct Global\n" + "{\n" + " int int_global;\n" + "};\n" + "\n" + "struct Final : ::Global\n" + "{\n" + " int int_final;\n" + "};\n" + "\n" + "Final c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_global"); + completions.append("int_final"); + completions.append("Final"); + completions.append("Global"); + QTest::newRow("case: derived as global and base as global") << code << completions; + + completions.clear(); + + code = "\n" + "struct Global\n" + "{\n" + " int int_global;\n" + "};\n" + "\n" + "namespace NS\n" + "{\n" + "struct Final : ::Global\n" + "{\n" + " int int_final;\n" + "};\n" + "}\n" + "\n" + "NS::Final c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_global"); + completions.append("int_final"); + completions.append("Final"); + completions.append("Global"); + QTest::newRow("case: derived is inside namespace, base as global") + << code << completions; + + completions.clear(); + + //this test does not work due to the bug QTCREATORBUG-7912 + + +// code = "\n" +// "struct Global\n" +// "{\n" +// " int int_global;\n" +// "};\n" +// "\n" +// "template \n" +// "struct Enclosing\n" +// "{\n" +// "struct Final : ::Global\n" +// "{\n" +// " int int_final;\n" +// "};\n" +// "}\n" +// "\n" +// "Enclosing::Final c;\n" +// "@\n" +// "// padding so we get the scope right\n"; + +// completions.append("int_global"); +// completions.append("int_final"); +// completions.append("Final"); +// completions.append("Global"); +// QTest::newRow("case: derived is enclosed by template, base as global") +// << code << completions; + +// completions.clear(); +} + +void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived() +{ + test_completion(); +} + +void CppToolsPlugin::test_completion_base_class_has_name_the_same_as_derived_data() +{ + QTest::addColumn("code"); + QTest::addColumn("expectedCompletions"); + + QByteArray code; + QStringList completions; + + code = "\n" + "struct A : A\n" + "{\n" + " int int_a;\n" + "};\n" + "\n" + "A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_a"); + completions.append("A"); + QTest::newRow("case: base class is derived class") << code << completions; + + completions.clear(); + + code = "\n" + "namespace NS\n" + "{\n" + "struct A : A\n" + "{\n" + " int int_a;\n" + "};\n" + "}\n" + "\n" + "NS::A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_a"); + completions.append("A"); + QTest::newRow("case: base class is derived class. class is in namespace") + << code << completions; + + completions.clear(); + + code = "\n" + "namespace NS\n" + "{\n" + "struct A : NS::A\n" + "{\n" + " int int_a;\n" + "};\n" + "}\n" + "\n" + "NS::A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_a"); + completions.append("A"); + QTest::newRow("case: base class is derived class. class is in namespace. " + "use scope operator for base class") << code << completions; + + completions.clear(); + + code = "\n" + "namespace NS1\n" + "{\n" + "struct A\n" + "{\n" + " int int_ns1_a;\n" + "};\n" + "}\n" + "namespace NS2\n" + "{\n" + "struct A : NS1::A\n" + "{\n" + " int int_ns2_a;\n" + "};\n" + "}\n" + "\n" + "NS2::A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_ns1_a"); + completions.append("int_ns2_a"); + completions.append("A"); + QTest::newRow("case: base class has the same name as derived but in different namespace") + << code << completions; + + completions.clear(); + + code = "\n" + "struct Enclosing\n" + "{\n" + "struct A\n" + "{\n" + " int int_enclosing_a;\n" + "};\n" + "};\n" + "namespace NS2\n" + "{\n" + "struct A : Enclosing::A\n" + "{\n" + " int int_ns2_a;\n" + "};\n" + "}\n" + "\n" + "NS2::A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_enclosing_a"); + completions.append("int_ns2_a"); + completions.append("A"); + QTest::newRow("case: base class has the same name as derived(in namespace) " + "but is nested by different class") << code << completions; + + completions.clear(); + + code = "\n" + "struct EnclosingBase\n" + "{\n" + "struct A\n" + "{\n" + " int int_enclosing_base_a;\n" + "};\n" + "};\n" + "struct EnclosingDerived\n" + "{\n" + "struct A : EnclosingBase::A\n" + "{\n" + " int int_enclosing_derived_a;\n" + "};\n" + "};\n" + "\n" + "EnclosingDerived::A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_enclosing_base_a"); + completions.append("int_enclosing_derived_a"); + completions.append("A"); + QTest::newRow("case: base class has the same name as derived(nested) " + "but is nested by different class") << code << completions; + + completions.clear(); + + code = "\n" + "template \n" + "struct A : A\n" + "{\n" + " int int_a;\n" + "};\n" + "\n" + "A c;\n" + "@\n" + "// padding so we get the scope right\n"; + + completions.append("int_a"); + completions.append("A"); + QTest::newRow("case: base class is derived class. class is a template") + << code << completions; + + completions.clear(); + +} diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index ba88d11896e..554a1877832 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -77,6 +77,7 @@ private slots: void switchHeaderSource(); #ifdef WITH_TESTS + // codegen tests void test_codegen_public_in_empty_class(); void test_codegen_public_in_nonempty_class(); @@ -95,6 +96,13 @@ private slots: void test_completion_template_1(); void test_completion_template_as_base(); void test_completion_template_as_base_data(); + void test_completion_use_global_identifier_as_base_class(); + 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(); + +private: + void test_completion(); #endif private: