xref: /openbmc/qemu/scripts/lib/kdoc/kdoc_parser.py (revision e101d33792530093fa0b0a6e5f43e4d8cfe4581e)
12b2765acSPeter Maydell#!/usr/bin/env python3
22b2765acSPeter Maydell# SPDX-License-Identifier: GPL-2.0
32b2765acSPeter Maydell# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
42b2765acSPeter Maydell#
52b2765acSPeter Maydell# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702
62b2765acSPeter Maydell
72b2765acSPeter Maydell"""
82b2765acSPeter Maydellkdoc_parser
92b2765acSPeter Maydell===========
102b2765acSPeter Maydell
112b2765acSPeter MaydellRead a C language source or header FILE and extract embedded
122b2765acSPeter Maydelldocumentation comments
132b2765acSPeter Maydell"""
142b2765acSPeter Maydell
152b2765acSPeter Maydellimport sys
162b2765acSPeter Maydellimport re
172b2765acSPeter Maydellfrom pprint import pformat
182b2765acSPeter Maydell
192b2765acSPeter Maydellfrom kdoc_re import NestedMatch, KernRe
202b2765acSPeter Maydellfrom kdoc_item import KdocItem
212b2765acSPeter Maydell
222b2765acSPeter Maydell#
232b2765acSPeter Maydell# Regular expressions used to parse kernel-doc markups at KernelDoc class.
242b2765acSPeter Maydell#
252b2765acSPeter Maydell# Let's declare them in lowercase outside any class to make easier to
262b2765acSPeter Maydell# convert from the python script.
272b2765acSPeter Maydell#
282b2765acSPeter Maydell# As those are evaluated at the beginning, no need to cache them
292b2765acSPeter Maydell#
302b2765acSPeter Maydell
312b2765acSPeter Maydell# Allow whitespace at end of comment start.
322b2765acSPeter Maydelldoc_start = KernRe(r'^/\*\*\s*$', cache=False)
332b2765acSPeter Maydell
342b2765acSPeter Maydelldoc_end = KernRe(r'\*/', cache=False)
352b2765acSPeter Maydelldoc_com = KernRe(r'\s*\*\s*', cache=False)
362b2765acSPeter Maydelldoc_com_body = KernRe(r'\s*\* ?', cache=False)
372b2765acSPeter Maydelldoc_decl = doc_com + KernRe(r'(\w+)', cache=False)
382b2765acSPeter Maydell
392b2765acSPeter Maydell# @params and a strictly limited set of supported section names
402b2765acSPeter Maydell# Specifically:
412b2765acSPeter Maydell#   Match @word:
422b2765acSPeter Maydell#         @...:
432b2765acSPeter Maydell#         @{section-name}:
442b2765acSPeter Maydell# while trying to not match literal block starts like "example::"
452b2765acSPeter Maydell#
462b2765acSPeter Maydellknown_section_names = 'description|context|returns?|notes?|examples?'
472b2765acSPeter Maydellknown_sections = KernRe(known_section_names, flags = re.I)
482b2765acSPeter Maydelldoc_sect = doc_com + \
492b2765acSPeter Maydell    KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$',
502b2765acSPeter Maydell           flags=re.I, cache=False)
512b2765acSPeter Maydell
522b2765acSPeter Maydelldoc_content = doc_com_body + KernRe(r'(.*)', cache=False)
532b2765acSPeter Maydelldoc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False)
542b2765acSPeter Maydelldoc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False)
552b2765acSPeter Maydelldoc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False)
562b2765acSPeter Maydelldoc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False)
572b2765acSPeter Maydellattribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)",
582b2765acSPeter Maydell               flags=re.I | re.S, cache=False)
592b2765acSPeter Maydell
602b2765acSPeter Maydellexport_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False)
612b2765acSPeter Maydellexport_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False)
622b2765acSPeter Maydell
632b2765acSPeter Maydelltype_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
642b2765acSPeter Maydell
652b2765acSPeter Maydell#
662b2765acSPeter Maydell# Tests for the beginning of a kerneldoc block in its various forms.
672b2765acSPeter Maydell#
682b2765acSPeter Maydelldoc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False)
692b2765acSPeter Maydelldoc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False)
702b2765acSPeter Maydelldoc_begin_func = KernRe(str(doc_com) +			# initial " * '
712b2765acSPeter Maydell                        r"(?:\w+\s*\*\s*)?" + 		# type (not captured)
722b2765acSPeter Maydell                        r'(?:define\s+)?' + 		# possible "define" (not captured)
732b2765acSPeter Maydell                        r'(\w+)\s*(?:\(\w*\))?\s*' +	# name and optional "(...)"
742b2765acSPeter Maydell                        r'(?:[-:].*)?$',		# description (not captured)
752b2765acSPeter Maydell                        cache = False)
762b2765acSPeter Maydell
772b2765acSPeter Maydell#
782b2765acSPeter Maydell# A little helper to get rid of excess white space
792b2765acSPeter Maydell#
802b2765acSPeter Maydellmulti_space = KernRe(r'\s\s+')
812b2765acSPeter Maydelldef trim_whitespace(s):
822b2765acSPeter Maydell    return multi_space.sub(' ', s.strip())
832b2765acSPeter Maydell
842b2765acSPeter Maydellclass state:
852b2765acSPeter Maydell    """
862b2765acSPeter Maydell    State machine enums
872b2765acSPeter Maydell    """
882b2765acSPeter Maydell
892b2765acSPeter Maydell    # Parser states
902b2765acSPeter Maydell    NORMAL        = 0        # normal code
912b2765acSPeter Maydell    NAME          = 1        # looking for function name
922b2765acSPeter Maydell    DECLARATION   = 2        # We have seen a declaration which might not be done
932b2765acSPeter Maydell    BODY          = 3        # the body of the comment
942b2765acSPeter Maydell    SPECIAL_SECTION = 4      # doc section ending with a blank line
952b2765acSPeter Maydell    PROTO         = 5        # scanning prototype
962b2765acSPeter Maydell    DOCBLOCK      = 6        # documentation block
972b2765acSPeter Maydell    INLINE_NAME   = 7        # gathering doc outside main block
982b2765acSPeter Maydell    INLINE_TEXT   = 8	     # reading the body of inline docs
992b2765acSPeter Maydell
1002b2765acSPeter Maydell    name = [
1012b2765acSPeter Maydell        "NORMAL",
1022b2765acSPeter Maydell        "NAME",
1032b2765acSPeter Maydell        "DECLARATION",
1042b2765acSPeter Maydell        "BODY",
1052b2765acSPeter Maydell        "SPECIAL_SECTION",
1062b2765acSPeter Maydell        "PROTO",
1072b2765acSPeter Maydell        "DOCBLOCK",
1082b2765acSPeter Maydell        "INLINE_NAME",
1092b2765acSPeter Maydell        "INLINE_TEXT",
1102b2765acSPeter Maydell    ]
1112b2765acSPeter Maydell
1122b2765acSPeter Maydell
1132b2765acSPeter MaydellSECTION_DEFAULT = "Description"  # default section
1142b2765acSPeter Maydell
1152b2765acSPeter Maydellclass KernelEntry:
1162b2765acSPeter Maydell
1172b2765acSPeter Maydell    def __init__(self, config, ln):
1182b2765acSPeter Maydell        self.config = config
1192b2765acSPeter Maydell
1202b2765acSPeter Maydell        self._contents = []
1212b2765acSPeter Maydell        self.prototype = ""
1222b2765acSPeter Maydell
1232b2765acSPeter Maydell        self.warnings = []
1242b2765acSPeter Maydell
1252b2765acSPeter Maydell        self.parameterlist = []
1262b2765acSPeter Maydell        self.parameterdescs = {}
1272b2765acSPeter Maydell        self.parametertypes = {}
1282b2765acSPeter Maydell        self.parameterdesc_start_lines = {}
1292b2765acSPeter Maydell
1302b2765acSPeter Maydell        self.section_start_lines = {}
1312b2765acSPeter Maydell        self.sections = {}
1322b2765acSPeter Maydell
1332b2765acSPeter Maydell        self.anon_struct_union = False
1342b2765acSPeter Maydell
1352b2765acSPeter Maydell        self.leading_space = None
1362b2765acSPeter Maydell
1372b2765acSPeter Maydell        # State flags
1382b2765acSPeter Maydell        self.brcount = 0
1392b2765acSPeter Maydell        self.declaration_start_line = ln + 1
1402b2765acSPeter Maydell
1412b2765acSPeter Maydell    #
1422b2765acSPeter Maydell    # Management of section contents
1432b2765acSPeter Maydell    #
1442b2765acSPeter Maydell    def add_text(self, text):
1452b2765acSPeter Maydell        self._contents.append(text)
1462b2765acSPeter Maydell
1472b2765acSPeter Maydell    def contents(self):
1482b2765acSPeter Maydell        return '\n'.join(self._contents) + '\n'
1492b2765acSPeter Maydell
1502b2765acSPeter Maydell    # TODO: rename to emit_message after removal of kernel-doc.pl
1512b2765acSPeter Maydell    def emit_msg(self, log_msg, warning=True):
1522b2765acSPeter Maydell        """Emit a message"""
1532b2765acSPeter Maydell
1542b2765acSPeter Maydell        if not warning:
1552b2765acSPeter Maydell            self.config.log.info(log_msg)
1562b2765acSPeter Maydell            return
1572b2765acSPeter Maydell
1582b2765acSPeter Maydell        # Delegate warning output to output logic, as this way it
1592b2765acSPeter Maydell        # will report warnings/info only for symbols that are output
1602b2765acSPeter Maydell
1612b2765acSPeter Maydell        self.warnings.append(log_msg)
1622b2765acSPeter Maydell        return
1632b2765acSPeter Maydell
1642b2765acSPeter Maydell    #
1652b2765acSPeter Maydell    # Begin a new section.
1662b2765acSPeter Maydell    #
1672b2765acSPeter Maydell    def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False):
1682b2765acSPeter Maydell        if dump:
1692b2765acSPeter Maydell            self.dump_section(start_new = True)
1702b2765acSPeter Maydell        self.section = title
1712b2765acSPeter Maydell        self.new_start_line = line_no
1722b2765acSPeter Maydell
1732b2765acSPeter Maydell    def dump_section(self, start_new=True):
1742b2765acSPeter Maydell        """
1752b2765acSPeter Maydell        Dumps section contents to arrays/hashes intended for that purpose.
1762b2765acSPeter Maydell        """
1772b2765acSPeter Maydell        #
1782b2765acSPeter Maydell        # If we have accumulated no contents in the default ("description")
1792b2765acSPeter Maydell        # section, don't bother.
1802b2765acSPeter Maydell        #
1812b2765acSPeter Maydell        if self.section == SECTION_DEFAULT and not self._contents:
1822b2765acSPeter Maydell            return
1832b2765acSPeter Maydell        name = self.section
1842b2765acSPeter Maydell        contents = self.contents()
1852b2765acSPeter Maydell
1862b2765acSPeter Maydell        if type_param.match(name):
1872b2765acSPeter Maydell            name = type_param.group(1)
1882b2765acSPeter Maydell
1892b2765acSPeter Maydell            self.parameterdescs[name] = contents
1902b2765acSPeter Maydell            self.parameterdesc_start_lines[name] = self.new_start_line
1912b2765acSPeter Maydell
1922b2765acSPeter Maydell            self.new_start_line = 0
1932b2765acSPeter Maydell
1942b2765acSPeter Maydell        else:
1952b2765acSPeter Maydell            if name in self.sections and self.sections[name] != "":
1962b2765acSPeter Maydell                # Only warn on user-specified duplicate section names
1972b2765acSPeter Maydell                if name != SECTION_DEFAULT:
1982b2765acSPeter Maydell                    self.emit_msg(self.new_start_line,
1992b2765acSPeter Maydell                                  f"duplicate section name '{name}'\n")
2002b2765acSPeter Maydell                # Treat as a new paragraph - add a blank line
2012b2765acSPeter Maydell                self.sections[name] += '\n' + contents
2022b2765acSPeter Maydell            else:
2032b2765acSPeter Maydell                self.sections[name] = contents
2042b2765acSPeter Maydell                self.section_start_lines[name] = self.new_start_line
2052b2765acSPeter Maydell                self.new_start_line = 0
2062b2765acSPeter Maydell
2072b2765acSPeter Maydell#        self.config.log.debug("Section: %s : %s", name, pformat(vars(self)))
2082b2765acSPeter Maydell
2092b2765acSPeter Maydell        if start_new:
2102b2765acSPeter Maydell            self.section = SECTION_DEFAULT
2112b2765acSPeter Maydell            self._contents = []
2122b2765acSPeter Maydell
2132b2765acSPeter Maydell
2142b2765acSPeter Maydellclass KernelDoc:
2152b2765acSPeter Maydell    """
2162b2765acSPeter Maydell    Read a C language source or header FILE and extract embedded
2172b2765acSPeter Maydell    documentation comments.
2182b2765acSPeter Maydell    """
2192b2765acSPeter Maydell
2202b2765acSPeter Maydell    # Section names
2212b2765acSPeter Maydell
2222b2765acSPeter Maydell    section_context = "Context"
2232b2765acSPeter Maydell    section_return = "Return"
2242b2765acSPeter Maydell
2252b2765acSPeter Maydell    undescribed = "-- undescribed --"
2262b2765acSPeter Maydell
2272b2765acSPeter Maydell    def __init__(self, config, fname):
2282b2765acSPeter Maydell        """Initialize internal variables"""
2292b2765acSPeter Maydell
2302b2765acSPeter Maydell        self.fname = fname
2312b2765acSPeter Maydell        self.config = config
2322b2765acSPeter Maydell
2332b2765acSPeter Maydell        # Initial state for the state machines
2342b2765acSPeter Maydell        self.state = state.NORMAL
2352b2765acSPeter Maydell
2362b2765acSPeter Maydell        # Store entry currently being processed
2372b2765acSPeter Maydell        self.entry = None
2382b2765acSPeter Maydell
2392b2765acSPeter Maydell        # Place all potential outputs into an array
2402b2765acSPeter Maydell        self.entries = []
2412b2765acSPeter Maydell
2422b2765acSPeter Maydell        #
2432b2765acSPeter Maydell        # We need Python 3.7 for its "dicts remember the insertion
2442b2765acSPeter Maydell        # order" guarantee
2452b2765acSPeter Maydell        #
2462b2765acSPeter Maydell        if sys.version_info.major == 3 and sys.version_info.minor < 7:
2472b2765acSPeter Maydell            self.emit_msg(0,
2482b2765acSPeter Maydell                          'Python 3.7 or later is required for correct results')
2492b2765acSPeter Maydell
2502b2765acSPeter Maydell    def emit_msg(self, ln, msg, warning=True):
2512b2765acSPeter Maydell        """Emit a message"""
2522b2765acSPeter Maydell
2532b2765acSPeter Maydell        log_msg = f"{self.fname}:{ln} {msg}"
2542b2765acSPeter Maydell
2552b2765acSPeter Maydell        if self.entry:
2562b2765acSPeter Maydell            self.entry.emit_msg(log_msg, warning)
2572b2765acSPeter Maydell            return
2582b2765acSPeter Maydell
2592b2765acSPeter Maydell        if warning:
2602b2765acSPeter Maydell            self.config.log.warning(log_msg)
2612b2765acSPeter Maydell        else:
2622b2765acSPeter Maydell            self.config.log.info(log_msg)
2632b2765acSPeter Maydell
2642b2765acSPeter Maydell    def dump_section(self, start_new=True):
2652b2765acSPeter Maydell        """
2662b2765acSPeter Maydell        Dumps section contents to arrays/hashes intended for that purpose.
2672b2765acSPeter Maydell        """
2682b2765acSPeter Maydell
2692b2765acSPeter Maydell        if self.entry:
2702b2765acSPeter Maydell            self.entry.dump_section(start_new)
2712b2765acSPeter Maydell
2722b2765acSPeter Maydell    # TODO: rename it to store_declaration after removal of kernel-doc.pl
2732b2765acSPeter Maydell    def output_declaration(self, dtype, name, **args):
2742b2765acSPeter Maydell        """
2752b2765acSPeter Maydell        Stores the entry into an entry array.
2762b2765acSPeter Maydell
2772b2765acSPeter Maydell        The actual output and output filters will be handled elsewhere
2782b2765acSPeter Maydell        """
2792b2765acSPeter Maydell
2802b2765acSPeter Maydell        item = KdocItem(name, dtype, self.entry.declaration_start_line, **args)
2812b2765acSPeter Maydell        item.warnings = self.entry.warnings
2822b2765acSPeter Maydell
2832b2765acSPeter Maydell        # Drop empty sections
2842b2765acSPeter Maydell        # TODO: improve empty sections logic to emit warnings
2852b2765acSPeter Maydell        sections = self.entry.sections
2862b2765acSPeter Maydell        for section in ["Description", "Return"]:
2872b2765acSPeter Maydell            if section in sections and not sections[section].rstrip():
2882b2765acSPeter Maydell                del sections[section]
2892b2765acSPeter Maydell        item.set_sections(sections, self.entry.section_start_lines)
2902b2765acSPeter Maydell        item.set_params(self.entry.parameterlist, self.entry.parameterdescs,
2912b2765acSPeter Maydell                        self.entry.parametertypes,
2922b2765acSPeter Maydell                        self.entry.parameterdesc_start_lines)
2932b2765acSPeter Maydell        self.entries.append(item)
2942b2765acSPeter Maydell
2952b2765acSPeter Maydell        self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args))
2962b2765acSPeter Maydell
2972b2765acSPeter Maydell    def reset_state(self, ln):
2982b2765acSPeter Maydell        """
2992b2765acSPeter Maydell        Ancillary routine to create a new entry. It initializes all
3002b2765acSPeter Maydell        variables used by the state machine.
3012b2765acSPeter Maydell        """
3022b2765acSPeter Maydell
3032b2765acSPeter Maydell        self.entry = KernelEntry(self.config, ln)
3042b2765acSPeter Maydell
3052b2765acSPeter Maydell        # State flags
3062b2765acSPeter Maydell        self.state = state.NORMAL
3072b2765acSPeter Maydell
3082b2765acSPeter Maydell    def push_parameter(self, ln, decl_type, param, dtype,
3092b2765acSPeter Maydell                       org_arg, declaration_name):
3102b2765acSPeter Maydell        """
3112b2765acSPeter Maydell        Store parameters and their descriptions at self.entry.
3122b2765acSPeter Maydell        """
3132b2765acSPeter Maydell
3142b2765acSPeter Maydell        if self.entry.anon_struct_union and dtype == "" and param == "}":
3152b2765acSPeter Maydell            return  # Ignore the ending }; from anonymous struct/union
3162b2765acSPeter Maydell
3172b2765acSPeter Maydell        self.entry.anon_struct_union = False
3182b2765acSPeter Maydell
3192b2765acSPeter Maydell        param = KernRe(r'[\[\)].*').sub('', param, count=1)
3202b2765acSPeter Maydell
3212b2765acSPeter Maydell        if dtype == "" and param.endswith("..."):
3222b2765acSPeter Maydell            if KernRe(r'\w\.\.\.$').search(param):
3232b2765acSPeter Maydell                # For named variable parameters of the form `x...`,
3242b2765acSPeter Maydell                # remove the dots
3252b2765acSPeter Maydell                param = param[:-3]
3262b2765acSPeter Maydell            else:
3272b2765acSPeter Maydell                # Handles unnamed variable parameters
3282b2765acSPeter Maydell                param = "..."
3292b2765acSPeter Maydell
3302b2765acSPeter Maydell            if param not in self.entry.parameterdescs or \
3312b2765acSPeter Maydell                not self.entry.parameterdescs[param]:
3322b2765acSPeter Maydell
3332b2765acSPeter Maydell                self.entry.parameterdescs[param] = "variable arguments"
3342b2765acSPeter Maydell
3352b2765acSPeter Maydell        elif dtype == "" and (not param or param == "void"):
3362b2765acSPeter Maydell            param = "void"
3372b2765acSPeter Maydell            self.entry.parameterdescs[param] = "no arguments"
3382b2765acSPeter Maydell
3392b2765acSPeter Maydell        elif dtype == "" and param in ["struct", "union"]:
3402b2765acSPeter Maydell            # Handle unnamed (anonymous) union or struct
3412b2765acSPeter Maydell            dtype = param
3422b2765acSPeter Maydell            param = "{unnamed_" + param + "}"
3432b2765acSPeter Maydell            self.entry.parameterdescs[param] = "anonymous\n"
3442b2765acSPeter Maydell            self.entry.anon_struct_union = True
3452b2765acSPeter Maydell
3462b2765acSPeter Maydell        # Handle cache group enforcing variables: they do not need
3472b2765acSPeter Maydell        # to be described in header files
3482b2765acSPeter Maydell        elif "__cacheline_group" in param:
3492b2765acSPeter Maydell            # Ignore __cacheline_group_begin and __cacheline_group_end
3502b2765acSPeter Maydell            return
3512b2765acSPeter Maydell
3522b2765acSPeter Maydell        # Warn if parameter has no description
3532b2765acSPeter Maydell        # (but ignore ones starting with # as these are not parameters
3542b2765acSPeter Maydell        # but inline preprocessor statements)
3552b2765acSPeter Maydell        if param not in self.entry.parameterdescs and not param.startswith("#"):
3562b2765acSPeter Maydell            self.entry.parameterdescs[param] = self.undescribed
3572b2765acSPeter Maydell
3582b2765acSPeter Maydell            if "." not in param:
3592b2765acSPeter Maydell                if decl_type == 'function':
3602b2765acSPeter Maydell                    dname = f"{decl_type} parameter"
3612b2765acSPeter Maydell                else:
3622b2765acSPeter Maydell                    dname = f"{decl_type} member"
3632b2765acSPeter Maydell
3642b2765acSPeter Maydell                self.emit_msg(ln,
3652b2765acSPeter Maydell                              f"{dname} '{param}' not described in '{declaration_name}'")
3662b2765acSPeter Maydell
3672b2765acSPeter Maydell        # Strip spaces from param so that it is one continuous string on
3682b2765acSPeter Maydell        # parameterlist. This fixes a problem where check_sections()
3692b2765acSPeter Maydell        # cannot find a parameter like "addr[6 + 2]" because it actually
3702b2765acSPeter Maydell        # appears as "addr[6", "+", "2]" on the parameter list.
3712b2765acSPeter Maydell        # However, it's better to maintain the param string unchanged for
3722b2765acSPeter Maydell        # output, so just weaken the string compare in check_sections()
3732b2765acSPeter Maydell        # to ignore "[blah" in a parameter string.
3742b2765acSPeter Maydell
3752b2765acSPeter Maydell        self.entry.parameterlist.append(param)
3762b2765acSPeter Maydell        org_arg = KernRe(r'\s\s+').sub(' ', org_arg)
3772b2765acSPeter Maydell        self.entry.parametertypes[param] = org_arg
3782b2765acSPeter Maydell
3792b2765acSPeter Maydell
3802b2765acSPeter Maydell    def create_parameter_list(self, ln, decl_type, args,
3812b2765acSPeter Maydell                              splitter, declaration_name):
3822b2765acSPeter Maydell        """
3832b2765acSPeter Maydell        Creates a list of parameters, storing them at self.entry.
3842b2765acSPeter Maydell        """
3852b2765acSPeter Maydell
3862b2765acSPeter Maydell        # temporarily replace all commas inside function pointer definition
3872b2765acSPeter Maydell        arg_expr = KernRe(r'(\([^\),]+),')
3882b2765acSPeter Maydell        while arg_expr.search(args):
3892b2765acSPeter Maydell            args = arg_expr.sub(r"\1#", args)
3902b2765acSPeter Maydell
3912b2765acSPeter Maydell        for arg in args.split(splitter):
3922b2765acSPeter Maydell            # Strip comments
3932b2765acSPeter Maydell            arg = KernRe(r'\/\*.*\*\/').sub('', arg)
3942b2765acSPeter Maydell
3952b2765acSPeter Maydell            # Ignore argument attributes
3962b2765acSPeter Maydell            arg = KernRe(r'\sPOS0?\s').sub(' ', arg)
3972b2765acSPeter Maydell
3982b2765acSPeter Maydell            # Strip leading/trailing spaces
3992b2765acSPeter Maydell            arg = arg.strip()
4002b2765acSPeter Maydell            arg = KernRe(r'\s+').sub(' ', arg, count=1)
4012b2765acSPeter Maydell
4022b2765acSPeter Maydell            if arg.startswith('#'):
4032b2765acSPeter Maydell                # Treat preprocessor directive as a typeless variable just to fill
4042b2765acSPeter Maydell                # corresponding data structures "correctly". Catch it later in
4052b2765acSPeter Maydell                # output_* subs.
4062b2765acSPeter Maydell
4072b2765acSPeter Maydell                # Treat preprocessor directive as a typeless variable
4082b2765acSPeter Maydell                self.push_parameter(ln, decl_type, arg, "",
4092b2765acSPeter Maydell                                    "", declaration_name)
4102b2765acSPeter Maydell
4112b2765acSPeter Maydell            elif KernRe(r'\(.+\)\s*\(').search(arg):
4122b2765acSPeter Maydell                # Pointer-to-function
4132b2765acSPeter Maydell
4142b2765acSPeter Maydell                arg = arg.replace('#', ',')
4152b2765acSPeter Maydell
4162b2765acSPeter Maydell                r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)')
4172b2765acSPeter Maydell                if r.match(arg):
4182b2765acSPeter Maydell                    param = r.group(1)
4192b2765acSPeter Maydell                else:
4202b2765acSPeter Maydell                    self.emit_msg(ln, f"Invalid param: {arg}")
4212b2765acSPeter Maydell                    param = arg
4222b2765acSPeter Maydell
4232b2765acSPeter Maydell                dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
4242b2765acSPeter Maydell                self.push_parameter(ln, decl_type, param, dtype,
4252b2765acSPeter Maydell                                    arg, declaration_name)
4262b2765acSPeter Maydell
4272b2765acSPeter Maydell            elif KernRe(r'\(.+\)\s*\[').search(arg):
4282b2765acSPeter Maydell                # Array-of-pointers
4292b2765acSPeter Maydell
4302b2765acSPeter Maydell                arg = arg.replace('#', ',')
4312b2765acSPeter Maydell                r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)')
4322b2765acSPeter Maydell                if r.match(arg):
4332b2765acSPeter Maydell                    param = r.group(1)
4342b2765acSPeter Maydell                else:
4352b2765acSPeter Maydell                    self.emit_msg(ln, f"Invalid param: {arg}")
4362b2765acSPeter Maydell                    param = arg
4372b2765acSPeter Maydell
4382b2765acSPeter Maydell                dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
4392b2765acSPeter Maydell
4402b2765acSPeter Maydell                self.push_parameter(ln, decl_type, param, dtype,
4412b2765acSPeter Maydell                                    arg, declaration_name)
4422b2765acSPeter Maydell
4432b2765acSPeter Maydell            elif arg:
4442b2765acSPeter Maydell                arg = KernRe(r'\s*:\s*').sub(":", arg)
4452b2765acSPeter Maydell                arg = KernRe(r'\s*\[').sub('[', arg)
4462b2765acSPeter Maydell
4472b2765acSPeter Maydell                args = KernRe(r'\s*,\s*').split(arg)
4482b2765acSPeter Maydell                if args[0] and '*' in args[0]:
4492b2765acSPeter Maydell                    args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])
4502b2765acSPeter Maydell
4512b2765acSPeter Maydell                first_arg = []
4522b2765acSPeter Maydell                r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$')
4532b2765acSPeter Maydell                if args[0] and r.match(args[0]):
4542b2765acSPeter Maydell                    args.pop(0)
4552b2765acSPeter Maydell                    first_arg.extend(r.group(1))
4562b2765acSPeter Maydell                    first_arg.append(r.group(2))
4572b2765acSPeter Maydell                else:
4582b2765acSPeter Maydell                    first_arg = KernRe(r'\s+').split(args.pop(0))
4592b2765acSPeter Maydell
4602b2765acSPeter Maydell                args.insert(0, first_arg.pop())
4612b2765acSPeter Maydell                dtype = ' '.join(first_arg)
4622b2765acSPeter Maydell
4632b2765acSPeter Maydell                for param in args:
4642b2765acSPeter Maydell                    if KernRe(r'^(\*+)\s*(.*)').match(param):
4652b2765acSPeter Maydell                        r = KernRe(r'^(\*+)\s*(.*)')
4662b2765acSPeter Maydell                        if not r.match(param):
4672b2765acSPeter Maydell                            self.emit_msg(ln, f"Invalid param: {param}")
4682b2765acSPeter Maydell                            continue
4692b2765acSPeter Maydell
4702b2765acSPeter Maydell                        param = r.group(1)
4712b2765acSPeter Maydell
4722b2765acSPeter Maydell                        self.push_parameter(ln, decl_type, r.group(2),
4732b2765acSPeter Maydell                                            f"{dtype} {r.group(1)}",
4742b2765acSPeter Maydell                                            arg, declaration_name)
4752b2765acSPeter Maydell
4762b2765acSPeter Maydell                    elif KernRe(r'(.*?):(\w+)').search(param):
4772b2765acSPeter Maydell                        r = KernRe(r'(.*?):(\w+)')
4782b2765acSPeter Maydell                        if not r.match(param):
4792b2765acSPeter Maydell                            self.emit_msg(ln, f"Invalid param: {param}")
4802b2765acSPeter Maydell                            continue
4812b2765acSPeter Maydell
4822b2765acSPeter Maydell                        if dtype != "":  # Skip unnamed bit-fields
4832b2765acSPeter Maydell                            self.push_parameter(ln, decl_type, r.group(1),
4842b2765acSPeter Maydell                                                f"{dtype}:{r.group(2)}",
4852b2765acSPeter Maydell                                                arg, declaration_name)
4862b2765acSPeter Maydell                    else:
4872b2765acSPeter Maydell                        self.push_parameter(ln, decl_type, param, dtype,
4882b2765acSPeter Maydell                                            arg, declaration_name)
4892b2765acSPeter Maydell
4902b2765acSPeter Maydell    def check_sections(self, ln, decl_name, decl_type):
4912b2765acSPeter Maydell        """
4922b2765acSPeter Maydell        Check for errors inside sections, emitting warnings if not found
4932b2765acSPeter Maydell        parameters are described.
4942b2765acSPeter Maydell        """
4952b2765acSPeter Maydell        for section in self.entry.sections:
4962b2765acSPeter Maydell            if section not in self.entry.parameterlist and \
4972b2765acSPeter Maydell               not known_sections.search(section):
4982b2765acSPeter Maydell                if decl_type == 'function':
4992b2765acSPeter Maydell                    dname = f"{decl_type} parameter"
5002b2765acSPeter Maydell                else:
5012b2765acSPeter Maydell                    dname = f"{decl_type} member"
5022b2765acSPeter Maydell                self.emit_msg(ln,
5032b2765acSPeter Maydell                              f"Excess {dname} '{section}' description in '{decl_name}'")
5042b2765acSPeter Maydell
5052b2765acSPeter Maydell    def check_return_section(self, ln, declaration_name, return_type):
5062b2765acSPeter Maydell        """
5072b2765acSPeter Maydell        If the function doesn't return void, warns about the lack of a
5082b2765acSPeter Maydell        return description.
5092b2765acSPeter Maydell        """
5102b2765acSPeter Maydell
5112b2765acSPeter Maydell        if not self.config.wreturn:
5122b2765acSPeter Maydell            return
5132b2765acSPeter Maydell
5142b2765acSPeter Maydell        # Ignore an empty return type (It's a macro)
5152b2765acSPeter Maydell        # Ignore functions with a "void" return type (but not "void *")
5162b2765acSPeter Maydell        if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type):
5172b2765acSPeter Maydell            return
5182b2765acSPeter Maydell
5192b2765acSPeter Maydell        if not self.entry.sections.get("Return", None):
5202b2765acSPeter Maydell            self.emit_msg(ln,
5212b2765acSPeter Maydell                          f"No description found for return value of '{declaration_name}'")
5222b2765acSPeter Maydell
5232b2765acSPeter Maydell    def dump_struct(self, ln, proto):
5242b2765acSPeter Maydell        """
5252b2765acSPeter Maydell        Store an entry for an struct or union
5262b2765acSPeter Maydell        """
5272b2765acSPeter Maydell
5282b2765acSPeter Maydell        type_pattern = r'(struct|union)'
5292b2765acSPeter Maydell
5302b2765acSPeter Maydell        qualifiers = [
5312b2765acSPeter Maydell            "__attribute__",
5322b2765acSPeter Maydell            "__packed",
5332b2765acSPeter Maydell            "__aligned",
5342b2765acSPeter Maydell            "____cacheline_aligned_in_smp",
5352b2765acSPeter Maydell            "____cacheline_aligned",
5362b2765acSPeter Maydell        ]
5372b2765acSPeter Maydell
5382b2765acSPeter Maydell        definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"
5392b2765acSPeter Maydell        struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)')
5402b2765acSPeter Maydell
5412b2765acSPeter Maydell        # Extract struct/union definition
5422b2765acSPeter Maydell        members = None
5432b2765acSPeter Maydell        declaration_name = None
5442b2765acSPeter Maydell        decl_type = None
5452b2765acSPeter Maydell
5462b2765acSPeter Maydell        r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body)
5472b2765acSPeter Maydell        if r.search(proto):
5482b2765acSPeter Maydell            decl_type = r.group(1)
5492b2765acSPeter Maydell            declaration_name = r.group(2)
5502b2765acSPeter Maydell            members = r.group(3)
5512b2765acSPeter Maydell        else:
5522b2765acSPeter Maydell            r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')
5532b2765acSPeter Maydell
5542b2765acSPeter Maydell            if r.search(proto):
5552b2765acSPeter Maydell                decl_type = r.group(1)
5562b2765acSPeter Maydell                declaration_name = r.group(3)
5572b2765acSPeter Maydell                members = r.group(2)
5582b2765acSPeter Maydell
5592b2765acSPeter Maydell        if not members:
5602b2765acSPeter Maydell            self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!")
5612b2765acSPeter Maydell            return
5622b2765acSPeter Maydell
5632b2765acSPeter Maydell        if self.entry.identifier != declaration_name:
5642b2765acSPeter Maydell            self.emit_msg(ln,
5652b2765acSPeter Maydell                          f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n")
5662b2765acSPeter Maydell            return
5672b2765acSPeter Maydell
5682b2765acSPeter Maydell        args_pattern = r'([^,)]+)'
5692b2765acSPeter Maydell
5702b2765acSPeter Maydell        sub_prefixes = [
5712b2765acSPeter Maydell            (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''),
5722b2765acSPeter Maydell            (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''),
5732b2765acSPeter Maydell
5742b2765acSPeter Maydell            # Strip comments
5752b2765acSPeter Maydell            (KernRe(r'\/\*.*?\*\/', re.S), ''),
5762b2765acSPeter Maydell
5772b2765acSPeter Maydell            # Strip attributes
5782b2765acSPeter Maydell            (attribute, ' '),
5792b2765acSPeter Maydell            (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
5802b2765acSPeter Maydell            (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
5812b2765acSPeter Maydell            (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
5822b2765acSPeter Maydell            (KernRe(r'\s*__packed\s*', re.S), ' '),
5832b2765acSPeter Maydell            (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
5842b2765acSPeter Maydell            (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '),
5852b2765acSPeter Maydell            (KernRe(r'\s*____cacheline_aligned', re.S), ' '),
5862b2765acSPeter Maydell
5872b2765acSPeter Maydell            # Unwrap struct_group macros based on this definition:
5882b2765acSPeter Maydell            # __struct_group(TAG, NAME, ATTRS, MEMBERS...)
5892b2765acSPeter Maydell            # which has variants like: struct_group(NAME, MEMBERS...)
5902b2765acSPeter Maydell            # Only MEMBERS arguments require documentation.
5912b2765acSPeter Maydell            #
5922b2765acSPeter Maydell            # Parsing them happens on two steps:
5932b2765acSPeter Maydell            #
5942b2765acSPeter Maydell            # 1. drop struct group arguments that aren't at MEMBERS,
5952b2765acSPeter Maydell            #    storing them as STRUCT_GROUP(MEMBERS)
5962b2765acSPeter Maydell            #
5972b2765acSPeter Maydell            # 2. remove STRUCT_GROUP() ancillary macro.
5982b2765acSPeter Maydell            #
5992b2765acSPeter Maydell            # The original logic used to remove STRUCT_GROUP() using an
6002b2765acSPeter Maydell            # advanced regex:
6012b2765acSPeter Maydell            #
6022b2765acSPeter Maydell            #   \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
6032b2765acSPeter Maydell            #
6042b2765acSPeter Maydell            # with two patterns that are incompatible with
6052b2765acSPeter Maydell            # Python re module, as it has:
6062b2765acSPeter Maydell            #
6072b2765acSPeter Maydell            #   - a recursive pattern: (?1)
6082b2765acSPeter Maydell            #   - an atomic grouping: (?>...)
6092b2765acSPeter Maydell            #
6102b2765acSPeter Maydell            # I tried a simpler version: but it didn't work either:
6112b2765acSPeter Maydell            #   \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
6122b2765acSPeter Maydell            #
6132b2765acSPeter Maydell            # As it doesn't properly match the end parenthesis on some cases.
6142b2765acSPeter Maydell            #
6152b2765acSPeter Maydell            # So, a better solution was crafted: there's now a NestedMatch
6162b2765acSPeter Maydell            # class that ensures that delimiters after a search are properly
6172b2765acSPeter Maydell            # matched. So, the implementation to drop STRUCT_GROUP() will be
6182b2765acSPeter Maydell            # handled in separate.
6192b2765acSPeter Maydell
6202b2765acSPeter Maydell            (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('),
6212b2765acSPeter Maydell            (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('),
6222b2765acSPeter Maydell            (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('),
6232b2765acSPeter Maydell            (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('),
6242b2765acSPeter Maydell
6252b2765acSPeter Maydell            # Replace macros
6262b2765acSPeter Maydell            #
6272b2765acSPeter Maydell            # TODO: use NestedMatch for FOO($1, $2, ...) matches
6282b2765acSPeter Maydell            #
6292b2765acSPeter Maydell            # it is better to also move those to the NestedMatch logic,
6302b2765acSPeter Maydell            # to ensure that parenthesis will be properly matched.
6312b2765acSPeter Maydell
6322b2765acSPeter Maydell            (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
6332b2765acSPeter Maydell            (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
6342b2765acSPeter Maydell            (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
6352b2765acSPeter Maydell            (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
6362b2765acSPeter Maydell            (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
6372b2765acSPeter Maydell            (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
6382b2765acSPeter Maydell            (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'),
6392b2765acSPeter Maydell            (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'),
6402b2765acSPeter Maydell            (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'),
6412b2765acSPeter Maydell            (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'),
6422b2765acSPeter Maydell        ]
6432b2765acSPeter Maydell
6442b2765acSPeter Maydell        # Regexes here are guaranteed to have the end limiter matching
6452b2765acSPeter Maydell        # the start delimiter. Yet, right now, only one replace group
6462b2765acSPeter Maydell        # is allowed.
6472b2765acSPeter Maydell
6482b2765acSPeter Maydell        sub_nested_prefixes = [
6492b2765acSPeter Maydell            (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
6502b2765acSPeter Maydell        ]
6512b2765acSPeter Maydell
6522b2765acSPeter Maydell        for search, sub in sub_prefixes:
6532b2765acSPeter Maydell            members = search.sub(sub, members)
6542b2765acSPeter Maydell
6552b2765acSPeter Maydell        nested = NestedMatch()
6562b2765acSPeter Maydell
6572b2765acSPeter Maydell        for search, sub in sub_nested_prefixes:
6582b2765acSPeter Maydell            members = nested.sub(search, sub, members)
6592b2765acSPeter Maydell
6602b2765acSPeter Maydell        # Keeps the original declaration as-is
6612b2765acSPeter Maydell        declaration = members
6622b2765acSPeter Maydell
6632b2765acSPeter Maydell        # Split nested struct/union elements
6642b2765acSPeter Maydell        #
6652b2765acSPeter Maydell        # This loop was simpler at the original kernel-doc perl version, as
6662b2765acSPeter Maydell        #   while ($members =~ m/$struct_members/) { ... }
6672b2765acSPeter Maydell        # reads 'members' string on each interaction.
6682b2765acSPeter Maydell        #
6692b2765acSPeter Maydell        # Python behavior is different: it parses 'members' only once,
6702b2765acSPeter Maydell        # creating a list of tuples from the first interaction.
6712b2765acSPeter Maydell        #
6722b2765acSPeter Maydell        # On other words, this won't get nested structs.
6732b2765acSPeter Maydell        #
6742b2765acSPeter Maydell        # So, we need to have an extra loop on Python to override such
6752b2765acSPeter Maydell        # re limitation.
6762b2765acSPeter Maydell
6772b2765acSPeter Maydell        while True:
6782b2765acSPeter Maydell            tuples = struct_members.findall(members)
6792b2765acSPeter Maydell            if not tuples:
6802b2765acSPeter Maydell                break
6812b2765acSPeter Maydell
6822b2765acSPeter Maydell            for t in tuples:
6832b2765acSPeter Maydell                newmember = ""
6842b2765acSPeter Maydell                maintype = t[0]
6852b2765acSPeter Maydell                s_ids = t[5]
6862b2765acSPeter Maydell                content = t[3]
6872b2765acSPeter Maydell
6882b2765acSPeter Maydell                oldmember = "".join(t)
6892b2765acSPeter Maydell
6902b2765acSPeter Maydell                for s_id in s_ids.split(','):
6912b2765acSPeter Maydell                    s_id = s_id.strip()
6922b2765acSPeter Maydell
6932b2765acSPeter Maydell                    newmember += f"{maintype} {s_id}; "
6942b2765acSPeter Maydell                    s_id = KernRe(r'[:\[].*').sub('', s_id)
6952b2765acSPeter Maydell                    s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)
6962b2765acSPeter Maydell
6972b2765acSPeter Maydell                    for arg in content.split(';'):
6982b2765acSPeter Maydell                        arg = arg.strip()
6992b2765acSPeter Maydell
7002b2765acSPeter Maydell                        if not arg:
7012b2765acSPeter Maydell                            continue
7022b2765acSPeter Maydell
7032b2765acSPeter Maydell                        r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)')
7042b2765acSPeter Maydell                        if r.match(arg):
7052b2765acSPeter Maydell                            # Pointer-to-function
7062b2765acSPeter Maydell                            dtype = r.group(1)
7072b2765acSPeter Maydell                            name = r.group(2)
7082b2765acSPeter Maydell                            extra = r.group(3)
7092b2765acSPeter Maydell
7102b2765acSPeter Maydell                            if not name:
7112b2765acSPeter Maydell                                continue
7122b2765acSPeter Maydell
7132b2765acSPeter Maydell                            if not s_id:
7142b2765acSPeter Maydell                                # Anonymous struct/union
7152b2765acSPeter Maydell                                newmember += f"{dtype}{name}{extra}; "
7162b2765acSPeter Maydell                            else:
7172b2765acSPeter Maydell                                newmember += f"{dtype}{s_id}.{name}{extra}; "
7182b2765acSPeter Maydell
7192b2765acSPeter Maydell                        else:
7202b2765acSPeter Maydell                            arg = arg.strip()
7212b2765acSPeter Maydell                            # Handle bitmaps
7222b2765acSPeter Maydell                            arg = KernRe(r':\s*\d+\s*').sub('', arg)
7232b2765acSPeter Maydell
7242b2765acSPeter Maydell                            # Handle arrays
7252b2765acSPeter Maydell                            arg = KernRe(r'\[.*\]').sub('', arg)
7262b2765acSPeter Maydell
7272b2765acSPeter Maydell                            # Handle multiple IDs
7282b2765acSPeter Maydell                            arg = KernRe(r'\s*,\s*').sub(',', arg)
7292b2765acSPeter Maydell
7302b2765acSPeter Maydell                            r = KernRe(r'(.*)\s+([\S+,]+)')
7312b2765acSPeter Maydell
7322b2765acSPeter Maydell                            if r.search(arg):
7332b2765acSPeter Maydell                                dtype = r.group(1)
7342b2765acSPeter Maydell                                names = r.group(2)
7352b2765acSPeter Maydell                            else:
7362b2765acSPeter Maydell                                newmember += f"{arg}; "
7372b2765acSPeter Maydell                                continue
7382b2765acSPeter Maydell
7392b2765acSPeter Maydell                            for name in names.split(','):
7402b2765acSPeter Maydell                                name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip()
7412b2765acSPeter Maydell
7422b2765acSPeter Maydell                                if not name:
7432b2765acSPeter Maydell                                    continue
7442b2765acSPeter Maydell
7452b2765acSPeter Maydell                                if not s_id:
7462b2765acSPeter Maydell                                    # Anonymous struct/union
7472b2765acSPeter Maydell                                    newmember += f"{dtype} {name}; "
7482b2765acSPeter Maydell                                else:
7492b2765acSPeter Maydell                                    newmember += f"{dtype} {s_id}.{name}; "
7502b2765acSPeter Maydell
7512b2765acSPeter Maydell                members = members.replace(oldmember, newmember)
7522b2765acSPeter Maydell
7532b2765acSPeter Maydell        # Ignore other nested elements, like enums
7542b2765acSPeter Maydell        members = re.sub(r'(\{[^\{\}]*\})', '', members)
7552b2765acSPeter Maydell
7562b2765acSPeter Maydell        self.create_parameter_list(ln, decl_type, members, ';',
7572b2765acSPeter Maydell                                   declaration_name)
7582b2765acSPeter Maydell        self.check_sections(ln, declaration_name, decl_type)
7592b2765acSPeter Maydell
7602b2765acSPeter Maydell        # Adjust declaration for better display
7612b2765acSPeter Maydell        declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)
7622b2765acSPeter Maydell        declaration = KernRe(r'\}\s+;').sub('};', declaration)
7632b2765acSPeter Maydell
7642b2765acSPeter Maydell        # Better handle inlined enums
7652b2765acSPeter Maydell        while True:
7662b2765acSPeter Maydell            r = KernRe(r'(enum\s+\{[^\}]+),([^\n])')
7672b2765acSPeter Maydell            if not r.search(declaration):
7682b2765acSPeter Maydell                break
7692b2765acSPeter Maydell
7702b2765acSPeter Maydell            declaration = r.sub(r'\1,\n\2', declaration)
7712b2765acSPeter Maydell
7722b2765acSPeter Maydell        def_args = declaration.split('\n')
7732b2765acSPeter Maydell        level = 1
7742b2765acSPeter Maydell        declaration = ""
7752b2765acSPeter Maydell        for clause in def_args:
7762b2765acSPeter Maydell
7772b2765acSPeter Maydell            clause = clause.strip()
7782b2765acSPeter Maydell            clause = KernRe(r'\s+').sub(' ', clause, count=1)
7792b2765acSPeter Maydell
7802b2765acSPeter Maydell            if not clause:
7812b2765acSPeter Maydell                continue
7822b2765acSPeter Maydell
7832b2765acSPeter Maydell            if '}' in clause and level > 1:
7842b2765acSPeter Maydell                level -= 1
7852b2765acSPeter Maydell
7862b2765acSPeter Maydell            if not KernRe(r'^\s*#').match(clause):
7872b2765acSPeter Maydell                declaration += "\t" * level
7882b2765acSPeter Maydell
7892b2765acSPeter Maydell            declaration += "\t" + clause + "\n"
7902b2765acSPeter Maydell            if "{" in clause and "}" not in clause:
7912b2765acSPeter Maydell                level += 1
7922b2765acSPeter Maydell
7932b2765acSPeter Maydell        self.output_declaration(decl_type, declaration_name,
7942b2765acSPeter Maydell                                definition=declaration,
7952b2765acSPeter Maydell                                purpose=self.entry.declaration_purpose)
7962b2765acSPeter Maydell
7972b2765acSPeter Maydell    def dump_enum(self, ln, proto):
7982b2765acSPeter Maydell        """
7992b2765acSPeter Maydell        Stores an enum inside self.entries array.
8002b2765acSPeter Maydell        """
8012b2765acSPeter Maydell
8022b2765acSPeter Maydell        # Ignore members marked private
8032b2765acSPeter Maydell        proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto)
8042b2765acSPeter Maydell        proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto)
8052b2765acSPeter Maydell
8062b2765acSPeter Maydell        # Strip comments
8072b2765acSPeter Maydell        proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto)
8082b2765acSPeter Maydell
8092b2765acSPeter Maydell        # Strip #define macros inside enums
8102b2765acSPeter Maydell        proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)
8112b2765acSPeter Maydell
8122b2765acSPeter Maydell        #
8132b2765acSPeter Maydell        # Parse out the name and members of the enum.  Typedef form first.
8142b2765acSPeter Maydell        #
8152b2765acSPeter Maydell        r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')
8162b2765acSPeter Maydell        if r.search(proto):
8172b2765acSPeter Maydell            declaration_name = r.group(2)
8182b2765acSPeter Maydell            members = r.group(1).rstrip()
8192b2765acSPeter Maydell        #
8202b2765acSPeter Maydell        # Failing that, look for a straight enum
8212b2765acSPeter Maydell        #
8222b2765acSPeter Maydell        else:
8232b2765acSPeter Maydell            r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}')
8242b2765acSPeter Maydell            if r.match(proto):
8252b2765acSPeter Maydell                declaration_name = r.group(1)
8262b2765acSPeter Maydell                members = r.group(2).rstrip()
8272b2765acSPeter Maydell        #
8282b2765acSPeter Maydell        # OK, this isn't going to work.
8292b2765acSPeter Maydell        #
8302b2765acSPeter Maydell            else:
8312b2765acSPeter Maydell                self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")
8322b2765acSPeter Maydell                return
8332b2765acSPeter Maydell        #
8342b2765acSPeter Maydell        # Make sure we found what we were expecting.
8352b2765acSPeter Maydell        #
8362b2765acSPeter Maydell        if self.entry.identifier != declaration_name:
8372b2765acSPeter Maydell            if self.entry.identifier == "":
8382b2765acSPeter Maydell                self.emit_msg(ln,
8392b2765acSPeter Maydell                              f"{proto}: wrong kernel-doc identifier on prototype")
8402b2765acSPeter Maydell            else:
8412b2765acSPeter Maydell                self.emit_msg(ln,
8422b2765acSPeter Maydell                              f"expecting prototype for enum {self.entry.identifier}. "
8432b2765acSPeter Maydell                              f"Prototype was for enum {declaration_name} instead")
8442b2765acSPeter Maydell            return
8452b2765acSPeter Maydell
8462b2765acSPeter Maydell        if not declaration_name:
8472b2765acSPeter Maydell            declaration_name = "(anonymous)"
8482b2765acSPeter Maydell        #
8492b2765acSPeter Maydell        # Parse out the name of each enum member, and verify that we
8502b2765acSPeter Maydell        # have a description for it.
8512b2765acSPeter Maydell        #
8522b2765acSPeter Maydell        member_set = set()
8532b2765acSPeter Maydell        members = KernRe(r'\([^;)]*\)').sub('', members)
8542b2765acSPeter Maydell        for arg in members.split(','):
8552b2765acSPeter Maydell            if not arg:
8562b2765acSPeter Maydell                continue
8572b2765acSPeter Maydell            arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg)
8582b2765acSPeter Maydell            self.entry.parameterlist.append(arg)
8592b2765acSPeter Maydell            if arg not in self.entry.parameterdescs:
8602b2765acSPeter Maydell                self.entry.parameterdescs[arg] = self.undescribed
8612b2765acSPeter Maydell                self.emit_msg(ln,
8622b2765acSPeter Maydell                              f"Enum value '{arg}' not described in enum '{declaration_name}'")
8632b2765acSPeter Maydell            member_set.add(arg)
8642b2765acSPeter Maydell        #
8652b2765acSPeter Maydell        # Ensure that every described member actually exists in the enum.
8662b2765acSPeter Maydell        #
8672b2765acSPeter Maydell        for k in self.entry.parameterdescs:
8682b2765acSPeter Maydell            if k not in member_set:
8692b2765acSPeter Maydell                self.emit_msg(ln,
8702b2765acSPeter Maydell                              f"Excess enum value '%{k}' description in '{declaration_name}'")
8712b2765acSPeter Maydell
8722b2765acSPeter Maydell        self.output_declaration('enum', declaration_name,
8732b2765acSPeter Maydell                                purpose=self.entry.declaration_purpose)
8742b2765acSPeter Maydell
8752b2765acSPeter Maydell    def dump_declaration(self, ln, prototype):
8762b2765acSPeter Maydell        """
8772b2765acSPeter Maydell        Stores a data declaration inside self.entries array.
8782b2765acSPeter Maydell        """
8792b2765acSPeter Maydell
8802b2765acSPeter Maydell        if self.entry.decl_type == "enum":
8812b2765acSPeter Maydell            self.dump_enum(ln, prototype)
8822b2765acSPeter Maydell        elif self.entry.decl_type == "typedef":
8832b2765acSPeter Maydell            self.dump_typedef(ln, prototype)
8842b2765acSPeter Maydell        elif self.entry.decl_type in ["union", "struct"]:
8852b2765acSPeter Maydell            self.dump_struct(ln, prototype)
8862b2765acSPeter Maydell        else:
8872b2765acSPeter Maydell            # This would be a bug
8882b2765acSPeter Maydell            self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')
8892b2765acSPeter Maydell
8902b2765acSPeter Maydell    def dump_function(self, ln, prototype):
8912b2765acSPeter Maydell        """
8922b2765acSPeter Maydell        Stores a function of function macro inside self.entries array.
8932b2765acSPeter Maydell        """
8942b2765acSPeter Maydell
8952b2765acSPeter Maydell        func_macro = False
8962b2765acSPeter Maydell        return_type = ''
8972b2765acSPeter Maydell        decl_type = 'function'
8982b2765acSPeter Maydell
8992b2765acSPeter Maydell        # Prefixes that would be removed
9002b2765acSPeter Maydell        sub_prefixes = [
9012b2765acSPeter Maydell            (r"^static +", "", 0),
9022b2765acSPeter Maydell            (r"^extern +", "", 0),
9032b2765acSPeter Maydell            (r"^asmlinkage +", "", 0),
9042b2765acSPeter Maydell            (r"^inline +", "", 0),
9052b2765acSPeter Maydell            (r"^__inline__ +", "", 0),
9062b2765acSPeter Maydell            (r"^__inline +", "", 0),
9072b2765acSPeter Maydell            (r"^__always_inline +", "", 0),
9082b2765acSPeter Maydell            (r"^noinline +", "", 0),
9092b2765acSPeter Maydell            (r"^__FORTIFY_INLINE +", "", 0),
910*7f58572cSPeter Maydell            (r"QEMU_[A-Z_]+ +", "", 0),
9112b2765acSPeter Maydell            (r"__init +", "", 0),
9122b2765acSPeter Maydell            (r"__init_or_module +", "", 0),
9132b2765acSPeter Maydell            (r"__deprecated +", "", 0),
9142b2765acSPeter Maydell            (r"__flatten +", "", 0),
9152b2765acSPeter Maydell            (r"__meminit +", "", 0),
9162b2765acSPeter Maydell            (r"__must_check +", "", 0),
9172b2765acSPeter Maydell            (r"__weak +", "", 0),
9182b2765acSPeter Maydell            (r"__sched +", "", 0),
9192b2765acSPeter Maydell            (r"_noprof", "", 0),
9202b2765acSPeter Maydell            (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0),
9212b2765acSPeter Maydell            (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0),
9222b2765acSPeter Maydell            (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0),
9232b2765acSPeter Maydell            (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0),
9242b2765acSPeter Maydell            (r"__attribute_const__ +", "", 0),
9252b2765acSPeter Maydell
9262b2765acSPeter Maydell            # It seems that Python support for re.X is broken:
9272b2765acSPeter Maydell            # At least for me (Python 3.13), this didn't work
9282b2765acSPeter Maydell#            (r"""
9292b2765acSPeter Maydell#              __attribute__\s*\(\(
9302b2765acSPeter Maydell#                (?:
9312b2765acSPeter Maydell#                    [\w\s]+          # attribute name
9322b2765acSPeter Maydell#                    (?:\([^)]*\))?   # attribute arguments
9332b2765acSPeter Maydell#                    \s*,?            # optional comma at the end
9342b2765acSPeter Maydell#                )+
9352b2765acSPeter Maydell#              \)\)\s+
9362b2765acSPeter Maydell#             """, "", re.X),
9372b2765acSPeter Maydell
9382b2765acSPeter Maydell            # So, remove whitespaces and comments from it
9392b2765acSPeter Maydell            (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0),
9402b2765acSPeter Maydell        ]
9412b2765acSPeter Maydell
9422b2765acSPeter Maydell        for search, sub, flags in sub_prefixes:
9432b2765acSPeter Maydell            prototype = KernRe(search, flags).sub(sub, prototype)
9442b2765acSPeter Maydell
9452b2765acSPeter Maydell        # Macros are a special case, as they change the prototype format
9462b2765acSPeter Maydell        new_proto = KernRe(r"^#\s*define\s+").sub("", prototype)
9472b2765acSPeter Maydell        if new_proto != prototype:
9482b2765acSPeter Maydell            is_define_proto = True
9492b2765acSPeter Maydell            prototype = new_proto
9502b2765acSPeter Maydell        else:
9512b2765acSPeter Maydell            is_define_proto = False
9522b2765acSPeter Maydell
9532b2765acSPeter Maydell        # Yes, this truly is vile.  We are looking for:
9542b2765acSPeter Maydell        # 1. Return type (may be nothing if we're looking at a macro)
9552b2765acSPeter Maydell        # 2. Function name
9562b2765acSPeter Maydell        # 3. Function parameters.
9572b2765acSPeter Maydell        #
9582b2765acSPeter Maydell        # All the while we have to watch out for function pointer parameters
9592b2765acSPeter Maydell        # (which IIRC is what the two sections are for), C types (these
9602b2765acSPeter Maydell        # regexps don't even start to express all the possibilities), and
9612b2765acSPeter Maydell        # so on.
9622b2765acSPeter Maydell        #
9632b2765acSPeter Maydell        # If you mess with these regexps, it's a good idea to check that
9642b2765acSPeter Maydell        # the following functions' documentation still comes out right:
9652b2765acSPeter Maydell        # - parport_register_device (function pointer parameters)
9662b2765acSPeter Maydell        # - atomic_set (macro)
9672b2765acSPeter Maydell        # - pci_match_device, __copy_to_user (long return type)
9682b2765acSPeter Maydell
9692b2765acSPeter Maydell        name = r'[a-zA-Z0-9_~:]+'
9702b2765acSPeter Maydell        prototype_end1 = r'[^\(]*'
9712b2765acSPeter Maydell        prototype_end2 = r'[^\{]*'
9722b2765acSPeter Maydell        prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)'
9732b2765acSPeter Maydell
9742b2765acSPeter Maydell        # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group.
9752b2765acSPeter Maydell        # So, this needs to be mapped in Python with (?:...)? or (?:...)+
9762b2765acSPeter Maydell
9772b2765acSPeter Maydell        type1 = r'(?:[\w\s]+)?'
9782b2765acSPeter Maydell        type2 = r'(?:[\w\s]+\*+)+'
9792b2765acSPeter Maydell
9802b2765acSPeter Maydell        found = False
9812b2765acSPeter Maydell
9822b2765acSPeter Maydell        if is_define_proto:
9832b2765acSPeter Maydell            r = KernRe(r'^()(' + name + r')\s+')
9842b2765acSPeter Maydell
9852b2765acSPeter Maydell            if r.search(prototype):
9862b2765acSPeter Maydell                return_type = ''
9872b2765acSPeter Maydell                declaration_name = r.group(2)
9882b2765acSPeter Maydell                func_macro = True
9892b2765acSPeter Maydell
9902b2765acSPeter Maydell                found = True
9912b2765acSPeter Maydell
9922b2765acSPeter Maydell        if not found:
9932b2765acSPeter Maydell            patterns = [
9942b2765acSPeter Maydell                rf'^()({name})\s*{prototype_end}',
9952b2765acSPeter Maydell                rf'^({type1})\s+({name})\s*{prototype_end}',
9962b2765acSPeter Maydell                rf'^({type2})\s*({name})\s*{prototype_end}',
9972b2765acSPeter Maydell            ]
9982b2765acSPeter Maydell
9992b2765acSPeter Maydell            for p in patterns:
10002b2765acSPeter Maydell                r = KernRe(p)
10012b2765acSPeter Maydell
10022b2765acSPeter Maydell                if r.match(prototype):
10032b2765acSPeter Maydell
10042b2765acSPeter Maydell                    return_type = r.group(1)
10052b2765acSPeter Maydell                    declaration_name = r.group(2)
10062b2765acSPeter Maydell                    args = r.group(3)
10072b2765acSPeter Maydell
10082b2765acSPeter Maydell                    self.create_parameter_list(ln, decl_type, args, ',',
10092b2765acSPeter Maydell                                               declaration_name)
10102b2765acSPeter Maydell
10112b2765acSPeter Maydell                    found = True
10122b2765acSPeter Maydell                    break
10132b2765acSPeter Maydell        if not found:
10142b2765acSPeter Maydell            self.emit_msg(ln,
10152b2765acSPeter Maydell                          f"cannot understand function prototype: '{prototype}'")
10162b2765acSPeter Maydell            return
10172b2765acSPeter Maydell
10182b2765acSPeter Maydell        if self.entry.identifier != declaration_name:
10192b2765acSPeter Maydell            self.emit_msg(ln,
10202b2765acSPeter Maydell                          f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead")
10212b2765acSPeter Maydell            return
10222b2765acSPeter Maydell
10232b2765acSPeter Maydell        self.check_sections(ln, declaration_name, "function")
10242b2765acSPeter Maydell
10252b2765acSPeter Maydell        self.check_return_section(ln, declaration_name, return_type)
10262b2765acSPeter Maydell
10272b2765acSPeter Maydell        if 'typedef' in return_type:
10282b2765acSPeter Maydell            self.output_declaration(decl_type, declaration_name,
10292b2765acSPeter Maydell                                    typedef=True,
10302b2765acSPeter Maydell                                    functiontype=return_type,
10312b2765acSPeter Maydell                                    purpose=self.entry.declaration_purpose,
10322b2765acSPeter Maydell                                    func_macro=func_macro)
10332b2765acSPeter Maydell        else:
10342b2765acSPeter Maydell            self.output_declaration(decl_type, declaration_name,
10352b2765acSPeter Maydell                                    typedef=False,
10362b2765acSPeter Maydell                                    functiontype=return_type,
10372b2765acSPeter Maydell                                    purpose=self.entry.declaration_purpose,
10382b2765acSPeter Maydell                                    func_macro=func_macro)
10392b2765acSPeter Maydell
10402b2765acSPeter Maydell    def dump_typedef(self, ln, proto):
10412b2765acSPeter Maydell        """
10422b2765acSPeter Maydell        Stores a typedef inside self.entries array.
10432b2765acSPeter Maydell        """
10442b2765acSPeter Maydell
10452b2765acSPeter Maydell        typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*'
10462b2765acSPeter Maydell        typedef_ident = r'\*?\s*(\w\S+)\s*'
10472b2765acSPeter Maydell        typedef_args = r'\s*\((.*)\);'
10482b2765acSPeter Maydell
10492b2765acSPeter Maydell        typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)
10502b2765acSPeter Maydell        typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args)
10512b2765acSPeter Maydell
10522b2765acSPeter Maydell        # Strip comments
10532b2765acSPeter Maydell        proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto)
10542b2765acSPeter Maydell
10552b2765acSPeter Maydell        # Parse function typedef prototypes
10562b2765acSPeter Maydell        for r in [typedef1, typedef2]:
10572b2765acSPeter Maydell            if not r.match(proto):
10582b2765acSPeter Maydell                continue
10592b2765acSPeter Maydell
10602b2765acSPeter Maydell            return_type = r.group(1).strip()
10612b2765acSPeter Maydell            declaration_name = r.group(2)
10622b2765acSPeter Maydell            args = r.group(3)
10632b2765acSPeter Maydell
10642b2765acSPeter Maydell            if self.entry.identifier != declaration_name:
10652b2765acSPeter Maydell                self.emit_msg(ln,
10662b2765acSPeter Maydell                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
10672b2765acSPeter Maydell                return
10682b2765acSPeter Maydell
10692b2765acSPeter Maydell            decl_type = 'function'
10702b2765acSPeter Maydell            self.create_parameter_list(ln, decl_type, args, ',', declaration_name)
10712b2765acSPeter Maydell
10722b2765acSPeter Maydell            self.output_declaration(decl_type, declaration_name,
10732b2765acSPeter Maydell                                    typedef=True,
10742b2765acSPeter Maydell                                    functiontype=return_type,
10752b2765acSPeter Maydell                                    purpose=self.entry.declaration_purpose)
10762b2765acSPeter Maydell            return
10772b2765acSPeter Maydell
10782b2765acSPeter Maydell        # Handle nested parentheses or brackets
10792b2765acSPeter Maydell        r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$')
10802b2765acSPeter Maydell        while r.search(proto):
10812b2765acSPeter Maydell            proto = r.sub('', proto)
10822b2765acSPeter Maydell
10832b2765acSPeter Maydell        # Parse simple typedefs
10842b2765acSPeter Maydell        r = KernRe(r'typedef.*\s+(\w+)\s*;')
10852b2765acSPeter Maydell        if r.match(proto):
10862b2765acSPeter Maydell            declaration_name = r.group(1)
10872b2765acSPeter Maydell
10882b2765acSPeter Maydell            if self.entry.identifier != declaration_name:
10892b2765acSPeter Maydell                self.emit_msg(ln,
10902b2765acSPeter Maydell                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
10912b2765acSPeter Maydell                return
10922b2765acSPeter Maydell
10932b2765acSPeter Maydell            self.output_declaration('typedef', declaration_name,
10942b2765acSPeter Maydell                                    purpose=self.entry.declaration_purpose)
10952b2765acSPeter Maydell            return
10962b2765acSPeter Maydell
10972b2765acSPeter Maydell        self.emit_msg(ln, "error: Cannot parse typedef!")
10982b2765acSPeter Maydell
10992b2765acSPeter Maydell    @staticmethod
11002b2765acSPeter Maydell    def process_export(function_set, line):
11012b2765acSPeter Maydell        """
11022b2765acSPeter Maydell        process EXPORT_SYMBOL* tags
11032b2765acSPeter Maydell
11042b2765acSPeter Maydell        This method doesn't use any variable from the class, so declare it
11052b2765acSPeter Maydell        with a staticmethod decorator.
11062b2765acSPeter Maydell        """
11072b2765acSPeter Maydell
11082b2765acSPeter Maydell        # We support documenting some exported symbols with different
11092b2765acSPeter Maydell        # names.  A horrible hack.
11102b2765acSPeter Maydell        suffixes = [ '_noprof' ]
11112b2765acSPeter Maydell
11122b2765acSPeter Maydell        # Note: it accepts only one EXPORT_SYMBOL* per line, as having
11132b2765acSPeter Maydell        # multiple export lines would violate Kernel coding style.
11142b2765acSPeter Maydell
11152b2765acSPeter Maydell        if export_symbol.search(line):
11162b2765acSPeter Maydell            symbol = export_symbol.group(2)
11172b2765acSPeter Maydell        elif export_symbol_ns.search(line):
11182b2765acSPeter Maydell            symbol = export_symbol_ns.group(2)
11192b2765acSPeter Maydell        else:
11202b2765acSPeter Maydell            return False
11212b2765acSPeter Maydell        #
11222b2765acSPeter Maydell        # Found an export, trim out any special suffixes
11232b2765acSPeter Maydell        #
11242b2765acSPeter Maydell        for suffix in suffixes:
11252b2765acSPeter Maydell            # Be backward compatible with Python < 3.9
11262b2765acSPeter Maydell            if symbol.endswith(suffix):
11272b2765acSPeter Maydell                symbol = symbol[:-len(suffix)]
11282b2765acSPeter Maydell        function_set.add(symbol)
11292b2765acSPeter Maydell        return True
11302b2765acSPeter Maydell
11312b2765acSPeter Maydell    def process_normal(self, ln, line):
11322b2765acSPeter Maydell        """
11332b2765acSPeter Maydell        STATE_NORMAL: looking for the /** to begin everything.
11342b2765acSPeter Maydell        """
11352b2765acSPeter Maydell
11362b2765acSPeter Maydell        if not doc_start.match(line):
11372b2765acSPeter Maydell            return
11382b2765acSPeter Maydell
11392b2765acSPeter Maydell        # start a new entry
11402b2765acSPeter Maydell        self.reset_state(ln)
11412b2765acSPeter Maydell
11422b2765acSPeter Maydell        # next line is always the function name
11432b2765acSPeter Maydell        self.state = state.NAME
11442b2765acSPeter Maydell
11452b2765acSPeter Maydell    def process_name(self, ln, line):
11462b2765acSPeter Maydell        """
11472b2765acSPeter Maydell        STATE_NAME: Looking for the "name - description" line
11482b2765acSPeter Maydell        """
11492b2765acSPeter Maydell        #
11502b2765acSPeter Maydell        # Check for a DOC: block and handle them specially.
11512b2765acSPeter Maydell        #
11522b2765acSPeter Maydell        if doc_block.search(line):
11532b2765acSPeter Maydell
11542b2765acSPeter Maydell            if not doc_block.group(1):
11552b2765acSPeter Maydell                self.entry.begin_section(ln, "Introduction")
11562b2765acSPeter Maydell            else:
11572b2765acSPeter Maydell                self.entry.begin_section(ln, doc_block.group(1))
11582b2765acSPeter Maydell
11592b2765acSPeter Maydell            self.entry.identifier = self.entry.section
11602b2765acSPeter Maydell            self.state = state.DOCBLOCK
11612b2765acSPeter Maydell        #
11622b2765acSPeter Maydell        # Otherwise we're looking for a normal kerneldoc declaration line.
11632b2765acSPeter Maydell        #
11642b2765acSPeter Maydell        elif doc_decl.search(line):
11652b2765acSPeter Maydell            self.entry.identifier = doc_decl.group(1)
11662b2765acSPeter Maydell
11672b2765acSPeter Maydell            # Test for data declaration
11682b2765acSPeter Maydell            if doc_begin_data.search(line):
11692b2765acSPeter Maydell                self.entry.decl_type = doc_begin_data.group(1)
11702b2765acSPeter Maydell                self.entry.identifier = doc_begin_data.group(2)
11712b2765acSPeter Maydell            #
11722b2765acSPeter Maydell            # Look for a function description
11732b2765acSPeter Maydell            #
11742b2765acSPeter Maydell            elif doc_begin_func.search(line):
11752b2765acSPeter Maydell                self.entry.identifier = doc_begin_func.group(1)
11762b2765acSPeter Maydell                self.entry.decl_type = "function"
11772b2765acSPeter Maydell            #
11782b2765acSPeter Maydell            # We struck out.
11792b2765acSPeter Maydell            #
11802b2765acSPeter Maydell            else:
11812b2765acSPeter Maydell                self.emit_msg(ln,
11822b2765acSPeter Maydell                              f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
11832b2765acSPeter Maydell                self.state = state.NORMAL
11842b2765acSPeter Maydell                return
11852b2765acSPeter Maydell            #
11862b2765acSPeter Maydell            # OK, set up for a new kerneldoc entry.
11872b2765acSPeter Maydell            #
11882b2765acSPeter Maydell            self.state = state.BODY
11892b2765acSPeter Maydell            self.entry.identifier = self.entry.identifier.strip(" ")
11902b2765acSPeter Maydell            # if there's no @param blocks need to set up default section here
11912b2765acSPeter Maydell            self.entry.begin_section(ln + 1)
11922b2765acSPeter Maydell            #
11932b2765acSPeter Maydell            # Find the description portion, which *should* be there but
11942b2765acSPeter Maydell            # isn't always.
11952b2765acSPeter Maydell            # (We should be able to capture this from the previous parsing - someday)
11962b2765acSPeter Maydell            #
11972b2765acSPeter Maydell            r = KernRe("[-:](.*)")
11982b2765acSPeter Maydell            if r.search(line):
11992b2765acSPeter Maydell                self.entry.declaration_purpose = trim_whitespace(r.group(1))
12002b2765acSPeter Maydell                self.state = state.DECLARATION
12012b2765acSPeter Maydell            else:
12022b2765acSPeter Maydell                self.entry.declaration_purpose = ""
12032b2765acSPeter Maydell
12042b2765acSPeter Maydell            if not self.entry.declaration_purpose and self.config.wshort_desc:
12052b2765acSPeter Maydell                self.emit_msg(ln,
12062b2765acSPeter Maydell                              f"missing initial short description on line:\n{line}")
12072b2765acSPeter Maydell
12082b2765acSPeter Maydell            if not self.entry.identifier and self.entry.decl_type != "enum":
12092b2765acSPeter Maydell                self.emit_msg(ln,
12102b2765acSPeter Maydell                              f"wrong kernel-doc identifier on line:\n{line}")
12112b2765acSPeter Maydell                self.state = state.NORMAL
12122b2765acSPeter Maydell
12132b2765acSPeter Maydell            if self.config.verbose:
12142b2765acSPeter Maydell                self.emit_msg(ln,
12152b2765acSPeter Maydell                              f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",
12162b2765acSPeter Maydell                                  warning=False)
12172b2765acSPeter Maydell        #
12182b2765acSPeter Maydell        # Failed to find an identifier. Emit a warning
12192b2765acSPeter Maydell        #
12202b2765acSPeter Maydell        else:
12212b2765acSPeter Maydell            self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
12222b2765acSPeter Maydell
12232b2765acSPeter Maydell    #
12242b2765acSPeter Maydell    # Helper function to determine if a new section is being started.
12252b2765acSPeter Maydell    #
12262b2765acSPeter Maydell    def is_new_section(self, ln, line):
12272b2765acSPeter Maydell        if doc_sect.search(line):
12282b2765acSPeter Maydell            self.state = state.BODY
12292b2765acSPeter Maydell            #
12302b2765acSPeter Maydell            # Pick out the name of our new section, tweaking it if need be.
12312b2765acSPeter Maydell            #
12322b2765acSPeter Maydell            newsection = doc_sect.group(1)
12332b2765acSPeter Maydell            if newsection.lower() == 'description':
12342b2765acSPeter Maydell                newsection = 'Description'
12352b2765acSPeter Maydell            elif newsection.lower() == 'context':
12362b2765acSPeter Maydell                newsection = 'Context'
12372b2765acSPeter Maydell                self.state = state.SPECIAL_SECTION
12382b2765acSPeter Maydell            elif newsection.lower() in ["@return", "@returns",
12392b2765acSPeter Maydell                                        "return", "returns"]:
12402b2765acSPeter Maydell                newsection = "Return"
12412b2765acSPeter Maydell                self.state = state.SPECIAL_SECTION
12422b2765acSPeter Maydell            elif newsection[0] == '@':
12432b2765acSPeter Maydell                self.state = state.SPECIAL_SECTION
12442b2765acSPeter Maydell            #
12452b2765acSPeter Maydell            # Initialize the contents, and get the new section going.
12462b2765acSPeter Maydell            #
12472b2765acSPeter Maydell            newcontents = doc_sect.group(2)
12482b2765acSPeter Maydell            if not newcontents:
12492b2765acSPeter Maydell                newcontents = ""
12502b2765acSPeter Maydell            self.dump_section()
12512b2765acSPeter Maydell            self.entry.begin_section(ln, newsection)
12522b2765acSPeter Maydell            self.entry.leading_space = None
12532b2765acSPeter Maydell
12542b2765acSPeter Maydell            self.entry.add_text(newcontents.lstrip())
12552b2765acSPeter Maydell            return True
12562b2765acSPeter Maydell        return False
12572b2765acSPeter Maydell
12582b2765acSPeter Maydell    #
12592b2765acSPeter Maydell    # Helper function to detect (and effect) the end of a kerneldoc comment.
12602b2765acSPeter Maydell    #
12612b2765acSPeter Maydell    def is_comment_end(self, ln, line):
12622b2765acSPeter Maydell        if doc_end.search(line):
12632b2765acSPeter Maydell            self.dump_section()
12642b2765acSPeter Maydell
12652b2765acSPeter Maydell            # Look for doc_com + <text> + doc_end:
12662b2765acSPeter Maydell            r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/')
12672b2765acSPeter Maydell            if r.match(line):
12682b2765acSPeter Maydell                self.emit_msg(ln, f"suspicious ending line: {line}")
12692b2765acSPeter Maydell
12702b2765acSPeter Maydell            self.entry.prototype = ""
12712b2765acSPeter Maydell            self.entry.new_start_line = ln + 1
12722b2765acSPeter Maydell
12732b2765acSPeter Maydell            self.state = state.PROTO
12742b2765acSPeter Maydell            return True
12752b2765acSPeter Maydell        return False
12762b2765acSPeter Maydell
12772b2765acSPeter Maydell
12782b2765acSPeter Maydell    def process_decl(self, ln, line):
12792b2765acSPeter Maydell        """
12802b2765acSPeter Maydell        STATE_DECLARATION: We've seen the beginning of a declaration
12812b2765acSPeter Maydell        """
12822b2765acSPeter Maydell        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
12832b2765acSPeter Maydell            return
12842b2765acSPeter Maydell        #
12852b2765acSPeter Maydell        # Look for anything with the " * " line beginning.
12862b2765acSPeter Maydell        #
12872b2765acSPeter Maydell        if doc_content.search(line):
12882b2765acSPeter Maydell            cont = doc_content.group(1)
12892b2765acSPeter Maydell            #
12902b2765acSPeter Maydell            # A blank line means that we have moved out of the declaration
12912b2765acSPeter Maydell            # part of the comment (without any "special section" parameter
12922b2765acSPeter Maydell            # descriptions).
12932b2765acSPeter Maydell            #
12942b2765acSPeter Maydell            if cont == "":
12952b2765acSPeter Maydell                self.state = state.BODY
12962b2765acSPeter Maydell            #
12972b2765acSPeter Maydell            # Otherwise we have more of the declaration section to soak up.
12982b2765acSPeter Maydell            #
12992b2765acSPeter Maydell            else:
13002b2765acSPeter Maydell                self.entry.declaration_purpose = \
13012b2765acSPeter Maydell                    trim_whitespace(self.entry.declaration_purpose + ' ' + cont)
13022b2765acSPeter Maydell        else:
13032b2765acSPeter Maydell            # Unknown line, ignore
13042b2765acSPeter Maydell            self.emit_msg(ln, f"bad line: {line}")
13052b2765acSPeter Maydell
13062b2765acSPeter Maydell
13072b2765acSPeter Maydell    def process_special(self, ln, line):
13082b2765acSPeter Maydell        """
13092b2765acSPeter Maydell        STATE_SPECIAL_SECTION: a section ending with a blank line
13102b2765acSPeter Maydell        """
13112b2765acSPeter Maydell        #
13122b2765acSPeter Maydell        # If we have hit a blank line (only the " * " marker), then this
13132b2765acSPeter Maydell        # section is done.
13142b2765acSPeter Maydell        #
13152b2765acSPeter Maydell        if KernRe(r"\s*\*\s*$").match(line):
13162b2765acSPeter Maydell            self.entry.begin_section(ln, dump = True)
13172b2765acSPeter Maydell            self.state = state.BODY
13182b2765acSPeter Maydell            return
13192b2765acSPeter Maydell        #
13202b2765acSPeter Maydell        # Not a blank line, look for the other ways to end the section.
13212b2765acSPeter Maydell        #
13222b2765acSPeter Maydell        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
13232b2765acSPeter Maydell            return
13242b2765acSPeter Maydell        #
13252b2765acSPeter Maydell        # OK, we should have a continuation of the text for this section.
13262b2765acSPeter Maydell        #
13272b2765acSPeter Maydell        if doc_content.search(line):
13282b2765acSPeter Maydell            cont = doc_content.group(1)
13292b2765acSPeter Maydell            #
13302b2765acSPeter Maydell            # If the lines of text after the first in a special section have
13312b2765acSPeter Maydell            # leading white space, we need to trim it out or Sphinx will get
13322b2765acSPeter Maydell            # confused.  For the second line (the None case), see what we
13332b2765acSPeter Maydell            # find there and remember it.
13342b2765acSPeter Maydell            #
13352b2765acSPeter Maydell            if self.entry.leading_space is None:
13362b2765acSPeter Maydell                r = KernRe(r'^(\s+)')
13372b2765acSPeter Maydell                if r.match(cont):
13382b2765acSPeter Maydell                    self.entry.leading_space = len(r.group(1))
13392b2765acSPeter Maydell                else:
13402b2765acSPeter Maydell                    self.entry.leading_space = 0
13412b2765acSPeter Maydell            #
13422b2765acSPeter Maydell            # Otherwise, before trimming any leading chars, be *sure*
13432b2765acSPeter Maydell            # that they are white space.  We should maybe warn if this
13442b2765acSPeter Maydell            # isn't the case.
13452b2765acSPeter Maydell            #
13462b2765acSPeter Maydell            for i in range(0, self.entry.leading_space):
13472b2765acSPeter Maydell                if cont[i] != " ":
13482b2765acSPeter Maydell                    self.entry.leading_space = i
13492b2765acSPeter Maydell                    break
13502b2765acSPeter Maydell            #
13512b2765acSPeter Maydell            # Add the trimmed result to the section and we're done.
13522b2765acSPeter Maydell            #
13532b2765acSPeter Maydell            self.entry.add_text(cont[self.entry.leading_space:])
13542b2765acSPeter Maydell        else:
13552b2765acSPeter Maydell            # Unknown line, ignore
13562b2765acSPeter Maydell            self.emit_msg(ln, f"bad line: {line}")
13572b2765acSPeter Maydell
13582b2765acSPeter Maydell    def process_body(self, ln, line):
13592b2765acSPeter Maydell        """
13602b2765acSPeter Maydell        STATE_BODY: the bulk of a kerneldoc comment.
13612b2765acSPeter Maydell        """
13622b2765acSPeter Maydell        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
13632b2765acSPeter Maydell            return
13642b2765acSPeter Maydell
13652b2765acSPeter Maydell        if doc_content.search(line):
13662b2765acSPeter Maydell            cont = doc_content.group(1)
13672b2765acSPeter Maydell            self.entry.add_text(cont)
13682b2765acSPeter Maydell        else:
13692b2765acSPeter Maydell            # Unknown line, ignore
13702b2765acSPeter Maydell            self.emit_msg(ln, f"bad line: {line}")
13712b2765acSPeter Maydell
13722b2765acSPeter Maydell    def process_inline_name(self, ln, line):
13732b2765acSPeter Maydell        """STATE_INLINE_NAME: beginning of docbook comments within a prototype."""
13742b2765acSPeter Maydell
13752b2765acSPeter Maydell        if doc_inline_sect.search(line):
13762b2765acSPeter Maydell            self.entry.begin_section(ln, doc_inline_sect.group(1))
13772b2765acSPeter Maydell            self.entry.add_text(doc_inline_sect.group(2).lstrip())
13782b2765acSPeter Maydell            self.state = state.INLINE_TEXT
13792b2765acSPeter Maydell        elif doc_inline_end.search(line):
13802b2765acSPeter Maydell            self.dump_section()
13812b2765acSPeter Maydell            self.state = state.PROTO
13822b2765acSPeter Maydell        elif doc_content.search(line):
13832b2765acSPeter Maydell            self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}")
13842b2765acSPeter Maydell            self.state = state.PROTO
13852b2765acSPeter Maydell        # else ... ??
13862b2765acSPeter Maydell
13872b2765acSPeter Maydell    def process_inline_text(self, ln, line):
13882b2765acSPeter Maydell        """STATE_INLINE_TEXT: docbook comments within a prototype."""
13892b2765acSPeter Maydell
13902b2765acSPeter Maydell        if doc_inline_end.search(line):
13912b2765acSPeter Maydell            self.dump_section()
13922b2765acSPeter Maydell            self.state = state.PROTO
13932b2765acSPeter Maydell        elif doc_content.search(line):
13942b2765acSPeter Maydell            self.entry.add_text(doc_content.group(1))
13952b2765acSPeter Maydell        # else ... ??
13962b2765acSPeter Maydell
13972b2765acSPeter Maydell    def syscall_munge(self, ln, proto):         # pylint: disable=W0613
13982b2765acSPeter Maydell        """
13992b2765acSPeter Maydell        Handle syscall definitions
14002b2765acSPeter Maydell        """
14012b2765acSPeter Maydell
14022b2765acSPeter Maydell        is_void = False
14032b2765acSPeter Maydell
14042b2765acSPeter Maydell        # Strip newlines/CR's
14052b2765acSPeter Maydell        proto = re.sub(r'[\r\n]+', ' ', proto)
14062b2765acSPeter Maydell
14072b2765acSPeter Maydell        # Check if it's a SYSCALL_DEFINE0
14082b2765acSPeter Maydell        if 'SYSCALL_DEFINE0' in proto:
14092b2765acSPeter Maydell            is_void = True
14102b2765acSPeter Maydell
14112b2765acSPeter Maydell        # Replace SYSCALL_DEFINE with correct return type & function name
14122b2765acSPeter Maydell        proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto)
14132b2765acSPeter Maydell
14142b2765acSPeter Maydell        r = KernRe(r'long\s+(sys_.*?),')
14152b2765acSPeter Maydell        if r.search(proto):
14162b2765acSPeter Maydell            proto = KernRe(',').sub('(', proto, count=1)
14172b2765acSPeter Maydell        elif is_void:
14182b2765acSPeter Maydell            proto = KernRe(r'\)').sub('(void)', proto, count=1)
14192b2765acSPeter Maydell
14202b2765acSPeter Maydell        # Now delete all of the odd-numbered commas in the proto
14212b2765acSPeter Maydell        # so that argument types & names don't have a comma between them
14222b2765acSPeter Maydell        count = 0
14232b2765acSPeter Maydell        length = len(proto)
14242b2765acSPeter Maydell
14252b2765acSPeter Maydell        if is_void:
14262b2765acSPeter Maydell            length = 0  # skip the loop if is_void
14272b2765acSPeter Maydell
14282b2765acSPeter Maydell        for ix in range(length):
14292b2765acSPeter Maydell            if proto[ix] == ',':
14302b2765acSPeter Maydell                count += 1
14312b2765acSPeter Maydell                if count % 2 == 1:
14322b2765acSPeter Maydell                    proto = proto[:ix] + ' ' + proto[ix + 1:]
14332b2765acSPeter Maydell
14342b2765acSPeter Maydell        return proto
14352b2765acSPeter Maydell
14362b2765acSPeter Maydell    def tracepoint_munge(self, ln, proto):
14372b2765acSPeter Maydell        """
14382b2765acSPeter Maydell        Handle tracepoint definitions
14392b2765acSPeter Maydell        """
14402b2765acSPeter Maydell
14412b2765acSPeter Maydell        tracepointname = None
14422b2765acSPeter Maydell        tracepointargs = None
14432b2765acSPeter Maydell
14442b2765acSPeter Maydell        # Match tracepoint name based on different patterns
14452b2765acSPeter Maydell        r = KernRe(r'TRACE_EVENT\((.*?),')
14462b2765acSPeter Maydell        if r.search(proto):
14472b2765acSPeter Maydell            tracepointname = r.group(1)
14482b2765acSPeter Maydell
14492b2765acSPeter Maydell        r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),')
14502b2765acSPeter Maydell        if r.search(proto):
14512b2765acSPeter Maydell            tracepointname = r.group(1)
14522b2765acSPeter Maydell
14532b2765acSPeter Maydell        r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),')
14542b2765acSPeter Maydell        if r.search(proto):
14552b2765acSPeter Maydell            tracepointname = r.group(2)
14562b2765acSPeter Maydell
14572b2765acSPeter Maydell        if tracepointname:
14582b2765acSPeter Maydell            tracepointname = tracepointname.lstrip()
14592b2765acSPeter Maydell
14602b2765acSPeter Maydell        r = KernRe(r'TP_PROTO\((.*?)\)')
14612b2765acSPeter Maydell        if r.search(proto):
14622b2765acSPeter Maydell            tracepointargs = r.group(1)
14632b2765acSPeter Maydell
14642b2765acSPeter Maydell        if not tracepointname or not tracepointargs:
14652b2765acSPeter Maydell            self.emit_msg(ln,
14662b2765acSPeter Maydell                          f"Unrecognized tracepoint format:\n{proto}\n")
14672b2765acSPeter Maydell        else:
14682b2765acSPeter Maydell            proto = f"static inline void trace_{tracepointname}({tracepointargs})"
14692b2765acSPeter Maydell            self.entry.identifier = f"trace_{self.entry.identifier}"
14702b2765acSPeter Maydell
14712b2765acSPeter Maydell        return proto
14722b2765acSPeter Maydell
14732b2765acSPeter Maydell    def process_proto_function(self, ln, line):
14742b2765acSPeter Maydell        """Ancillary routine to process a function prototype"""
14752b2765acSPeter Maydell
14762b2765acSPeter Maydell        # strip C99-style comments to end of line
14772b2765acSPeter Maydell        line = KernRe(r"\/\/.*$", re.S).sub('', line)
14782b2765acSPeter Maydell        #
14792b2765acSPeter Maydell        # Soak up the line's worth of prototype text, stopping at { or ; if present.
14802b2765acSPeter Maydell        #
14812b2765acSPeter Maydell        if KernRe(r'\s*#\s*define').match(line):
14822b2765acSPeter Maydell            self.entry.prototype = line
14832b2765acSPeter Maydell        elif not line.startswith('#'):   # skip other preprocessor stuff
14842b2765acSPeter Maydell            r = KernRe(r'([^\{]*)')
14852b2765acSPeter Maydell            if r.match(line):
14862b2765acSPeter Maydell                self.entry.prototype += r.group(1) + " "
14872b2765acSPeter Maydell        #
14882b2765acSPeter Maydell        # If we now have the whole prototype, clean it up and declare victory.
14892b2765acSPeter Maydell        #
14902b2765acSPeter Maydell        if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line):
14912b2765acSPeter Maydell            # strip comments and surrounding spaces
14922b2765acSPeter Maydell            self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip()
14932b2765acSPeter Maydell            #
14942b2765acSPeter Maydell            # Handle self.entry.prototypes for function pointers like:
14952b2765acSPeter Maydell            #       int (*pcs_config)(struct foo)
14962b2765acSPeter Maydell            # by turning it into
14972b2765acSPeter Maydell            #	    int pcs_config(struct foo)
14982b2765acSPeter Maydell            #
14992b2765acSPeter Maydell            r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)')
15002b2765acSPeter Maydell            self.entry.prototype = r.sub(r'\1\2', self.entry.prototype)
15012b2765acSPeter Maydell            #
15022b2765acSPeter Maydell            # Handle special declaration syntaxes
15032b2765acSPeter Maydell            #
15042b2765acSPeter Maydell            if 'SYSCALL_DEFINE' in self.entry.prototype:
15052b2765acSPeter Maydell                self.entry.prototype = self.syscall_munge(ln,
15062b2765acSPeter Maydell                                                          self.entry.prototype)
15072b2765acSPeter Maydell            else:
15082b2765acSPeter Maydell                r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
15092b2765acSPeter Maydell                if r.search(self.entry.prototype):
15102b2765acSPeter Maydell                    self.entry.prototype = self.tracepoint_munge(ln,
15112b2765acSPeter Maydell                                                                 self.entry.prototype)
15122b2765acSPeter Maydell            #
15132b2765acSPeter Maydell            # ... and we're done
15142b2765acSPeter Maydell            #
15152b2765acSPeter Maydell            self.dump_function(ln, self.entry.prototype)
15162b2765acSPeter Maydell            self.reset_state(ln)
15172b2765acSPeter Maydell
15182b2765acSPeter Maydell    def process_proto_type(self, ln, line):
15192b2765acSPeter Maydell        """Ancillary routine to process a type"""
15202b2765acSPeter Maydell
15212b2765acSPeter Maydell        # Strip C99-style comments and surrounding whitespace
15222b2765acSPeter Maydell        line = KernRe(r"//.*$", re.S).sub('', line).strip()
15232b2765acSPeter Maydell        if not line:
15242b2765acSPeter Maydell            return # nothing to see here
15252b2765acSPeter Maydell
15262b2765acSPeter Maydell        # To distinguish preprocessor directive from regular declaration later.
15272b2765acSPeter Maydell        if line.startswith('#'):
15282b2765acSPeter Maydell            line += ";"
15292b2765acSPeter Maydell        #
15302b2765acSPeter Maydell        # Split the declaration on any of { } or ;, and accumulate pieces
15312b2765acSPeter Maydell        # until we hit a semicolon while not inside {brackets}
15322b2765acSPeter Maydell        #
15332b2765acSPeter Maydell        r = KernRe(r'(.*?)([{};])')
15342b2765acSPeter Maydell        for chunk in r.split(line):
15352b2765acSPeter Maydell            if chunk:  # Ignore empty matches
15362b2765acSPeter Maydell                self.entry.prototype += chunk
15372b2765acSPeter Maydell                #
15382b2765acSPeter Maydell                # This cries out for a match statement ... someday after we can
15392b2765acSPeter Maydell                # drop Python 3.9 ...
15402b2765acSPeter Maydell                #
15412b2765acSPeter Maydell                if chunk == '{':
15422b2765acSPeter Maydell                    self.entry.brcount += 1
15432b2765acSPeter Maydell                elif chunk == '}':
15442b2765acSPeter Maydell                    self.entry.brcount -= 1
15452b2765acSPeter Maydell                elif chunk == ';' and self.entry.brcount <= 0:
15462b2765acSPeter Maydell                    self.dump_declaration(ln, self.entry.prototype)
15472b2765acSPeter Maydell                    self.reset_state(ln)
15482b2765acSPeter Maydell                    return
15492b2765acSPeter Maydell        #
15502b2765acSPeter Maydell        # We hit the end of the line while still in the declaration; put
15512b2765acSPeter Maydell        # in a space to represent the newline.
15522b2765acSPeter Maydell        #
15532b2765acSPeter Maydell        self.entry.prototype += ' '
15542b2765acSPeter Maydell
15552b2765acSPeter Maydell    def process_proto(self, ln, line):
15562b2765acSPeter Maydell        """STATE_PROTO: reading a function/whatever prototype."""
15572b2765acSPeter Maydell
15582b2765acSPeter Maydell        if doc_inline_oneline.search(line):
15592b2765acSPeter Maydell            self.entry.begin_section(ln, doc_inline_oneline.group(1))
15602b2765acSPeter Maydell            self.entry.add_text(doc_inline_oneline.group(2))
15612b2765acSPeter Maydell            self.dump_section()
15622b2765acSPeter Maydell
15632b2765acSPeter Maydell        elif doc_inline_start.search(line):
15642b2765acSPeter Maydell            self.state = state.INLINE_NAME
15652b2765acSPeter Maydell
15662b2765acSPeter Maydell        elif self.entry.decl_type == 'function':
15672b2765acSPeter Maydell            self.process_proto_function(ln, line)
15682b2765acSPeter Maydell
15692b2765acSPeter Maydell        else:
15702b2765acSPeter Maydell            self.process_proto_type(ln, line)
15712b2765acSPeter Maydell
15722b2765acSPeter Maydell    def process_docblock(self, ln, line):
15732b2765acSPeter Maydell        """STATE_DOCBLOCK: within a DOC: block."""
15742b2765acSPeter Maydell
15752b2765acSPeter Maydell        if doc_end.search(line):
15762b2765acSPeter Maydell            self.dump_section()
15772b2765acSPeter Maydell            self.output_declaration("doc", self.entry.identifier)
15782b2765acSPeter Maydell            self.reset_state(ln)
15792b2765acSPeter Maydell
15802b2765acSPeter Maydell        elif doc_content.search(line):
15812b2765acSPeter Maydell            self.entry.add_text(doc_content.group(1))
15822b2765acSPeter Maydell
15832b2765acSPeter Maydell    def parse_export(self):
15842b2765acSPeter Maydell        """
15852b2765acSPeter Maydell        Parses EXPORT_SYMBOL* macros from a single Kernel source file.
15862b2765acSPeter Maydell        """
15872b2765acSPeter Maydell
15882b2765acSPeter Maydell        export_table = set()
15892b2765acSPeter Maydell
15902b2765acSPeter Maydell        try:
15912b2765acSPeter Maydell            with open(self.fname, "r", encoding="utf8",
15922b2765acSPeter Maydell                      errors="backslashreplace") as fp:
15932b2765acSPeter Maydell
15942b2765acSPeter Maydell                for line in fp:
15952b2765acSPeter Maydell                    self.process_export(export_table, line)
15962b2765acSPeter Maydell
15972b2765acSPeter Maydell        except IOError:
15982b2765acSPeter Maydell            return None
15992b2765acSPeter Maydell
16002b2765acSPeter Maydell        return export_table
16012b2765acSPeter Maydell
16022b2765acSPeter Maydell    #
16032b2765acSPeter Maydell    # The state/action table telling us which function to invoke in
16042b2765acSPeter Maydell    # each state.
16052b2765acSPeter Maydell    #
16062b2765acSPeter Maydell    state_actions = {
16072b2765acSPeter Maydell        state.NORMAL:			process_normal,
16082b2765acSPeter Maydell        state.NAME:			process_name,
16092b2765acSPeter Maydell        state.BODY:			process_body,
16102b2765acSPeter Maydell        state.DECLARATION:		process_decl,
16112b2765acSPeter Maydell        state.SPECIAL_SECTION:		process_special,
16122b2765acSPeter Maydell        state.INLINE_NAME:		process_inline_name,
16132b2765acSPeter Maydell        state.INLINE_TEXT:		process_inline_text,
16142b2765acSPeter Maydell        state.PROTO:			process_proto,
16152b2765acSPeter Maydell        state.DOCBLOCK:			process_docblock,
16162b2765acSPeter Maydell        }
16172b2765acSPeter Maydell
16182b2765acSPeter Maydell    def parse_kdoc(self):
16192b2765acSPeter Maydell        """
16202b2765acSPeter Maydell        Open and process each line of a C source file.
16212b2765acSPeter Maydell        The parsing is controlled via a state machine, and the line is passed
16222b2765acSPeter Maydell        to a different process function depending on the state. The process
16232b2765acSPeter Maydell        function may update the state as needed.
16242b2765acSPeter Maydell
16252b2765acSPeter Maydell        Besides parsing kernel-doc tags, it also parses export symbols.
16262b2765acSPeter Maydell        """
16272b2765acSPeter Maydell
16282b2765acSPeter Maydell        prev = ""
16292b2765acSPeter Maydell        prev_ln = None
16302b2765acSPeter Maydell        export_table = set()
16312b2765acSPeter Maydell
16322b2765acSPeter Maydell        try:
16332b2765acSPeter Maydell            with open(self.fname, "r", encoding="utf8",
16342b2765acSPeter Maydell                      errors="backslashreplace") as fp:
16352b2765acSPeter Maydell                for ln, line in enumerate(fp):
16362b2765acSPeter Maydell
16372b2765acSPeter Maydell                    line = line.expandtabs().strip("\n")
16382b2765acSPeter Maydell
16392b2765acSPeter Maydell                    # Group continuation lines on prototypes
16402b2765acSPeter Maydell                    if self.state == state.PROTO:
16412b2765acSPeter Maydell                        if line.endswith("\\"):
16422b2765acSPeter Maydell                            prev += line.rstrip("\\")
16432b2765acSPeter Maydell                            if not prev_ln:
16442b2765acSPeter Maydell                                prev_ln = ln
16452b2765acSPeter Maydell                            continue
16462b2765acSPeter Maydell
16472b2765acSPeter Maydell                        if prev:
16482b2765acSPeter Maydell                            ln = prev_ln
16492b2765acSPeter Maydell                            line = prev + line
16502b2765acSPeter Maydell                            prev = ""
16512b2765acSPeter Maydell                            prev_ln = None
16522b2765acSPeter Maydell
16532b2765acSPeter Maydell                    self.config.log.debug("%d %s: %s",
16542b2765acSPeter Maydell                                          ln, state.name[self.state],
16552b2765acSPeter Maydell                                          line)
16562b2765acSPeter Maydell
16572b2765acSPeter Maydell                    # This is an optimization over the original script.
16582b2765acSPeter Maydell                    # There, when export_file was used for the same file,
16592b2765acSPeter Maydell                    # it was read twice. Here, we use the already-existing
16602b2765acSPeter Maydell                    # loop to parse exported symbols as well.
16612b2765acSPeter Maydell                    #
16622b2765acSPeter Maydell                    if (self.state != state.NORMAL) or \
16632b2765acSPeter Maydell                       not self.process_export(export_table, line):
16642b2765acSPeter Maydell                        # Hand this line to the appropriate state handler
16652b2765acSPeter Maydell                        self.state_actions[self.state](self, ln, line)
16662b2765acSPeter Maydell
16672b2765acSPeter Maydell        except OSError:
16682b2765acSPeter Maydell            self.config.log.error(f"Error: Cannot open file {self.fname}")
16692b2765acSPeter Maydell
16702b2765acSPeter Maydell        return export_table, self.entries
1671