-
The loop in DocumentParsePlan::removeTargetsForListener() caches m_targets.cend(), which can become invalidated when a target is erased inside this loop. Comparing to the invalidated end iterator leads to undefined behavior. This undefined behavior makes QmlJS's test_files crash every other time, both on my system and on the CI. The alternating crashes of this test can be seen by clicking Next Build repeatedly starting from the first build since the bug was introduced: https://build.kde.org/job/KDevelop/job/kdevelop/job/kf5-qt5%20SUSEQt5.15/165/testReport/junit/projectroot.plugins.qmljs/tests/test_files/ KDevelop often crashes because of this undefined behavior too (see the bug report referenced below). m_targets is a QSet, which is implemented in terms of QHash. QHash's end iterator `e` equals the d-pointer `d` via anonymous union. So the only way cend() can be invalidated is if QSet::erase() detaches. That's possible, because an entire DocumentParsePlan is copied in BackgroundParserPrivate::parseDocumentsInternal(): const DocumentParsePlan parsePlan = *parsePlanConstIt; The involvement of multithreading explains why test_files crashes every other time rather than each time. Examining call stacks of both test_files and kdevelop crashes confirms this hypothesis: a background thread crashes in DocumentParsePlan::removeTargetsForListener() while the main thread waits on the `m_mutex.lock()` line in parseDocumentsInternal(). This bug was introduced in 5ee9b9fe. BUG: 445699
38713121