Skip to content

Commit

Permalink
qapi: Divorce QAPIDoc from QAPIParseError
Browse files Browse the repository at this point in the history
QAPIDoc stores a reference to QAPIParser just to pass it to
QAPIParseError.  The resulting error position depends on the state of
the parser.  It happens to be the current comment line.  Servicable,
but action at a distance.

The commit before previous moved most uses of QAPIParseError from
QAPIDoc to QAPIParser.  There are just three left.  Convert them to
QAPISemError.  This involves passing info to a few methods.  Then drop
the reference to QAPIParser.

The three errors lose the column number.  Not really interesting here:
it's the comment line's indentation.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-ID: <20240216145841.2099240-17-armbru@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
  • Loading branch information
Markus Armbruster committed Feb 26, 2024
1 parent 629c507 commit adb0193
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 42 deletions.
66 changes: 28 additions & 38 deletions scripts/qapi/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ def get_doc(self) -> 'QAPIDoc':
symbol = line[1:-1]
if not symbol:
raise QAPIParseError(self, "name required after '@'")
doc = QAPIDoc(self, info, symbol)
doc = QAPIDoc(info, symbol)
self.accept(False)
line = self.get_doc_line()
no_more_args = False
Expand All @@ -518,7 +518,7 @@ def get_doc(self) -> 'QAPIDoc':
line = self.get_doc_line()
while (line is not None
and (match := self._match_at_name_colon(line))):
doc.new_feature(match.group(1))
doc.new_feature(self.info, match.group(1))
text = line[match.end():]
if text:
doc.append_line(text)
Expand All @@ -536,7 +536,7 @@ def get_doc(self) -> 'QAPIDoc':
% match.group(1))
while (line is not None
and (match := self._match_at_name_colon(line))):
doc.new_argument(match.group(1))
doc.new_argument(self.info, match.group(1))
text = line[match.end():]
if text:
doc.append_line(text)
Expand All @@ -546,7 +546,7 @@ def get_doc(self) -> 'QAPIDoc':
r'(Returns|Since|Notes?|Examples?|TODO): *',
line):
# tagged section
doc.new_tagged_section(match.group(1))
doc.new_tagged_section(self.info, match.group(1))
text = line[match.end():]
if text:
doc.append_line(text)
Expand All @@ -558,13 +558,13 @@ def get_doc(self) -> 'QAPIDoc':
"unexpected '=' markup in definition documentation")
else:
# tag-less paragraph
doc.ensure_untagged_section()
doc.ensure_untagged_section(self.info)
doc.append_line(line)
line = self.get_doc_paragraph(doc)
else:
# Free-form documentation
doc = QAPIDoc(self, info)
doc.ensure_untagged_section()
doc = QAPIDoc(info)
doc.ensure_untagged_section(self.info)
first = True
while line is not None:
if match := self._match_at_name_colon(line):
Expand Down Expand Up @@ -607,12 +607,10 @@ class QAPIDoc:
"""

class Section:
def __init__(self, parser: QAPISchemaParser,
def __init__(self, info: QAPISourceInfo,
tag: Optional[str] = None):
# section source info, i.e. where it begins
self.info = parser.info
# parser, for error messages about indentation
self._parser = parser
self.info = info
# section tag, if any ('Returns', '@name', ...)
self.tag = tag
# section text without tag
Expand All @@ -622,27 +620,20 @@ def append_line(self, line: str) -> None:
self.text += line + '\n'

class ArgSection(Section):
def __init__(self, parser: QAPISchemaParser,
tag: str):
super().__init__(parser, tag)
def __init__(self, info: QAPISourceInfo, tag: str):
super().__init__(info, tag)
self.member: Optional['QAPISchemaMember'] = None

def connect(self, member: 'QAPISchemaMember') -> None:
self.member = member

def __init__(self, parser: QAPISchemaParser, info: QAPISourceInfo,
symbol: Optional[str] = None):
# self._parser is used to report errors with QAPIParseError. The
# resulting error position depends on the state of the parser.
# It happens to be the beginning of the comment. More or less
# servicable, but action at a distance.
self._parser = parser
def __init__(self, info: QAPISourceInfo, symbol: Optional[str] = None):
# info points to the doc comment block's first line
self.info = info
# definition doc's symbol, None for free-form doc
self.symbol: Optional[str] = symbol
# the sections in textual order
self.all_sections: List[QAPIDoc.Section] = [QAPIDoc.Section(parser)]
self.all_sections: List[QAPIDoc.Section] = [QAPIDoc.Section(info)]
# the body section
self.body: Optional[QAPIDoc.Section] = self.all_sections[0]
# dicts mapping parameter/feature names to their description
Expand All @@ -658,44 +649,43 @@ def end(self) -> None:
raise QAPISemError(
section.info, "text required after '%s:'" % section.tag)

def ensure_untagged_section(self) -> None:
def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
if self.all_sections and not self.all_sections[-1].tag:
# extend current section
self.all_sections[-1].text += '\n'
return
# start new section
section = self.Section(self._parser)
section = self.Section(info)
self.sections.append(section)
self.all_sections.append(section)

def new_tagged_section(self, tag: str) -> None:
def new_tagged_section(self, info: QAPISourceInfo, tag: str) -> None:
if tag in ('Returns', 'Since'):
for section in self.all_sections:
if isinstance(section, self.ArgSection):
continue
if section.tag == tag:
raise QAPIParseError(
self._parser, "duplicated '%s' section" % tag)
section = self.Section(self._parser, tag)
raise QAPISemError(
info, "duplicated '%s' section" % tag)
section = self.Section(info, tag)
self.sections.append(section)
self.all_sections.append(section)

def _new_description(self, name: str,
def _new_description(self, info: QAPISourceInfo, name: str,
desc: Dict[str, ArgSection]) -> None:
if not name:
raise QAPIParseError(self._parser, "invalid parameter name")
raise QAPISemError(info, "invalid parameter name")
if name in desc:
raise QAPIParseError(self._parser,
"'%s' parameter name duplicated" % name)
section = self.ArgSection(self._parser, '@' + name)
raise QAPISemError(info, "'%s' parameter name duplicated" % name)
section = self.ArgSection(info, '@' + name)
self.all_sections.append(section)
desc[name] = section

def new_argument(self, name: str) -> None:
self._new_description(name, self.args)
def new_argument(self, info: QAPISourceInfo, name: str) -> None:
self._new_description(info, name, self.args)

def new_feature(self, name: str) -> None:
self._new_description(name, self.features)
def new_feature(self, info: QAPISourceInfo, name: str) -> None:
self._new_description(info, name, self.features)

def append_line(self, line: str) -> None:
self.all_sections[-1].append_line(line)
Expand All @@ -707,7 +697,7 @@ def connect_member(self, member: 'QAPISchemaMember') -> None:
"%s '%s' lacks documentation"
% (member.role, member.name))
self.args[member.name] = QAPIDoc.ArgSection(
self._parser, '@' + member.name)
self.info, '@' + member.name)
self.args[member.name].connect(member)

def connect_feature(self, feature: 'QAPISchemaFeature') -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-duplicated-arg.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
doc-duplicated-arg.json:6:1: 'a' parameter name duplicated
doc-duplicated-arg.json:6: 'a' parameter name duplicated
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-duplicated-return.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
doc-duplicated-return.json:8:1: duplicated 'Returns' section
doc-duplicated-return.json:8: duplicated 'Returns' section
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-duplicated-since.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
doc-duplicated-since.json:8:1: duplicated 'Since' section
doc-duplicated-since.json:8: duplicated 'Since' section
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-empty-arg.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
doc-empty-arg.json:5:1: invalid parameter name
doc-empty-arg.json:5: invalid parameter name

0 comments on commit adb0193

Please sign in to comment.