diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp
index 2c4e4d312594dc1f9e4c7cd37e83f008cae1b60c..1ef9e4ef989d16c82801641bc2243dadfbc1d60a 100644
--- a/src/widgets/kernel/qaction.cpp
+++ b/src/widgets/kernel/qaction.cpp
@@ -39,10 +39,10 @@
 #include "qapplication.h"
 #include "qevent.h"
 #include "qlist.h"
-#include "qdebug.h"
 #include <private/qshortcutmap_p.h>
 #include <private/qapplication_p.h>
 #include <private/qmenu_p.h>
+#include <private/qdebug_p.h>
 
 #define QAPP_CHECK(functionName) \
     if (!qApp) { \
@@ -1296,6 +1296,31 @@ bool QAction::isIconVisibleInMenu() const
     return d->iconVisibleInMenu;
 }
 
+#ifndef QT_NO_DEBUG_STREAM
+Q_WIDGETS_EXPORT QDebug operator<<(QDebug d, const QAction *action)
+{
+    QDebugStateSaver saver(d);
+    d.nospace();
+    d << "QAction(" << static_cast<const void *>(action);
+    if (action) {
+        d << " text=" << action->text();
+        if (!action->toolTip().isEmpty())
+            d << " toolTip=" << action->toolTip();
+        if (action->isCheckable())
+            d << " checked=" << action->isChecked();
+        if (!action->shortcut().isEmpty())
+            d << " shortcut=" << action->shortcut();
+        d << " menuRole=";
+        QtDebugUtils::formatQEnum(d, action->menuRole());
+        d << " visible=" << action->isVisible();
+    } else {
+        d << '0';
+    }
+    d << ')';
+    return d;
+}
+#endif // QT_NO_DEBUG_STREAM
+
 QT_END_NAMESPACE
 
 #include "moc_qaction.cpp"
diff --git a/src/widgets/kernel/qaction.h b/src/widgets/kernel/qaction.h
index 8ef26b60bf1c2af11a793e92737f35d45186ea59..0eb2c60c7057d2ae76c917c0170a2c12e6a82125 100644
--- a/src/widgets/kernel/qaction.h
+++ b/src/widgets/kernel/qaction.h
@@ -203,6 +203,10 @@ private:
 #endif
 };
 
+#ifndef QT_NO_DEBUG_STREAM
+Q_WIDGETS_EXPORT QDebug operator<<(QDebug, const QAction *);
+#endif
+
 QT_BEGIN_INCLUDE_NAMESPACE
 #include <QtWidgets/qactiongroup.h>
 QT_END_INCLUDE_NAMESPACE