summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Sturmlechner <asturm@gentoo.org>2017-04-23 15:54:19 +0200
committerAndreas Sturmlechner <asturm@gentoo.org>2017-04-23 21:07:31 +0200
commit87e6967f8b82468e077a8f2004732e2db1cbf751 (patch)
tree160d57d4bb1c65498069219e4756d4a35be8dec6 /dev-qt/qtcore/files
parentkde-apps: Add blockers against kde-apps/kdepim-l10n (diff)
downloadgentoo-87e6967f8b82468e077a8f2004732e2db1cbf751.tar.gz
gentoo-87e6967f8b82468e077a8f2004732e2db1cbf751.tar.bz2
gentoo-87e6967f8b82468e077a8f2004732e2db1cbf751.zip
dev-qt/qtcore: Try to fix random plasmashell crashes
Backports from Qt-5.8.0. See also: https://bugs.kde.org/show_bug.cgi?id=342763 Package-Manager: Portage-2.3.3, Repoman-2.3.1
Diffstat (limited to 'dev-qt/qtcore/files')
-rw-r--r--dev-qt/qtcore/files/qtcore-5.7.1-qipm.patch152
-rw-r--r--dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-1.patch252
-rw-r--r--dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-2.patch195
3 files changed, 599 insertions, 0 deletions
diff --git a/dev-qt/qtcore/files/qtcore-5.7.1-qipm.patch b/dev-qt/qtcore/files/qtcore-5.7.1-qipm.patch
new file mode 100644
index 000000000000..abea14588075
--- /dev/null
+++ b/dev-qt/qtcore/files/qtcore-5.7.1-qipm.patch
@@ -0,0 +1,152 @@
+From baad82d242a4d8c1af6c87faaa7f25584183fd53 Mon Sep 17 00:00:00 2001
+From: Stephen Kelly <steveire@gmail.com>
+Date: Tue, 20 Dec 2016 00:44:12 +0000
+Subject: [PATCH] QIPM: Persist model indexes after emitting layoutChange, not
+ before
+
+Callers can persist a QModelIndex which was not persisted before in a
+slot connected to the signal, and such a persisted index must be updated
+in the course of the layoutChange.
+
+Store the indexes to persist after emitting the signal.
+
+Task-number: QTBUG-32981
+Change-Id: Ibee4c0d84817d72603a03fe5b22fdeefeac0695e
+Reviewed-by: David Faure <david.faure@kdab.com>
+---
+ src/corelib/itemmodels/qidentityproxymodel.cpp | 18 ++---
+ .../tst_qidentityproxymodel.cpp | 76 ++++++++++++++++++++++
+ 2 files changed, 85 insertions(+), 9 deletions(-)
+
+diff --git a/src/corelib/itemmodels/qidentityproxymodel.cpp b/src/corelib/itemmodels/qidentityproxymodel.cpp
+index e537793..7c30679 100644
+--- a/src/corelib/itemmodels/qidentityproxymodel.cpp
++++ b/src/corelib/itemmodels/qidentityproxymodel.cpp
+@@ -496,15 +496,6 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe
+ {
+ Q_Q(QIdentityProxyModel);
+
+- const auto proxyPersistentIndexes = q->persistentIndexList();
+- for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) {
+- proxyIndexes << proxyPersistentIndex;
+- Q_ASSERT(proxyPersistentIndex.isValid());
+- const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
+- Q_ASSERT(srcPersistentIndex.isValid());
+- layoutChangePersistentIndexes << srcPersistentIndex;
+- }
+-
+ QList<QPersistentModelIndex> parents;
+ parents.reserve(sourceParents.size());
+ for (const QPersistentModelIndex &parent : sourceParents) {
+@@ -518,6 +509,15 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe
+ }
+
+ q->layoutAboutToBeChanged(parents, hint);
++
++ const auto proxyPersistentIndexes = q->persistentIndexList();
++ for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) {
++ proxyIndexes << proxyPersistentIndex;
++ Q_ASSERT(proxyPersistentIndex.isValid());
++ const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
++ Q_ASSERT(srcPersistentIndex.isValid());
++ layoutChangePersistentIndexes << srcPersistentIndex;
++ }
+ }
+
+ void QIdentityProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
+diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
+index e946f31..564b854 100644
+--- a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
++++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
+@@ -68,6 +68,8 @@ private slots:
+
+ void itemData();
+
++ void persistIndexOnLayoutChange();
++
+ protected:
+ void verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex());
+
+@@ -377,5 +379,79 @@ void tst_QIdentityProxyModel::itemData()
+ QCOMPARE(proxy.itemData(topIndex).value(Qt::DisplayRole).toString(), QStringLiteral("Monday_appended"));
+ }
+
++void dump(QAbstractItemModel* model, QString const& indent = " - ", QModelIndex const& parent = {})
++{
++ for (auto row = 0; row < model->rowCount(parent); ++row)
++ {
++ auto idx = model->index(row, 0, parent);
++ qDebug() << (indent + idx.data().toString());
++ dump(model, indent + "- ", idx);
++ }
++}
++
++void tst_QIdentityProxyModel::persistIndexOnLayoutChange()
++{
++ DynamicTreeModel model;
++
++ QList<int> ancestors;
++ for (auto i = 0; i < 3; ++i)
++ {
++ Q_UNUSED(i);
++ ModelInsertCommand insertCommand(&model);
++ insertCommand.setAncestorRowNumbers(ancestors);
++ insertCommand.setStartRow(0);
++ insertCommand.setEndRow(0);
++ insertCommand.doCommand();
++ ancestors.push_back(0);
++ }
++ ModelInsertCommand insertCommand(&model);
++ insertCommand.setAncestorRowNumbers(ancestors);
++ insertCommand.setStartRow(0);
++ insertCommand.setEndRow(1);
++ insertCommand.doCommand();
++
++ // dump(&model);
++ // " - 1"
++ // " - - 2"
++ // " - - - 3"
++ // " - - - - 4"
++ // " - - - - 5"
++
++ QIdentityProxyModel proxy;
++ proxy.setSourceModel(&model);
++
++ QPersistentModelIndex persistentIndex;
++
++ QPersistentModelIndex sourcePersistentIndex = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
++
++ QCOMPARE(sourcePersistentIndex.data().toString(), QStringLiteral("5"));
++
++ bool gotLayoutAboutToBeChanged = false;
++ bool gotLayoutChanged = false;
++
++ QObject::connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, &proxy, [&proxy, &persistentIndex, &gotLayoutAboutToBeChanged]
++ {
++ gotLayoutAboutToBeChanged = true;
++ persistentIndex = proxy.match(proxy.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
++ });
++
++ QObject::connect(&proxy, &QAbstractItemModel::layoutChanged, &proxy, [&proxy, &persistentIndex, &sourcePersistentIndex, &gotLayoutChanged]
++ {
++ gotLayoutChanged = true;
++ QCOMPARE(QModelIndex(persistentIndex), proxy.mapFromSource(sourcePersistentIndex));
++ });
++
++ ModelChangeChildrenLayoutsCommand layoutChangeCommand(&model, 0);
++
++ layoutChangeCommand.setAncestorRowNumbers(QList<int>{0, 0, 0});
++ layoutChangeCommand.setSecondAncestorRowNumbers(QList<int>{0, 0});
++
++ layoutChangeCommand.doCommand();
++
++ QVERIFY(gotLayoutAboutToBeChanged);
++ QVERIFY(gotLayoutChanged);
++ QVERIFY(persistentIndex.isValid());
++}
++
+ QTEST_MAIN(tst_QIdentityProxyModel)
+ #include "tst_qidentityproxymodel.moc"
+--
+2.7.4
+
diff --git a/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-1.patch b/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-1.patch
new file mode 100644
index 000000000000..9edbe18617a2
--- /dev/null
+++ b/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-1.patch
@@ -0,0 +1,252 @@
+From 3bd0fd8f97e7a33a874929a383a42e6c710bfff3 Mon Sep 17 00:00:00 2001
+From: Stephen Kelly <steveire@gmail.com>
+Date: Sat, 17 Dec 2016 06:20:06 +0000
+Subject: [PATCH] QSFPM: Fix handling of source model layout change
+
+In sourceLayoutAboutToBeChanged the source model update is ignored if
+the affected parents are filtered out anyway. The same logic is
+attempted in the sourceLayoutChanged slot, but there the early-return
+logic is applied too late - the mapping is cleared before performing the
+early-return. Because pointers into the mapping are used in the
+internalPointer of QModelIndexes in this class, persistent indexes used
+later will segfault when attempting to dereference it.
+
+Additionally, if a parent becomes invalid as a result of the
+layoutChange, it would be filtered out by the condition in the loop,
+resulting in a different result in the comparison of emptiness of the
+parents container.
+
+Fix that by persisting the parent's container, and performing the test
+for early-return before clearing the mapping.
+
+Task-number: QTBUG-47711
+Task-number: QTBUG-32981
+Change-Id: If45e8a1c97d39454160f52041bc9ae7e337dce97
+Reviewed-by: David Faure <david.faure@kdab.com>
+---
+ src/corelib/itemmodels/qsortfilterproxymodel.cpp | 31 ++---
+ .../tst_qsortfilterproxymodel.cpp | 126 +++++++++++++++++++++
+ 2 files changed, 137 insertions(+), 20 deletions(-)
+
+diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+index b0ddfa8..3331521 100644
+--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
++++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+@@ -171,6 +171,7 @@ public:
+ QRowsRemoval itemsBeingRemoved;
+
+ QModelIndexPairList saved_persistent_indexes;
++ QList<QPersistentModelIndex> saved_layoutChange_parents;
+
+ QHash<QModelIndex, Mapping *>::const_iterator create_mapping(
+ const QModelIndex &source_parent) const;
+@@ -1331,23 +1332,23 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<Q
+ Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
+ saved_persistent_indexes.clear();
+
+- QList<QPersistentModelIndex> parents;
++ saved_layoutChange_parents.clear();
+ for (const QPersistentModelIndex &parent : sourceParents) {
+ if (!parent.isValid()) {
+- parents << QPersistentModelIndex();
++ saved_layoutChange_parents << QPersistentModelIndex();
+ continue;
+ }
+ const QModelIndex mappedParent = q->mapFromSource(parent);
+ // Might be filtered out.
+ if (mappedParent.isValid())
+- parents << mappedParent;
++ saved_layoutChange_parents << mappedParent;
+ }
+
+ // All parents filtered out.
+- if (!sourceParents.isEmpty() && parents.isEmpty())
++ if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
+ return;
+
+- emit q->layoutAboutToBeChanged(parents);
++ emit q->layoutAboutToBeChanged(saved_layoutChange_parents);
+ if (persistent.indexes.isEmpty())
+ return;
+
+@@ -1359,6 +1360,9 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersisten
+ Q_Q(QSortFilterProxyModel);
+ Q_UNUSED(hint); // We can't forward Hint because we might filter additional rows or columns
+
++ if (!sourceParents.isEmpty() && saved_layoutChange_parents.isEmpty())
++ return;
++
+ // Optimize: We only actually have to clear the mapping related to the contents of
+ // sourceParents, not everything.
+ qDeleteAll(source_index_mapping);
+@@ -1373,21 +1377,8 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersisten
+ source_index_mapping.clear();
+ }
+
+- QList<QPersistentModelIndex> parents;
+- for (const QPersistentModelIndex &parent : sourceParents) {
+- if (!parent.isValid()) {
+- parents << QPersistentModelIndex();
+- continue;
+- }
+- const QModelIndex mappedParent = q->mapFromSource(parent);
+- if (mappedParent.isValid())
+- parents << mappedParent;
+- }
+-
+- if (!sourceParents.isEmpty() && parents.isEmpty())
+- return;
+-
+- emit q->layoutChanged(parents);
++ emit q->layoutChanged(saved_layoutChange_parents);
++ saved_layoutChange_parents.clear();
+ }
+
+ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
+diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+index 38e3c68..6b98d9f 100644
+--- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
++++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+@@ -145,6 +145,8 @@ private slots:
+ void canDropMimeData();
+ void filterHint();
+
++ void sourceLayoutChangeLeavesValidPersistentIndexes();
++
+ protected:
+ void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
+ void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
+@@ -4181,5 +4183,129 @@ void tst_QSortFilterProxyModel::filterHint()
+ QAbstractItemModel::NoLayoutChangeHint);
+ }
+
++/**
++
++ Creates a model where each item has one child, to a set depth,
++ and the last item has no children. For a model created with
++ setDepth(4):
++
++ - 1
++ - - 2
++ - - - 3
++ - - - - 4
++*/
++class StepTreeModel : public QAbstractItemModel
++{
++ Q_OBJECT
++public:
++ StepTreeModel(QObject * parent = 0)
++ : QAbstractItemModel(parent), m_depth(0) {}
++
++ int columnCount(const QModelIndex& = QModelIndex()) const override { return 1; }
++
++ int rowCount(const QModelIndex& parent = QModelIndex()) const override
++ {
++ quintptr parentId = (parent.isValid()) ? parent.internalId() : 0;
++ return (parentId < m_depth) ? 1 : 0;
++ }
++
++ QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override
++ {
++ if (role != Qt::DisplayRole)
++ return QVariant();
++
++ return QString::number(index.internalId());
++ }
++
++ QModelIndex index(int, int, const QModelIndex& parent = QModelIndex()) const override
++ {
++ quintptr parentId = (parent.isValid()) ? parent.internalId() : 0;
++ if (parentId >= m_depth)
++ return QModelIndex();
++
++ return createIndex(0, 0, parentId + 1);
++ }
++
++ QModelIndex parent(const QModelIndex& index) const override
++ {
++ if (index.internalId() == 0)
++ return QModelIndex();
++
++ return createIndex(0, 0, index.internalId() - 1);
++ }
++
++ void setDepth(quintptr depth)
++ {
++ int parentIdWithLayoutChange = (m_depth < depth) ? m_depth : depth;
++
++ QList<QPersistentModelIndex> parentsOfLayoutChange;
++ parentsOfLayoutChange.push_back(createIndex(0, 0, parentIdWithLayoutChange));
++
++ layoutAboutToBeChanged(parentsOfLayoutChange);
++
++ auto existing = persistentIndexList();
++
++ QList<QModelIndex> updated;
++
++ for (auto idx : existing) {
++ if (indexDepth(idx) <= depth)
++ updated.push_back(idx);
++ else
++ updated.push_back({});
++ }
++
++ m_depth = depth;
++
++ changePersistentIndexList(existing, updated);
++
++ layoutChanged(parentsOfLayoutChange);
++ }
++
++private:
++ static quintptr indexDepth(QModelIndex const& index)
++ {
++ return (index.isValid()) ? 1 + indexDepth(index.parent()) : 0;
++ }
++
++private:
++ quintptr m_depth;
++};
++
++void tst_QSortFilterProxyModel::sourceLayoutChangeLeavesValidPersistentIndexes()
++{
++ StepTreeModel model;
++ Q_SET_OBJECT_NAME(model);
++ model.setDepth(4);
++
++ QSortFilterProxyModel proxy1;
++ proxy1.setSourceModel(&model);
++ Q_SET_OBJECT_NAME(proxy1);
++
++ proxy1.setFilterRegExp("1|2");
++
++ // The current state of things:
++ // model proxy
++ // - 1 - 1
++ // - - 2 - - 2
++ // - - - 3
++ // - - - - 4
++
++ // The setDepth call below removes '4' with a layoutChanged call.
++ // Because the proxy filters that out anyway, the proxy doesn't need
++ // to emit any signals or update persistent indexes.
++
++ QPersistentModelIndex persistentIndex = proxy1.index(0, 0, proxy1.index(0, 0));
++
++ model.setDepth(3);
++
++ // Calling parent() causes the internalPointer to be used.
++ // Before fixing QTBUG-47711, that could be a dangling pointer.
++ // The use of qDebug here makes sufficient use of the heap to
++ // cause corruption at runtime with normal use on linux (before
++ // the fix). valgrind confirms the fix.
++ qDebug() << persistentIndex.parent();
++ QVERIFY(persistentIndex.parent().isValid());
++}
++
+ QTEST_MAIN(tst_QSortFilterProxyModel)
+ #include "tst_qsortfilterproxymodel.moc"
+--
+2.7.4
+
+
diff --git a/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-2.patch b/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-2.patch
new file mode 100644
index 000000000000..973649637e66
--- /dev/null
+++ b/dev-qt/qtcore/files/qtcore-5.7.1-qsfpm-2.patch
@@ -0,0 +1,195 @@
+From 0874861bcc70313c343aba5e5566ed30b69eed1c Mon Sep 17 00:00:00 2001
+From: Stephen Kelly <steveire@gmail.com>
+Date: Mon, 19 Dec 2016 21:13:57 +0000
+Subject: [PATCH] QSFPM: Remove data manipulation from move handlers
+
+Similar to the fix in the parent commit, incorrect updating of the
+internal data structures during layout changes can lead to dangling
+pointers being dereferenced later. Moves are treated as layoutChanges
+by this proxy by forwarding to the appropriate method. However, data is
+incorrectly cleared prior to that forwarding. Remove that, and let the
+layoutChange handling take appropriate action.
+
+Change-Id: Iee951e37152328a4e6a5fb8e5385c32a2fe4c0bd
+Reviewed-by: David Faure <david.faure@kdab.com>
+---
+ src/corelib/itemmodels/qsortfilterproxymodel.cpp | 67 ++++------------------
+ .../tst_qsortfilterproxymodel.cpp | 46 +++++++++++++++
+ 2 files changed, 58 insertions(+), 55 deletions(-)
+
+diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+index 3331521..226a240 100644
+--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
++++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+@@ -1418,49 +1418,27 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
+ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
+ {
+- Q_Q(QSortFilterProxyModel);
+ // Because rows which are contiguous in the source model might not be contiguous
+ // in the proxy due to sorting, the best thing we can do here is be specific about what
+ // parents are having their children changed.
+ // Optimize: Emit move signals if the proxy is not sorted. Will need to account for rows
+ // being filtered out though.
+
+- saved_persistent_indexes.clear();
+-
+ QList<QPersistentModelIndex> parents;
+- parents << q->mapFromSource(sourceParent);
++ parents << sourceParent;
+ if (sourceParent != destParent)
+- parents << q->mapFromSource(destParent);
+- emit q->layoutAboutToBeChanged(parents);
+- if (persistent.indexes.isEmpty())
+- return;
+- saved_persistent_indexes = store_persistent_indexes();
++ parents << destParent;
++ _q_sourceLayoutAboutToBeChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
+ }
+
+ void QSortFilterProxyModelPrivate::_q_sourceRowsMoved(
+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
+ {
+- Q_Q(QSortFilterProxyModel);
+-
+- // Optimize: We only need to clear and update the persistent indexes which are children of
+- // sourceParent or destParent
+- qDeleteAll(source_index_mapping);
+- source_index_mapping.clear();
+-
+- update_persistent_indexes(saved_persistent_indexes);
+- saved_persistent_indexes.clear();
+-
+- if (dynamic_sortfilter && update_source_sort_column()) {
+- //update_source_sort_column might have created wrong mapping so we have to clear it again
+- qDeleteAll(source_index_mapping);
+- source_index_mapping.clear();
+- }
+-
+ QList<QPersistentModelIndex> parents;
+- parents << q->mapFromSource(sourceParent);
++ parents << sourceParent;
+ if (sourceParent != destParent)
+- parents << q->mapFromSource(destParent);
+- emit q->layoutChanged(parents);
++ parents << destParent;
++ _q_sourceLayoutChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
+ }
+
+ void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(
+@@ -1522,42 +1500,21 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
+ void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
+ {
+- Q_Q(QSortFilterProxyModel);
+-
+- saved_persistent_indexes.clear();
+-
+ QList<QPersistentModelIndex> parents;
+- parents << q->mapFromSource(sourceParent);
++ parents << sourceParent;
+ if (sourceParent != destParent)
+- parents << q->mapFromSource(destParent);
+- emit q->layoutAboutToBeChanged(parents);
+-
+- if (persistent.indexes.isEmpty())
+- return;
+- saved_persistent_indexes = store_persistent_indexes();
++ parents << destParent;
++ _q_sourceLayoutAboutToBeChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
+ }
+
+ void QSortFilterProxyModelPrivate::_q_sourceColumnsMoved(
+ const QModelIndex &sourceParent, int /* sourceStart */, int /* sourceEnd */, const QModelIndex &destParent, int /* dest */)
+ {
+- Q_Q(QSortFilterProxyModel);
+-
+- qDeleteAll(source_index_mapping);
+- source_index_mapping.clear();
+-
+- update_persistent_indexes(saved_persistent_indexes);
+- saved_persistent_indexes.clear();
+-
+- if (dynamic_sortfilter && update_source_sort_column()) {
+- qDeleteAll(source_index_mapping);
+- source_index_mapping.clear();
+- }
+-
+ QList<QPersistentModelIndex> parents;
+- parents << q->mapFromSource(sourceParent);
++ parents << sourceParent;
+ if (sourceParent != destParent)
+- parents << q->mapFromSource(destParent);
+- emit q->layoutChanged(parents);
++ parents << destParent;
++ _q_sourceLayoutChanged(parents, QAbstractItemModel::NoLayoutChangeHint);
+ }
+
+ /*!
+diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+index 6b98d9f..7b6c470 100644
+--- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
++++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+@@ -146,6 +146,7 @@ private slots:
+ void filterHint();
+
+ void sourceLayoutChangeLeavesValidPersistentIndexes();
++ void rowMoveLeavesValidPersistentIndexes();
+
+ protected:
+ void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
+@@ -4307,5 +4308,50 @@ void tst_QSortFilterProxyModel::sourceLayoutChangeLeavesValidPersistentIndexes()
+ QVERIFY(persistentIndex.parent().isValid());
+ }
+
++void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes()
++{
++ DynamicTreeModel model;
++ Q_SET_OBJECT_NAME(model);
++
++ QList<int> ancestors;
++ for (auto i = 0; i < 5; ++i)
++ {
++ Q_UNUSED(i);
++ ModelInsertCommand insertCommand(&model);
++ insertCommand.setAncestorRowNumbers(ancestors);
++ insertCommand.setStartRow(0);
++ insertCommand.setEndRow(0);
++ insertCommand.doCommand();
++ ancestors.push_back(0);
++ }
++
++ QSortFilterProxyModel proxy1;
++ proxy1.setSourceModel(&model);
++ Q_SET_OBJECT_NAME(proxy1);
++
++ proxy1.setFilterRegExp("1|2");
++
++ auto item5 = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
++ auto item3 = model.match(model.index(0, 0), Qt::DisplayRole, "3", 1, Qt::MatchRecursive).first();
++
++ Q_ASSERT(item5.isValid());
++ Q_ASSERT(item3.isValid());
++
++ QPersistentModelIndex persistentIndex = proxy1.match(proxy1.index(0, 0), Qt::DisplayRole, "2", 1, Qt::MatchRecursive).first();
++
++ ModelMoveCommand moveCommand(&model, 0);
++ moveCommand.setAncestorRowNumbers(QList<int>{0, 0, 0, 0});
++ moveCommand.setStartRow(0);
++ moveCommand.setEndRow(0);
++ moveCommand.setDestRow(0);
++ moveCommand.setDestAncestors(QList<int>{0, 0, 0});
++ moveCommand.doCommand();
++
++ // Calling parent() causes the internalPointer to be used.
++ // Before fixing QTBUG-47711 (moveRows case), that could be
++ // a dangling pointer.
++ QVERIFY(persistentIndex.parent().isValid());
++}
++
+ QTEST_MAIN(tst_QSortFilterProxyModel)
+ #include "tst_qsortfilterproxymodel.moc"
+--
+2.7.4
+
+