CppEditor: Properly handle multiple inheritance

... in "Insert Virtual Functions" quickfix.

Fixes: QTCREATORBUG-12223
Change-Id: I7dad7c219017a8c7b10b08190e35d1899ca5dfe6
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2020-08-17 12:02:47 +02:00
parent 919dc86c37
commit 86728b84f1
3 changed files with 175 additions and 52 deletions

View File

@@ -584,21 +584,23 @@ public:
if (!name || name->asDestructorNameId())
continue;
const Function *firstVirtual = nullptr;
QList<const Function * > firstVirtuals;
const bool isVirtual = FunctionUtils::isVirtualFunction(
func, interface.context(), &firstVirtual);
func, interface.context(), &firstVirtuals);
if (!isVirtual)
continue;
if (func->isFinal()) {
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
FunctionItem *next = nullptr;
for (FunctionItem *removed = first; next != first; removed = next) {
next = removed->nextOverride;
m_factory->classFunctionModel->removeFunction(removed);
delete removed;
};
virtualFunctions.remove(firstVirtual);
for (const Function *firstVirtual : qAsConst(firstVirtuals)) {
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
FunctionItem *next = nullptr;
for (FunctionItem *removed = first; next != first; removed = next) {
next = removed->nextOverride;
m_factory->classFunctionModel->removeFunction(removed);
delete removed;
};
virtualFunctions.remove(firstVirtual);
}
}
continue;
}
@@ -606,15 +608,21 @@ public:
// - virtual const QMetaObject *metaObject() const;
// - virtual void *qt_metacast(const char *);
// - virtual int qt_metacall(QMetaObject::Call, int, void **);
if (printer.prettyName(firstVirtual->enclosingClass()->name())
== QLatin1String("QObject")) {
const QString funcName = printer.prettyName(func->name());
if (funcName == QLatin1String("metaObject")
|| funcName == QLatin1String("qt_metacast")
|| funcName == QLatin1String("qt_metacall")) {
continue;
bool skip = false;
for (const Function *firstVirtual : qAsConst(firstVirtuals)) {
if (printer.prettyName(firstVirtual->enclosingClass()->name())
== QLatin1String("QObject")) {
const QString funcName = printer.prettyName(func->name());
if (funcName == QLatin1String("metaObject")
|| funcName == QLatin1String("qt_metacast")
|| funcName == QLatin1String("qt_metacall")) {
skip = true;
break;
}
}
}
if (skip)
continue;
// Do not implement existing functions inside target class
bool funcExistsInClass = false;
@@ -634,7 +642,7 @@ public:
}
// Construct function item
const bool isReimplemented = (func != firstVirtual);
const bool isReimplemented = !firstVirtuals.contains(func);
const bool isPureVirtual = func->isPureVirtual();
QString itemName = printer.prettyType(func->type(), func->name());
if (isPureVirtual)
@@ -648,16 +656,24 @@ public:
factory->setHasReimplementedFunctions(true);
funcItem->reimplemented = true;
funcItem->alreadyFound = funcExistsInClass;
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
if (!first->alreadyFound) {
while (first->checked != isPureVirtual) {
first->checked = isPureVirtual;
first = first->nextOverride;
for (const Function *firstVirtual : qAsConst(firstVirtuals)) {
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
if (!first->alreadyFound) {
while (first->checked != isPureVirtual) {
first->checked = isPureVirtual;
first = first->nextOverride;
}
}
funcItem->checked = first->checked;
FunctionItem *prev = funcItem;
for (FunctionItem *next = funcItem->nextOverride;
next && next != funcItem; next = next->nextOverride) {
prev = next;
}
prev->nextOverride = first->nextOverride;
first->nextOverride = funcItem;
}
funcItem->checked = first->checked;
funcItem->nextOverride = first->nextOverride;
first->nextOverride = funcItem;
}
} else {
if (!funcExistsInClass) {
@@ -758,6 +774,7 @@ public:
targetCoN = targetContext.globalNamespace();
UseMinimalNames useMinimalNames(targetCoN);
Control *control = context().bindings()->control().data();
QList<const Function *> insertedFunctions;
foreach (ClassItem *classItem, m_factory->classFunctionModel->classes) {
if (classItem->checkState() == Qt::Unchecked)
continue;
@@ -769,6 +786,14 @@ public:
if (funcItem->reimplemented || funcItem->alreadyFound || !funcItem->checked)
continue;
const auto cmp = [funcItem](const Function *f) {
return f->name()->match(funcItem->function->name())
&& f->type().match(funcItem->function->type());
};
if (Utils::contains(insertedFunctions, cmp))
continue;
insertedFunctions.append(funcItem->function);
if (first) {
// Add comment
const QString comment = QLatin1String("\n// ") +
@@ -824,10 +849,13 @@ public:
}
// Write header file
headerFile->setChangeSet(headerChangeSet);
headerFile->appendIndentRange(Utils::ChangeSet::Range(m_insertPosDecl, m_insertPosDecl + 1));
headerFile->setOpenEditor(true, m_insertPosDecl);
headerFile->apply();
if (!headerChangeSet.isEmpty()) {
headerFile->setChangeSet(headerChangeSet);
headerFile->appendIndentRange(Utils::ChangeSet::Range(m_insertPosDecl,
m_insertPosDecl + 1));
headerFile->setOpenEditor(true, m_insertPosDecl);
headerFile->apply();
}
// Insert in implementation file
if (m_factory->settings()->implementationMode
@@ -877,9 +905,12 @@ public:
implementationChangeSet.insert(insertPos, QLatin1String("\n\n") + defText);
}
implementationFile->setChangeSet(implementationChangeSet);
implementationFile->appendIndentRange(Utils::ChangeSet::Range(insertPos, insertPos + 1));
implementationFile->apply();
if (!implementationChangeSet.isEmpty()) {
implementationFile->setChangeSet(implementationChangeSet);
implementationFile->appendIndentRange(Utils::ChangeSet::Range(insertPos,
insertPos + 1));
implementationFile->apply();
}
}
}
@@ -1761,6 +1792,56 @@ void CppEditorPlugin::test_quickfix_InsertVirtualMethods_data()
" virtual int d();\n"
"};\n"
);
// Check: Insert multiply-inherited virtual function only once.
QTest::newRow("multiple_inheritance_insert")
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << false << true << _(
"struct Base1 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Base2 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct @Derived : Base1, Base2 {\n"
"};\n") << _(
"struct Base1 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Base2 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Derived : Base1, Base2 {\n\n"
" // Base2 interface\n"
"public:\n"
" void virt() override;\n"
"};\n");
// Check: Do not insert multiply-inherited virtual function that has been re-implemented
// along the way.
QTest::newRow("multiple_inheritance_no_insert")
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << false << true << _(
"struct Base1 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Base2 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Derived1 : Base1, Base2 {\n"
" void virt() override;\n"
"};\n\n"
"struct @Derived2 : Derived1\n"
"};\n") << _(
"struct Base1 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Base2 {\n"
" virtual void virt() = 0;\n"
"};\n\n"
"struct Derived1 : Base1, Base2 {\n"
" void virt() override;\n"
"};\n\n"
"struct Derived2 : Derived1\n"
"};\n");
}
void CppEditorPlugin::test_quickfix_InsertVirtualMethods()