From 6c10778826a873b9012d95e63298a6f879debcaa Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:52 -0400 Subject: docs/sphinx: remove special parsing for freeform sections Remove the QAPI doc section heading syntax, use plain rST section headings instead. Tests and documentation are updated to match. Interestingly, Plain rST headings work fine before this patch, except for over- and underlining with '=', which the doc parser rejected as invalid QAPI doc section heading in free-form comments. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-5-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Add more detail to commit message] Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'scripts/qapi/parser.py') diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 949d9e8bff..aad7e249f8 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -597,22 +597,15 @@ class QAPISchemaParser: # Free-form documentation doc = QAPIDoc(info) doc.ensure_untagged_section(self.info) - first = True while line is not None: if match := self._match_at_name_colon(line): raise QAPIParseError( self, "'@%s:' not allowed in free-form documentation" % match.group(1)) - if line.startswith('='): - if not first: - raise QAPIParseError( - self, - "'=' heading must come first in a comment block") doc.append_line(line) self.accept(False) line = self.get_doc_line() - first = False self.accept() doc.end() -- cgit 1.4.1 From 62c4dc4b69ef7dcfcc476913a9c5fc15329e0290 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:53 -0400 Subject: qapi: lift restriction on using '=' in doc blocks We reject lines starting with '=' in definition documentation. This made sense when such lines were headings in free-form documentation, but not in definition documentation. Before the previous commit, lines starting with '=' were headings in free-form documentation, and rejected in definition documentation, where such headings could not work. The previous commit dropped the headings feature from free-form documentation, because we can simply use plain rST headings. Rejecting them in definition documentation no longer makes sense, so drop that, too. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-6-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Amend commit message to explain why] Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 4 ---- tests/qapi-schema/doc-bad-section.err | 1 - tests/qapi-schema/doc-bad-section.json | 10 ---------- tests/qapi-schema/doc-bad-section.out | 0 tests/qapi-schema/meson.build | 1 - 5 files changed, 16 deletions(-) delete mode 100644 tests/qapi-schema/doc-bad-section.err delete mode 100644 tests/qapi-schema/doc-bad-section.json delete mode 100644 tests/qapi-schema/doc-bad-section.out (limited to 'scripts/qapi/parser.py') diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index aad7e249f8..d43a123cd7 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -584,10 +584,6 @@ class QAPISchemaParser: doc.append_line(text) line = self.get_doc_indented(doc) no_more_args = True - elif line.startswith('='): - raise QAPIParseError( - self, - "unexpected '=' markup in definition documentation") else: # plain paragraph doc.ensure_untagged_section(self.info) diff --git a/tests/qapi-schema/doc-bad-section.err b/tests/qapi-schema/doc-bad-section.err deleted file mode 100644 index 785cacc08c..0000000000 --- a/tests/qapi-schema/doc-bad-section.err +++ /dev/null @@ -1 +0,0 @@ -doc-bad-section.json:5:1: unexpected '=' markup in definition documentation diff --git a/tests/qapi-schema/doc-bad-section.json b/tests/qapi-schema/doc-bad-section.json deleted file mode 100644 index 8175d95867..0000000000 --- a/tests/qapi-schema/doc-bad-section.json +++ /dev/null @@ -1,10 +0,0 @@ -# = section within an expression comment - -## -# @Enum: -# == No good here -# @one: The _one_ {and only} -# -# @two is undocumented -## -{ 'enum': 'Enum', 'data': [ 'one', 'two' ] } diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 9577178b6f..c47025d16d 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -61,7 +61,6 @@ schemas = [ 'doc-bad-event-arg.json', 'doc-bad-feature.json', 'doc-bad-indent.json', - 'doc-bad-section.json', 'doc-bad-symbol.json', 'doc-bad-union-member.json', 'doc-before-include.json', -- cgit 1.4.1 From 636c96cd77d39aaec3e1c09b9990b76b015566e1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:10:43 -0400 Subject: qapi: Fix undocumented return values by generating something MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated command documentation lacks information on return value in several cases, e.g. query-tpm. The obvious fix would be to require a Returns: section when a command returns something. However, note that many existing Returns: sections are pretty useless: the description is basically the return type, which then gets rendered like "Return: ". This suggests that a description is often not really necessary, and requiring one isn't useful. Instead, generate the obvious minimal thing when Returns: is absent: "Return: ". This auto-generated Return documentation is placed is as follows: 1. If we have arguments, return goes right after them. 2. Else if we have errors, return goes right before them. 3. Else if we have features, return goes right before them. 4. Else return goes right after the intro To facilitate this algorithm, a "TODO:" hack line is used to separate the intro from the remainder of the documentation block in cases where there are no other sections to separate the intro from e.g. examples and additional detail meant to appear below the key sections of interest. Signed-off-by: John Snow Message-ID: <20250711051045.51110-3-jsnow@redhat.com> Reviewed-by: Markus Armbruster [_insert_near_kind() code replaced by something simpler, commit message amended to explain why we're doing this] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 14 ++++++++------ qapi/machine.json | 2 ++ scripts/qapi/parser.py | 37 +++++++++++++++++++++++++++++++++++++ scripts/qapi/schema.py | 3 +++ 4 files changed, 50 insertions(+), 6 deletions(-) (limited to 'scripts/qapi/parser.py') diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index b794cde697..c2f09bac16 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -258,16 +258,18 @@ class Transmogrifier: def visit_returns(self, section: QAPIDoc.Section) -> None: assert isinstance(self.entity, QAPISchemaCommand) rtype = self.entity.ret_type - # q_empty can produce None, but we won't be documenting anything - # without an explicit return statement in the doc block, and we - # should not have any such explicit statements when there is no - # return value. + # return statements will not be present (and won't be + # autogenerated) for any command that doesn't return + # *something*, so rtype will always be defined here. assert rtype typ = self.format_type(rtype) assert typ - assert section.text - self.add_field("return", typ, section.text, section.info) + + if section.text: + self.add_field("return", typ, section.text, section.info) + else: + self.add_lines(f":return-nodesc: {typ}", section.info) def visit_errors(self, section: QAPIDoc.Section) -> None: # If the section text does not start with a space, it means text diff --git a/qapi/machine.json b/qapi/machine.json index af00a20b1f..2d6a137f21 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1303,6 +1303,8 @@ # Return the amount of initially allocated and present hotpluggable # (if enabled) memory in bytes. # +# TODO: This line is a hack to separate the example from the body +# # .. qmp-example:: # # -> { "execute": "query-memory-size-summary" } diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index d43a123cd7..2529edf81a 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -804,6 +804,43 @@ class QAPIDoc: % feature.name) self.features[feature.name].connect(feature) + def ensure_returns(self, info: QAPISourceInfo) -> None: + + def _insert_near_kind( + kind: QAPIDoc.Kind, + new_sect: QAPIDoc.Section, + after: bool = False, + ) -> bool: + for idx, sect in enumerate(reversed(self.all_sections)): + if sect.kind == kind: + pos = len(self.all_sections) - idx - 1 + if after: + pos += 1 + self.all_sections.insert(pos, new_sect) + return True + return False + + if any(s.kind == QAPIDoc.Kind.RETURNS for s in self.all_sections): + return + + # Stub "Returns" section for undocumented returns value + stub = QAPIDoc.Section(info, QAPIDoc.Kind.RETURNS) + + if any(_insert_near_kind(kind, stub, after) for kind, after in ( + # 1. If arguments, right after those. + (QAPIDoc.Kind.MEMBER, True), + # 2. Elif errors, right *before* those. + (QAPIDoc.Kind.ERRORS, False), + # 3. Elif features, right *before* those. + (QAPIDoc.Kind.FEATURE, False), + )): + return + + # Otherwise, it should go right after the intro. The intro + # is always the first section and is always present (even + # when empty), so we can insert directly at index=1 blindly. + self.all_sections.insert(1, stub) + def check_expr(self, expr: QAPIExpression) -> None: if 'command' in expr: if self.returns and 'returns' not in expr: diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cbe3b5aa91..3abddea352 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1062,6 +1062,9 @@ class QAPISchemaCommand(QAPISchemaDefinition): if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) + if self.ret_type and self.info: + doc.ensure_returns(self.info) + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_command( -- cgit 1.4.1