Commit 7e9913f0 authored by Przemyslaw Gorszkowski's avatar Przemyslaw Gorszkowski Committed by hjk

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: default avatarhjk <qthjk@ovi.com>
parent b02e954e
......@@ -789,15 +789,37 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, ClassOrNamespac
return instantiation;
}
if (allBases.isEmpty() || allBases.size() == knownUsings.size())
return reference;
QList<const Name *> fullyQualifiedNameForReferenceClass =
LookupContext::fullyQualifiedName(referenceClass);
// Find the missing bases for regular (non-template) types.
// Ex.: class A : public B<Some>::Type {};
foreach (const Name *baseName, allBases) {
ClassOrNamespace *binding = this;
if (const QualifiedNameId *qBaseName = baseName->asQualifiedNameId()) {
QList<const Name *> 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;
}
......
......@@ -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<QByteArray>("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<QByteArray>("code");
QTest::addColumn<QStringList>("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 <typename T>\n"
// "struct Enclosing\n"
// "{\n"
// "struct Final : ::Global\n"
// "{\n"
// " int int_final;\n"
// "};\n"
// "}\n"
// "\n"
// "Enclosing<int>::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<QByteArray>("code");
QTest::addColumn<QStringList>("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 <typename T>\n"
"struct A : A\n"
"{\n"
" int int_a;\n"
"};\n"
"\n"
"A<int> 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();
}
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment