diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py
index 657c05a81a4..5126b450a5f 100644
--- a/docs/sphinx/compat.py
+++ b/docs/sphinx/compat.py
@@ -12,9 +12,11 @@
 
 from docutils import nodes
 from docutils.nodes import Element, Node, Text
+from docutils.statemachine import StringList
 
 import sphinx
 from sphinx import addnodes
+from sphinx.directives import ObjectDescription
 from sphinx.environment import BuildEnvironment
 from sphinx.roles import XRefRole
 from sphinx.util import docfields
@@ -28,6 +30,7 @@
 
 
 MAKE_XREF_WORKAROUND = sphinx.version_info[:3] < (4, 1, 0)
+SOURCE_LOCATION_FIX = (5, 3, 0) <= sphinx.version_info[:3] < (6, 2, 0)
 
 
 space_node: Callable[[str], Node]
@@ -156,3 +159,33 @@ class CompatTypedField(CompatFieldMixin, docfields.TypedField):
     Field = CompatField
     GroupedField = CompatGroupedField
     TypedField = CompatTypedField
+
+
+class ParserFix(ObjectDescription):
+
+    _temp_content: StringList
+    _temp_offset: int
+    _temp_node: Optional[addnodes.desc_content]
+
+    def before_content(self) -> None:
+        # Work around a sphinx bug and parse the content ourselves.
+        self._temp_content = self.content
+        self._temp_offset = self.content_offset
+        self._temp_node = None
+
+        if SOURCE_LOCATION_FIX:
+            self._temp_node = addnodes.desc_content()
+            self.state.nested_parse(
+                self.content, self.content_offset, self._temp_node
+            )
+            # Sphinx will try to parse the content block itself,
+            # Give it nothingness to parse instead.
+            self.content = StringList()
+            self.content_offset = 0
+
+    def transform_content(self, contentnode: addnodes.desc_content) -> None:
+        # Sphinx workaround: Inject our parsed content and restore state.
+        if self._temp_node:
+            contentnode += self._temp_node.children
+            self.content = self._temp_content
+            self.content_offset = self._temp_offset
diff --git a/docs/sphinx/qapi-domain.py b/docs/sphinx/qapi-domain.py
index ebdf9074391..00d32d44e1d 100644
--- a/docs/sphinx/qapi-domain.py
+++ b/docs/sphinx/qapi-domain.py
@@ -21,18 +21,17 @@
 
 from docutils import nodes
 from docutils.parsers.rst import directives
-from docutils.statemachine import StringList
 
 from collapse import CollapseNode
 from compat import (
     Field,
     GroupedField,
+    ParserFix,
     TypedField,
     keyword_node,
     nested_parse,
     space_node,
 )
-import sphinx
 from sphinx import addnodes
 from sphinx.addnodes import desc_signature, pending_xref
 from sphinx.directives import ObjectDescription
@@ -170,7 +169,7 @@ def since_validator(param: str) -> str:
 Signature = str
 
 
-class QAPIObject(ObjectDescription[Signature]):
+class QAPIObject(ParserFix, ObjectDescription[Signature]):
     """
     Description of a generic QAPI object.
 
@@ -436,31 +435,14 @@ def _validate_field(self, field: nodes.field) -> None:
             )
             logger.warning(msg, location=field)
 
-    def before_content(self) -> None:
-        # Work around a sphinx bug and parse the content ourselves.
-        self._temp_content = self.content
-        self._temp_offset = self.content_offset
-        self._temp_node = None
-
-        if (5, 3, 0) <= sphinx.version_info[:3] < (6, 2, 0):
-            self._temp_node = addnodes.desc_content()
-            self.state.nested_parse(
-                self.content, self.content_offset, self._temp_node
-            )
-            # Sphinx will try to parse the content block itself,
-            # Give it nothingness to parse instead.
-            self.content = StringList()
-            self.content_offset = 0
-
     def transform_content(self, contentnode: addnodes.desc_content) -> None:
+        # This hook runs after before_content and the nested parse, but
+        # before the DocFieldTransformer is executed.
+        super().transform_content(contentnode)
+
+        # Bookmark this content_node for later use in after_content().
         self.content_node = contentnode
 
-        # Sphinx workaround: Inject our parsed content and restore state.
-        if self._temp_node:
-            contentnode += self._temp_node.children
-            self.content = self._temp_content
-            self.content_offset = self._temp_offset
-
         self._add_infopips(contentnode)
         self._merge_adjoining_field_lists(contentnode)
 
