Enforce the relative ordering of "Intro", "Members", "Features", "Errors" and "Returns" sections.
Signed-off-by: John Snow <[email protected]> --- docs/devel/qapi-code-gen.rst | 19 +++++++++++------ scripts/qapi/parser.py | 41 ++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 3ac1c2dae18..29e9c5a3da6 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -985,12 +985,19 @@ When documentation is required (see pragma_ 'doc-required'), every definition must have documentation. Definition documentation starts with a description naming the -definition with an optional indented overview (the "intro"), -:ref:`descriptions <doc-descrip>` of each argument (for commands and -events), member (for structs and unions), branch (for alternates), or -value (for enums), :ref:`feature descriptions <doc-feat>` of each -feature (if any), and finally optional :ref:`tagged sections -<doc-tagged>`. +definition with an optional indented overview (the "intro"). + +The introduction is followed by :ref:`descriptions <doc-descrip>` of +each argument (for commands and events), member (for structs and +unions), branch (for alternates), or value (for enums). + +:ref:`Tagged sections <doc-tagged>` follow in order: Returns (for +commands), Errors (for commands), and lastly :ref:`feature +descriptions <doc-feat>` if applicable. + +Definition documentation concludes with rST-formatted detail +paragraphs (including examples, notes, &c), and finally an optional +"Since" tagged section at the very end. .. _doc-descrip: diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index fe284a3f866..94a60a23482 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -545,6 +545,28 @@ def get_doc(self) -> 'QAPIDoc': doc.all_sections.append(QAPIDoc.Section(info, QAPIDoc.Kind.INTRO)) line = self.get_doc_indented(doc) no_more_args = False + last_ordered_section = QAPIDoc.Kind.INTRO + + def _tag_check(this: Union['QAPIDoc.Kind', str]) -> None: + nonlocal last_ordered_section + if isinstance(this, str): + this = QAPIDoc.Kind.from_string(this) + + if this in (QAPIDoc.Kind.TODO, QAPIDoc.Kind.SINCE, + QAPIDoc.Kind.DETAILS): + return + + if this.value < last_ordered_section.value: + raise QAPIParseError( + self, + f"'{this}' section cannot appear after " + f"'{last_ordered_section}' section, please re-order " + "the sections and adjust phrasing as necessary to " + "ensure consistent documentation flow between the " + "source code and the rendered HTML manual" + ) + + last_ordered_section = this while line is not None: # Blank lines @@ -558,6 +580,7 @@ def get_doc(self) -> 'QAPIDoc': if doc.has_features: raise QAPIParseError( self, "duplicated 'Features:' line") + _tag_check(QAPIDoc.Kind.FEATURE) self.accept(False) line = self.get_doc_line() while line == '': @@ -581,6 +604,7 @@ def get_doc(self) -> 'QAPIDoc': self, "description of '@%s:' follows a section" % match.group(1)) + _tag_check(QAPIDoc.Kind.MEMBER) while (line is not None and (match := self._match_at_name_colon(line))): doc.new_argument(self.info, match.group(1)) @@ -621,6 +645,7 @@ def get_doc(self) -> 'QAPIDoc': ) raise QAPIParseError(self, emsg) + _tag_check(match.group(1)) doc.new_tagged_section( self.info, QAPIDoc.Kind.from_string(match.group(1)) @@ -632,6 +657,7 @@ def get_doc(self) -> 'QAPIDoc': no_more_args = True else: # plain paragraph(s) + _tag_check(QAPIDoc.Kind.DETAILS) doc.ensure_untagged_section(self.info) doc.append_line(line) line = self.get_doc_paragraph(doc) @@ -674,14 +700,17 @@ class QAPIDoc: """ class Kind(enum.Enum): - DETAILS = 0 + # The order here is the order in which sections must appear in + # source code; with the exception of 'TODO' which may appear + # anywhere but is treated as a comment. + INTRO = 0 MEMBER = 1 - FEATURE = 2 - RETURNS = 3 - ERRORS = 4 - SINCE = 5 + RETURNS = 2 + ERRORS = 3 + FEATURE = 4 + DETAILS = 5 TODO = 6 - INTRO = 7 + SINCE = 7 @staticmethod def from_string(kind: str) -> 'QAPIDoc.Kind': -- 2.55.0
