TextEditor: fix crash on reload

Since TextDocument::openImpl potentially processes events it could
delete TextMarks. So tracking them in TextDocument::reload can be
considered unsafe. Track them in TextDocumentLayout instead and remove
the tracked mark if it gets deleted while reloading the document.

Task-number: QTCREATORBUG-29004
Change-Id: I9d0478e9c763b49f145c1bbaeed1a0b602757014
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
David Schulz
2023-04-05 12:48:19 +02:00
parent 78cf563142
commit f773c09f33
5 changed files with 33 additions and 6 deletions

View File

@@ -836,15 +836,14 @@ bool TextDocument::reload(QString *errorString, const FilePath &realFilePath)
emit aboutToReload(); emit aboutToReload();
auto documentLayout = auto documentLayout =
qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout()); qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
TextMarks marks;
if (documentLayout) if (documentLayout)
marks = documentLayout->documentClosing(); // removes text marks non-permanently documentLayout->documentAboutToReload(); // removes text marks non-permanently
bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true) bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true)
== OpenResult::Success; == OpenResult::Success;
if (documentLayout) if (documentLayout)
documentLayout->documentReloaded(marks, this); // re-adds text marks documentLayout->documentReloaded(this); // re-adds text marks
emit reloadFinished(success); emit reloadFinished(success);
return success; return success;
} }

View File

@@ -656,6 +656,7 @@ QSizeF TextDocumentLayout::documentSize() const
TextMarks TextDocumentLayout::documentClosing() TextMarks TextDocumentLayout::documentClosing()
{ {
QTC_ASSERT(m_reloadMarks.isEmpty(), resetReloadMarks());
TextMarks marks; TextMarks marks;
for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) { for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
if (auto data = static_cast<TextBlockUserData *>(block.userData())) if (auto data = static_cast<TextBlockUserData *>(block.userData()))
@@ -664,9 +665,17 @@ TextMarks TextDocumentLayout::documentClosing()
return marks; return marks;
} }
void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTextDocument) void TextDocumentLayout::documentAboutToReload()
{ {
for (TextMark *mark : std::as_const(marks)) { m_reloadMarks = documentClosing();
for (TextMark *mark : std::as_const(m_reloadMarks))
mark->setDeleteCallback([this, mark] { m_reloadMarks.removeOne(mark); });
}
void TextDocumentLayout::documentReloaded(TextDocument *baseTextDocument)
{
for (TextMark *mark : std::as_const(m_reloadMarks)) {
mark->setDeleteCallback({});
int blockNumber = mark->lineNumber() - 1; int blockNumber = mark->lineNumber() - 1;
QTextBlock block = document()->findBlockByNumber(blockNumber); QTextBlock block = document()->findBlockByNumber(blockNumber);
if (block.isValid()) { if (block.isValid()) {
@@ -680,6 +689,7 @@ void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTex
mark->removedFromEditor(); mark->removedFromEditor();
} }
} }
m_reloadMarks.clear();
requestUpdate(); requestUpdate();
} }
@@ -721,6 +731,13 @@ void TextDocumentLayout::requestUpdateNow()
requestUpdate(); requestUpdate();
} }
void TextDocumentLayout::resetReloadMarks()
{
for (TextMark *mark : std::as_const(m_reloadMarks))
mark->setDeleteCallback({});
m_reloadMarks.clear();
}
static QRectF replacementBoundingRect(const QTextDocument *replacement) static QRectF replacementBoundingRect(const QTextDocument *replacement)
{ {
QTC_ASSERT(replacement, return {}); QTC_ASSERT(replacement, return {});

View File

@@ -245,7 +245,8 @@ public:
QRectF blockBoundingRect(const QTextBlock &block) const override; QRectF blockBoundingRect(const QTextBlock &block) const override;
TextMarks documentClosing(); TextMarks documentClosing();
void documentReloaded(TextMarks marks, TextDocument *baseextDocument); void documentAboutToReload();
void documentReloaded(TextDocument *baseextDocument);
void updateMarksLineNumber(); void updateMarksLineNumber();
void updateMarksBlock(const QTextBlock &block); void updateMarksBlock(const QTextBlock &block);
void scheduleUpdate(); void scheduleUpdate();
@@ -253,6 +254,8 @@ public:
private: private:
bool m_updateScheduled = false; bool m_updateScheduled = false;
TextMarks m_reloadMarks;
void resetReloadMarks();
signals: signals:
void updateExtraArea(); void updateExtraArea();

View File

@@ -81,6 +81,8 @@ TextMark::~TextMark()
TextMarkRegistry::remove(this); TextMarkRegistry::remove(this);
if (m_baseTextDocument) if (m_baseTextDocument)
m_baseTextDocument->removeMark(this); m_baseTextDocument->removeMark(this);
if (m_deleteCallback)
m_deleteCallback();
m_baseTextDocument = nullptr; m_baseTextDocument = nullptr;
} }

View File

@@ -118,12 +118,15 @@ public:
bool isLocationMarker() const;; bool isLocationMarker() const;;
void setIsLocationMarker(bool newIsLocationMarker); void setIsLocationMarker(bool newIsLocationMarker);
protected: protected:
void setSettingsPage(Utils::Id settingsPage); void setSettingsPage(Utils::Id settingsPage);
private: private:
Q_DISABLE_COPY(TextMark) Q_DISABLE_COPY(TextMark)
void setDeleteCallback(const std::function<void()> &callback) { m_deleteCallback = callback; };
TextDocument *m_baseTextDocument = nullptr; TextDocument *m_baseTextDocument = nullptr;
Utils::FilePath m_fileName; Utils::FilePath m_fileName;
int m_lineNumber = 0; int m_lineNumber = 0;
@@ -141,6 +144,9 @@ private:
QVector<QAction *> m_actions; // FIXME Remove in master QVector<QAction *> m_actions; // FIXME Remove in master
std::function<QList<QAction *>()> m_actionsProvider; std::function<QList<QAction *>()> m_actionsProvider;
Utils::Id m_settingsPage; Utils::Id m_settingsPage;
std::function<void()> m_deleteCallback;
friend class TextDocumentLayout;
}; };
} // namespace TextEditor } // namespace TextEditor