Bug 503813 - Crash on using the Menu Key in the default editor in a directory with git
Summary: Crash on using the Menu Key in the default editor in a directory with git
Status: CONFIRMED
Alias: None
Product: kdevelop
Classification: Applications
Component: general (other bugs)
Version First Reported In: git master
Platform: Arch Linux Linux
: NOR crash
Target Milestone: ---
Assignee: kdevelop-bugs-null
URL:
Keywords: drkonqi
Depends on:
Blocks:
 
Reported: 2025-05-05 22:05 UTC by ulterno
Modified: 2025-05-16 13:39 UTC (History)
1 user (show)

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report: https://crash-reports.kde.org/organizations/kde/issues/173731/events/7773b1e044874c0f859bacc7fea87746/


Attachments
New crash information added by DrKonqi (113.79 KB, text/plain)
2025-05-05 22:05 UTC, ulterno
Details
attachment-3408275-0.html (1.86 KB, text/html)
2025-05-09 12:11 UTC, ulterno
Details
Second context menu: call stack with debug symbols (16.86 KB, text/plain)
2025-05-10 08:24 UTC, Igor Kushnir
Details
Slow GitPlugin::hasStashes() experiment (2.16 KB, patch)
2025-05-16 13:39 UTC, Igor Kushnir
Details

Note You need to log in before you can comment on or make changes to this bug.
Description ulterno 2025-05-05 22:05:17 UTC
Application: kdevelop (6.2.250400 (25.04.0))

ApplicationNotResponding [ANR]: false
Qt Version: 6.9.0
Frameworks Version: 6.13.0
Operating System: Linux 6.14.4-arch1-2 x86_64
Windowing System: Wayland
Distribution: EndeavourOS
DrKonqi: 6.3.4 [CoredumpBackend]

-- Information about the crash:
STEPS FOR REPRODUCTION
0. Create a file in a directory with a git
  - Minimally, just `git init; touch file1;` will do
1. Open the file in the KDevelop's text editor
2. Click somewhere within the text editor
3. Press the "Menu" key on the keyboard
  - the one that's usually next to the Right-Ctrl key

