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