OBSERVATION
1. The right click menu opens
2. KDevelop GUI freezes afterwards (keyboard arrow keys don't change the selection on the menu either)
3. Crash after a few seconds
Additionally, 
3. The server side decoration by KWin still works
4. Clicking on the decoration may cause the opened menu (otherwise frozen to the user) to close

EXPECTATION
- The right click menu should be usable and the application should not crash right after
  - Same as how it works when using the Mouse Right Click

The crash can be reproduced every time.

-- Backtrace (Reduced):
#5  std::__atomic_base<QThread*>::load (this=<optimized out>, __m=<optimized out>, this=<optimized out>, __m=<optimized out>) at /usr/include/c++/14.2.1/bits/atomic_base.h:831
#6  std::atomic<QThread*>::load (this=<optimized out>, __m=<optimized out>, this=<optimized out>, __m=<optimized out>) at /usr/include/c++/14.2.1/atomic:582
#7  QAtomicOps<QThread*>::loadAcquire<QThread*> (_q_value=..., _q_value=...) at /usr/src/debug/qt6-base/qtbase/src/corelib/thread/qatomic_cxx11.h:214
#8  QBasicAtomicPointer<QThread>::loadAcquire (this=<optimized out>, this=<optimized out>) at /usr/src/debug/qt6-base/qtbase/src/corelib/thread/qbasicatomic.h:177
#9  QObject::thread (this=this@entry=0x62e9909f3100) at /usr/src/debug/qt6-base/qtbase/src/corelib/kernel/qobject.cpp:1612


Reported using DrKonqi
Comment 1 ulterno 2025-05-05 22:05:19 UTC
Created attachment 180970 [details]
New crash information added by DrKonqi

DrKonqi auto-attaching complete backtrace.
Comment 2 Igor Kushnir 2025-05-06 08:53:38 UTC
My Menu key is remapped away, so I cannot easily reproduce. Does the crash happen on a single system or multiple systems? Is this a recent regression? Does a similar crash occur in Kate and other KDE or Qt applications?

The backtrace doesn't seem to indicate that the bug is in KDevelop. Perhaps this crash is a consequence of not rebuilding Qt, KF and KDevelop against the gcc package recently updated to the version 15?
Comment 3 ulterno 2025-05-06 15:05:14 UTC
1. Just tried by deactivating the git plugin and the crash stopped happening. Perhaps the plugin adds some functionality in case of the Menu Keyboard key, which is otherwise absent in case of the mouse RMB?
- Will stop using the plugin for now, since all I used it for, was to look at what branch I was in, without having to look at the terminal
- The same version of the git Plugin (0.9) didn't cause the crash on Debian 12, though


2. I have another Arch system which is a few weeks out of date. I can try on that.
- I haven't used KDevelop in a while, so can't say anything about "recent regression", but I probably did use the menu key before
3. I'd rather not downgrade gcc to check that, but if all else ends up a dead end, I can consider it.
Comment 4 Igor Kushnir 2025-05-06 15:35:52 UTC
(In reply to ulterno from comment #3)
> 1. Just tried by deactivating the git plugin and the crash stopped
> happening. Perhaps the plugin adds some functionality in case of the Menu
> Keyboard key, which is otherwise absent in case of the mouse RMB?
I would be very surprised if the git plugin had special handling of the Menu key. I think the bug is in more general code - Qt, KF or elsewhere in KDevelop. So please check a few other KDE/Qt apps, especially Kate.
Comment 5 ulterno 2025-05-06 16:01:43 UTC
I forgot to mention before:

- I already tried Kate and KWrite, before sending the first crash report
- I usually use QtCreator (just trying to switch full-time to KDevelop) and have checked the menu key with that
- Also, I tend to use the Menu key quite a bit, so I cannot think of any place to use it where I haven't already

Lastly, I have been using it since the last 15-20 minutes without a crash, just by disabling the git plugin.

On the other hand, I don't consider it to be directly related to the Plugin either, considering that it takes a while to crash after the UI stops responding.

I should get more information when I try it on the other installation
Comment 6 ulterno 2025-05-06 16:21:40 UTC
Successfully reproduced the behaviour on an older version as follows:

KDevelop: 6.1.241203 (24.12.3)
KDE Frameworks: 6.12.0
Qt: Using 6.9.0 and built against 6.9.0
EndeavourOS (Wayland)
Build ABI: x86_64-little_endian-lp64
Kernel: linux 6.14.1-zen1-1-zen


I was unable to install DrKonqi on that without updating, so ...
Comment 7 Igor Kushnir 2025-05-06 20:16:00 UTC
I have no idea why the crash happens. Nothing looks wrong in the KDevelop part of the backtrace. If you build KDevelop locally, you could experiment with various changes to the relevant code and possibly figure out what triggers the crash.
Comment 8 Igor Kushnir 2025-05-07 05:20:53 UTC
One problem with the git plugin is its synchronous process execution and the accompanying nested event loops. GitPlugin::additionalMenuEntries() calls GitPlugin::hasStashes(), which calls emptyOutput(), which calls KJob::exec(), which runs a git process.

You can try inserting the following local variable at the top of additionalMenuEntries(): `QPointer menuGuard(menu);` and check whether it is null after the hasStashes() call.
Comment 9 ulterno 2025-05-07 09:14:47 UTC
Guess I should have picked this out when reading the journal, but got it finally.

This happens right after I clicked the menu button:

kdevplatform.shell: populateContextMenu() called while we still handled another menu.
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2

My guesses are:
1. Either the Keyboard button press event or the 'request context menu' event has been passed on to the parent widget, causing 2x context menu
2. For some reason, the context menu is requested twice, when using the keyboard menu key

I am unable to use the debugger for this case, because that causes a break at:
kpluginfactory.h: function: template<typename T> inline T *KPluginFactory::create(QObject *parent, const QVariantList &args)
Leading to "Unable to continue debugged process", which should probably be another bug.
Comment 10 ulterno 2025-05-07 09:16:15 UTC
(In reply to ulterno from comment #9)
> Guess I should have picked this out when reading the journal, but got it
> finally.
> 
> This happens right after I clicked the menu button:
> 
> kdevplatform.shell: populateContextMenu() called while we still handled
> another menu.
> KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
> 
> My guesses are:
> 1. Either the Keyboard button press event or the 'request context menu'
> event has been passed on to the parent widget, causing 2x context menu
> 2. For some reason, the context menu is requested twice, when using the
> keyboard menu key
> 
> I am unable to use the debugger for this case, because that causes a break
> at:
> kpluginfactory.h: function: template<typename T> inline T
> *KPluginFactory::create(QObject *parent, const QVariantList &args)
> Leading to "Unable to continue debugged process", which should probably be
> another bug.

The above was done with tag v25.04.0 as I am yet unable to load plugins when using master
Comment 11 Igor Kushnir 2025-05-07 09:25:11 UTC
(In reply to ulterno from comment #10)
> The above was done with tag v25.04.0 as I am yet unable to load plugins when
> using master
You need to install KDevelop (possibly into a user-owned directory) and run `source /path/to/kdevelop/build/prefix.sh` before `./bin/kdevelop`. See https://invent.kde.org/kdevelop/kdevelop/-/merge_requests/490#note_766527 for details.

> kdevplatform.shell: populateContextMenu() called while we still handled
> another menu.
So the old menu is probably destroyed when the new one appears, which causes the segfault. Try the QPointer I suggested in the previous comment.
Comment 12 ulterno 2025-05-07 16:53:31 UTC
So I went with this code:

void GitPlugin::additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls)
{
	qDebug() << __PRETTY_FUNCTION__;
	QPointer menuGuard (menu);
	qDebug() << "Menu guarded";
	m_urls = urls;

	QDir dir = urlDir (urls);
	qDebug() << "URLs be URL'ed";
	if (!menu)
	{
		qDebug() << "Heehee HAaahaaa !!!";
		return;
	}
	qDebug() << menu << "  Still exists";
	bool hasSt = hasStashes (dir);
	qDebug() << menu << "  stashes hashed";
	menu->addAction (i18nc ("@action:inmenu", "Rebase"), this, SLOT (ctxRebase()));
	menu->addSeparator()->setText (i18nc ("@title:menu", "Git Stashes"));
	menu->addAction(i18nc("@action:inmenu", "Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(hasSt);
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash")), i18nc("@action:inmenu", "Push Stash"), this, SLOT(ctxPushStash()));
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash-pop")), i18nc("@action:inmenu", "Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(hasSt);
}

gitjob.cpp

GitJob::GitJob(const QDir& workingDir, KDevelop::IPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity)
    : DVcsJob(workingDir, parent, verbosity)
{
	static QMutex aMutex;
	QMutexLocker lkr (&aMutex);
	qDebug() << __PRETTY_FUNCTION__;
	setType (VcsJob::UserType);
	process()->setEnv (QStringLiteral ("GIT_OPTIONAL_LOCKS"), QStringLiteral ("0"));
}
___

After the build,
cp    ..../build/kdevgit.so      /usr/lib/qt6/plugins/kdevplatform/62/kdevgit.so
sync
Because I couldn't reliably manage what you suggested, sorry. I'll try that again later.

With QT_LOGGING_RULES=default.debug=true

Got the following output:

kdevplatform.shell: populateContextMenu() called while we still handled another menu.
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
QMenu(0x558d14c30390)   Still exists
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
QMenu(0x558d14c30390)   stashes hashed
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
QMenu(0x558d14c50360)   Still exists
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
22:19:01: The process crashed.
Comment 13 ulterno 2025-05-07 16:58:56 UTC
As you see, additionalMenuEntries is being called twice, which, to me seems that something above it is doing something.
Comment 14 Igor Kushnir 2025-05-07 17:26:15 UTC
> and check whether it is null **after** the hasStashes() call.
Before the hasStashes() call no event loop is entered, so the menu guard cannot possibly be null.

Your Menu key could be sticky and register twice, but a software bug is more likely. GitPlugin needs a guard fix and an early return even if the duplicate menu bug is fixed. The menu could be destroyed for some other reason while the git process is running (could take seconds during a severe system load or in a huge long-since garbage-collected git repository).

> Because I couldn't reliably manage what you suggested, sorry. I'll try that again later.
Set the CMAKE_INSTALL_PREFIX CMake variable to some directory within your home directory; (cmake) configure, generate; make install or ninja install; cd /path/to/the/installed-dir; source /path/to/build-dir/prefix.sh; ./bin/kdevelop
Comment 15 Igor Kushnir 2025-05-07 17:32:19 UTC
You can also connect a lambda to the QObject::destroyed signal of the menu object; print a message and set a breakpoint in the lambda; the frame stack when the breakpoint is hit would tell you from where the old menu is destroyed.
Comment 16 ulterno 2025-05-08 04:00:52 UTC
> and check whether it is null **after** the hasStashes() call.
> Before the hasStashes() call no event loop is entered, so the menu guard cannot possibly be null.

I did that on my first try. It does not reach the debug output after hashStashes() on the second time, in that case. Which is why I tried hacking stuff inside it.
I should have sent the previous one too.
But here, I did it again:

"2025-05-08T09:09:21.150" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
"2025-05-08T09:09:21.152" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
kdevplatform.shell: populateContextMenu() called while we still handled another menu.
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
QMenu(0x5eed0fb11740)   Still exists --1
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
QMenu(0x5eed0fb11740)   stashes hashed
QMenu(0x5eed0fb11740)   Still exists --2
Complete
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
QMenu(0x5eed0f858a50)   Still exists --1
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
zsh: segmentation fault (core dumped)  ./bin/kdevelop

___ Related code:
void GitPlugin::additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls)
{
	qDebug() << __PRETTY_FUNCTION__;
	QPointer menuGuard (menu);
	qDebug() << "Menu guarded";
	m_urls = urls;

	QDir dir = urlDir (urls);
	qDebug() << "URLs be URL'ed";
	if (!menu)
	{
		qDebug() << "Heehee HAaahaaa !!!";
		return;
	}
	qDebug() << menu << "  Still exists --1";
	bool hasSt = hasStashes (dir);
	qDebug() << menu << "  stashes hashed";
	if (!menuGuard)
	{
		qDebug() << "Heehee HAaahaaa !!!";
		return;
	}
	qDebug() << menu << "  Still exists --2";
	menu->addAction (i18nc ("@action:inmenu", "Rebase"), this, SLOT (ctxRebase()));
	menu->addSeparator()->setText (i18nc ("@title:menu", "Git Stashes"));
	menu->addAction(i18nc("@action:inmenu", "Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(hasSt);
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash")), i18nc("@action:inmenu", "Push Stash"), this, SLOT(ctxPushStash()));
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash-pop")), i18nc("@action:inmenu", "Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(hasSt);
	qDebug() << "Complete";
}
___


>  (cmake) configure, generate;

I see. I had left this part to the IDE, because it was more convenient to set the variables in that. Perhaps I shouldn't have, because then it started trying to put /kdev_filtersd.so into /usr/lib/qt6/plugins/kf6/ktexttemplate/kdev_filtersd.so


But doing it all in the terminal, worked fine.
Comment 17 Igor Kushnir 2025-05-08 07:10:13 UTC
(In reply to ulterno from comment #16)
> QMenu(0x5eed0f858a50)   Still exists --1
> GitJob::GitJob(const QDir&, KDevelop::IPlugin*,
> KDevelop::OutputJob::OutputJobVerbosity)
> KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
> zsh: segmentation fault (core dumped)  ./bin/kdevelop
I think a segfault now occurs at the line `qDebug() << menu << "  stashes hashed";`. You cannot dereference (debug-printing does that) a dangling menu pointer without checking the menuGuard for null first.
Comment 18 Igor Kushnir 2025-05-08 10:53:40 UTC
(In reply to ulterno from comment #16)
> I see. I had left this part to the IDE, because it was more convenient to
> set the variables in that. Perhaps I shouldn't have, because then it started
> trying to put /kdev_filtersd.so into
> /usr/lib/qt6/plugins/kf6/ktexttemplate/kdev_filtersd.so
If the IDE is KDevelop, then you might have triggered Bug 421621.
> But doing it all in the terminal, worked fine.
I usually do this in cmake-gui.
Comment 19 ulterno 2025-05-08 11:37:30 UTC
Going with this code:

void GitPlugin::additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls)
{
	qDebug() << __PRETTY_FUNCTION__;
	QPointer menuGuard (menu);
	qDebug() << "Menu guarded";
	m_urls = urls;

	QDir dir = urlDir (urls);
	qDebug() << "URLs be URL'ed";
	if (!menu)
	{
		qDebug() << "Heehee HAaahaaa !!!";
		return;
	}
	qDebug() << "  Still exists --1" << (long) ((void *) menu);
	bool hasSt = hasStashes (dir);
	qDebug() << "  stashes hashed";
	if (menuGuard.isNull()) // I had forgotten removing the '!' after adding the isNull, before
	{
		qDebug() << "Menu destroyed !!";
	}
	else
	{
		qDebug() << "Menu exists";
	}
	qDebug() << "  Point --2" << (long) ((void *) menu);
	menu->addAction (i18nc ("@action:inmenu", "Rebase"), this, SLOT (ctxRebase()));
	menu->addSeparator()->setText (i18nc ("@title:menu", "Git Stashes"));
	menu->addAction(i18nc("@action:inmenu", "Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(hasSt);
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash")), i18nc("@action:inmenu", "Push Stash"), this, SLOT(ctxPushStash()));
    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash-pop")), i18nc("@action:inmenu", "Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(hasSt);
	qDebug() << "Complete";
}

___

Full output:

Library Paths:  QList("/usr/lib/qt6/plugins/kiconthemes6", "/run/media/ulterno/Data/code/KDE/KDevelop/install/lib/plugins", "/usr/lib/qt6/plugins", "/run/media/ulterno/Data/code/KDE/KDevelop/install/bin")
Found 65 plugins: QList("KDevDefinesAndIncludesManager", "KDevMakeBuilder", "kdevclangtidy", "kdevexecute", "kdevastyle", "KDevProjectFilter", "scratchpad", "kdevexternalscript", "KDevCMakeBuilder", "KDevProjectManagerView", "kdevgrepview", "kdevcustomscript", "kdevdocker", "KDevGenericManager", "kdevpatchreview", "kdevdocumentview", "kdevcodeutils", "kdevvcschangesviewplugin", "KDevCustomMakeManager", "kdevcontextbrowser", "KDevManPage", "KDevWelcomePage", "kdevclassbrowser", "kdevswitchtobuddy", "kdevsourceformatter", "kdevcppcheck", "kdevperforce", "kdevclangsupport", "kdevgdb", "KDevCMakeManager", "kdevbazaar", "kdevdocumentswitcher", "kdevandroid", "KDevOutlineView", "KDevStandardOutputView", "kdevghprovider", "kdevkonsoleview", "kdevflatpak", "KDevCMakeDocumentation", "kdevopenwith", "kdevfilemanager", "kdevqthelp", "kdevappwizard", "kdevexecuteplasmoid", "kdevproblemreporter", "kdevfiletemplates", "KDevQMakeManager", "KDevMesonManager", "KDevQMakeBuilder", "KDevNinjaBuilder", "kdevlldb", "kdevheaptrack", "kdevcraft", "KDevCustomBuildSystem", "kdevgit", "kdevquickopen", "kdevexecutescript", "kdevclazy", "kdevtestview", "kdevphplanguagesupport", "kdevpythonlanguagesupport", "kdevphpunitprovider", "kdevsubversion", "kdevphpdocs", "kdevpdb")
kdevplatform.serialization: version mismatch or no version hint; expected version: 100794372
kdevplatform.serialization: "The data-repository at /home/ulterno/.cache/kdevduchain/kdevelop-{1e439ed4-b9a5-4126-9dec-5096ebd432ca} has to be cleared."
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
kdevplatform.shell: Unable to find a plugin named ' "kateprojectplugin" '!
qt.core.qobject.connect: QObject::connect(KateApp, KateTextHintManager): invalid nullptr parameter
qt.core.qobject.connect: QObject::connect(QObject, KTextEditorIntegration::MainWindow): invalid nullptr parameter
qt.core.qobject.connect: QObject::connect: No such signal KDevelop::MainWindow::tabForToolViewAdded(QWidget *, QWidget *)
qt.core.qobject.connect: QObject::connect:  (sender name:   'MainWindow')
qt.core.qobject.connect: QObject::connect(KateApp, DiagnosticsView): invalid nullptr parameter
QLayout: Cannot add a null widget to QHBoxLayout/
<<<<---- I press the menu button at this point ---->>>>
"2025-05-08T17:02:12.542" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
"2025-05-08T17:02:12.544" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
kdevplatform.shell: populateContextMenu() called while we still handled another menu.
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
  Still exists --1 97270432047568
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
  stashes hashed
Menu exists
  Point --2 97270432047568
Complete
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
  Still exists --1 97270431786208
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
  stashes hashed
Menu destroyed !!
  Point --2 97270431786208
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
zsh: segmentation fault (core dumped)  ./bin/kdevelop


_____

I was using qtcreator and using the cmake flags in the Project options, but maybe something similar to that bug.

Either way, since the debugger exits at an unrelated point there, the IDE is not particularly doing anything +ive, so the terminal is fine.
Comment 20 Igor Kushnir 2025-05-08 11:55:26 UTC
OK, so the simplest crash fix is to add and check the QPointer guard. Can be cherry-picked to the stable KDevelop branch. Would you like to create a merge request with the fix?

If you want to figure out why two menus are created, a debugger could help.
> Either way, since the debugger exits at an unrelated point there, the IDE is
> not particularly doing anything +ive, so the terminal is fine.
You can try the GDB command line. I use the following gdb-kdev alias configured in ~/.bashrc to enable KDevelop's pretty printers for Qt types:
    alias gdb-no-debuginfod=$'gdb -ex "set debuginfod enabled off"'
    alias gdb-kdev=$'gdb-no-debuginfod -ex "python sys.path.insert(0, \'/usr/share/kdevgdb/printers\')" -ex "source /usr/share/kdevgdb/printers/gdbinit"'

Or debug KDevelop in KDevelop. This requires environment configuration from prefix.sh and specifying a different session in the Arguments line edit of the kdevelop Launch Configurations page.
Comment 21 ulterno 2025-05-08 13:38:01 UTC
> OK, so the simplest crash fix is to add and check the QPointer guard. Can be cherry-picked to the stable KDevelop branch. Would you like to create a merge request with the fix?

I think you might be missing something here.


___


Made a little change to this code:

	if (menuGuard.isNull())
	{
		qDebug() << "Menu destroyed !! Returning...";
		return;
	}

____

Resultant output:

"2025-05-08T18:58:28.114" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
"2025-05-08T18:58:28.116" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show
kdevplatform.shell: populateContextMenu() called while we still handled another menu.
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
  Still exists --1 100515359718720
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
  stashes hashed
Menu exists
  Point --2 100515359718720
Complete
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
Menu guarded
URLs be URL'ed
  Still exists --1 100515349942048
GitJob::GitJob(const QDir&, KDevelop::IPlugin*, KDevelop::OutputJob::OutputJobVerbosity)
  stashes hashed
Menu destroyed !! Returning...
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
zsh: segmentation fault (core dumped)  ./bin/kdevelop

____

Unless the guard is supposed to be somewhere else, that I haven't realised.

For now, all the times I have tried (with minor variations here and there), there has been no case where I prevented a crash after getting:
kdevplatform.shell: populateContextMenu() called while we still handled another menu.

______

Thanks for the tip. I have been meaning to use the KDevelop debugging UI, just never got to it.

As for this case, as I mentioned before:

The debugger breaks at:
kpluginfactory.h: function: template<typename T> inline T *KPluginFactory::create(QObject *parent, const QVariantList &args)

And then gives a "Unable to continue debugged process". I see that in a message box from QtCreator on trying to resume running. Meaning that I am unable to reach the code in question, when using the debugger.

This happens despite me not seeing a signal (SEGV/ABRT etc), so I'll have to check this one out first.
Comment 22 Igor Kushnir 2025-05-08 13:52:59 UTC
(In reply to ulterno from comment #21)
> Unless the guard is supposed to be somewhere else, that I haven't realised.
No, your debugging code looks good now.
> For now, all the times I have tried (with minor variations here and there),
> there has been no case where I prevented a crash after getting:
> kdevplatform.shell: populateContextMenu() called while we still handled
> another menu.
Where does it crash now? (check the backtrace). Maybe another similar fix is needed elsewhere...

I suspect that a bug in Qt Creator prevents you from debugging KDevelop in it. So I suggested the GDB command line or the KDevelop debugger UI, which might work. I regularly debug KDevelop in KDevelop.

Another thing you could try is create a small Qt application and see if two menus are created in it too when the Menu key is pressed. Then you could report a Qt bug with the minimum reproducible example attached.

By the way, have you verified that only one menu is created when you right-click instead of pressing the Menu key?
Comment 23 ulterno 2025-05-08 14:19:14 UTC
> Another thing you could try is create a small Qt application and see if two menus are created in it too when the Menu key is pressed. Then you could report a Qt bug with the minimum reproducible example attached.

Well, if that happened, I would expect it to crash in other applications using KTextEditor::View, like this KWrite that I am using.

> By the way, have you verified that only one menu is created when you right-click instead of pressing the Menu key?

At least visually, I see only a single menu.

But as you see from this line in the debug output:
"2025-05-08T18:58:28.116" KDevelop::TextDocument::createViewWidget(QWidget*)::<lambda()> Context menu about to show

Caused by:

// Added code
	connect (view, &KTextEditor::View::contextMenuAboutToShow, this,
		[]()
		{
			qDebug() << QDateTime::currentDateTime().toString (Qt::ISODateWithMs) << __PRETTY_FUNCTION__
					 << "Context menu about to show";
		});
// Above this line
	connect (view, &KTextEditor::View::contextMenuAboutToShow, this, &TextDocument::populateContextMenu);
	
	
The signal, contextMenuAboutToShow is called twice.


After finding this out, I went on a tangent trying to see if doing stuff with KTextEditor might fix something, which I couldn't check back then, because I wasn't able to set up KDevelop to use it despite setting  -DKF6TextEditor_DIR:PATH=/run/media/ulterno/Data/code/KDE/Frameworks/KTextEditor/install/lib/cmake/KF6TextEditor in QtCreator.

Guess I can try that again.
Comment 24 Igor Kushnir 2025-05-08 14:27:12 UTC
(In reply to ulterno from comment #23)
> Well, if that happened, I would expect it to crash in other applications
> using KTextEditor::View, like this KWrite that I am using.
Not necessarily. Other apps probably do not enter nested event loops while populating context menus, hence no crash.
> At least visually, I see only a single menu.
Well, one of the two menus likely closes the other one immediately, so you wouldn't even notice without a crash.
Comment 25 Igor Kushnir 2025-05-08 14:33:39 UTC
(In reply to Igor Kushnir from comment #24)
> > By the way, have you verified that only one menu is created when you right-click instead of pressing the Menu key?
> 
> At least visually, I see only a single menu.
(forgot about the context in my previous reply) By verification I mean check the debug output you have added to KDevelop: is GitPlugin::additionalMenuEntries() invoked only once when you right-click?
Comment 26 ulterno 2025-05-08 18:14:59 UTC
- I forgot to mention before, but I checked the keyboard input using libinput and it was working fine. Not sticky keys/double presses

> GitPlugin::additionalMenuEntries() invoked only once when you right-click?

Yes, in case of a right-click, it is invoked only once, while in case of the Menu key, it gets invoked twice.
Furthermore, I added a connect to KTextEditor::View::contextMenuAboutToShow, which shows me that the signal is called twice when I use the Menu key, while only one when I use the RMB.

___

Just recently logged into an X11 session to reproduce the same and:
The error output of:
> kdevplatform.shell: populateContextMenu() called while we still handled another menu.
was not displayed and the rest worked just fine.

So I will go by considering that the double context menu is something coming from some Wayland related thing.

___

On the KDevelop side though, I see that in a normal case (Right Mouse Button), I get:

virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
virtual void KDevelop::DVcsJob::start() "2025-05-08T22:45:05.561" Unlocked 135532689649032
virtual void KDevelop::DVcsJob::start() "2025-05-08T22:45:08.787" Unlocked 135532689649032

despite having other (other than git) vcs plugins deactivated.

Corresponding code:
   static QMutex xtex;
   QMutexLocker c (&xtex);
   qDebug() << __PRETTY_FUNCTION__ << QDateTime::currentDateTime().toString (Qt::ISODateWithMs) << "Unlocked"
            << (long) ((void *) &xtex);
___

So, the DVcsJob is called twice, in case of normal operation.

This becomes 4 times in case the Menu Key is used:

virtual void KDevelop::DVcsJob::start() "2025-05-08T22:56:38.418" Unlocked 128381549084040
kdevplatform.shell: populateContextMenu() called while we still handled another menu.
virtual void KDevelop::DVcsJob::start() "2025-05-08T22:56:38.420" Unlocked 128381549084040
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
virtual void KDevelop::DVcsJob::start() "2025-05-08T22:56:38.423" Unlocked 128381549084040
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
virtual void KDevelop::DVcsJob::start() "2025-05-08T22:56:38.430" Unlocked 128381549084040
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
zsh: segmentation fault (core dumped)  bin/kdevelop


___

I also loaded core dumps in QtCreator and :

First it crashed in the additionalMenuEntries function as you expected.
On adding the menu guard, next it crashed in the SwitchToBuddyPlugin::contextMenuExtension at         auto* action = new QAction(i18nc("@action:inmenu", "Switch to '%1'", url.fileName()), parent);
I bypassed the function:
Next try, got a crash at  ContextMenuExtension::populateMenu in function populateMenuWithGroup(menu, extensions, VcsGroup); [line 138]
    at groupMenu->addAction(action); with only 1 item in the __for_range of  for (QAction* action : std::as_const(groupActions))
    
___

Diff for the final try on top of edd57f491d32f4fed360bfc2a1f25c02464dc3c0:

diff --git a/kdevplatform/shell/plugincontroller.cpp b/kdevplatform/shell/plugincontroller.cpp
index 177581bdb9..e50a876cee 100644
--- a/kdevplatform/shell/plugincontroller.cpp
+++ b/kdevplatform/shell/plugincontroller.cpp
@@ -776,20 +776,24 @@ QList<ContextMenuExtension> PluginController::queryPluginsForContextMenuExtensio
     // 1) "Cppcheck" actions, "Vera++" actions - first run
     // 2) "Vera++" actions, "Cppcheck" actions - some other run.
     QMultiMap<QString, IPlugin*> sortedPlugins;
-    for (auto it = d->loadedPlugins.constBegin(); it != d->loadedPlugins.constEnd(); ++it) {
-        sortedPlugins.insert(it.key().name(), it.value());
-    }
+	for (auto it = d->loadedPlugins.constBegin(); it != d->loadedPlugins.constEnd(); ++it)
+	{
+		sortedPlugins.insert (it.key().name(), it.value());
+	}
 
-    QList<ContextMenuExtension> exts;
-    exts.reserve(sortedPlugins.size());
+	QList<ContextMenuExtension> exts;
+	exts.reserve(sortedPlugins.size());
     for (IPlugin* plugin : std::as_const(sortedPlugins)) {
-        exts << plugin->contextMenuExtension(context, parent);
-    }
+		qDebug() << "Calling contextMenuExtension of: " << plugin->objectName() << plugin->componentName()
+					<< "Error?: " << plugin->hasError();
+
+		exts << plugin->contextMenuExtension (context, parent);
+	}
 
-    exts << Core::self()->debugControllerInternal()->contextMenuExtension(context, parent);
-    exts << Core::self()->documentationControllerInternal()->contextMenuExtension(context, parent);
-    exts << Core::self()->sourceFormatterControllerInternal()->contextMenuExtension(context, parent);
-    exts << Core::self()->runControllerInternal()->contextMenuExtension(context, parent);
+	exts << Core::self()->debugControllerInternal()->contextMenuExtension (context, parent);
+	exts << Core::self()->documentationControllerInternal()->contextMenuExtension (context, parent);
+	exts << Core::self()->sourceFormatterControllerInternal()->contextMenuExtension (context, parent);
+	exts << Core::self()->runControllerInternal()->contextMenuExtension(context, parent);
     exts << Core::self()->projectControllerInternal()->contextMenuExtension(context, parent);
 
     return exts;
diff --git a/kdevplatform/vcs/dvcs/dvcsjob.cpp b/kdevplatform/vcs/dvcs/dvcsjob.cpp
index 3f33d27090..c024477a93 100644
--- a/kdevplatform/vcs/dvcs/dvcsjob.cpp
+++ b/kdevplatform/vcs/dvcs/dvcsjob.cpp
@@ -18,6 +18,7 @@
 #include <QStringList>
 #include <QDir>
 #include <QUrl>
+#include <QMutexLocker>
 
 #include <KLocalizedString>
 #include <KShell>
@@ -165,30 +166,35 @@ QVariant DVcsJob::fetchResults()
 void DVcsJob::start()
 {
     Q_D(DVcsJob);
-
-    Q_ASSERT_X(d->status != JobRunning, "DVCSjob::start", "Another process was started using this job class");
-
-    const QDir& workingdir = directory();
-    if( !workingdir.exists() ) {
-        QString error = i18n( "Working Directory does not exist: %1", d->childproc->workingDirectory() );
-        d->model->appendLine(error);
+	static QMutex xtex;
+	QMutexLocker c (&xtex);
+	qDebug() << __PRETTY_FUNCTION__ << QDateTime::currentDateTime().toString (Qt::ISODateWithMs) << "Unlocked"
+			 << (long) ((void *) &xtex);
+	Q_ASSERT_X (d->status != JobRunning, "DVCSjob::start", "Another process was started using this job class");
+
+	const QDir &workingdir = directory();
+	if (!workingdir.exists())
+	{
+		QString error = i18n ("Working Directory does not exist: %1", d->childproc->workingDirectory());
+		d->model->appendLine(error);
         setError( 255 );
         setErrorText(error);
         d->status = JobFailed;
         emitResult();
         return;
-    }
-    if( !workingdir.isAbsolute() ) {
-        QString error = i18n( "Working Directory is not absolute: %1", d->childproc->workingDirectory() );
+	}
+	if (!workingdir.isAbsolute())
+	{
+		QString error = i18n( "Working Directory is not absolute: %1", d->childproc->workingDirectory() );
         d->model->appendLine(error);
         setError( 255 );
         setErrorText(error);
         d->status = JobFailed;
         emitResult();
         return;
-    }
+	}
 
-    QString commandDisplay = KShell::joinArgs(dvcsCommand());
+	QString commandDisplay = KShell::joinArgs(dvcsCommand());
     qCDebug(VCS) << "Execute dvcs command:" << commandDisplay;
 
     QString service;
diff --git a/plugins/git/gitplugin.cpp b/plugins/git/gitplugin.cpp
index 2adaf07c62..6d42e5f80a 100644
--- a/plugins/git/gitplugin.cpp
+++ b/plugins/git/gitplugin.cpp
@@ -243,16 +243,30 @@ bool GitPlugin::hasModifications(const QDir& repo, const QUrl& file)
 
 void GitPlugin::additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls)
 {
-    m_urls = urls;
-
-    QDir dir=urlDir(urls);
-    bool hasSt = hasStashes(dir);
-
-    menu->addAction(i18nc("@action:inmenu", "Rebase"), this, SLOT(ctxRebase()));
-    menu->addSeparator()->setText(i18nc("@title:menu", "Git Stashes"));
-    menu->addAction(i18nc("@action:inmenu", "Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(hasSt);
-    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash")), i18nc("@action:inmenu", "Push Stash"), this, SLOT(ctxPushStash()));
-    menu->addAction(QIcon::fromTheme(QStringLiteral("vcs-stash-pop")), i18nc("@action:inmenu", "Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(hasSt);
+	qDebug() << __PRETTY_FUNCTION__;
+	QPointer menu_g (menu);
+	m_urls = urls;
+
+	QDir dir = urlDir (urls);
+	bool hasSt = hasStashes (dir);
+
+	if (menu_g.isNull())
+	{
+		qDebug() << __PRETTY_FUNCTION__ << (long) ((void *) menu_g) << " Gone. ";
+		return;
+	}
+	menu->addAction (i18nc ("@action:inmenu", "Rebase"), this, &GitPlugin::ctxRebase);
+	menu->addSeparator()->setText (i18nc ("@title:menu", "Git Stashes"));
+	menu->addAction (i18nc ("@action:inmenu", "Stash Manager"), this, &GitPlugin::ctxStashManager)->setEnabled (hasSt);
+	menu->addAction (QIcon::fromTheme (QStringLiteral ("vcs-stash")),
+		i18nc ("@action:inmenu", "Push Stash"),
+		this,
+		&GitPlugin::ctxPushStash);
+	menu->addAction (QIcon::fromTheme (QStringLiteral ("vcs-stash-pop")),
+			i18nc ("@action:inmenu", "Pop Stash"),
+			this,
+			&GitPlugin::ctxPopStash)
+		->setEnabled (hasSt);
 }
 
 void GitPlugin::ctxRebase()
diff --git a/plugins/switchtobuddy/switchtobuddyplugin.cpp b/plugins/switchtobuddy/switchtobuddyplugin.cpp
index f74bcccd92..9e773b2147 100644
--- a/plugins/switchtobuddy/switchtobuddyplugin.cpp
+++ b/plugins/switchtobuddy/switchtobuddyplugin.cpp
@@ -89,7 +89,8 @@ SwitchToBuddyPlugin::~SwitchToBuddyPlugin()
 
 ContextMenuExtension SwitchToBuddyPlugin::contextMenuExtension(Context* context, QWidget* parent)
 {
-    auto* ctx = dynamic_cast<EditorContext*>(context);
+	return ContextMenuExtension();
+	auto* ctx = dynamic_cast<EditorContext*>(context);
     if (!ctx) {
         return ContextMenuExtension();
     }

Attaching the core dump too
Comment 27 ulterno 2025-05-08 18:27:00 UTC
On second thought, I'd rather not send a 3GB core dump
Comment 28 Igor Kushnir 2025-05-08 18:55:47 UTC
If SwitchToBuddyPlugin::contextMenuExtension() does not enter a nested event loop, it does not need a QPointer guard. Rather it should never be invoked in the first place if the menu object is destroyed within GitPlugin::additionalMenuEntries().

I think the QPointer guards for the QMenu objects are needed:
1) in GitPlugin::additionalMenuEntries();
2) in DistributedVersionControlPlugin::contextMenuExtension() - checked after the additionalMenuEntries() call, which may enter a nested event loop;
3) in PluginController::queryPluginsForContextMenuExtensions() - checked after each contextMenuExtension() call (not sure about the calls after the loop, such as debugControllerInternal()->contextMenuExtension(), depends on whether they can enter a nested event loop);
4) in TextDocument::populateContextMenu() - checked after the call to queryPluginsForContextMenuExtensions().

Hopefully the rest of the call stack does not care if the menu object is destroyed.

This need for patching-up in multiple places illustrates why nested event loops are best avoided. But if the KTextEditor interface that emits the context menu signal does not allow asynchronous population of the menu, then avoiding nested event loops would require a new KTextEditor interface...
Comment 29 ulterno 2025-05-08 21:43:46 UTC
I just realised a major oversight.
I had been assuming that the Menu key had been causing double context menus all the time.
But no. Turns out, the double context menu only happens when the file in the current view is in a directory with .git

I checked that out after having added a debug output in KTextEditor::ViewPrivate::aboutToShowContextMenu and trying out the menu key in Kate and KWrite.

Here's the full output of how it went with KDevelop.

kdevplatform.serialization: "The data-repository at /home/ulterno/.cache/kdevduchain/kdevelop-{1e439ed4-b9a5-4126-9dec-5096ebd432ca} has to be cleared."
virtual void KDevelop::DVcsJob::start() "2025-05-09T03:04:14.218" Unlocked 129257412382088
kdevplatform.shell: Unable to find a plugin named ' "kateprojectplugin" '!
qt.core.qobject.connect: QObject::connect(KateApp, KateTextHintManager): invalid nullptr parameter
qt.core.qobject.connect: QObject::connect(QObject, KTextEditorIntegration::MainWindow): invalid nullptr parameter
qt.core.qobject.connect: QObject::connect: No such signal KDevelop::MainWindow::tabForToolViewAdded(QWidget *, QWidget *)
qt.core.qobject.connect: QObject::connect:  (sender name:   'MainWindow')
qt.core.qobject.connect: QObject::connect(KateApp, DiagnosticsView): invalid nullptr parameter
QLayout: Cannot add a null widget to QHBoxLayout/

<<<<<----- Using Menu Key on a file that is NOT in a directory with a git repo ----->>>>>
QMenu::aboutToShow KTextEditor::ViewPrivate::contextMenu() const::<lambda()>
void KTextEditor::ViewPrivate::aboutToShowContextMenu()
qt.core.qobject.connect: QObject::disconnect: Unexpected nullptr parameter <<<--- Ignore this one. I just added another disconnect hoping to hack a fix. That did nothing
Calling contextMenuExtension of:  "" "kdevastyle" Error?:  false
Calling contextMenuExtension of:  "" "kdevandroid" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevcmakedocumentation" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangtidy" Error?:  false
Calling contextMenuExtension of:  "" "kdevclassbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevclazy" Error?:  false
Calling contextMenuExtension of:  "" "kdevcontextbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevcodeutils" Error?:  false
Calling contextMenuExtension of:  "" "kdevcppcheck" Error?:  false
Calling contextMenuExtension of:  "" "kdevcraft" Error?:  false
Calling contextMenuExtension of:  "" "kdevdefinesandincludesmanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustommakemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustomscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocker" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentview" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecute" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecutescript" Error?:  false
Calling contextMenuExtension of:  "" "kdevexternalscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevfiletemplates" Error?:  false
Calling contextMenuExtension of:  "" "kdevgrepview" Error?:  false
Calling contextMenuExtension of:  "" "kdevflatpak" Error?:  false
Calling contextMenuExtension of:  "Git" "kdevgit" Error?:  false
Calling contextMenuExtension of:  "" "kdevghprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevheaptrack" Error?:  false
Calling contextMenuExtension of:  "" "kdevfilemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevwelcomepage" Error?:  false
Calling contextMenuExtension of:  "" "kdevkonsoleview" Error?:  false
Calling contextMenuExtension of:  "" "kdevlldb" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevmakebuilder" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentswitcher" Error?:  false
Calling contextMenuExtension of:  "" "kdevappwizard" Error?:  false
Calling contextMenuExtension of:  "" "kdevopenwith" Error?:  false
Calling contextMenuExtension of:  "" "kdevoutlineview" Error?:  false
Calling contextMenuExtension of:  "" "kdevstandardoutputview" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpdocs" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpunitprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecuteplasmoid" Error?:  false
Calling contextMenuExtension of:  "" "kdevproblemreporter" Error?:  false
Calling contextMenuExtension of:  "" "kdevprojectmanagerview" Error?:  false
Calling contextMenuExtension of:  "" "kdevpdbsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevqthelp" Error?:  false
Calling contextMenuExtension of:  "" "kdevquickopen" Error?:  false
Calling contextMenuExtension of:  "" "scratchpad" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevswitchtobuddy" Error?:  false
Calling contextMenuExtension of:  "" "kdevtestview" Error?:  false

<<<<<----- Using Menu Key on a file that is in a directory with a git repo ----->>>>>
QMenu::aboutToShow KTextEditor::ViewPrivate::contextMenu() const::<lambda()>
void KTextEditor::ViewPrivate::aboutToShowContextMenu()
qt.core.qobject.connect: QObject::disconnect: Unexpected nullptr parameter
Calling contextMenuExtension of:  "" "kdevastyle" Error?:  false
Calling contextMenuExtension of:  "" "kdevandroid" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevcmakedocumentation" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangtidy" Error?:  false
Calling contextMenuExtension of:  "" "kdevclassbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevclazy" Error?:  false
Calling contextMenuExtension of:  "" "kdevcontextbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevcodeutils" Error?:  false
Calling contextMenuExtension of:  "" "kdevcppcheck" Error?:  false
Calling contextMenuExtension of:  "" "kdevcraft" Error?:  false
Calling contextMenuExtension of:  "" "kdevdefinesandincludesmanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustommakemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustomscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocker" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentview" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecute" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecutescript" Error?:  false
Calling contextMenuExtension of:  "" "kdevexternalscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevfiletemplates" Error?:  false
Calling contextMenuExtension of:  "" "kdevgrepview" Error?:  false
Calling contextMenuExtension of:  "" "kdevflatpak" Error?:  false
Calling contextMenuExtension of:  "Git" "kdevgit" Error?:  false
virtual void KDevelop::DVcsJob::start() "2025-05-09T03:04:25.055" Unlocked 129257412382088
QMenu::aboutToShow KTextEditor::ViewPrivate::contextMenu() const::<lambda()>
void KTextEditor::ViewPrivate::aboutToShowContextMenu()
qt.core.qobject.connect: QObject::disconnect: Unexpected nullptr parameter
kdevplatform.shell: populateContextMenu() called while we still handled another menu.
Calling contextMenuExtension of:  "" "kdevastyle" Error?:  false
Calling contextMenuExtension of:  "" "kdevandroid" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevcmakedocumentation" Error?:  false
Calling contextMenuExtension of:  "" "kdevclangtidy" Error?:  false
Calling contextMenuExtension of:  "" "kdevclassbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevclazy" Error?:  false
Calling contextMenuExtension of:  "" "kdevcontextbrowser" Error?:  false
Calling contextMenuExtension of:  "" "kdevcodeutils" Error?:  false
Calling contextMenuExtension of:  "" "kdevcppcheck" Error?:  false
Calling contextMenuExtension of:  "" "kdevcraft" Error?:  false
Calling contextMenuExtension of:  "" "kdevdefinesandincludesmanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustommakemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevcustomscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocker" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentview" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecute" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecutescript" Error?:  false
Calling contextMenuExtension of:  "" "kdevexternalscript" Error?:  false
Calling contextMenuExtension of:  "" "kdevfiletemplates" Error?:  false
Calling contextMenuExtension of:  "" "kdevgrepview" Error?:  false
Calling contextMenuExtension of:  "" "kdevflatpak" Error?:  false
Calling contextMenuExtension of:  "Git" "kdevgit" Error?:  false
virtual void KDevelop::DVcsJob::start() "2025-05-09T03:04:25.056" Unlocked 129257412382088
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
virtual void KDevelop::DVcsJob::start() "2025-05-09T03:04:25.058" Unlocked 129257412382088
Calling contextMenuExtension of:  "" "kdevghprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevheaptrack" Error?:  false
Calling contextMenuExtension of:  "" "kdevfilemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevwelcomepage" Error?:  false
Calling contextMenuExtension of:  "" "kdevkonsoleview" Error?:  false
Calling contextMenuExtension of:  "" "kdevlldb" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevmakebuilder" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentswitcher" Error?:  false
Calling contextMenuExtension of:  "" "kdevappwizard" Error?:  false
Calling contextMenuExtension of:  "" "kdevopenwith" Error?:  false
Calling contextMenuExtension of:  "" "kdevoutlineview" Error?:  false
Calling contextMenuExtension of:  "" "kdevstandardoutputview" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpdocs" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpunitprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecuteplasmoid" Error?:  false
Calling contextMenuExtension of:  "" "kdevproblemreporter" Error?:  false
Calling contextMenuExtension of:  "" "kdevprojectmanagerview" Error?:  false
Calling contextMenuExtension of:  "" "kdevpdbsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevqthelp" Error?:  false
Calling contextMenuExtension of:  "" "kdevquickopen" Error?:  false
Calling contextMenuExtension of:  "" "scratchpad" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevswitchtobuddy" Error?:  false
Calling contextMenuExtension of:  "" "kdevtestview" Error?:  false
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&)
virtual void KDevelop::DVcsJob::start() "2025-05-09T03:04:25.062" Unlocked 129257412382088
virtual void GitPlugin::additionalMenuEntries(QMenu*, const QList<QUrl>&) 0  Gone. 
Calling contextMenuExtension of:  "" "kdevghprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevheaptrack" Error?:  false
Calling contextMenuExtension of:  "" "kdevfilemanager" Error?:  false
Calling contextMenuExtension of:  "" "kdevwelcomepage" Error?:  false
Calling contextMenuExtension of:  "" "kdevkonsoleview" Error?:  false
Calling contextMenuExtension of:  "" "kdevlldb" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevmakebuilder" Error?:  false
Calling contextMenuExtension of:  "" "kdevdocumentswitcher" Error?:  false
Calling contextMenuExtension of:  "" "kdevappwizard" Error?:  false
Calling contextMenuExtension of:  "" "kdevopenwith" Error?:  false
Calling contextMenuExtension of:  "" "kdevoutlineview" Error?:  false
Calling contextMenuExtension of:  "" "kdevstandardoutputview" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpdocs" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevphpunitprovider" Error?:  false
Calling contextMenuExtension of:  "" "kdevexecuteplasmoid" Error?:  false
Calling contextMenuExtension of:  "" "kdevproblemreporter" Error?:  false
Calling contextMenuExtension of:  "" "kdevprojectmanagerview" Error?:  false
Calling contextMenuExtension of:  "" "kdevpdbsupport" Error?:  false
Calling contextMenuExtension of:  "" "kdevqthelp" Error?:  false
Calling contextMenuExtension of:  "" "kdevquickopen" Error?:  false
Calling contextMenuExtension of:  "" "scratchpad" Error?:  false
Calling contextMenuExtension of:  "" "" Error?:  false
Calling contextMenuExtension of:  "" "kdevswitchtobuddy" Error?:  false
Calling contextMenuExtension of:  "" "kdevtestview" Error?:  false
KCrash: Application 'kdevelop' crashing... crashRecursionCounter = 2
zsh: segmentation fault (core dumped)  kdevelop

____

I really think we should try reproducing it on someone else's system. Who knows, it is just something wonky going on with my hardware, causing the same behaviour in both installations
Comment 30 ulterno 2025-05-09 04:28:10 UTC
Checked the same with master (a3fae1651f9a9f60eec6f7406df82480b85cf2b2)
Comment 31 Igor Kushnir 2025-05-09 08:17:19 UTC
(In reply to ulterno from comment #29)
> I had been assuming that the Menu key had been causing double context menus
> all the time.
> But no. Turns out, the double context menu only happens when the file in the
> current view is in a directory with .git
Do you mean that both the Menu key AND a version-controlled document are required? Or can you reproduce the double context menu with a right click? Also is the bug really reproducible only under Wayland or did you test a non-git-version-controlled document under X11?

I suspect that the menu key causes another context menu when the previous one fails to appear synchronously because of the nested event loop in the git plugin (which is entered only if the document is git-version-controlled). You can try to reproduce the bug with the git plugin disabled by entering a nested event loop in some other context-menu-populating function. For instance, use https://doc.qt.io/qt-6/qprocess.html#execute to execute the command `sleep 1`. If that works, you can try a single event processing: https://doc.qt.io/qt-6/qcoreapplication.html#processEvents . A call stack of the main thread at the time of the second menu handling (e.g. at the beginning of GitPlugin::additionalMenuEntries()) might help explain the behavior (most likely rooted in Qt or lower-level libraries).

> I really think we should try reproducing it on someone else's system. Who
> knows, it is just something wonky going on with my hardware, causing the
> same behaviour in both installations
OK, I'll disable my key remapping and try to reproduce today or tomorrow.
Comment 32 Igor Kushnir 2025-05-09 08:22:32 UTC
(In reply to Igor Kushnir from comment #31)
> If that works, you can try a single event processing:
> https://doc.qt.io/qt-6/qcoreapplication.html#processEvents
If executing a QProcess works but a single event processing does not, try 2 or 3 consecutive calls to QCoreApplication::processEvents() (the first event loop might post a new event and the second one - handle it).
Comment 33 ulterno 2025-05-09 09:58:30 UTC
> Do you mean that both the Menu key AND a version-controlled document are required?

Yes, it is the "AND".
However, it even works if the "version-controlled document" is gitignored and even if the .git repo has just been inited with nothing staged (refer STEP 0 of REPRODUCTION)

_

> Also is the bug really reproducible only under Wayland or did you test a non-git-version-controlled document under X11?

I recall having tried both type of documents and not getting the double context menu (not even the warning), but I'll try again, just to make sure.

___

Just in case you are wondering, adding a return at the point giving the error, does not prevent the crash (or the frozen GUI before it).


void KDevelop::TextDocument::populateContextMenu( KTextEditor::View* v, QMenu* menu )
{
    Q_D(TextDocument);

    if (d->addedContextMenu) {
        qCWarning(SHELL) << "populateContextMenu() called while we still handled another menu.";
        d->cleanContextMenu();
        return; // Added a return here
    }
...

Though it does cause the extra menu entries to not appear.

I will respond to other points after a while
Comment 34 Igor Kushnir 2025-05-09 11:45:49 UTC
(In reply to ulterno from comment #33)
> > Do you mean that both the Menu key AND a version-controlled document are required?
> 
> Yes, it is the "AND".
> However, it even works if the "version-controlled document" is gitignored
> and even if the .git repo has just been inited with nothing staged (refer
> STEP 0 of REPRODUCTION)
If GitPlugin::isValidDirectory() called from DistributedVersionControlPlugin::contextMenuExtension() returns false, additionalMenuEntries() is never invoked.

> Just in case you are wondering, adding a return at the point giving the
> error, does not prevent the crash (or the frozen GUI before it).
That's because d->cleanContextMenu() schedules destruction of the previous menu, so the QPointer guards are necessary to prevent the crash.
Comment 35 ulterno 2025-05-09 12:11:53 UTC
Created attachment 181105 [details]
attachment-3408275-0.html

Hey! That's right!
Although it's not good code, returning before d->cleanContextMenu() 🤭
makes it not crash.
Also, all menu entries get filled and I checked the Git menu entries, which
turned up with valid, working actions.

On Fri, 9 May 2025 at 17:15, Igor Kushnir <bugzilla_noreply@kde.org> wrote:

> https://bugs.kde.org/show_bug.cgi?id=503813
>
> --- Comment #34 from Igor Kushnir <igorkuo@gmail.com> ---
> (In reply to ulterno from comment #33)
> > > Do you mean that both the Menu key AND a version-controlled document
> are required?
> >
> > Yes, it is the "AND".
> > However, it even works if the "version-controlled document" is gitignored
> > and even if the .git repo has just been inited with nothing staged (refer
> > STEP 0 of REPRODUCTION)
> If GitPlugin::isValidDirectory() called from
> DistributedVersionControlPlugin::contextMenuExtension() returns false,
> additionalMenuEntries() is never invoked.
>
> > Just in case you are wondering, adding a return at the point giving the
> > error, does not prevent the crash (or the frozen GUI before it).
> That's because d->cleanContextMenu() schedules destruction of the previous
> menu, so the QPointer guards are necessary to prevent the crash.
>
> --
> You are receiving this mail because:
> You reported the bug.
Comment 36 Igor Kushnir 2025-05-09 19:34:35 UTC
(In reply to Igor Kushnir from comment #31)
> OK, I'll disable my key remapping and try to reproduce today or tomorrow.
Reproduced easily in Plasma+Wayland but not in Xfce+X11. The call stack when the second context menu is created (obtained while debugging KDevelop in KDevelop, placed a breakpoint on the line `d->cleanContextMenu();` in populateContextMenu()):
#0 KDevelop::TextDocument::populateContextMenu (this=0x55f8a0e2c020, v=0x55f89e79c4c0, menu=0x55f8a07439f0) at kdevelop/kdevplatform/shell/textdocument.cpp:724
#1 0x00007fc888d992f2 in QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}::operator()() const (__closure=0x7fff1371c420) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:127
#2 0x00007fc888d995d2 in QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}>(void**, QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}&&) (args=0x7fff1371c5c0, fn=...) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:65
#3 0x00007fc888d99364 in QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call (f=(void (KDevelop::TextDocument::*)(KDevelop::TextDocument * const, KTextEditor::View *, QMenu *)) 0x7fc888d93594 <KDevelop::TextDocument::populateContextMenu(KTextEditor::View*, QMenu*)>, o=0x55f8a0e2c020, arg=0x7fff1371c5c0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:126
#4 0x00007fc888d98e2e in QtPrivate::FunctionPointer<void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call<QtPrivate::List<KTextEditor::View*, QMenu*>, void> (f=(void (KDevelop::TextDocument::*)(KDevelop::TextDocument * const, KTextEditor::View *, QMenu *)) 0x7fc888d93594 <KDevelop::TextDocument::populateContextMenu(KTextEditor::View*, QMenu*)>, o=0x55f8a0e2c020, arg=0x7fff1371c5c0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:174
#5 0x00007fc888d988c3 in QtPrivate::QCallableObject<void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), QtPrivate::List<KTextEditor::View*, QMenu*>, void>::impl (which=1, this_=0x55f8a1058c60, r=0x55f8a0e2c020, a=0x7fff1371c5c0, ret=0x0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:545
#6 0x00007fc8837b6cc9 in ??? () at /usr/lib/libQt6Core.so.6
#7 0x00007fc886a3d881 in KTextEditor::View::contextMenuAboutToShow(KTextEditor::View*, QMenu*) () at /usr/lib/libKF6TextEditor.so.6
#8 0x00007fc8837b6cc9 in ??? () at /usr/lib/libQt6Core.so.6
#9 0x00007fc8852eb6f6 in ??? () at /usr/lib/libQt6Widgets.so.6
#10 0x00007fc8852ec8f4 in QMenu::popup(QPoint const&, QAction*) () at /usr/lib/libQt6Widgets.so.6
#11 0x00007fc8869e1761 in ??? () at /usr/lib/libKF6TextEditor.so.6
#12 0x00007fc885151e39 in QWidget::event(QEvent*) () at /usr/lib/libQt6Widgets.so.6
#13 0x00007fc8850fed9e in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#14 0x00007fc885104278 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#15 0x00007fc88375a018 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#16 0x00007fc88375a3f2 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/lib/libQt6Core.so.6
#17 0x00007fc8839cfea8 in ??? () at /usr/lib/libQt6Core.so.6
#18 0x00007fc875d061e4 in ??? () at /usr/lib/libglib-2.0.so.0
#19 0x00007fc875d69e97 in ??? () at /usr/lib/libglib-2.0.so.0
#20 0x00007fc875d05615 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#21 0x00007fc8839cd59d in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#22 0x00007fc883765376 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#23 0x00007fc884f8cabc in KJob::exec() () at /usr/lib/libKF6CoreAddons.so.6
#24 0x00007fc8296a1aac in GitPlugin::getLsFiles (this=0x55f89fef26b0, directory=..., args=QList<QString> (size = 2) = {...}, verbosity=KDevelop::OutputJob::Silent) at kdevelop/plugins/git/gitplugin.cpp:1506
#25 0x00007fc82969751c in GitPlugin::isVersionControlled (this=0x55f89fef26b0, path=file:///path/to/main.cpp) at kdevelop/plugins/git/gitplugin.cpp:369
#26 0x00007fc882e7e0f4 in KDevelop::VcsPluginHelperPrivate::createMenu (this=0x55f89fec5de0, parent=0x55f89f221260) at kdevelop/kdevplatform/vcs/vcspluginhelper.cpp:137
#27 0x00007fc882e78571 in KDevelop::VcsPluginHelper::commonActions (this=0x55f89fef25b0, parent=0x55f89f221260) at kdevelop/kdevplatform/vcs/vcspluginhelper.cpp:210
#28 0x00007fc882ed06cf in KDevelop::DistributedVersionControlPlugin::contextMenuExtension (this=0x55f89fef26b0, context=0x7fff1371d450, parent=0x55f89f221260) at kdevelop/kdevplatform/vcs/dvcs/dvcsplugin.cpp:94
#29 0x00007fc888cfba3e in KDevelop::PluginController::queryPluginsForContextMenuExtensions (this=0x55f89f26b940, context=0x7fff1371d450, parent=0x55f89f221260) at kdevelop/kdevplatform/shell/plugincontroller.cpp:786
#30 0x00007fc888d937cc in KDevelop::TextDocument::populateContextMenu (this=0x55f8a0e2c020, v=0x55f89e79c4c0, menu=0x55f8a07439f0) at kdevelop/kdevplatform/shell/textdocument.cpp:733
#31 0x00007fc888d992f2 in QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}::operator()() const (__closure=0x7fff1371d520) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:127
#32 0x00007fc888d995d2 in QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}>(void**, QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call(void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), KDevelop::TextDocument*, void**)::{lambda()#1}&&) (args=0x7fff1371d6c0, fn=...) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:65
#33 0x00007fc888d99364 in QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>, QtPrivate::List<KTextEditor::View*, QMenu*>, void, void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call (f=(void (KDevelop::TextDocument::*)(KDevelop::TextDocument * const, KTextEditor::View *, QMenu *)) 0x7fc888d93594 <KDevelop::TextDocument::populateContextMenu(KTextEditor::View*, QMenu*)>, o=0x55f8a0e2c020, arg=0x7fff1371d6c0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:126
#34 0x00007fc888d98e2e in QtPrivate::FunctionPointer<void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*)>::call<QtPrivate::List<KTextEditor::View*, QMenu*>, void> (f=(void (KDevelop::TextDocument::*)(KDevelop::TextDocument * const, KTextEditor::View *, QMenu *)) 0x7fc888d93594 <KDevelop::TextDocument::populateContextMenu(KTextEditor::View*, QMenu*)>, o=0x55f8a0e2c020, arg=0x7fff1371d6c0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:174
#35 0x00007fc888d988c3 in QtPrivate::QCallableObject<void (KDevelop::TextDocument::*)(KTextEditor::View*, QMenu*), QtPrivate::List<KTextEditor::View*, QMenu*>, void>::impl (which=1, this_=0x55f8a1058c60, r=0x55f8a0e2c020, a=0x7fff1371d6c0, ret=0x0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:545
#36 0x00007fc8837b6cc9 in ??? () at /usr/lib/libQt6Core.so.6
#37 0x00007fc886a3d881 in KTextEditor::View::contextMenuAboutToShow(KTextEditor::View*, QMenu*) () at /usr/lib/libKF6TextEditor.so.6
#38 0x00007fc8837b6cc9 in ??? () at /usr/lib/libQt6Core.so.6
#39 0x00007fc8852eb6f6 in ??? () at /usr/lib/libQt6Widgets.so.6
#40 0x00007fc8852ec8f4 in QMenu::popup(QPoint const&, QAction*) () at /usr/lib/libQt6Widgets.so.6
#41 0x00007fc8869e1761 in ??? () at /usr/lib/libKF6TextEditor.so.6
#42 0x00007fc885151e39 in QWidget::event(QEvent*) () at /usr/lib/libQt6Widgets.so.6
#43 0x00007fc8850fed9e in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#44 0x00007fc885104278 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#45 0x00007fc88375a018 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#46 0x00007fc88516e2b1 in ??? () at /usr/lib/libQt6Widgets.so.6
#47 0x00007fc8850fed9e in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#48 0x00007fc88375a018 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#49 0x00007fc88478e348 in QGuiApplicationPrivate::processContextMenuEvent(QWindowSystemInterfacePrivate::ContextMenuEvent*) () at /usr/lib/libQt6Gui.so.6
#50 0x00007fc88480d0c4 in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Gui.so.6
#51 0x00007fc88480d2b7 in QWindowSystemInterface::flushWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Gui.so.6
#52 0x00007fc8837a54aa in QObject::event(QEvent*) () at /usr/lib/libQt6Core.so.6
#53 0x00007fc8850fed9e in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#54 0x00007fc88375a018 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#55 0x00007fc88375a3f2 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/lib/libQt6Core.so.6
#56 0x00007fc8839cfea8 in ??? () at /usr/lib/libQt6Core.so.6
#57 0x00007fc875d061e4 in ??? () at /usr/lib/libglib-2.0.so.0
#58 0x00007fc875d69e97 in ??? () at /usr/lib/libglib-2.0.so.0
#59 0x00007fc875d05615 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#60 0x00007fc8839cd59d in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#61 0x00007fc883765376 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#62 0x00007fc88375d159 in QCoreApplication::exec() () at /usr/lib/libQt6Core.so.6
#63 0x000055f87de11ec3 in main (argc=3, argv=0x7fff1371ef58) at kdevelop/app/main.cpp:836

Unfortunately no Qt or KF debug symbols. I'll try to install them via debuginfod tomorrow (not sure that'll work because Manjaro packages may be behind Arch packages, so version mismatches happen).
Comment 37 ulterno 2025-05-10 01:11:38 UTC
> ... But if the KTextEditor interface that emits the
> context menu signal does not allow asynchronous population of the menu, then
> avoiding nested event loops would require a new KTextEditor interface...

Seems to me like it does, now:

https://invent.kde.org/ulterno/UsingKTextEditor
Comment 38 Igor Kushnir 2025-05-10 08:24:45 UTC
Created attachment 181127 [details]
Second context menu: call stack with debug symbols

Analyzed the second context menu call stack with debug symbols (attached) in KDevelop after following the instructions in my comments #5 and #6 to Bug 486932. Qt gets to QCoreApplication::notifyInternal2() and eventually to KateViewInternal::contextMenuEvent() with different preceding call stacks in the primary and the nested event loop. Maybe the same context menu event is delivered again before being accepted? If the event duplication is reproducible with a small Qt-only example (just override QWidget::contextMenuEvent() and process events before accepting the event), it can be reported as a Qt bug.

Maybe KateViewInternal::contextMenuEvent() can detect that the event is spurious or something (or perhaps the second event is exactly the same as the event that is currently being handled) and return early instead of handling it (not sure whether to accept or reject the extra event). 

Changing the relevant context menu handling logic in KDevelop code seems difficult and risky because of the extreme complexity of the current context menu workflow as the commit message of https://commits.kde.org/kdevelop/d940a88e8bd92031cd7ffce2450cc24f266e6acc explains.
Comment 39 ulterno 2025-05-15 05:19:54 UTC
Successfully reproduced similar behaviour to the bug in
https://invent.kde.org/ulterno/UsingKTextEditor/-/commit/aab2bcf1b29aadabb89992404c94bf955a989e43

Not crashing, but calling the Menu twice, when using the keyboard Menu key.

Attempting the same inside QTextEdit::contextMenuEvent, did not reproduce the bug. i.e. The menu was not called twice when using the keyboard Menu key.
So, only able to do it with KTextEditor::View

___

Modifying the function as shown below:

void KDevelop::TextDocument::populateContextMenu( KTextEditor::View* v, QMenu* menu )
{
...

	if (d->addedContextMenu)
	{
		qCWarning (SHELL) << "populateContextMenu() called while we still handled another menu.";
		if (menu == d->currentContextMenu)
		{
			// Of the same thing, then don't delete and don't retry. This line depends upon cleanContextMenu working as required
			qCDebug (SHELL) << "Pointer of the new context menu is the same... Not cleaning, not remaking";
			return;
		}
		d->cleanContextMenu();
	}
...
}

can be considered a hotfix.
Comment 40 ulterno 2025-05-15 05:26:06 UTC
(In reply to Igor Kushnir from comment #38)

> Changing the relevant context menu handling logic in KDevelop code seems
> difficult and risky because of the extreme complexity of the current context
> menu workflow as the commit message of
> https://commits.kde.org/kdevelop/d940a88e8bd92031cd7ffce2450cc24f266e6acc
> explains.

I am willing to take up the task after finishing 3 others I have lined up (and getting good enough in the process) as long as someone can take up testing
Comment 41 ulterno 2025-05-15 05:30:44 UTC
> I am willing to take up the task after finishing 3 others I have lined up
> (and getting good enough in the process) as long as someone can take up
> testing

It seems preferable to have this kind of thing work asynchronously, 
specially if we end up implementing Remote Objects with a Server - Client architecture to use KDevelop through ssh.
Even if the above doesn't happen, it would be good to not have the GUI start hanging if someone is maybe using a slower HDD with some plugin having a heavy IO load or even if they are opening a project on a USB Flash drive
Comment 42 Igor Kushnir 2025-05-15 09:01:49 UTC
(In reply to ulterno from comment #39)
> Attempting the same inside QTextEdit::contextMenuEvent, did not reproduce
> the bug. i.e. The menu was not called twice when using the keyboard Menu key.
> So, only able to do it with KTextEditor::View
This is strange. Maybe the bug is that KTextEditor somehow sends the same event again? Locating such a bug in KTextEditor should be much easier than in Qt: much less code to consider and much higher-level code, not dealing with OS-level APIs

> Modifying the function as shown below: ...
> 		if (menu == d->currentContextMenu)
> 		{
> 			// Of the same thing, then don't delete and don't retry. This line
> depends upon cleanContextMenu working as required
> 			qCDebug (SHELL) << "Pointer of the new context menu is the same... Not
> cleaning, not remaking";
> 			return;
> 		}
> ... can be considered a hotfix.
This hotfix seems risky in view of the following info from the commit message of the aforementioned commit https://commits.kde.org/kdevelop/d940a88e8bd92031cd7ffce2450cc24f266e6acc :
For some yet to explore internal reason, if there is a plugin with a
"ktexteditor_popup" menu to merge in, then the QMenu object instance is the
same across all KTextEditor::View instances, otherwise each view has its own.
More, even per view one gets a new QMenu object every time the view becomes the
active one again (and thus merging into the app XMLGUI structure is done).

I don't see how KDevelop can tell whether the old context menu request is obsolete and the menu should be repopulated, or if the second context menu event is just an exact duplicate of the currently handled one, and therefore should be ignored. Comparing the context menu event objects that arrive to KateViewInternal::contextMenuEvent() should be less risky (especially if the event pointers match), as I suggested in the previous comment. Maybe the root cause is even in the short definition of KateViewInternal::contextMenuEvent(), e.g. the special handling for the Keyboard event reason therein: `if (e->reason() == QContextMenuEvent::Keyboard)`.

(In reply to ulterno from comment #41)
> > I am willing to take up the task after finishing 3 others I have lined up
> > (and getting good enough in the process) as long as someone can take up
> > testing
There is no dedicated KDevelop QA team. We rely on developer testing and automatic tests. Whoever makes a change is expected to do the bulk of the testing and is most responsible for regressions.
> It seems preferable to have this kind of thing work asynchronously, 
> specially if we end up implementing Remote Objects with a Server - Client
> architecture to use KDevelop through ssh.
> Even if the above doesn't happen, it would be good to not have the GUI start
> hanging if someone is maybe using a slower HDD with some plugin having a
> heavy IO load or even if they are opening a project on a USB Flash drive
I agree that asynchronous event handling is much better. The problem is getting there. I suspect a lot of KDevelop code would have to be changed. And some fair amount of testing with different plugins, e.g. CTags, would be needed on X11 and Wayland. And the immediate gain seems to be small because this bug is somewhat obscure and probably can be fixed or worked around much easier in KTextEditor.

This trouble with nested event loops is similar to the much more impactful (measured by the age and the length of the CC list) Bug 435235.
Comment 43 ulterno 2025-05-15 13:30:35 UTC
I too, find it better to fix in KTextEditor itself: BUG 504260

___

Regarding the async and testing, of course I am willing to test as much as I can, but since this will change a lot for a lot of plug-ins, many of which, I wouldn't even have used, the MR is bound to last quite a while. before getting into master.
But we can consider the possibility, if and when I get to doing it.

Worst case, I will have a closed MR, with some code that someone might (not) find useful, picking up in the future.
Comment 44 Igor Kushnir 2025-05-15 15:05:31 UTC
(In reply to ulterno from comment #43)
> Regarding the async and testing, of course I am willing to test as much as I
> can, but since this will change a lot for a lot of plug-ins, many of which,
> I wouldn't even have used, the MR is bound to last quite a while. before
> getting into master.
> But we can consider the possibility, if and when I get to doing it.
Since the asynchronous handling is clearly better, feel free to try and make it work. Note that the context menu handler of the git plugin enters at least two nested event loops: first to figure out whether the document(s) are within a git repository, then to test the existence of stashes.
Comment 45 Igor Kushnir 2025-05-16 13:39:02 UTC
Created attachment 181384 [details]
Slow GitPlugin::hasStashes() experiment

At least under X11, even if the `git stash` command takes a long time as the attached patch simulates, right-click context menu events are handled one after another rather than simultaneously, and therefore these is no crash. See also the commit message in the patch.