xref: /linux/scripts/lib/kdoc/kdoc_parser.py (revision 3e443d167327b10966166c1953631936547b03d0)
1d966dc65SMauro Carvalho Chehab#!/usr/bin/env python3
2d966dc65SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0
3d966dc65SMauro Carvalho Chehab# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
4d966dc65SMauro Carvalho Chehab#
5d966dc65SMauro Carvalho Chehab# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702
6d966dc65SMauro Carvalho Chehab
7d966dc65SMauro Carvalho Chehab"""
8d966dc65SMauro Carvalho Chehabkdoc_parser
9d966dc65SMauro Carvalho Chehab===========
10d966dc65SMauro Carvalho Chehab
11d966dc65SMauro Carvalho ChehabRead a C language source or header FILE and extract embedded
12d966dc65SMauro Carvalho Chehabdocumentation comments
13d966dc65SMauro Carvalho Chehab"""
14d966dc65SMauro Carvalho Chehab
15d966dc65SMauro Carvalho Chehabimport re
16d966dc65SMauro Carvalho Chehabfrom pprint import pformat
17d966dc65SMauro Carvalho Chehab
1804a383ceSMauro Carvalho Chehabfrom kdoc_re import NestedMatch, KernRe
19d966dc65SMauro Carvalho Chehab
20d966dc65SMauro Carvalho Chehab
21d966dc65SMauro Carvalho Chehab#
22d966dc65SMauro Carvalho Chehab# Regular expressions used to parse kernel-doc markups at KernelDoc class.
23d966dc65SMauro Carvalho Chehab#
24d966dc65SMauro Carvalho Chehab# Let's declare them in lowercase outside any class to make easier to
25d966dc65SMauro Carvalho Chehab# convert from the python script.
26d966dc65SMauro Carvalho Chehab#
27d966dc65SMauro Carvalho Chehab# As those are evaluated at the beginning, no need to cache them
28d966dc65SMauro Carvalho Chehab#
29d966dc65SMauro Carvalho Chehab
30d966dc65SMauro Carvalho Chehab# Allow whitespace at end of comment start.
3104a383ceSMauro Carvalho Chehabdoc_start = KernRe(r'^/\*\*\s*$', cache=False)
32d966dc65SMauro Carvalho Chehab
3304a383ceSMauro Carvalho Chehabdoc_end = KernRe(r'\*/', cache=False)
3404a383ceSMauro Carvalho Chehabdoc_com = KernRe(r'\s*\*\s*', cache=False)
3504a383ceSMauro Carvalho Chehabdoc_com_body = KernRe(r'\s*\* ?', cache=False)
3604a383ceSMauro Carvalho Chehabdoc_decl = doc_com + KernRe(r'(\w+)', cache=False)
37d966dc65SMauro Carvalho Chehab
38d966dc65SMauro Carvalho Chehab# @params and a strictly limited set of supported section names
39d966dc65SMauro Carvalho Chehab# Specifically:
40d966dc65SMauro Carvalho Chehab#   Match @word:
41d966dc65SMauro Carvalho Chehab#         @...:
42d966dc65SMauro Carvalho Chehab#         @{section-name}:
43d966dc65SMauro Carvalho Chehab# while trying to not match literal block starts like "example::"
44d966dc65SMauro Carvalho Chehab#
45d966dc65SMauro Carvalho Chehabdoc_sect = doc_com + \
4604a383ceSMauro Carvalho Chehab            KernRe(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$',
47d966dc65SMauro Carvalho Chehab                flags=re.I, cache=False)
48d966dc65SMauro Carvalho Chehab
4904a383ceSMauro Carvalho Chehabdoc_content = doc_com_body + KernRe(r'(.*)', cache=False)
5004a383ceSMauro Carvalho Chehabdoc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False)
5104a383ceSMauro Carvalho Chehabdoc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False)
5204a383ceSMauro Carvalho Chehabdoc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False)
5304a383ceSMauro Carvalho Chehabdoc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False)
5404a383ceSMauro Carvalho Chehabdoc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False)
5504a383ceSMauro Carvalho Chehabattribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)",
56d966dc65SMauro Carvalho Chehab               flags=re.I | re.S, cache=False)
57d966dc65SMauro Carvalho Chehab
5804a383ceSMauro Carvalho Chehabexport_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False)
5904a383ceSMauro Carvalho Chehabexport_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False)
60d966dc65SMauro Carvalho Chehab
6104a383ceSMauro Carvalho Chehabtype_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
62d966dc65SMauro Carvalho Chehab
63f9cdbc57SMauro Carvalho Chehabclass state:
64d966dc65SMauro Carvalho Chehab    """
65f9cdbc57SMauro Carvalho Chehab    State machine enums
66d966dc65SMauro Carvalho Chehab    """
67d966dc65SMauro Carvalho Chehab
68d966dc65SMauro Carvalho Chehab    # Parser states
69f9cdbc57SMauro Carvalho Chehab    NORMAL        = 0        # normal code
70f9cdbc57SMauro Carvalho Chehab    NAME          = 1        # looking for function name
71f9cdbc57SMauro Carvalho Chehab    BODY_MAYBE    = 2        # body - or maybe more description
72f9cdbc57SMauro Carvalho Chehab    BODY          = 3        # the body of the comment
73f9cdbc57SMauro Carvalho Chehab    BODY_WITH_BLANK_LINE = 4 # the body which has a blank line
74f9cdbc57SMauro Carvalho Chehab    PROTO         = 5        # scanning prototype
75f9cdbc57SMauro Carvalho Chehab    DOCBLOCK      = 6        # documentation block
76f9cdbc57SMauro Carvalho Chehab    INLINE        = 7        # gathering doc outside main block
77d966dc65SMauro Carvalho Chehab
78f9cdbc57SMauro Carvalho Chehab    name = [
79d966dc65SMauro Carvalho Chehab        "NORMAL",
80d966dc65SMauro Carvalho Chehab        "NAME",
81d966dc65SMauro Carvalho Chehab        "BODY_MAYBE",
82d966dc65SMauro Carvalho Chehab        "BODY",
83d966dc65SMauro Carvalho Chehab        "BODY_WITH_BLANK_LINE",
84d966dc65SMauro Carvalho Chehab        "PROTO",
85d966dc65SMauro Carvalho Chehab        "DOCBLOCK",
86d966dc65SMauro Carvalho Chehab        "INLINE",
87d966dc65SMauro Carvalho Chehab    ]
88d966dc65SMauro Carvalho Chehab
89d966dc65SMauro Carvalho Chehab    # Inline documentation state
90f9cdbc57SMauro Carvalho Chehab    INLINE_NA     = 0 # not applicable ($state != INLINE)
91f9cdbc57SMauro Carvalho Chehab    INLINE_NAME   = 1 # looking for member name (@foo:)
92f9cdbc57SMauro Carvalho Chehab    INLINE_TEXT   = 2 # looking for member documentation
93f9cdbc57SMauro Carvalho Chehab    INLINE_END    = 3 # done
94f9cdbc57SMauro Carvalho Chehab    INLINE_ERROR  = 4 # error - Comment without header was found.
95d966dc65SMauro Carvalho Chehab                      # Spit a warning as it's not
96d966dc65SMauro Carvalho Chehab                      # proper kernel-doc and ignore the rest.
97d966dc65SMauro Carvalho Chehab
98f9cdbc57SMauro Carvalho Chehab    inline_name = [
99d966dc65SMauro Carvalho Chehab        "",
100d966dc65SMauro Carvalho Chehab        "_NAME",
101d966dc65SMauro Carvalho Chehab        "_TEXT",
102d966dc65SMauro Carvalho Chehab        "_END",
103d966dc65SMauro Carvalho Chehab        "_ERROR",
104d966dc65SMauro Carvalho Chehab    ]
105d966dc65SMauro Carvalho Chehab
106*e3b42e94SMauro Carvalho ChehabSECTION_DEFAULT = "Description"  # default section
107*e3b42e94SMauro Carvalho Chehab
108*e3b42e94SMauro Carvalho Chehabclass KernelEntry:
109*e3b42e94SMauro Carvalho Chehab
110*e3b42e94SMauro Carvalho Chehab    def __init__(self, config, ln):
111*e3b42e94SMauro Carvalho Chehab        self.config = config
112*e3b42e94SMauro Carvalho Chehab
113*e3b42e94SMauro Carvalho Chehab        self.contents = ""
114*e3b42e94SMauro Carvalho Chehab        self.function = ""
115*e3b42e94SMauro Carvalho Chehab        self.sectcheck = ""
116*e3b42e94SMauro Carvalho Chehab        self.struct_actual = ""
117*e3b42e94SMauro Carvalho Chehab        self.prototype = ""
118*e3b42e94SMauro Carvalho Chehab
119*e3b42e94SMauro Carvalho Chehab        self.warnings = []
120*e3b42e94SMauro Carvalho Chehab
121*e3b42e94SMauro Carvalho Chehab        self.parameterlist = []
122*e3b42e94SMauro Carvalho Chehab        self.parameterdescs = {}
123*e3b42e94SMauro Carvalho Chehab        self.parametertypes = {}
124*e3b42e94SMauro Carvalho Chehab        self.parameterdesc_start_lines = {}
125*e3b42e94SMauro Carvalho Chehab
126*e3b42e94SMauro Carvalho Chehab        self.section_start_lines = {}
127*e3b42e94SMauro Carvalho Chehab        self.sectionlist = []
128*e3b42e94SMauro Carvalho Chehab        self.sections = {}
129*e3b42e94SMauro Carvalho Chehab
130*e3b42e94SMauro Carvalho Chehab        self.anon_struct_union = False
131*e3b42e94SMauro Carvalho Chehab
132*e3b42e94SMauro Carvalho Chehab        self.leading_space = None
133*e3b42e94SMauro Carvalho Chehab
134*e3b42e94SMauro Carvalho Chehab        # State flags
135*e3b42e94SMauro Carvalho Chehab        self.brcount = 0
136*e3b42e94SMauro Carvalho Chehab
137*e3b42e94SMauro Carvalho Chehab        self.in_doc_sect = False
138*e3b42e94SMauro Carvalho Chehab        self.declaration_start_line = ln + 1
139*e3b42e94SMauro Carvalho Chehab
140*e3b42e94SMauro Carvalho Chehab    # TODO: rename to emit_message after removal of kernel-doc.pl
141*e3b42e94SMauro Carvalho Chehab    def emit_msg(self, log_msg, warning=True):
142*e3b42e94SMauro Carvalho Chehab        """Emit a message"""
143*e3b42e94SMauro Carvalho Chehab
144*e3b42e94SMauro Carvalho Chehab        if not warning:
145*e3b42e94SMauro Carvalho Chehab            self.config.log.info(log_msg)
146*e3b42e94SMauro Carvalho Chehab            return
147*e3b42e94SMauro Carvalho Chehab
148*e3b42e94SMauro Carvalho Chehab        # Delegate warning output to output logic, as this way it
149*e3b42e94SMauro Carvalho Chehab        # will report warnings/info only for symbols that are output
150*e3b42e94SMauro Carvalho Chehab
151*e3b42e94SMauro Carvalho Chehab        self.warnings.append(log_msg)
152*e3b42e94SMauro Carvalho Chehab        return
153*e3b42e94SMauro Carvalho Chehab
154*e3b42e94SMauro Carvalho Chehab    def dump_section(self, start_new=True):
155*e3b42e94SMauro Carvalho Chehab        """
156*e3b42e94SMauro Carvalho Chehab        Dumps section contents to arrays/hashes intended for that purpose.
157*e3b42e94SMauro Carvalho Chehab        """
158*e3b42e94SMauro Carvalho Chehab
159*e3b42e94SMauro Carvalho Chehab        name = self.section
160*e3b42e94SMauro Carvalho Chehab        contents = self.contents
161*e3b42e94SMauro Carvalho Chehab
162*e3b42e94SMauro Carvalho Chehab        if type_param.match(name):
163*e3b42e94SMauro Carvalho Chehab            name = type_param.group(1)
164*e3b42e94SMauro Carvalho Chehab
165*e3b42e94SMauro Carvalho Chehab            self.parameterdescs[name] = contents
166*e3b42e94SMauro Carvalho Chehab            self.parameterdesc_start_lines[name] = self.new_start_line
167*e3b42e94SMauro Carvalho Chehab
168*e3b42e94SMauro Carvalho Chehab            self.sectcheck += name + " "
169*e3b42e94SMauro Carvalho Chehab            self.new_start_line = 0
170*e3b42e94SMauro Carvalho Chehab
171*e3b42e94SMauro Carvalho Chehab        elif name == "@...":
172*e3b42e94SMauro Carvalho Chehab            name = "..."
173*e3b42e94SMauro Carvalho Chehab            self.parameterdescs[name] = contents
174*e3b42e94SMauro Carvalho Chehab            self.sectcheck += name + " "
175*e3b42e94SMauro Carvalho Chehab            self.parameterdesc_start_lines[name] = self.new_start_line
176*e3b42e94SMauro Carvalho Chehab            self.new_start_line = 0
177*e3b42e94SMauro Carvalho Chehab
178*e3b42e94SMauro Carvalho Chehab        else:
179*e3b42e94SMauro Carvalho Chehab            if name in self.sections and self.sections[name] != "":
180*e3b42e94SMauro Carvalho Chehab                # Only warn on user-specified duplicate section names
181*e3b42e94SMauro Carvalho Chehab                if name != SECTION_DEFAULT:
182*e3b42e94SMauro Carvalho Chehab                    self.emit_msg(self.new_start_line,
183*e3b42e94SMauro Carvalho Chehab                                  f"duplicate section name '{name}'\n")
184*e3b42e94SMauro Carvalho Chehab                self.sections[name] += contents
185*e3b42e94SMauro Carvalho Chehab            else:
186*e3b42e94SMauro Carvalho Chehab                self.sections[name] = contents
187*e3b42e94SMauro Carvalho Chehab                self.sectionlist.append(name)
188*e3b42e94SMauro Carvalho Chehab                self.section_start_lines[name] = self.new_start_line
189*e3b42e94SMauro Carvalho Chehab                self.new_start_line = 0
190*e3b42e94SMauro Carvalho Chehab
191*e3b42e94SMauro Carvalho Chehab#        self.config.log.debug("Section: %s : %s", name, pformat(vars(self)))
192*e3b42e94SMauro Carvalho Chehab
193*e3b42e94SMauro Carvalho Chehab        if start_new:
194*e3b42e94SMauro Carvalho Chehab            self.section = SECTION_DEFAULT
195*e3b42e94SMauro Carvalho Chehab            self.contents = ""
196*e3b42e94SMauro Carvalho Chehab
197f9cdbc57SMauro Carvalho Chehab
198f9cdbc57SMauro Carvalho Chehabclass KernelDoc:
199f9cdbc57SMauro Carvalho Chehab    """
200f9cdbc57SMauro Carvalho Chehab    Read a C language source or header FILE and extract embedded
201f9cdbc57SMauro Carvalho Chehab    documentation comments.
202f9cdbc57SMauro Carvalho Chehab    """
203f9cdbc57SMauro Carvalho Chehab
204d966dc65SMauro Carvalho Chehab    # Section names
205d966dc65SMauro Carvalho Chehab
206d966dc65SMauro Carvalho Chehab    section_intro = "Introduction"
207d966dc65SMauro Carvalho Chehab    section_context = "Context"
208d966dc65SMauro Carvalho Chehab    section_return = "Return"
209d966dc65SMauro Carvalho Chehab
210d966dc65SMauro Carvalho Chehab    undescribed = "-- undescribed --"
211d966dc65SMauro Carvalho Chehab
212d966dc65SMauro Carvalho Chehab    def __init__(self, config, fname):
213d966dc65SMauro Carvalho Chehab        """Initialize internal variables"""
214d966dc65SMauro Carvalho Chehab
215d966dc65SMauro Carvalho Chehab        self.fname = fname
216d966dc65SMauro Carvalho Chehab        self.config = config
217d966dc65SMauro Carvalho Chehab
218d966dc65SMauro Carvalho Chehab        # Initial state for the state machines
219f9cdbc57SMauro Carvalho Chehab        self.state = state.NORMAL
220f9cdbc57SMauro Carvalho Chehab        self.inline_doc_state = state.INLINE_NA
221d966dc65SMauro Carvalho Chehab
222d966dc65SMauro Carvalho Chehab        # Store entry currently being processed
223d966dc65SMauro Carvalho Chehab        self.entry = None
224d966dc65SMauro Carvalho Chehab
225d966dc65SMauro Carvalho Chehab        # Place all potential outputs into an array
226d966dc65SMauro Carvalho Chehab        self.entries = []
227d966dc65SMauro Carvalho Chehab
228*e3b42e94SMauro Carvalho Chehab    def emit_msg(self, ln, msg, warning=True):
229d966dc65SMauro Carvalho Chehab        """Emit a message"""
230d966dc65SMauro Carvalho Chehab
2319cbc2d3bSMauro Carvalho Chehab        log_msg = f"{self.fname}:{ln} {msg}"
2329cbc2d3bSMauro Carvalho Chehab
2339cbc2d3bSMauro Carvalho Chehab        if self.entry:
234*e3b42e94SMauro Carvalho Chehab            self.entry.emit_msg(log_msg, warning)
2359cbc2d3bSMauro Carvalho Chehab            return
2369cbc2d3bSMauro Carvalho Chehab
237*e3b42e94SMauro Carvalho Chehab        if warning:
2389cbc2d3bSMauro Carvalho Chehab            self.config.log.warning(log_msg)
239*e3b42e94SMauro Carvalho Chehab        else:
240*e3b42e94SMauro Carvalho Chehab            self.config.log.info(log_msg)
241d966dc65SMauro Carvalho Chehab
242d966dc65SMauro Carvalho Chehab    def dump_section(self, start_new=True):
243d966dc65SMauro Carvalho Chehab        """
244d966dc65SMauro Carvalho Chehab        Dumps section contents to arrays/hashes intended for that purpose.
245d966dc65SMauro Carvalho Chehab        """
246d966dc65SMauro Carvalho Chehab
247*e3b42e94SMauro Carvalho Chehab        if self.entry:
248*e3b42e94SMauro Carvalho Chehab            self.entry.dump_section(start_new)
249d966dc65SMauro Carvalho Chehab
250485f6f79SMauro Carvalho Chehab    # TODO: rename it to store_declaration after removal of kernel-doc.pl
251d966dc65SMauro Carvalho Chehab    def output_declaration(self, dtype, name, **args):
252d966dc65SMauro Carvalho Chehab        """
253d966dc65SMauro Carvalho Chehab        Stores the entry into an entry array.
254d966dc65SMauro Carvalho Chehab
255d966dc65SMauro Carvalho Chehab        The actual output and output filters will be handled elsewhere
256d966dc65SMauro Carvalho Chehab        """
257d966dc65SMauro Carvalho Chehab
258d966dc65SMauro Carvalho Chehab        # The implementation here is different than the original kernel-doc:
259d966dc65SMauro Carvalho Chehab        # instead of checking for output filters or actually output anything,
260d966dc65SMauro Carvalho Chehab        # it just stores the declaration content at self.entries, as the
261d966dc65SMauro Carvalho Chehab        # output will happen on a separate class.
262d966dc65SMauro Carvalho Chehab        #
263d966dc65SMauro Carvalho Chehab        # For now, we're keeping the same name of the function just to make
264d966dc65SMauro Carvalho Chehab        # easier to compare the source code of both scripts
265d966dc65SMauro Carvalho Chehab
266d966dc65SMauro Carvalho Chehab        args["declaration_start_line"] = self.entry.declaration_start_line
267d966dc65SMauro Carvalho Chehab        args["type"] = dtype
2689cbc2d3bSMauro Carvalho Chehab        args["warnings"] = self.entry.warnings
269d966dc65SMauro Carvalho Chehab
270485f6f79SMauro Carvalho Chehab        # TODO: use colletions.OrderedDict to remove sectionlist
271d966dc65SMauro Carvalho Chehab
272d966dc65SMauro Carvalho Chehab        sections = args.get('sections', {})
273d966dc65SMauro Carvalho Chehab        sectionlist = args.get('sectionlist', [])
274d966dc65SMauro Carvalho Chehab
275d966dc65SMauro Carvalho Chehab        # Drop empty sections
276485f6f79SMauro Carvalho Chehab        # TODO: improve empty sections logic to emit warnings
277d966dc65SMauro Carvalho Chehab        for section in ["Description", "Return"]:
278d966dc65SMauro Carvalho Chehab            if section in sectionlist:
279d966dc65SMauro Carvalho Chehab                if not sections[section].rstrip():
280d966dc65SMauro Carvalho Chehab                    del sections[section]
281d966dc65SMauro Carvalho Chehab                    sectionlist.remove(section)
282d966dc65SMauro Carvalho Chehab
283d966dc65SMauro Carvalho Chehab        self.entries.append((name, args))
284d966dc65SMauro Carvalho Chehab
285d966dc65SMauro Carvalho Chehab        self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args))
286d966dc65SMauro Carvalho Chehab
287d966dc65SMauro Carvalho Chehab    def reset_state(self, ln):
288d966dc65SMauro Carvalho Chehab        """
289d966dc65SMauro Carvalho Chehab        Ancillary routine to create a new entry. It initializes all
290d966dc65SMauro Carvalho Chehab        variables used by the state machine.
291d966dc65SMauro Carvalho Chehab        """
292d966dc65SMauro Carvalho Chehab
293*e3b42e94SMauro Carvalho Chehab        self.entry = KernelEntry(self.config, ln)
294d966dc65SMauro Carvalho Chehab
295d966dc65SMauro Carvalho Chehab        # State flags
296f9cdbc57SMauro Carvalho Chehab        self.state = state.NORMAL
297f9cdbc57SMauro Carvalho Chehab        self.inline_doc_state = state.INLINE_NA
298d966dc65SMauro Carvalho Chehab
299d966dc65SMauro Carvalho Chehab    def push_parameter(self, ln, decl_type, param, dtype,
300d966dc65SMauro Carvalho Chehab                       org_arg, declaration_name):
301d966dc65SMauro Carvalho Chehab        """
302d966dc65SMauro Carvalho Chehab        Store parameters and their descriptions at self.entry.
303d966dc65SMauro Carvalho Chehab        """
304d966dc65SMauro Carvalho Chehab
305d966dc65SMauro Carvalho Chehab        if self.entry.anon_struct_union and dtype == "" and param == "}":
306d966dc65SMauro Carvalho Chehab            return  # Ignore the ending }; from anonymous struct/union
307d966dc65SMauro Carvalho Chehab
308d966dc65SMauro Carvalho Chehab        self.entry.anon_struct_union = False
309d966dc65SMauro Carvalho Chehab
31004a383ceSMauro Carvalho Chehab        param = KernRe(r'[\[\)].*').sub('', param, count=1)
311d966dc65SMauro Carvalho Chehab
312d966dc65SMauro Carvalho Chehab        if dtype == "" and param.endswith("..."):
31304a383ceSMauro Carvalho Chehab            if KernRe(r'\w\.\.\.$').search(param):
314d966dc65SMauro Carvalho Chehab                # For named variable parameters of the form `x...`,
315d966dc65SMauro Carvalho Chehab                # remove the dots
316d966dc65SMauro Carvalho Chehab                param = param[:-3]
317d966dc65SMauro Carvalho Chehab            else:
318d966dc65SMauro Carvalho Chehab                # Handles unnamed variable parameters
319d966dc65SMauro Carvalho Chehab                param = "..."
320d966dc65SMauro Carvalho Chehab
321d966dc65SMauro Carvalho Chehab            if param not in self.entry.parameterdescs or \
322d966dc65SMauro Carvalho Chehab                not self.entry.parameterdescs[param]:
323d966dc65SMauro Carvalho Chehab
324d966dc65SMauro Carvalho Chehab                self.entry.parameterdescs[param] = "variable arguments"
325d966dc65SMauro Carvalho Chehab
326d966dc65SMauro Carvalho Chehab        elif dtype == "" and (not param or param == "void"):
327d966dc65SMauro Carvalho Chehab            param = "void"
328d966dc65SMauro Carvalho Chehab            self.entry.parameterdescs[param] = "no arguments"
329d966dc65SMauro Carvalho Chehab
330d966dc65SMauro Carvalho Chehab        elif dtype == "" and param in ["struct", "union"]:
331d966dc65SMauro Carvalho Chehab            # Handle unnamed (anonymous) union or struct
332d966dc65SMauro Carvalho Chehab            dtype = param
333d966dc65SMauro Carvalho Chehab            param = "{unnamed_" + param + "}"
334d966dc65SMauro Carvalho Chehab            self.entry.parameterdescs[param] = "anonymous\n"
335d966dc65SMauro Carvalho Chehab            self.entry.anon_struct_union = True
336d966dc65SMauro Carvalho Chehab
337d966dc65SMauro Carvalho Chehab        # Handle cache group enforcing variables: they do not need
338d966dc65SMauro Carvalho Chehab        # to be described in header files
339d966dc65SMauro Carvalho Chehab        elif "__cacheline_group" in param:
340d966dc65SMauro Carvalho Chehab            # Ignore __cacheline_group_begin and __cacheline_group_end
341d966dc65SMauro Carvalho Chehab            return
342d966dc65SMauro Carvalho Chehab
343d966dc65SMauro Carvalho Chehab        # Warn if parameter has no description
344d966dc65SMauro Carvalho Chehab        # (but ignore ones starting with # as these are not parameters
345d966dc65SMauro Carvalho Chehab        # but inline preprocessor statements)
346d966dc65SMauro Carvalho Chehab        if param not in self.entry.parameterdescs and not param.startswith("#"):
347d966dc65SMauro Carvalho Chehab            self.entry.parameterdescs[param] = self.undescribed
348d966dc65SMauro Carvalho Chehab
3499cbc2d3bSMauro Carvalho Chehab            if "." not in param:
350d966dc65SMauro Carvalho Chehab                if decl_type == 'function':
351d966dc65SMauro Carvalho Chehab                    dname = f"{decl_type} parameter"
352d966dc65SMauro Carvalho Chehab                else:
353d966dc65SMauro Carvalho Chehab                    dname = f"{decl_type} member"
354d966dc65SMauro Carvalho Chehab
355*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
356d966dc65SMauro Carvalho Chehab                              f"{dname} '{param}' not described in '{declaration_name}'")
357d966dc65SMauro Carvalho Chehab
358d966dc65SMauro Carvalho Chehab        # Strip spaces from param so that it is one continuous string on
359d966dc65SMauro Carvalho Chehab        # parameterlist. This fixes a problem where check_sections()
360d966dc65SMauro Carvalho Chehab        # cannot find a parameter like "addr[6 + 2]" because it actually
361d966dc65SMauro Carvalho Chehab        # appears as "addr[6", "+", "2]" on the parameter list.
362d966dc65SMauro Carvalho Chehab        # However, it's better to maintain the param string unchanged for
363d966dc65SMauro Carvalho Chehab        # output, so just weaken the string compare in check_sections()
364d966dc65SMauro Carvalho Chehab        # to ignore "[blah" in a parameter string.
365d966dc65SMauro Carvalho Chehab
366d966dc65SMauro Carvalho Chehab        self.entry.parameterlist.append(param)
36704a383ceSMauro Carvalho Chehab        org_arg = KernRe(r'\s\s+').sub(' ', org_arg)
368d966dc65SMauro Carvalho Chehab        self.entry.parametertypes[param] = org_arg
369d966dc65SMauro Carvalho Chehab
370d966dc65SMauro Carvalho Chehab    def save_struct_actual(self, actual):
371d966dc65SMauro Carvalho Chehab        """
372d966dc65SMauro Carvalho Chehab        Strip all spaces from the actual param so that it looks like
373d966dc65SMauro Carvalho Chehab        one string item.
374d966dc65SMauro Carvalho Chehab        """
375d966dc65SMauro Carvalho Chehab
37604a383ceSMauro Carvalho Chehab        actual = KernRe(r'\s*').sub("", actual, count=1)
377d966dc65SMauro Carvalho Chehab
378d966dc65SMauro Carvalho Chehab        self.entry.struct_actual += actual + " "
379d966dc65SMauro Carvalho Chehab
380d966dc65SMauro Carvalho Chehab    def create_parameter_list(self, ln, decl_type, args,
381d966dc65SMauro Carvalho Chehab                              splitter, declaration_name):
382d966dc65SMauro Carvalho Chehab        """
383d966dc65SMauro Carvalho Chehab        Creates a list of parameters, storing them at self.entry.
384d966dc65SMauro Carvalho Chehab        """
385d966dc65SMauro Carvalho Chehab
386d966dc65SMauro Carvalho Chehab        # temporarily replace all commas inside function pointer definition
38704a383ceSMauro Carvalho Chehab        arg_expr = KernRe(r'(\([^\),]+),')
388d966dc65SMauro Carvalho Chehab        while arg_expr.search(args):
389d966dc65SMauro Carvalho Chehab            args = arg_expr.sub(r"\1#", args)
390d966dc65SMauro Carvalho Chehab
391d966dc65SMauro Carvalho Chehab        for arg in args.split(splitter):
392d966dc65SMauro Carvalho Chehab            # Strip comments
39304a383ceSMauro Carvalho Chehab            arg = KernRe(r'\/\*.*\*\/').sub('', arg)
394d966dc65SMauro Carvalho Chehab
395d966dc65SMauro Carvalho Chehab            # Ignore argument attributes
39604a383ceSMauro Carvalho Chehab            arg = KernRe(r'\sPOS0?\s').sub(' ', arg)
397d966dc65SMauro Carvalho Chehab
398d966dc65SMauro Carvalho Chehab            # Strip leading/trailing spaces
399d966dc65SMauro Carvalho Chehab            arg = arg.strip()
40004a383ceSMauro Carvalho Chehab            arg = KernRe(r'\s+').sub(' ', arg, count=1)
401d966dc65SMauro Carvalho Chehab
402d966dc65SMauro Carvalho Chehab            if arg.startswith('#'):
403d966dc65SMauro Carvalho Chehab                # Treat preprocessor directive as a typeless variable just to fill
404d966dc65SMauro Carvalho Chehab                # corresponding data structures "correctly". Catch it later in
405d966dc65SMauro Carvalho Chehab                # output_* subs.
406d966dc65SMauro Carvalho Chehab
407d966dc65SMauro Carvalho Chehab                # Treat preprocessor directive as a typeless variable
408d966dc65SMauro Carvalho Chehab                self.push_parameter(ln, decl_type, arg, "",
409d966dc65SMauro Carvalho Chehab                                    "", declaration_name)
410d966dc65SMauro Carvalho Chehab
41104a383ceSMauro Carvalho Chehab            elif KernRe(r'\(.+\)\s*\(').search(arg):
412d966dc65SMauro Carvalho Chehab                # Pointer-to-function
413d966dc65SMauro Carvalho Chehab
414d966dc65SMauro Carvalho Chehab                arg = arg.replace('#', ',')
415d966dc65SMauro Carvalho Chehab
41604a383ceSMauro Carvalho Chehab                r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)')
417d966dc65SMauro Carvalho Chehab                if r.match(arg):
418d966dc65SMauro Carvalho Chehab                    param = r.group(1)
419d966dc65SMauro Carvalho Chehab                else:
420*e3b42e94SMauro Carvalho Chehab                    self.emit_msg(ln, f"Invalid param: {arg}")
421d966dc65SMauro Carvalho Chehab                    param = arg
422d966dc65SMauro Carvalho Chehab
42304a383ceSMauro Carvalho Chehab                dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
424d966dc65SMauro Carvalho Chehab                self.save_struct_actual(param)
425d966dc65SMauro Carvalho Chehab                self.push_parameter(ln, decl_type, param, dtype,
426d966dc65SMauro Carvalho Chehab                                    arg, declaration_name)
427d966dc65SMauro Carvalho Chehab
42804a383ceSMauro Carvalho Chehab            elif KernRe(r'\(.+\)\s*\[').search(arg):
429d966dc65SMauro Carvalho Chehab                # Array-of-pointers
430d966dc65SMauro Carvalho Chehab
431d966dc65SMauro Carvalho Chehab                arg = arg.replace('#', ',')
43204a383ceSMauro Carvalho Chehab                r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)')
433d966dc65SMauro Carvalho Chehab                if r.match(arg):
434d966dc65SMauro Carvalho Chehab                    param = r.group(1)
435d966dc65SMauro Carvalho Chehab                else:
436*e3b42e94SMauro Carvalho Chehab                    self.emit_msg(ln, f"Invalid param: {arg}")
437d966dc65SMauro Carvalho Chehab                    param = arg
438d966dc65SMauro Carvalho Chehab
43904a383ceSMauro Carvalho Chehab                dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
440d966dc65SMauro Carvalho Chehab
441d966dc65SMauro Carvalho Chehab                self.save_struct_actual(param)
442d966dc65SMauro Carvalho Chehab                self.push_parameter(ln, decl_type, param, dtype,
443d966dc65SMauro Carvalho Chehab                                    arg, declaration_name)
444d966dc65SMauro Carvalho Chehab
445d966dc65SMauro Carvalho Chehab            elif arg:
44604a383ceSMauro Carvalho Chehab                arg = KernRe(r'\s*:\s*').sub(":", arg)
44704a383ceSMauro Carvalho Chehab                arg = KernRe(r'\s*\[').sub('[', arg)
448d966dc65SMauro Carvalho Chehab
44904a383ceSMauro Carvalho Chehab                args = KernRe(r'\s*,\s*').split(arg)
450d966dc65SMauro Carvalho Chehab                if args[0] and '*' in args[0]:
451d966dc65SMauro Carvalho Chehab                    args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])
452d966dc65SMauro Carvalho Chehab
453d966dc65SMauro Carvalho Chehab                first_arg = []
45404a383ceSMauro Carvalho Chehab                r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$')
455d966dc65SMauro Carvalho Chehab                if args[0] and r.match(args[0]):
456d966dc65SMauro Carvalho Chehab                    args.pop(0)
457d966dc65SMauro Carvalho Chehab                    first_arg.extend(r.group(1))
458d966dc65SMauro Carvalho Chehab                    first_arg.append(r.group(2))
459d966dc65SMauro Carvalho Chehab                else:
46004a383ceSMauro Carvalho Chehab                    first_arg = KernRe(r'\s+').split(args.pop(0))
461d966dc65SMauro Carvalho Chehab
462d966dc65SMauro Carvalho Chehab                args.insert(0, first_arg.pop())
463d966dc65SMauro Carvalho Chehab                dtype = ' '.join(first_arg)
464d966dc65SMauro Carvalho Chehab
465d966dc65SMauro Carvalho Chehab                for param in args:
46604a383ceSMauro Carvalho Chehab                    if KernRe(r'^(\*+)\s*(.*)').match(param):
46704a383ceSMauro Carvalho Chehab                        r = KernRe(r'^(\*+)\s*(.*)')
468d966dc65SMauro Carvalho Chehab                        if not r.match(param):
469*e3b42e94SMauro Carvalho Chehab                            self.emit_msg(ln, f"Invalid param: {param}")
470d966dc65SMauro Carvalho Chehab                            continue
471d966dc65SMauro Carvalho Chehab
472d966dc65SMauro Carvalho Chehab                        param = r.group(1)
473d966dc65SMauro Carvalho Chehab
474d966dc65SMauro Carvalho Chehab                        self.save_struct_actual(r.group(2))
475d966dc65SMauro Carvalho Chehab                        self.push_parameter(ln, decl_type, r.group(2),
476d966dc65SMauro Carvalho Chehab                                            f"{dtype} {r.group(1)}",
477d966dc65SMauro Carvalho Chehab                                            arg, declaration_name)
478d966dc65SMauro Carvalho Chehab
47904a383ceSMauro Carvalho Chehab                    elif KernRe(r'(.*?):(\w+)').search(param):
48004a383ceSMauro Carvalho Chehab                        r = KernRe(r'(.*?):(\w+)')
481d966dc65SMauro Carvalho Chehab                        if not r.match(param):
482*e3b42e94SMauro Carvalho Chehab                            self.emit_msg(ln, f"Invalid param: {param}")
483d966dc65SMauro Carvalho Chehab                            continue
484d966dc65SMauro Carvalho Chehab
485d966dc65SMauro Carvalho Chehab                        if dtype != "":  # Skip unnamed bit-fields
486d966dc65SMauro Carvalho Chehab                            self.save_struct_actual(r.group(1))
487d966dc65SMauro Carvalho Chehab                            self.push_parameter(ln, decl_type, r.group(1),
488d966dc65SMauro Carvalho Chehab                                                f"{dtype}:{r.group(2)}",
489d966dc65SMauro Carvalho Chehab                                                arg, declaration_name)
490d966dc65SMauro Carvalho Chehab                    else:
491d966dc65SMauro Carvalho Chehab                        self.save_struct_actual(param)
492d966dc65SMauro Carvalho Chehab                        self.push_parameter(ln, decl_type, param, dtype,
493d966dc65SMauro Carvalho Chehab                                            arg, declaration_name)
494d966dc65SMauro Carvalho Chehab
495d966dc65SMauro Carvalho Chehab    def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck):
496d966dc65SMauro Carvalho Chehab        """
497d966dc65SMauro Carvalho Chehab        Check for errors inside sections, emitting warnings if not found
498d966dc65SMauro Carvalho Chehab        parameters are described.
499d966dc65SMauro Carvalho Chehab        """
500d966dc65SMauro Carvalho Chehab
501d966dc65SMauro Carvalho Chehab        sects = sectcheck.split()
502d966dc65SMauro Carvalho Chehab        prms = prmscheck.split()
503d966dc65SMauro Carvalho Chehab        err = False
504d966dc65SMauro Carvalho Chehab
505d966dc65SMauro Carvalho Chehab        for sx in range(len(sects)):                  # pylint: disable=C0200
506d966dc65SMauro Carvalho Chehab            err = True
507d966dc65SMauro Carvalho Chehab            for px in range(len(prms)):               # pylint: disable=C0200
508d966dc65SMauro Carvalho Chehab                prm_clean = prms[px]
50904a383ceSMauro Carvalho Chehab                prm_clean = KernRe(r'\[.*\]').sub('', prm_clean)
510d966dc65SMauro Carvalho Chehab                prm_clean = attribute.sub('', prm_clean)
511d966dc65SMauro Carvalho Chehab
512d966dc65SMauro Carvalho Chehab                # ignore array size in a parameter string;
513d966dc65SMauro Carvalho Chehab                # however, the original param string may contain
514d966dc65SMauro Carvalho Chehab                # spaces, e.g.:  addr[6 + 2]
515d966dc65SMauro Carvalho Chehab                # and this appears in @prms as "addr[6" since the
516d966dc65SMauro Carvalho Chehab                # parameter list is split at spaces;
517d966dc65SMauro Carvalho Chehab                # hence just ignore "[..." for the sections check;
51804a383ceSMauro Carvalho Chehab                prm_clean = KernRe(r'\[.*').sub('', prm_clean)
519d966dc65SMauro Carvalho Chehab
520d966dc65SMauro Carvalho Chehab                if prm_clean == sects[sx]:
521d966dc65SMauro Carvalho Chehab                    err = False
522d966dc65SMauro Carvalho Chehab                    break
523d966dc65SMauro Carvalho Chehab
524d966dc65SMauro Carvalho Chehab            if err:
525d966dc65SMauro Carvalho Chehab                if decl_type == 'function':
526d966dc65SMauro Carvalho Chehab                    dname = f"{decl_type} parameter"
527d966dc65SMauro Carvalho Chehab                else:
528d966dc65SMauro Carvalho Chehab                    dname = f"{decl_type} member"
529d966dc65SMauro Carvalho Chehab
530*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
531d966dc65SMauro Carvalho Chehab                              f"Excess {dname} '{sects[sx]}' description in '{decl_name}'")
532d966dc65SMauro Carvalho Chehab
533d966dc65SMauro Carvalho Chehab    def check_return_section(self, ln, declaration_name, return_type):
534d966dc65SMauro Carvalho Chehab        """
535d966dc65SMauro Carvalho Chehab        If the function doesn't return void, warns about the lack of a
536d966dc65SMauro Carvalho Chehab        return description.
537d966dc65SMauro Carvalho Chehab        """
538d966dc65SMauro Carvalho Chehab
539d966dc65SMauro Carvalho Chehab        if not self.config.wreturn:
540d966dc65SMauro Carvalho Chehab            return
541d966dc65SMauro Carvalho Chehab
542d966dc65SMauro Carvalho Chehab        # Ignore an empty return type (It's a macro)
543d966dc65SMauro Carvalho Chehab        # Ignore functions with a "void" return type (but not "void *")
54404a383ceSMauro Carvalho Chehab        if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type):
545d966dc65SMauro Carvalho Chehab            return
546d966dc65SMauro Carvalho Chehab
547d966dc65SMauro Carvalho Chehab        if not self.entry.sections.get("Return", None):
548*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln,
549d966dc65SMauro Carvalho Chehab                          f"No description found for return value of '{declaration_name}'")
550d966dc65SMauro Carvalho Chehab
551d966dc65SMauro Carvalho Chehab    def dump_struct(self, ln, proto):
552d966dc65SMauro Carvalho Chehab        """
553d966dc65SMauro Carvalho Chehab        Store an entry for an struct or union
554d966dc65SMauro Carvalho Chehab        """
555d966dc65SMauro Carvalho Chehab
556d966dc65SMauro Carvalho Chehab        type_pattern = r'(struct|union)'
557d966dc65SMauro Carvalho Chehab
558d966dc65SMauro Carvalho Chehab        qualifiers = [
559d966dc65SMauro Carvalho Chehab            "__attribute__",
560d966dc65SMauro Carvalho Chehab            "__packed",
561d966dc65SMauro Carvalho Chehab            "__aligned",
562d966dc65SMauro Carvalho Chehab            "____cacheline_aligned_in_smp",
563d966dc65SMauro Carvalho Chehab            "____cacheline_aligned",
564d966dc65SMauro Carvalho Chehab        ]
565d966dc65SMauro Carvalho Chehab
566d966dc65SMauro Carvalho Chehab        definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"
56704a383ceSMauro Carvalho Chehab        struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)')
568d966dc65SMauro Carvalho Chehab
569d966dc65SMauro Carvalho Chehab        # Extract struct/union definition
570d966dc65SMauro Carvalho Chehab        members = None
571d966dc65SMauro Carvalho Chehab        declaration_name = None
572d966dc65SMauro Carvalho Chehab        decl_type = None
573d966dc65SMauro Carvalho Chehab
57404a383ceSMauro Carvalho Chehab        r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body)
575d966dc65SMauro Carvalho Chehab        if r.search(proto):
576d966dc65SMauro Carvalho Chehab            decl_type = r.group(1)
577d966dc65SMauro Carvalho Chehab            declaration_name = r.group(2)
578d966dc65SMauro Carvalho Chehab            members = r.group(3)
579d966dc65SMauro Carvalho Chehab        else:
58004a383ceSMauro Carvalho Chehab            r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')
581d966dc65SMauro Carvalho Chehab
582d966dc65SMauro Carvalho Chehab            if r.search(proto):
583d966dc65SMauro Carvalho Chehab                decl_type = r.group(1)
584d966dc65SMauro Carvalho Chehab                declaration_name = r.group(3)
585d966dc65SMauro Carvalho Chehab                members = r.group(2)
586d966dc65SMauro Carvalho Chehab
587d966dc65SMauro Carvalho Chehab        if not members:
588*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!")
589d966dc65SMauro Carvalho Chehab            return
590d966dc65SMauro Carvalho Chehab
591d966dc65SMauro Carvalho Chehab        if self.entry.identifier != declaration_name:
592*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln,
593d966dc65SMauro Carvalho Chehab                          f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n")
594d966dc65SMauro Carvalho Chehab            return
595d966dc65SMauro Carvalho Chehab
596d966dc65SMauro Carvalho Chehab        args_pattern = r'([^,)]+)'
597d966dc65SMauro Carvalho Chehab
598d966dc65SMauro Carvalho Chehab        sub_prefixes = [
59904a383ceSMauro Carvalho Chehab            (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''),
60004a383ceSMauro Carvalho Chehab            (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''),
601d966dc65SMauro Carvalho Chehab
602d966dc65SMauro Carvalho Chehab            # Strip comments
60304a383ceSMauro Carvalho Chehab            (KernRe(r'\/\*.*?\*\/', re.S), ''),
604d966dc65SMauro Carvalho Chehab
605d966dc65SMauro Carvalho Chehab            # Strip attributes
606d966dc65SMauro Carvalho Chehab            (attribute, ' '),
60704a383ceSMauro Carvalho Chehab            (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
60804a383ceSMauro Carvalho Chehab            (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
60904a383ceSMauro Carvalho Chehab            (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
61004a383ceSMauro Carvalho Chehab            (KernRe(r'\s*__packed\s*', re.S), ' '),
61104a383ceSMauro Carvalho Chehab            (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
61204a383ceSMauro Carvalho Chehab            (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '),
61304a383ceSMauro Carvalho Chehab            (KernRe(r'\s*____cacheline_aligned', re.S), ' '),
614d966dc65SMauro Carvalho Chehab
615d966dc65SMauro Carvalho Chehab            # Unwrap struct_group macros based on this definition:
616d966dc65SMauro Carvalho Chehab            # __struct_group(TAG, NAME, ATTRS, MEMBERS...)
617d966dc65SMauro Carvalho Chehab            # which has variants like: struct_group(NAME, MEMBERS...)
618d966dc65SMauro Carvalho Chehab            # Only MEMBERS arguments require documentation.
619d966dc65SMauro Carvalho Chehab            #
620d966dc65SMauro Carvalho Chehab            # Parsing them happens on two steps:
621d966dc65SMauro Carvalho Chehab            #
622d966dc65SMauro Carvalho Chehab            # 1. drop struct group arguments that aren't at MEMBERS,
623d966dc65SMauro Carvalho Chehab            #    storing them as STRUCT_GROUP(MEMBERS)
624d966dc65SMauro Carvalho Chehab            #
625d966dc65SMauro Carvalho Chehab            # 2. remove STRUCT_GROUP() ancillary macro.
626d966dc65SMauro Carvalho Chehab            #
627d966dc65SMauro Carvalho Chehab            # The original logic used to remove STRUCT_GROUP() using an
628d966dc65SMauro Carvalho Chehab            # advanced regex:
629d966dc65SMauro Carvalho Chehab            #
630d966dc65SMauro Carvalho Chehab            #   \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
631d966dc65SMauro Carvalho Chehab            #
632d966dc65SMauro Carvalho Chehab            # with two patterns that are incompatible with
633d966dc65SMauro Carvalho Chehab            # Python re module, as it has:
634d966dc65SMauro Carvalho Chehab            #
635d966dc65SMauro Carvalho Chehab            #   - a recursive pattern: (?1)
636d966dc65SMauro Carvalho Chehab            #   - an atomic grouping: (?>...)
637d966dc65SMauro Carvalho Chehab            #
638d966dc65SMauro Carvalho Chehab            # I tried a simpler version: but it didn't work either:
639d966dc65SMauro Carvalho Chehab            #   \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
640d966dc65SMauro Carvalho Chehab            #
641d966dc65SMauro Carvalho Chehab            # As it doesn't properly match the end parenthesis on some cases.
642d966dc65SMauro Carvalho Chehab            #
643d966dc65SMauro Carvalho Chehab            # So, a better solution was crafted: there's now a NestedMatch
644d966dc65SMauro Carvalho Chehab            # class that ensures that delimiters after a search are properly
645d966dc65SMauro Carvalho Chehab            # matched. So, the implementation to drop STRUCT_GROUP() will be
646d966dc65SMauro Carvalho Chehab            # handled in separate.
647d966dc65SMauro Carvalho Chehab
64804a383ceSMauro Carvalho Chehab            (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('),
64904a383ceSMauro Carvalho Chehab            (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('),
65004a383ceSMauro Carvalho Chehab            (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('),
65104a383ceSMauro Carvalho Chehab            (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('),
652d966dc65SMauro Carvalho Chehab
653d966dc65SMauro Carvalho Chehab            # Replace macros
654d966dc65SMauro Carvalho Chehab            #
655485f6f79SMauro Carvalho Chehab            # TODO: use NestedMatch for FOO($1, $2, ...) matches
656485f6f79SMauro Carvalho Chehab            #
657485f6f79SMauro Carvalho Chehab            # it is better to also move those to the NestedMatch logic,
658d966dc65SMauro Carvalho Chehab            # to ensure that parenthesis will be properly matched.
659d966dc65SMauro Carvalho Chehab
66004a383ceSMauro Carvalho Chehab            (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
66104a383ceSMauro Carvalho Chehab            (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
66204a383ceSMauro Carvalho Chehab            (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
66304a383ceSMauro Carvalho Chehab            (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
66404a383ceSMauro Carvalho Chehab            (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
66504a383ceSMauro Carvalho Chehab            (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
66604a383ceSMauro Carvalho Chehab            (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'),
66704a383ceSMauro Carvalho Chehab            (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'),
66804a383ceSMauro Carvalho Chehab            (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'),
669d966dc65SMauro Carvalho Chehab        ]
670d966dc65SMauro Carvalho Chehab
671d966dc65SMauro Carvalho Chehab        # Regexes here are guaranteed to have the end limiter matching
672d966dc65SMauro Carvalho Chehab        # the start delimiter. Yet, right now, only one replace group
673d966dc65SMauro Carvalho Chehab        # is allowed.
674d966dc65SMauro Carvalho Chehab
675d966dc65SMauro Carvalho Chehab        sub_nested_prefixes = [
676d966dc65SMauro Carvalho Chehab            (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
677d966dc65SMauro Carvalho Chehab        ]
678d966dc65SMauro Carvalho Chehab
679d966dc65SMauro Carvalho Chehab        for search, sub in sub_prefixes:
680d966dc65SMauro Carvalho Chehab            members = search.sub(sub, members)
681d966dc65SMauro Carvalho Chehab
682d966dc65SMauro Carvalho Chehab        nested = NestedMatch()
683d966dc65SMauro Carvalho Chehab
684d966dc65SMauro Carvalho Chehab        for search, sub in sub_nested_prefixes:
685d966dc65SMauro Carvalho Chehab            members = nested.sub(search, sub, members)
686d966dc65SMauro Carvalho Chehab
687d966dc65SMauro Carvalho Chehab        # Keeps the original declaration as-is
688d966dc65SMauro Carvalho Chehab        declaration = members
689d966dc65SMauro Carvalho Chehab
690d966dc65SMauro Carvalho Chehab        # Split nested struct/union elements
691d966dc65SMauro Carvalho Chehab        #
692d966dc65SMauro Carvalho Chehab        # This loop was simpler at the original kernel-doc perl version, as
693d966dc65SMauro Carvalho Chehab        #   while ($members =~ m/$struct_members/) { ... }
694d966dc65SMauro Carvalho Chehab        # reads 'members' string on each interaction.
695d966dc65SMauro Carvalho Chehab        #
696d966dc65SMauro Carvalho Chehab        # Python behavior is different: it parses 'members' only once,
697d966dc65SMauro Carvalho Chehab        # creating a list of tuples from the first interaction.
698d966dc65SMauro Carvalho Chehab        #
699d966dc65SMauro Carvalho Chehab        # On other words, this won't get nested structs.
700d966dc65SMauro Carvalho Chehab        #
701d966dc65SMauro Carvalho Chehab        # So, we need to have an extra loop on Python to override such
702d966dc65SMauro Carvalho Chehab        # re limitation.
703d966dc65SMauro Carvalho Chehab
704d966dc65SMauro Carvalho Chehab        while True:
705d966dc65SMauro Carvalho Chehab            tuples = struct_members.findall(members)
706d966dc65SMauro Carvalho Chehab            if not tuples:
707d966dc65SMauro Carvalho Chehab                break
708d966dc65SMauro Carvalho Chehab
709d966dc65SMauro Carvalho Chehab            for t in tuples:
710d966dc65SMauro Carvalho Chehab                newmember = ""
711d966dc65SMauro Carvalho Chehab                maintype = t[0]
712d966dc65SMauro Carvalho Chehab                s_ids = t[5]
713d966dc65SMauro Carvalho Chehab                content = t[3]
714d966dc65SMauro Carvalho Chehab
715d966dc65SMauro Carvalho Chehab                oldmember = "".join(t)
716d966dc65SMauro Carvalho Chehab
717d966dc65SMauro Carvalho Chehab                for s_id in s_ids.split(','):
718d966dc65SMauro Carvalho Chehab                    s_id = s_id.strip()
719d966dc65SMauro Carvalho Chehab
720d966dc65SMauro Carvalho Chehab                    newmember += f"{maintype} {s_id}; "
72104a383ceSMauro Carvalho Chehab                    s_id = KernRe(r'[:\[].*').sub('', s_id)
72204a383ceSMauro Carvalho Chehab                    s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)
723d966dc65SMauro Carvalho Chehab
724d966dc65SMauro Carvalho Chehab                    for arg in content.split(';'):
725d966dc65SMauro Carvalho Chehab                        arg = arg.strip()
726d966dc65SMauro Carvalho Chehab
727d966dc65SMauro Carvalho Chehab                        if not arg:
728d966dc65SMauro Carvalho Chehab                            continue
729d966dc65SMauro Carvalho Chehab
73004a383ceSMauro Carvalho Chehab                        r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)')
731d966dc65SMauro Carvalho Chehab                        if r.match(arg):
732d966dc65SMauro Carvalho Chehab                            # Pointer-to-function
733d966dc65SMauro Carvalho Chehab                            dtype = r.group(1)
734d966dc65SMauro Carvalho Chehab                            name = r.group(2)
735d966dc65SMauro Carvalho Chehab                            extra = r.group(3)
736d966dc65SMauro Carvalho Chehab
737d966dc65SMauro Carvalho Chehab                            if not name:
738d966dc65SMauro Carvalho Chehab                                continue
739d966dc65SMauro Carvalho Chehab
740d966dc65SMauro Carvalho Chehab                            if not s_id:
741d966dc65SMauro Carvalho Chehab                                # Anonymous struct/union
742d966dc65SMauro Carvalho Chehab                                newmember += f"{dtype}{name}{extra}; "
743d966dc65SMauro Carvalho Chehab                            else:
744d966dc65SMauro Carvalho Chehab                                newmember += f"{dtype}{s_id}.{name}{extra}; "
745d966dc65SMauro Carvalho Chehab
746d966dc65SMauro Carvalho Chehab                        else:
747d966dc65SMauro Carvalho Chehab                            arg = arg.strip()
748d966dc65SMauro Carvalho Chehab                            # Handle bitmaps
74904a383ceSMauro Carvalho Chehab                            arg = KernRe(r':\s*\d+\s*').sub('', arg)
750d966dc65SMauro Carvalho Chehab
751d966dc65SMauro Carvalho Chehab                            # Handle arrays
75204a383ceSMauro Carvalho Chehab                            arg = KernRe(r'\[.*\]').sub('', arg)
753d966dc65SMauro Carvalho Chehab
754d966dc65SMauro Carvalho Chehab                            # Handle multiple IDs
75504a383ceSMauro Carvalho Chehab                            arg = KernRe(r'\s*,\s*').sub(',', arg)
756d966dc65SMauro Carvalho Chehab
75704a383ceSMauro Carvalho Chehab                            r = KernRe(r'(.*)\s+([\S+,]+)')
758d966dc65SMauro Carvalho Chehab
759d966dc65SMauro Carvalho Chehab                            if r.search(arg):
760d966dc65SMauro Carvalho Chehab                                dtype = r.group(1)
761d966dc65SMauro Carvalho Chehab                                names = r.group(2)
762d966dc65SMauro Carvalho Chehab                            else:
763d966dc65SMauro Carvalho Chehab                                newmember += f"{arg}; "
764d966dc65SMauro Carvalho Chehab                                continue
765d966dc65SMauro Carvalho Chehab
766d966dc65SMauro Carvalho Chehab                            for name in names.split(','):
76704a383ceSMauro Carvalho Chehab                                name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip()
768d966dc65SMauro Carvalho Chehab
769d966dc65SMauro Carvalho Chehab                                if not name:
770d966dc65SMauro Carvalho Chehab                                    continue
771d966dc65SMauro Carvalho Chehab
772d966dc65SMauro Carvalho Chehab                                if not s_id:
773d966dc65SMauro Carvalho Chehab                                    # Anonymous struct/union
774d966dc65SMauro Carvalho Chehab                                    newmember += f"{dtype} {name}; "
775d966dc65SMauro Carvalho Chehab                                else:
776d966dc65SMauro Carvalho Chehab                                    newmember += f"{dtype} {s_id}.{name}; "
777d966dc65SMauro Carvalho Chehab
778d966dc65SMauro Carvalho Chehab                members = members.replace(oldmember, newmember)
779d966dc65SMauro Carvalho Chehab
780d966dc65SMauro Carvalho Chehab        # Ignore other nested elements, like enums
781d966dc65SMauro Carvalho Chehab        members = re.sub(r'(\{[^\{\}]*\})', '', members)
782d966dc65SMauro Carvalho Chehab
783d966dc65SMauro Carvalho Chehab        self.create_parameter_list(ln, decl_type, members, ';',
784d966dc65SMauro Carvalho Chehab                                   declaration_name)
785d966dc65SMauro Carvalho Chehab        self.check_sections(ln, declaration_name, decl_type,
786d966dc65SMauro Carvalho Chehab                            self.entry.sectcheck, self.entry.struct_actual)
787d966dc65SMauro Carvalho Chehab
788d966dc65SMauro Carvalho Chehab        # Adjust declaration for better display
78904a383ceSMauro Carvalho Chehab        declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)
79004a383ceSMauro Carvalho Chehab        declaration = KernRe(r'\}\s+;').sub('};', declaration)
791d966dc65SMauro Carvalho Chehab
792d966dc65SMauro Carvalho Chehab        # Better handle inlined enums
793d966dc65SMauro Carvalho Chehab        while True:
79404a383ceSMauro Carvalho Chehab            r = KernRe(r'(enum\s+\{[^\}]+),([^\n])')
795d966dc65SMauro Carvalho Chehab            if not r.search(declaration):
796d966dc65SMauro Carvalho Chehab                break
797d966dc65SMauro Carvalho Chehab
798d966dc65SMauro Carvalho Chehab            declaration = r.sub(r'\1,\n\2', declaration)
799d966dc65SMauro Carvalho Chehab
800d966dc65SMauro Carvalho Chehab        def_args = declaration.split('\n')
801d966dc65SMauro Carvalho Chehab        level = 1
802d966dc65SMauro Carvalho Chehab        declaration = ""
803d966dc65SMauro Carvalho Chehab        for clause in def_args:
804d966dc65SMauro Carvalho Chehab
805d966dc65SMauro Carvalho Chehab            clause = clause.strip()
80604a383ceSMauro Carvalho Chehab            clause = KernRe(r'\s+').sub(' ', clause, count=1)
807d966dc65SMauro Carvalho Chehab
808d966dc65SMauro Carvalho Chehab            if not clause:
809d966dc65SMauro Carvalho Chehab                continue
810d966dc65SMauro Carvalho Chehab
811d966dc65SMauro Carvalho Chehab            if '}' in clause and level > 1:
812d966dc65SMauro Carvalho Chehab                level -= 1
813d966dc65SMauro Carvalho Chehab
81404a383ceSMauro Carvalho Chehab            if not KernRe(r'^\s*#').match(clause):
815d966dc65SMauro Carvalho Chehab                declaration += "\t" * level
816d966dc65SMauro Carvalho Chehab
817d966dc65SMauro Carvalho Chehab            declaration += "\t" + clause + "\n"
818d966dc65SMauro Carvalho Chehab            if "{" in clause and "}" not in clause:
819d966dc65SMauro Carvalho Chehab                level += 1
820d966dc65SMauro Carvalho Chehab
821d966dc65SMauro Carvalho Chehab        self.output_declaration(decl_type, declaration_name,
822d966dc65SMauro Carvalho Chehab                                struct=declaration_name,
823d966dc65SMauro Carvalho Chehab                                definition=declaration,
824d966dc65SMauro Carvalho Chehab                                parameterlist=self.entry.parameterlist,
825d966dc65SMauro Carvalho Chehab                                parameterdescs=self.entry.parameterdescs,
826d966dc65SMauro Carvalho Chehab                                parametertypes=self.entry.parametertypes,
827c3597ab2SMauro Carvalho Chehab                                parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
828d966dc65SMauro Carvalho Chehab                                sectionlist=self.entry.sectionlist,
829d966dc65SMauro Carvalho Chehab                                sections=self.entry.sections,
830c3597ab2SMauro Carvalho Chehab                                section_start_lines=self.entry.section_start_lines,
831d966dc65SMauro Carvalho Chehab                                purpose=self.entry.declaration_purpose)
832d966dc65SMauro Carvalho Chehab
833d966dc65SMauro Carvalho Chehab    def dump_enum(self, ln, proto):
834d966dc65SMauro Carvalho Chehab        """
835d966dc65SMauro Carvalho Chehab        Stores an enum inside self.entries array.
836d966dc65SMauro Carvalho Chehab        """
837d966dc65SMauro Carvalho Chehab
838d966dc65SMauro Carvalho Chehab        # Ignore members marked private
83904a383ceSMauro Carvalho Chehab        proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto)
84004a383ceSMauro Carvalho Chehab        proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto)
841d966dc65SMauro Carvalho Chehab
842d966dc65SMauro Carvalho Chehab        # Strip comments
84304a383ceSMauro Carvalho Chehab        proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto)
844d966dc65SMauro Carvalho Chehab
845d966dc65SMauro Carvalho Chehab        # Strip #define macros inside enums
84604a383ceSMauro Carvalho Chehab        proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)
847d966dc65SMauro Carvalho Chehab
848d966dc65SMauro Carvalho Chehab        members = None
849d966dc65SMauro Carvalho Chehab        declaration_name = None
850d966dc65SMauro Carvalho Chehab
85104a383ceSMauro Carvalho Chehab        r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')
852d966dc65SMauro Carvalho Chehab        if r.search(proto):
853d966dc65SMauro Carvalho Chehab            declaration_name = r.group(2)
854d966dc65SMauro Carvalho Chehab            members = r.group(1).rstrip()
855d966dc65SMauro Carvalho Chehab        else:
85604a383ceSMauro Carvalho Chehab            r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}')
857d966dc65SMauro Carvalho Chehab            if r.match(proto):
858d966dc65SMauro Carvalho Chehab                declaration_name = r.group(1)
859d966dc65SMauro Carvalho Chehab                members = r.group(2).rstrip()
860d966dc65SMauro Carvalho Chehab
861d966dc65SMauro Carvalho Chehab        if not members:
862*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")
863d966dc65SMauro Carvalho Chehab            return
864d966dc65SMauro Carvalho Chehab
865d966dc65SMauro Carvalho Chehab        if self.entry.identifier != declaration_name:
866d966dc65SMauro Carvalho Chehab            if self.entry.identifier == "":
867*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
868d966dc65SMauro Carvalho Chehab                              f"{proto}: wrong kernel-doc identifier on prototype")
869d966dc65SMauro Carvalho Chehab            else:
870*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
871d966dc65SMauro Carvalho Chehab                              f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead")
872d966dc65SMauro Carvalho Chehab            return
873d966dc65SMauro Carvalho Chehab
874d966dc65SMauro Carvalho Chehab        if not declaration_name:
875d966dc65SMauro Carvalho Chehab            declaration_name = "(anonymous)"
876d966dc65SMauro Carvalho Chehab
877d966dc65SMauro Carvalho Chehab        member_set = set()
878d966dc65SMauro Carvalho Chehab
87904a383ceSMauro Carvalho Chehab        members = KernRe(r'\([^;]*?[\)]').sub('', members)
880d966dc65SMauro Carvalho Chehab
881d966dc65SMauro Carvalho Chehab        for arg in members.split(','):
882d966dc65SMauro Carvalho Chehab            if not arg:
883d966dc65SMauro Carvalho Chehab                continue
88404a383ceSMauro Carvalho Chehab            arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg)
885d966dc65SMauro Carvalho Chehab            self.entry.parameterlist.append(arg)
886d966dc65SMauro Carvalho Chehab            if arg not in self.entry.parameterdescs:
887d966dc65SMauro Carvalho Chehab                self.entry.parameterdescs[arg] = self.undescribed
888*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
889d966dc65SMauro Carvalho Chehab                              f"Enum value '{arg}' not described in enum '{declaration_name}'")
890d966dc65SMauro Carvalho Chehab            member_set.add(arg)
891d966dc65SMauro Carvalho Chehab
892d966dc65SMauro Carvalho Chehab        for k in self.entry.parameterdescs:
893d966dc65SMauro Carvalho Chehab            if k not in member_set:
894*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
895d966dc65SMauro Carvalho Chehab                              f"Excess enum value '%{k}' description in '{declaration_name}'")
896d966dc65SMauro Carvalho Chehab
897d966dc65SMauro Carvalho Chehab        self.output_declaration('enum', declaration_name,
898d966dc65SMauro Carvalho Chehab                                enum=declaration_name,
899d966dc65SMauro Carvalho Chehab                                parameterlist=self.entry.parameterlist,
900d966dc65SMauro Carvalho Chehab                                parameterdescs=self.entry.parameterdescs,
901c3597ab2SMauro Carvalho Chehab                                parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
902d966dc65SMauro Carvalho Chehab                                sectionlist=self.entry.sectionlist,
903d966dc65SMauro Carvalho Chehab                                sections=self.entry.sections,
904c3597ab2SMauro Carvalho Chehab                                section_start_lines=self.entry.section_start_lines,
905d966dc65SMauro Carvalho Chehab                                purpose=self.entry.declaration_purpose)
906d966dc65SMauro Carvalho Chehab
907d966dc65SMauro Carvalho Chehab    def dump_declaration(self, ln, prototype):
908d966dc65SMauro Carvalho Chehab        """
909d966dc65SMauro Carvalho Chehab        Stores a data declaration inside self.entries array.
910d966dc65SMauro Carvalho Chehab        """
911d966dc65SMauro Carvalho Chehab
912d966dc65SMauro Carvalho Chehab        if self.entry.decl_type == "enum":
913d966dc65SMauro Carvalho Chehab            self.dump_enum(ln, prototype)
914d966dc65SMauro Carvalho Chehab            return
915d966dc65SMauro Carvalho Chehab
916d966dc65SMauro Carvalho Chehab        if self.entry.decl_type == "typedef":
917d966dc65SMauro Carvalho Chehab            self.dump_typedef(ln, prototype)
918d966dc65SMauro Carvalho Chehab            return
919d966dc65SMauro Carvalho Chehab
920d966dc65SMauro Carvalho Chehab        if self.entry.decl_type in ["union", "struct"]:
921d966dc65SMauro Carvalho Chehab            self.dump_struct(ln, prototype)
922d966dc65SMauro Carvalho Chehab            return
923d966dc65SMauro Carvalho Chehab
924d966dc65SMauro Carvalho Chehab        self.output_declaration(self.entry.decl_type, prototype,
925d966dc65SMauro Carvalho Chehab                                entry=self.entry)
926d966dc65SMauro Carvalho Chehab
927d966dc65SMauro Carvalho Chehab    def dump_function(self, ln, prototype):
928d966dc65SMauro Carvalho Chehab        """
929d966dc65SMauro Carvalho Chehab        Stores a function of function macro inside self.entries array.
930d966dc65SMauro Carvalho Chehab        """
931d966dc65SMauro Carvalho Chehab
932d966dc65SMauro Carvalho Chehab        func_macro = False
933d966dc65SMauro Carvalho Chehab        return_type = ''
934d966dc65SMauro Carvalho Chehab        decl_type = 'function'
935d966dc65SMauro Carvalho Chehab
936d966dc65SMauro Carvalho Chehab        # Prefixes that would be removed
937d966dc65SMauro Carvalho Chehab        sub_prefixes = [
938d966dc65SMauro Carvalho Chehab            (r"^static +", "", 0),
939d966dc65SMauro Carvalho Chehab            (r"^extern +", "", 0),
940d966dc65SMauro Carvalho Chehab            (r"^asmlinkage +", "", 0),
941d966dc65SMauro Carvalho Chehab            (r"^inline +", "", 0),
942d966dc65SMauro Carvalho Chehab            (r"^__inline__ +", "", 0),
943d966dc65SMauro Carvalho Chehab            (r"^__inline +", "", 0),
944d966dc65SMauro Carvalho Chehab            (r"^__always_inline +", "", 0),
945d966dc65SMauro Carvalho Chehab            (r"^noinline +", "", 0),
946d966dc65SMauro Carvalho Chehab            (r"^__FORTIFY_INLINE +", "", 0),
947d966dc65SMauro Carvalho Chehab            (r"__init +", "", 0),
948d966dc65SMauro Carvalho Chehab            (r"__init_or_module +", "", 0),
949d966dc65SMauro Carvalho Chehab            (r"__deprecated +", "", 0),
950d966dc65SMauro Carvalho Chehab            (r"__flatten +", "", 0),
951d966dc65SMauro Carvalho Chehab            (r"__meminit +", "", 0),
952d966dc65SMauro Carvalho Chehab            (r"__must_check +", "", 0),
953d966dc65SMauro Carvalho Chehab            (r"__weak +", "", 0),
954d966dc65SMauro Carvalho Chehab            (r"__sched +", "", 0),
955d966dc65SMauro Carvalho Chehab            (r"_noprof", "", 0),
956d966dc65SMauro Carvalho Chehab            (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0),
957d966dc65SMauro Carvalho Chehab            (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0),
958d966dc65SMauro Carvalho Chehab            (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0),
959d966dc65SMauro Carvalho Chehab            (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0),
960d966dc65SMauro Carvalho Chehab            (r"__attribute_const__ +", "", 0),
961d966dc65SMauro Carvalho Chehab
962d966dc65SMauro Carvalho Chehab            # It seems that Python support for re.X is broken:
963d966dc65SMauro Carvalho Chehab            # At least for me (Python 3.13), this didn't work
964d966dc65SMauro Carvalho Chehab#            (r"""
965d966dc65SMauro Carvalho Chehab#              __attribute__\s*\(\(
966d966dc65SMauro Carvalho Chehab#                (?:
967d966dc65SMauro Carvalho Chehab#                    [\w\s]+          # attribute name
968d966dc65SMauro Carvalho Chehab#                    (?:\([^)]*\))?   # attribute arguments
969d966dc65SMauro Carvalho Chehab#                    \s*,?            # optional comma at the end
970d966dc65SMauro Carvalho Chehab#                )+
971d966dc65SMauro Carvalho Chehab#              \)\)\s+
972d966dc65SMauro Carvalho Chehab#             """, "", re.X),
973d966dc65SMauro Carvalho Chehab
974d966dc65SMauro Carvalho Chehab            # So, remove whitespaces and comments from it
975d966dc65SMauro Carvalho Chehab            (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0),
976d966dc65SMauro Carvalho Chehab        ]
977d966dc65SMauro Carvalho Chehab
978d966dc65SMauro Carvalho Chehab        for search, sub, flags in sub_prefixes:
97904a383ceSMauro Carvalho Chehab            prototype = KernRe(search, flags).sub(sub, prototype)
980d966dc65SMauro Carvalho Chehab
981d966dc65SMauro Carvalho Chehab        # Macros are a special case, as they change the prototype format
98204a383ceSMauro Carvalho Chehab        new_proto = KernRe(r"^#\s*define\s+").sub("", prototype)
983d966dc65SMauro Carvalho Chehab        if new_proto != prototype:
984d966dc65SMauro Carvalho Chehab            is_define_proto = True
985d966dc65SMauro Carvalho Chehab            prototype = new_proto
986d966dc65SMauro Carvalho Chehab        else:
987d966dc65SMauro Carvalho Chehab            is_define_proto = False
988d966dc65SMauro Carvalho Chehab
989d966dc65SMauro Carvalho Chehab        # Yes, this truly is vile.  We are looking for:
990d966dc65SMauro Carvalho Chehab        # 1. Return type (may be nothing if we're looking at a macro)
991d966dc65SMauro Carvalho Chehab        # 2. Function name
992d966dc65SMauro Carvalho Chehab        # 3. Function parameters.
993d966dc65SMauro Carvalho Chehab        #
994d966dc65SMauro Carvalho Chehab        # All the while we have to watch out for function pointer parameters
995d966dc65SMauro Carvalho Chehab        # (which IIRC is what the two sections are for), C types (these
996d966dc65SMauro Carvalho Chehab        # regexps don't even start to express all the possibilities), and
997d966dc65SMauro Carvalho Chehab        # so on.
998d966dc65SMauro Carvalho Chehab        #
999d966dc65SMauro Carvalho Chehab        # If you mess with these regexps, it's a good idea to check that
1000d966dc65SMauro Carvalho Chehab        # the following functions' documentation still comes out right:
1001d966dc65SMauro Carvalho Chehab        # - parport_register_device (function pointer parameters)
1002d966dc65SMauro Carvalho Chehab        # - atomic_set (macro)
1003d966dc65SMauro Carvalho Chehab        # - pci_match_device, __copy_to_user (long return type)
1004d966dc65SMauro Carvalho Chehab
1005d966dc65SMauro Carvalho Chehab        name = r'[a-zA-Z0-9_~:]+'
1006d966dc65SMauro Carvalho Chehab        prototype_end1 = r'[^\(]*'
1007d966dc65SMauro Carvalho Chehab        prototype_end2 = r'[^\{]*'
1008d966dc65SMauro Carvalho Chehab        prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)'
1009d966dc65SMauro Carvalho Chehab
1010d966dc65SMauro Carvalho Chehab        # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group.
1011d966dc65SMauro Carvalho Chehab        # So, this needs to be mapped in Python with (?:...)? or (?:...)+
1012d966dc65SMauro Carvalho Chehab
1013d966dc65SMauro Carvalho Chehab        type1 = r'(?:[\w\s]+)?'
1014d966dc65SMauro Carvalho Chehab        type2 = r'(?:[\w\s]+\*+)+'
1015d966dc65SMauro Carvalho Chehab
1016d966dc65SMauro Carvalho Chehab        found = False
1017d966dc65SMauro Carvalho Chehab
1018d966dc65SMauro Carvalho Chehab        if is_define_proto:
101904a383ceSMauro Carvalho Chehab            r = KernRe(r'^()(' + name + r')\s+')
1020d966dc65SMauro Carvalho Chehab
1021d966dc65SMauro Carvalho Chehab            if r.search(prototype):
1022d966dc65SMauro Carvalho Chehab                return_type = ''
1023d966dc65SMauro Carvalho Chehab                declaration_name = r.group(2)
1024d966dc65SMauro Carvalho Chehab                func_macro = True
1025d966dc65SMauro Carvalho Chehab
1026d966dc65SMauro Carvalho Chehab                found = True
1027d966dc65SMauro Carvalho Chehab
1028d966dc65SMauro Carvalho Chehab        if not found:
1029d966dc65SMauro Carvalho Chehab            patterns = [
1030d966dc65SMauro Carvalho Chehab                rf'^()({name})\s*{prototype_end}',
1031d966dc65SMauro Carvalho Chehab                rf'^({type1})\s+({name})\s*{prototype_end}',
1032d966dc65SMauro Carvalho Chehab                rf'^({type2})\s*({name})\s*{prototype_end}',
1033d966dc65SMauro Carvalho Chehab            ]
1034d966dc65SMauro Carvalho Chehab
1035d966dc65SMauro Carvalho Chehab            for p in patterns:
103604a383ceSMauro Carvalho Chehab                r = KernRe(p)
1037d966dc65SMauro Carvalho Chehab
1038d966dc65SMauro Carvalho Chehab                if r.match(prototype):
1039d966dc65SMauro Carvalho Chehab
1040d966dc65SMauro Carvalho Chehab                    return_type = r.group(1)
1041d966dc65SMauro Carvalho Chehab                    declaration_name = r.group(2)
1042d966dc65SMauro Carvalho Chehab                    args = r.group(3)
1043d966dc65SMauro Carvalho Chehab
1044d966dc65SMauro Carvalho Chehab                    self.create_parameter_list(ln, decl_type, args, ',',
1045d966dc65SMauro Carvalho Chehab                                               declaration_name)
1046d966dc65SMauro Carvalho Chehab
1047d966dc65SMauro Carvalho Chehab                    found = True
1048d966dc65SMauro Carvalho Chehab                    break
1049d966dc65SMauro Carvalho Chehab        if not found:
1050*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln,
1051d966dc65SMauro Carvalho Chehab                          f"cannot understand function prototype: '{prototype}'")
1052d966dc65SMauro Carvalho Chehab            return
1053d966dc65SMauro Carvalho Chehab
1054d966dc65SMauro Carvalho Chehab        if self.entry.identifier != declaration_name:
1055*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln,
1056d966dc65SMauro Carvalho Chehab                          f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead")
1057d966dc65SMauro Carvalho Chehab            return
1058d966dc65SMauro Carvalho Chehab
1059d966dc65SMauro Carvalho Chehab        prms = " ".join(self.entry.parameterlist)
1060d966dc65SMauro Carvalho Chehab        self.check_sections(ln, declaration_name, "function",
1061d966dc65SMauro Carvalho Chehab                            self.entry.sectcheck, prms)
1062d966dc65SMauro Carvalho Chehab
1063d966dc65SMauro Carvalho Chehab        self.check_return_section(ln, declaration_name, return_type)
1064d966dc65SMauro Carvalho Chehab
1065d966dc65SMauro Carvalho Chehab        if 'typedef' in return_type:
1066d966dc65SMauro Carvalho Chehab            self.output_declaration(decl_type, declaration_name,
1067d966dc65SMauro Carvalho Chehab                                    function=declaration_name,
1068d966dc65SMauro Carvalho Chehab                                    typedef=True,
1069d966dc65SMauro Carvalho Chehab                                    functiontype=return_type,
1070d966dc65SMauro Carvalho Chehab                                    parameterlist=self.entry.parameterlist,
1071d966dc65SMauro Carvalho Chehab                                    parameterdescs=self.entry.parameterdescs,
1072d966dc65SMauro Carvalho Chehab                                    parametertypes=self.entry.parametertypes,
1073c3597ab2SMauro Carvalho Chehab                                    parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
1074d966dc65SMauro Carvalho Chehab                                    sectionlist=self.entry.sectionlist,
1075d966dc65SMauro Carvalho Chehab                                    sections=self.entry.sections,
1076c3597ab2SMauro Carvalho Chehab                                    section_start_lines=self.entry.section_start_lines,
1077d966dc65SMauro Carvalho Chehab                                    purpose=self.entry.declaration_purpose,
1078d966dc65SMauro Carvalho Chehab                                    func_macro=func_macro)
1079d966dc65SMauro Carvalho Chehab        else:
1080d966dc65SMauro Carvalho Chehab            self.output_declaration(decl_type, declaration_name,
1081d966dc65SMauro Carvalho Chehab                                    function=declaration_name,
1082d966dc65SMauro Carvalho Chehab                                    typedef=False,
1083d966dc65SMauro Carvalho Chehab                                    functiontype=return_type,
1084d966dc65SMauro Carvalho Chehab                                    parameterlist=self.entry.parameterlist,
1085d966dc65SMauro Carvalho Chehab                                    parameterdescs=self.entry.parameterdescs,
1086d966dc65SMauro Carvalho Chehab                                    parametertypes=self.entry.parametertypes,
1087c3597ab2SMauro Carvalho Chehab                                    parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
1088d966dc65SMauro Carvalho Chehab                                    sectionlist=self.entry.sectionlist,
1089d966dc65SMauro Carvalho Chehab                                    sections=self.entry.sections,
1090c3597ab2SMauro Carvalho Chehab                                    section_start_lines=self.entry.section_start_lines,
1091d966dc65SMauro Carvalho Chehab                                    purpose=self.entry.declaration_purpose,
1092d966dc65SMauro Carvalho Chehab                                    func_macro=func_macro)
1093d966dc65SMauro Carvalho Chehab
1094d966dc65SMauro Carvalho Chehab    def dump_typedef(self, ln, proto):
1095d966dc65SMauro Carvalho Chehab        """
1096d966dc65SMauro Carvalho Chehab        Stores a typedef inside self.entries array.
1097d966dc65SMauro Carvalho Chehab        """
1098d966dc65SMauro Carvalho Chehab
1099de258fa8SSean Anderson        typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*'
1100d966dc65SMauro Carvalho Chehab        typedef_ident = r'\*?\s*(\w\S+)\s*'
1101d966dc65SMauro Carvalho Chehab        typedef_args = r'\s*\((.*)\);'
1102d966dc65SMauro Carvalho Chehab
110304a383ceSMauro Carvalho Chehab        typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)
110404a383ceSMauro Carvalho Chehab        typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args)
1105d966dc65SMauro Carvalho Chehab
1106d966dc65SMauro Carvalho Chehab        # Strip comments
110704a383ceSMauro Carvalho Chehab        proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto)
1108d966dc65SMauro Carvalho Chehab
1109d966dc65SMauro Carvalho Chehab        # Parse function typedef prototypes
1110d966dc65SMauro Carvalho Chehab        for r in [typedef1, typedef2]:
1111d966dc65SMauro Carvalho Chehab            if not r.match(proto):
1112d966dc65SMauro Carvalho Chehab                continue
1113d966dc65SMauro Carvalho Chehab
1114d966dc65SMauro Carvalho Chehab            return_type = r.group(1).strip()
1115d966dc65SMauro Carvalho Chehab            declaration_name = r.group(2)
1116d966dc65SMauro Carvalho Chehab            args = r.group(3)
1117d966dc65SMauro Carvalho Chehab
1118d966dc65SMauro Carvalho Chehab            if self.entry.identifier != declaration_name:
1119*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1120d966dc65SMauro Carvalho Chehab                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
1121d966dc65SMauro Carvalho Chehab                return
1122d966dc65SMauro Carvalho Chehab
1123d966dc65SMauro Carvalho Chehab            decl_type = 'function'
1124d966dc65SMauro Carvalho Chehab            self.create_parameter_list(ln, decl_type, args, ',', declaration_name)
1125d966dc65SMauro Carvalho Chehab
1126d966dc65SMauro Carvalho Chehab            self.output_declaration(decl_type, declaration_name,
1127d966dc65SMauro Carvalho Chehab                                    function=declaration_name,
1128d966dc65SMauro Carvalho Chehab                                    typedef=True,
1129d966dc65SMauro Carvalho Chehab                                    functiontype=return_type,
1130d966dc65SMauro Carvalho Chehab                                    parameterlist=self.entry.parameterlist,
1131d966dc65SMauro Carvalho Chehab                                    parameterdescs=self.entry.parameterdescs,
1132d966dc65SMauro Carvalho Chehab                                    parametertypes=self.entry.parametertypes,
1133c3597ab2SMauro Carvalho Chehab                                    parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
1134d966dc65SMauro Carvalho Chehab                                    sectionlist=self.entry.sectionlist,
1135d966dc65SMauro Carvalho Chehab                                    sections=self.entry.sections,
1136c3597ab2SMauro Carvalho Chehab                                    section_start_lines=self.entry.section_start_lines,
1137d966dc65SMauro Carvalho Chehab                                    purpose=self.entry.declaration_purpose)
1138d966dc65SMauro Carvalho Chehab            return
1139d966dc65SMauro Carvalho Chehab
1140d966dc65SMauro Carvalho Chehab        # Handle nested parentheses or brackets
114104a383ceSMauro Carvalho Chehab        r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$')
1142d966dc65SMauro Carvalho Chehab        while r.search(proto):
1143d966dc65SMauro Carvalho Chehab            proto = r.sub('', proto)
1144d966dc65SMauro Carvalho Chehab
1145d966dc65SMauro Carvalho Chehab        # Parse simple typedefs
114604a383ceSMauro Carvalho Chehab        r = KernRe(r'typedef.*\s+(\w+)\s*;')
1147d966dc65SMauro Carvalho Chehab        if r.match(proto):
1148d966dc65SMauro Carvalho Chehab            declaration_name = r.group(1)
1149d966dc65SMauro Carvalho Chehab
1150d966dc65SMauro Carvalho Chehab            if self.entry.identifier != declaration_name:
1151*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1152*e3b42e94SMauro Carvalho Chehab                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
1153d966dc65SMauro Carvalho Chehab                return
1154d966dc65SMauro Carvalho Chehab
1155d966dc65SMauro Carvalho Chehab            self.output_declaration('typedef', declaration_name,
1156d966dc65SMauro Carvalho Chehab                                    typedef=declaration_name,
1157d966dc65SMauro Carvalho Chehab                                    sectionlist=self.entry.sectionlist,
1158d966dc65SMauro Carvalho Chehab                                    sections=self.entry.sections,
1159c3597ab2SMauro Carvalho Chehab                                    section_start_lines=self.entry.section_start_lines,
1160d966dc65SMauro Carvalho Chehab                                    purpose=self.entry.declaration_purpose)
1161d966dc65SMauro Carvalho Chehab            return
1162d966dc65SMauro Carvalho Chehab
1163*e3b42e94SMauro Carvalho Chehab        self.emit_msg(ln, "error: Cannot parse typedef!")
1164d966dc65SMauro Carvalho Chehab
1165d966dc65SMauro Carvalho Chehab    @staticmethod
116616740c29SMauro Carvalho Chehab    def process_export(function_set, line):
1167d966dc65SMauro Carvalho Chehab        """
1168d966dc65SMauro Carvalho Chehab        process EXPORT_SYMBOL* tags
1169d966dc65SMauro Carvalho Chehab
117016740c29SMauro Carvalho Chehab        This method doesn't use any variable from the class, so declare it
117116740c29SMauro Carvalho Chehab        with a staticmethod decorator.
1172d966dc65SMauro Carvalho Chehab        """
1173d966dc65SMauro Carvalho Chehab
117416740c29SMauro Carvalho Chehab        # Note: it accepts only one EXPORT_SYMBOL* per line, as having
117516740c29SMauro Carvalho Chehab        # multiple export lines would violate Kernel coding style.
117616740c29SMauro Carvalho Chehab
1177d966dc65SMauro Carvalho Chehab        if export_symbol.search(line):
1178d966dc65SMauro Carvalho Chehab            symbol = export_symbol.group(2)
117916740c29SMauro Carvalho Chehab            function_set.add(symbol)
118016740c29SMauro Carvalho Chehab            return
1181d966dc65SMauro Carvalho Chehab
1182d966dc65SMauro Carvalho Chehab        if export_symbol_ns.search(line):
1183d966dc65SMauro Carvalho Chehab            symbol = export_symbol_ns.group(2)
118416740c29SMauro Carvalho Chehab            function_set.add(symbol)
1185d966dc65SMauro Carvalho Chehab
1186d966dc65SMauro Carvalho Chehab    def process_normal(self, ln, line):
1187d966dc65SMauro Carvalho Chehab        """
1188d966dc65SMauro Carvalho Chehab        STATE_NORMAL: looking for the /** to begin everything.
1189d966dc65SMauro Carvalho Chehab        """
1190d966dc65SMauro Carvalho Chehab
1191d966dc65SMauro Carvalho Chehab        if not doc_start.match(line):
1192d966dc65SMauro Carvalho Chehab            return
1193d966dc65SMauro Carvalho Chehab
1194d966dc65SMauro Carvalho Chehab        # start a new entry
1195c3597ab2SMauro Carvalho Chehab        self.reset_state(ln)
1196d966dc65SMauro Carvalho Chehab        self.entry.in_doc_sect = False
1197d966dc65SMauro Carvalho Chehab
1198d966dc65SMauro Carvalho Chehab        # next line is always the function name
1199f9cdbc57SMauro Carvalho Chehab        self.state = state.NAME
1200d966dc65SMauro Carvalho Chehab
1201d966dc65SMauro Carvalho Chehab    def process_name(self, ln, line):
1202d966dc65SMauro Carvalho Chehab        """
1203d966dc65SMauro Carvalho Chehab        STATE_NAME: Looking for the "name - description" line
1204d966dc65SMauro Carvalho Chehab        """
1205d966dc65SMauro Carvalho Chehab
1206d966dc65SMauro Carvalho Chehab        if doc_block.search(line):
1207d966dc65SMauro Carvalho Chehab            self.entry.new_start_line = ln
1208d966dc65SMauro Carvalho Chehab
1209d966dc65SMauro Carvalho Chehab            if not doc_block.group(1):
1210d966dc65SMauro Carvalho Chehab                self.entry.section = self.section_intro
1211d966dc65SMauro Carvalho Chehab            else:
1212d966dc65SMauro Carvalho Chehab                self.entry.section = doc_block.group(1)
1213d966dc65SMauro Carvalho Chehab
1214408269aeSMauro Carvalho Chehab            self.entry.identifier = self.entry.section
1215f9cdbc57SMauro Carvalho Chehab            self.state = state.DOCBLOCK
1216d966dc65SMauro Carvalho Chehab            return
1217d966dc65SMauro Carvalho Chehab
1218d966dc65SMauro Carvalho Chehab        if doc_decl.search(line):
1219d966dc65SMauro Carvalho Chehab            self.entry.identifier = doc_decl.group(1)
1220d966dc65SMauro Carvalho Chehab            self.entry.is_kernel_comment = False
1221d966dc65SMauro Carvalho Chehab
1222d966dc65SMauro Carvalho Chehab            decl_start = str(doc_com)       # comment block asterisk
1223d966dc65SMauro Carvalho Chehab            fn_type = r"(?:\w+\s*\*\s*)?"  # type (for non-functions)
1224d966dc65SMauro Carvalho Chehab            parenthesis = r"(?:\(\w*\))?"   # optional parenthesis on function
1225d966dc65SMauro Carvalho Chehab            decl_end = r"(?:[-:].*)"         # end of the name part
1226d966dc65SMauro Carvalho Chehab
1227d966dc65SMauro Carvalho Chehab            # test for pointer declaration type, foo * bar() - desc
122804a383ceSMauro Carvalho Chehab            r = KernRe(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$")
1229d966dc65SMauro Carvalho Chehab            if r.search(line):
1230d966dc65SMauro Carvalho Chehab                self.entry.identifier = r.group(1)
1231d966dc65SMauro Carvalho Chehab
1232d966dc65SMauro Carvalho Chehab            # Test for data declaration
123304a383ceSMauro Carvalho Chehab            r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)")
1234d966dc65SMauro Carvalho Chehab            if r.search(line):
1235d966dc65SMauro Carvalho Chehab                self.entry.decl_type = r.group(1)
1236d966dc65SMauro Carvalho Chehab                self.entry.identifier = r.group(2)
1237d966dc65SMauro Carvalho Chehab                self.entry.is_kernel_comment = True
1238d966dc65SMauro Carvalho Chehab            else:
1239d966dc65SMauro Carvalho Chehab                # Look for foo() or static void foo() - description;
1240d966dc65SMauro Carvalho Chehab                # or misspelt identifier
1241d966dc65SMauro Carvalho Chehab
124204a383ceSMauro Carvalho Chehab                r1 = KernRe(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$")
124304a383ceSMauro Carvalho Chehab                r2 = KernRe(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$")
1244d966dc65SMauro Carvalho Chehab
1245d966dc65SMauro Carvalho Chehab                for r in [r1, r2]:
1246d966dc65SMauro Carvalho Chehab                    if r.search(line):
1247d966dc65SMauro Carvalho Chehab                        self.entry.identifier = r.group(1)
1248d966dc65SMauro Carvalho Chehab                        self.entry.decl_type = "function"
1249d966dc65SMauro Carvalho Chehab
125004a383ceSMauro Carvalho Chehab                        r = KernRe(r"define\s+")
1251d966dc65SMauro Carvalho Chehab                        self.entry.identifier = r.sub("", self.entry.identifier)
1252d966dc65SMauro Carvalho Chehab                        self.entry.is_kernel_comment = True
1253d966dc65SMauro Carvalho Chehab                        break
1254d966dc65SMauro Carvalho Chehab
1255d966dc65SMauro Carvalho Chehab            self.entry.identifier = self.entry.identifier.strip(" ")
1256d966dc65SMauro Carvalho Chehab
1257f9cdbc57SMauro Carvalho Chehab            self.state = state.BODY
1258d966dc65SMauro Carvalho Chehab
1259d966dc65SMauro Carvalho Chehab            # if there's no @param blocks need to set up default section here
1260*e3b42e94SMauro Carvalho Chehab            self.entry.section = SECTION_DEFAULT
1261d966dc65SMauro Carvalho Chehab            self.entry.new_start_line = ln + 1
1262d966dc65SMauro Carvalho Chehab
126304a383ceSMauro Carvalho Chehab            r = KernRe("[-:](.*)")
1264d966dc65SMauro Carvalho Chehab            if r.search(line):
1265d966dc65SMauro Carvalho Chehab                # strip leading/trailing/multiple spaces
1266d966dc65SMauro Carvalho Chehab                self.entry.descr = r.group(1).strip(" ")
1267d966dc65SMauro Carvalho Chehab
126804a383ceSMauro Carvalho Chehab                r = KernRe(r"\s+")
1269d966dc65SMauro Carvalho Chehab                self.entry.descr = r.sub(" ", self.entry.descr)
1270d966dc65SMauro Carvalho Chehab                self.entry.declaration_purpose = self.entry.descr
1271f9cdbc57SMauro Carvalho Chehab                self.state = state.BODY_MAYBE
1272d966dc65SMauro Carvalho Chehab            else:
1273d966dc65SMauro Carvalho Chehab                self.entry.declaration_purpose = ""
1274d966dc65SMauro Carvalho Chehab
1275d966dc65SMauro Carvalho Chehab            if not self.entry.is_kernel_comment:
1276*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1277d966dc65SMauro Carvalho Chehab                              f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
1278f9cdbc57SMauro Carvalho Chehab                self.state = state.NORMAL
1279d966dc65SMauro Carvalho Chehab
1280d966dc65SMauro Carvalho Chehab            if not self.entry.declaration_purpose and self.config.wshort_desc:
1281*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1282d966dc65SMauro Carvalho Chehab                              f"missing initial short description on line:\n{line}")
1283d966dc65SMauro Carvalho Chehab
1284d966dc65SMauro Carvalho Chehab            if not self.entry.identifier and self.entry.decl_type != "enum":
1285*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1286d966dc65SMauro Carvalho Chehab                              f"wrong kernel-doc identifier on line:\n{line}")
1287f9cdbc57SMauro Carvalho Chehab                self.state = state.NORMAL
1288d966dc65SMauro Carvalho Chehab
1289d966dc65SMauro Carvalho Chehab            if self.config.verbose:
1290*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1291d966dc65SMauro Carvalho Chehab                              f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",
1292d966dc65SMauro Carvalho Chehab                                  warning=False)
1293d966dc65SMauro Carvalho Chehab
1294d966dc65SMauro Carvalho Chehab            return
1295d966dc65SMauro Carvalho Chehab
1296d966dc65SMauro Carvalho Chehab        # Failed to find an identifier. Emit a warning
1297*e3b42e94SMauro Carvalho Chehab        self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
1298d966dc65SMauro Carvalho Chehab
1299d966dc65SMauro Carvalho Chehab    def process_body(self, ln, line):
1300d966dc65SMauro Carvalho Chehab        """
1301d966dc65SMauro Carvalho Chehab        STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
1302d966dc65SMauro Carvalho Chehab        """
1303d966dc65SMauro Carvalho Chehab
1304f9cdbc57SMauro Carvalho Chehab        if self.state == state.BODY_WITH_BLANK_LINE:
130504a383ceSMauro Carvalho Chehab            r = KernRe(r"\s*\*\s?\S")
1306d966dc65SMauro Carvalho Chehab            if r.match(line):
1307d966dc65SMauro Carvalho Chehab                self.dump_section()
1308*e3b42e94SMauro Carvalho Chehab                self.entry.section = SECTION_DEFAULT
1309c3597ab2SMauro Carvalho Chehab                self.entry.new_start_line = ln
1310d966dc65SMauro Carvalho Chehab                self.entry.contents = ""
1311d966dc65SMauro Carvalho Chehab
1312d966dc65SMauro Carvalho Chehab        if doc_sect.search(line):
1313d966dc65SMauro Carvalho Chehab            self.entry.in_doc_sect = True
1314d966dc65SMauro Carvalho Chehab            newsection = doc_sect.group(1)
1315d966dc65SMauro Carvalho Chehab
1316d966dc65SMauro Carvalho Chehab            if newsection.lower() in ["description", "context"]:
1317d966dc65SMauro Carvalho Chehab                newsection = newsection.title()
1318d966dc65SMauro Carvalho Chehab
1319d966dc65SMauro Carvalho Chehab            # Special case: @return is a section, not a param description
1320d966dc65SMauro Carvalho Chehab            if newsection.lower() in ["@return", "@returns",
1321d966dc65SMauro Carvalho Chehab                                      "return", "returns"]:
1322d966dc65SMauro Carvalho Chehab                newsection = "Return"
1323d966dc65SMauro Carvalho Chehab
1324d966dc65SMauro Carvalho Chehab            # Perl kernel-doc has a check here for contents before sections.
1325d966dc65SMauro Carvalho Chehab            # the logic there is always false, as in_doc_sect variable is
1326d966dc65SMauro Carvalho Chehab            # always true. So, just don't implement Wcontents_before_sections
1327d966dc65SMauro Carvalho Chehab
1328d966dc65SMauro Carvalho Chehab            # .title()
1329d966dc65SMauro Carvalho Chehab            newcontents = doc_sect.group(2)
1330d966dc65SMauro Carvalho Chehab            if not newcontents:
1331d966dc65SMauro Carvalho Chehab                newcontents = ""
1332d966dc65SMauro Carvalho Chehab
1333d966dc65SMauro Carvalho Chehab            if self.entry.contents.strip("\n"):
1334d966dc65SMauro Carvalho Chehab                self.dump_section()
1335d966dc65SMauro Carvalho Chehab
1336d966dc65SMauro Carvalho Chehab            self.entry.new_start_line = ln
1337d966dc65SMauro Carvalho Chehab            self.entry.section = newsection
1338d966dc65SMauro Carvalho Chehab            self.entry.leading_space = None
1339d966dc65SMauro Carvalho Chehab
1340d966dc65SMauro Carvalho Chehab            self.entry.contents = newcontents.lstrip()
1341d966dc65SMauro Carvalho Chehab            if self.entry.contents:
1342d966dc65SMauro Carvalho Chehab                self.entry.contents += "\n"
1343d966dc65SMauro Carvalho Chehab
1344f9cdbc57SMauro Carvalho Chehab            self.state = state.BODY
1345d966dc65SMauro Carvalho Chehab            return
1346d966dc65SMauro Carvalho Chehab
1347d966dc65SMauro Carvalho Chehab        if doc_end.search(line):
1348d966dc65SMauro Carvalho Chehab            self.dump_section()
1349d966dc65SMauro Carvalho Chehab
1350d966dc65SMauro Carvalho Chehab            # Look for doc_com + <text> + doc_end:
135104a383ceSMauro Carvalho Chehab            r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/')
1352d966dc65SMauro Carvalho Chehab            if r.match(line):
1353*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln, f"suspicious ending line: {line}")
1354d966dc65SMauro Carvalho Chehab
1355d966dc65SMauro Carvalho Chehab            self.entry.prototype = ""
1356d966dc65SMauro Carvalho Chehab            self.entry.new_start_line = ln + 1
1357d966dc65SMauro Carvalho Chehab
1358f9cdbc57SMauro Carvalho Chehab            self.state = state.PROTO
1359d966dc65SMauro Carvalho Chehab            return
1360d966dc65SMauro Carvalho Chehab
1361d966dc65SMauro Carvalho Chehab        if doc_content.search(line):
1362d966dc65SMauro Carvalho Chehab            cont = doc_content.group(1)
1363d966dc65SMauro Carvalho Chehab
1364d966dc65SMauro Carvalho Chehab            if cont == "":
1365d966dc65SMauro Carvalho Chehab                if self.entry.section == self.section_context:
1366d966dc65SMauro Carvalho Chehab                    self.dump_section()
1367d966dc65SMauro Carvalho Chehab
1368d966dc65SMauro Carvalho Chehab                    self.entry.new_start_line = ln
1369f9cdbc57SMauro Carvalho Chehab                    self.state = state.BODY
1370d966dc65SMauro Carvalho Chehab                else:
1371*e3b42e94SMauro Carvalho Chehab                    if self.entry.section != SECTION_DEFAULT:
1372f9cdbc57SMauro Carvalho Chehab                        self.state = state.BODY_WITH_BLANK_LINE
1373d966dc65SMauro Carvalho Chehab                    else:
1374f9cdbc57SMauro Carvalho Chehab                        self.state = state.BODY
1375d966dc65SMauro Carvalho Chehab
1376d966dc65SMauro Carvalho Chehab                    self.entry.contents += "\n"
1377d966dc65SMauro Carvalho Chehab
1378f9cdbc57SMauro Carvalho Chehab            elif self.state == state.BODY_MAYBE:
1379d966dc65SMauro Carvalho Chehab
1380d966dc65SMauro Carvalho Chehab                # Continued declaration purpose
1381d966dc65SMauro Carvalho Chehab                self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip()
1382d966dc65SMauro Carvalho Chehab                self.entry.declaration_purpose += " " + cont
1383d966dc65SMauro Carvalho Chehab
138404a383ceSMauro Carvalho Chehab                r = KernRe(r"\s+")
1385d966dc65SMauro Carvalho Chehab                self.entry.declaration_purpose = r.sub(' ',
1386d966dc65SMauro Carvalho Chehab                                                       self.entry.declaration_purpose)
1387d966dc65SMauro Carvalho Chehab
1388d966dc65SMauro Carvalho Chehab            else:
1389d966dc65SMauro Carvalho Chehab                if self.entry.section.startswith('@') or        \
1390d966dc65SMauro Carvalho Chehab                   self.entry.section == self.section_context:
1391d966dc65SMauro Carvalho Chehab                    if self.entry.leading_space is None:
139204a383ceSMauro Carvalho Chehab                        r = KernRe(r'^(\s+)')
1393d966dc65SMauro Carvalho Chehab                        if r.match(cont):
1394d966dc65SMauro Carvalho Chehab                            self.entry.leading_space = len(r.group(1))
1395d966dc65SMauro Carvalho Chehab                        else:
1396d966dc65SMauro Carvalho Chehab                            self.entry.leading_space = 0
1397d966dc65SMauro Carvalho Chehab
1398d966dc65SMauro Carvalho Chehab                    # Double-check if leading space are realy spaces
1399d966dc65SMauro Carvalho Chehab                    pos = 0
1400d966dc65SMauro Carvalho Chehab                    for i in range(0, self.entry.leading_space):
1401d966dc65SMauro Carvalho Chehab                        if cont[i] != " ":
1402d966dc65SMauro Carvalho Chehab                            break
1403d966dc65SMauro Carvalho Chehab                        pos += 1
1404d966dc65SMauro Carvalho Chehab
1405d966dc65SMauro Carvalho Chehab                    cont = cont[pos:]
1406d966dc65SMauro Carvalho Chehab
1407d966dc65SMauro Carvalho Chehab                    # NEW LOGIC:
1408d966dc65SMauro Carvalho Chehab                    # In case it is different, update it
1409d966dc65SMauro Carvalho Chehab                    if self.entry.leading_space != pos:
1410d966dc65SMauro Carvalho Chehab                        self.entry.leading_space = pos
1411d966dc65SMauro Carvalho Chehab
1412d966dc65SMauro Carvalho Chehab                self.entry.contents += cont + "\n"
1413d966dc65SMauro Carvalho Chehab            return
1414d966dc65SMauro Carvalho Chehab
1415d966dc65SMauro Carvalho Chehab        # Unknown line, ignore
1416*e3b42e94SMauro Carvalho Chehab        self.emit_msg(ln, f"bad line: {line}")
1417d966dc65SMauro Carvalho Chehab
1418d966dc65SMauro Carvalho Chehab    def process_inline(self, ln, line):
1419d966dc65SMauro Carvalho Chehab        """STATE_INLINE: docbook comments within a prototype."""
1420d966dc65SMauro Carvalho Chehab
1421f9cdbc57SMauro Carvalho Chehab        if self.inline_doc_state == state.INLINE_NAME and \
1422d966dc65SMauro Carvalho Chehab           doc_inline_sect.search(line):
1423d966dc65SMauro Carvalho Chehab            self.entry.section = doc_inline_sect.group(1)
1424d966dc65SMauro Carvalho Chehab            self.entry.new_start_line = ln
1425d966dc65SMauro Carvalho Chehab
1426d966dc65SMauro Carvalho Chehab            self.entry.contents = doc_inline_sect.group(2).lstrip()
1427d966dc65SMauro Carvalho Chehab            if self.entry.contents != "":
1428d966dc65SMauro Carvalho Chehab                self.entry.contents += "\n"
1429d966dc65SMauro Carvalho Chehab
1430f9cdbc57SMauro Carvalho Chehab            self.inline_doc_state = state.INLINE_TEXT
1431d966dc65SMauro Carvalho Chehab            # Documentation block end */
1432d966dc65SMauro Carvalho Chehab            return
1433d966dc65SMauro Carvalho Chehab
1434d966dc65SMauro Carvalho Chehab        if doc_inline_end.search(line):
1435d966dc65SMauro Carvalho Chehab            if self.entry.contents not in ["", "\n"]:
1436d966dc65SMauro Carvalho Chehab                self.dump_section()
1437d966dc65SMauro Carvalho Chehab
1438f9cdbc57SMauro Carvalho Chehab            self.state = state.PROTO
1439f9cdbc57SMauro Carvalho Chehab            self.inline_doc_state = state.INLINE_NA
1440d966dc65SMauro Carvalho Chehab            return
1441d966dc65SMauro Carvalho Chehab
1442d966dc65SMauro Carvalho Chehab        if doc_content.search(line):
1443f9cdbc57SMauro Carvalho Chehab            if self.inline_doc_state == state.INLINE_TEXT:
1444d966dc65SMauro Carvalho Chehab                self.entry.contents += doc_content.group(1) + "\n"
1445d966dc65SMauro Carvalho Chehab                if not self.entry.contents.strip(" ").rstrip("\n"):
1446d966dc65SMauro Carvalho Chehab                    self.entry.contents = ""
1447d966dc65SMauro Carvalho Chehab
1448f9cdbc57SMauro Carvalho Chehab            elif self.inline_doc_state == state.INLINE_NAME:
1449*e3b42e94SMauro Carvalho Chehab                self.emit_msg(ln,
1450d966dc65SMauro Carvalho Chehab                              f"Incorrect use of kernel-doc format: {line}")
1451d966dc65SMauro Carvalho Chehab
1452f9cdbc57SMauro Carvalho Chehab                self.inline_doc_state = state.INLINE_ERROR
1453d966dc65SMauro Carvalho Chehab
1454d966dc65SMauro Carvalho Chehab    def syscall_munge(self, ln, proto):         # pylint: disable=W0613
1455d966dc65SMauro Carvalho Chehab        """
1456d966dc65SMauro Carvalho Chehab        Handle syscall definitions
1457d966dc65SMauro Carvalho Chehab        """
1458d966dc65SMauro Carvalho Chehab
1459d966dc65SMauro Carvalho Chehab        is_void = False
1460d966dc65SMauro Carvalho Chehab
1461d966dc65SMauro Carvalho Chehab        # Strip newlines/CR's
1462d966dc65SMauro Carvalho Chehab        proto = re.sub(r'[\r\n]+', ' ', proto)
1463d966dc65SMauro Carvalho Chehab
1464d966dc65SMauro Carvalho Chehab        # Check if it's a SYSCALL_DEFINE0
1465d966dc65SMauro Carvalho Chehab        if 'SYSCALL_DEFINE0' in proto:
1466d966dc65SMauro Carvalho Chehab            is_void = True
1467d966dc65SMauro Carvalho Chehab
1468d966dc65SMauro Carvalho Chehab        # Replace SYSCALL_DEFINE with correct return type & function name
146904a383ceSMauro Carvalho Chehab        proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto)
1470d966dc65SMauro Carvalho Chehab
147104a383ceSMauro Carvalho Chehab        r = KernRe(r'long\s+(sys_.*?),')
1472d966dc65SMauro Carvalho Chehab        if r.search(proto):
147304a383ceSMauro Carvalho Chehab            proto = KernRe(',').sub('(', proto, count=1)
1474d966dc65SMauro Carvalho Chehab        elif is_void:
147504a383ceSMauro Carvalho Chehab            proto = KernRe(r'\)').sub('(void)', proto, count=1)
1476d966dc65SMauro Carvalho Chehab
1477d966dc65SMauro Carvalho Chehab        # Now delete all of the odd-numbered commas in the proto
1478d966dc65SMauro Carvalho Chehab        # so that argument types & names don't have a comma between them
1479d966dc65SMauro Carvalho Chehab        count = 0
1480d966dc65SMauro Carvalho Chehab        length = len(proto)
1481d966dc65SMauro Carvalho Chehab
1482d966dc65SMauro Carvalho Chehab        if is_void:
1483d966dc65SMauro Carvalho Chehab            length = 0  # skip the loop if is_void
1484d966dc65SMauro Carvalho Chehab
1485d966dc65SMauro Carvalho Chehab        for ix in range(length):
1486d966dc65SMauro Carvalho Chehab            if proto[ix] == ',':
1487d966dc65SMauro Carvalho Chehab                count += 1
1488d966dc65SMauro Carvalho Chehab                if count % 2 == 1:
1489d966dc65SMauro Carvalho Chehab                    proto = proto[:ix] + ' ' + proto[ix + 1:]
1490d966dc65SMauro Carvalho Chehab
1491d966dc65SMauro Carvalho Chehab        return proto
1492d966dc65SMauro Carvalho Chehab
1493d966dc65SMauro Carvalho Chehab    def tracepoint_munge(self, ln, proto):
1494d966dc65SMauro Carvalho Chehab        """
1495d966dc65SMauro Carvalho Chehab        Handle tracepoint definitions
1496d966dc65SMauro Carvalho Chehab        """
1497d966dc65SMauro Carvalho Chehab
1498d966dc65SMauro Carvalho Chehab        tracepointname = None
1499d966dc65SMauro Carvalho Chehab        tracepointargs = None
1500d966dc65SMauro Carvalho Chehab
1501d966dc65SMauro Carvalho Chehab        # Match tracepoint name based on different patterns
150204a383ceSMauro Carvalho Chehab        r = KernRe(r'TRACE_EVENT\((.*?),')
1503d966dc65SMauro Carvalho Chehab        if r.search(proto):
1504d966dc65SMauro Carvalho Chehab            tracepointname = r.group(1)
1505d966dc65SMauro Carvalho Chehab
150604a383ceSMauro Carvalho Chehab        r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),')
1507d966dc65SMauro Carvalho Chehab        if r.search(proto):
1508d966dc65SMauro Carvalho Chehab            tracepointname = r.group(1)
1509d966dc65SMauro Carvalho Chehab
151004a383ceSMauro Carvalho Chehab        r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),')
1511d966dc65SMauro Carvalho Chehab        if r.search(proto):
1512d966dc65SMauro Carvalho Chehab            tracepointname = r.group(2)
1513d966dc65SMauro Carvalho Chehab
1514d966dc65SMauro Carvalho Chehab        if tracepointname:
1515d966dc65SMauro Carvalho Chehab            tracepointname = tracepointname.lstrip()
1516d966dc65SMauro Carvalho Chehab
151704a383ceSMauro Carvalho Chehab        r = KernRe(r'TP_PROTO\((.*?)\)')
1518d966dc65SMauro Carvalho Chehab        if r.search(proto):
1519d966dc65SMauro Carvalho Chehab            tracepointargs = r.group(1)
1520d966dc65SMauro Carvalho Chehab
1521d966dc65SMauro Carvalho Chehab        if not tracepointname or not tracepointargs:
1522*e3b42e94SMauro Carvalho Chehab            self.emit_msg(ln,
1523d966dc65SMauro Carvalho Chehab                          f"Unrecognized tracepoint format:\n{proto}\n")
1524d966dc65SMauro Carvalho Chehab        else:
1525d966dc65SMauro Carvalho Chehab            proto = f"static inline void trace_{tracepointname}({tracepointargs})"
1526d966dc65SMauro Carvalho Chehab            self.entry.identifier = f"trace_{self.entry.identifier}"
1527d966dc65SMauro Carvalho Chehab
1528d966dc65SMauro Carvalho Chehab        return proto
1529d966dc65SMauro Carvalho Chehab
1530d966dc65SMauro Carvalho Chehab    def process_proto_function(self, ln, line):
1531d966dc65SMauro Carvalho Chehab        """Ancillary routine to process a function prototype"""
1532d966dc65SMauro Carvalho Chehab
1533d966dc65SMauro Carvalho Chehab        # strip C99-style comments to end of line
153404a383ceSMauro Carvalho Chehab        r = KernRe(r"\/\/.*$", re.S)
1535d966dc65SMauro Carvalho Chehab        line = r.sub('', line)
1536d966dc65SMauro Carvalho Chehab
153704a383ceSMauro Carvalho Chehab        if KernRe(r'\s*#\s*define').match(line):
1538d966dc65SMauro Carvalho Chehab            self.entry.prototype = line
1539d966dc65SMauro Carvalho Chehab        elif line.startswith('#'):
1540d966dc65SMauro Carvalho Chehab            # Strip other macros like #ifdef/#ifndef/#endif/...
1541d966dc65SMauro Carvalho Chehab            pass
1542d966dc65SMauro Carvalho Chehab        else:
154304a383ceSMauro Carvalho Chehab            r = KernRe(r'([^\{]*)')
1544d966dc65SMauro Carvalho Chehab            if r.match(line):
1545d966dc65SMauro Carvalho Chehab                self.entry.prototype += r.group(1) + " "
1546d966dc65SMauro Carvalho Chehab
154704a383ceSMauro Carvalho Chehab        if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line):
1548d966dc65SMauro Carvalho Chehab            # strip comments
154904a383ceSMauro Carvalho Chehab            r = KernRe(r'/\*.*?\*/')
1550d966dc65SMauro Carvalho Chehab            self.entry.prototype = r.sub('', self.entry.prototype)
1551d966dc65SMauro Carvalho Chehab
1552d966dc65SMauro Carvalho Chehab            # strip newlines/cr's
155304a383ceSMauro Carvalho Chehab            r = KernRe(r'[\r\n]+')
1554d966dc65SMauro Carvalho Chehab            self.entry.prototype = r.sub(' ', self.entry.prototype)
1555d966dc65SMauro Carvalho Chehab
1556d966dc65SMauro Carvalho Chehab            # strip leading spaces
155704a383ceSMauro Carvalho Chehab            r = KernRe(r'^\s+')
1558d966dc65SMauro Carvalho Chehab            self.entry.prototype = r.sub('', self.entry.prototype)
1559d966dc65SMauro Carvalho Chehab
1560d966dc65SMauro Carvalho Chehab            # Handle self.entry.prototypes for function pointers like:
1561d966dc65SMauro Carvalho Chehab            #       int (*pcs_config)(struct foo)
1562d966dc65SMauro Carvalho Chehab
156304a383ceSMauro Carvalho Chehab            r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)')
1564d966dc65SMauro Carvalho Chehab            self.entry.prototype = r.sub(r'\1\2', self.entry.prototype)
1565d966dc65SMauro Carvalho Chehab
1566d966dc65SMauro Carvalho Chehab            if 'SYSCALL_DEFINE' in self.entry.prototype:
1567d966dc65SMauro Carvalho Chehab                self.entry.prototype = self.syscall_munge(ln,
1568d966dc65SMauro Carvalho Chehab                                                          self.entry.prototype)
1569d966dc65SMauro Carvalho Chehab
157004a383ceSMauro Carvalho Chehab            r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
1571d966dc65SMauro Carvalho Chehab            if r.search(self.entry.prototype):
1572d966dc65SMauro Carvalho Chehab                self.entry.prototype = self.tracepoint_munge(ln,
1573d966dc65SMauro Carvalho Chehab                                                             self.entry.prototype)
1574d966dc65SMauro Carvalho Chehab
1575d966dc65SMauro Carvalho Chehab            self.dump_function(ln, self.entry.prototype)
1576d966dc65SMauro Carvalho Chehab            self.reset_state(ln)
1577d966dc65SMauro Carvalho Chehab
1578d966dc65SMauro Carvalho Chehab    def process_proto_type(self, ln, line):
1579d966dc65SMauro Carvalho Chehab        """Ancillary routine to process a type"""
1580d966dc65SMauro Carvalho Chehab
1581d966dc65SMauro Carvalho Chehab        # Strip newlines/cr's.
158204a383ceSMauro Carvalho Chehab        line = KernRe(r'[\r\n]+', re.S).sub(' ', line)
1583d966dc65SMauro Carvalho Chehab
1584d966dc65SMauro Carvalho Chehab        # Strip leading spaces
158504a383ceSMauro Carvalho Chehab        line = KernRe(r'^\s+', re.S).sub('', line)
1586d966dc65SMauro Carvalho Chehab
1587d966dc65SMauro Carvalho Chehab        # Strip trailing spaces
158804a383ceSMauro Carvalho Chehab        line = KernRe(r'\s+$', re.S).sub('', line)
1589d966dc65SMauro Carvalho Chehab
1590d966dc65SMauro Carvalho Chehab        # Strip C99-style comments to the end of the line
159104a383ceSMauro Carvalho Chehab        line = KernRe(r"\/\/.*$", re.S).sub('', line)
1592d966dc65SMauro Carvalho Chehab
1593d966dc65SMauro Carvalho Chehab        # To distinguish preprocessor directive from regular declaration later.
1594d966dc65SMauro Carvalho Chehab        if line.startswith('#'):
1595d966dc65SMauro Carvalho Chehab            line += ";"
1596d966dc65SMauro Carvalho Chehab
159704a383ceSMauro Carvalho Chehab        r = KernRe(r'([^\{\};]*)([\{\};])(.*)')
1598d966dc65SMauro Carvalho Chehab        while True:
1599d966dc65SMauro Carvalho Chehab            if r.search(line):
1600d966dc65SMauro Carvalho Chehab                if self.entry.prototype:
1601d966dc65SMauro Carvalho Chehab                    self.entry.prototype += " "
1602d966dc65SMauro Carvalho Chehab                self.entry.prototype += r.group(1) + r.group(2)
1603d966dc65SMauro Carvalho Chehab
1604d966dc65SMauro Carvalho Chehab                self.entry.brcount += r.group(2).count('{')
1605d966dc65SMauro Carvalho Chehab                self.entry.brcount -= r.group(2).count('}')
1606d966dc65SMauro Carvalho Chehab
1607d966dc65SMauro Carvalho Chehab                self.entry.brcount = max(self.entry.brcount, 0)
1608d966dc65SMauro Carvalho Chehab
1609d966dc65SMauro Carvalho Chehab                if r.group(2) == ';' and self.entry.brcount == 0:
1610d966dc65SMauro Carvalho Chehab                    self.dump_declaration(ln, self.entry.prototype)
1611d966dc65SMauro Carvalho Chehab                    self.reset_state(ln)
1612d966dc65SMauro Carvalho Chehab                    break
1613d966dc65SMauro Carvalho Chehab
1614d966dc65SMauro Carvalho Chehab                line = r.group(3)
1615d966dc65SMauro Carvalho Chehab            else:
1616d966dc65SMauro Carvalho Chehab                self.entry.prototype += line
1617d966dc65SMauro Carvalho Chehab                break
1618d966dc65SMauro Carvalho Chehab
1619d966dc65SMauro Carvalho Chehab    def process_proto(self, ln, line):
1620d966dc65SMauro Carvalho Chehab        """STATE_PROTO: reading a function/whatever prototype."""
1621d966dc65SMauro Carvalho Chehab
1622d966dc65SMauro Carvalho Chehab        if doc_inline_oneline.search(line):
1623d966dc65SMauro Carvalho Chehab            self.entry.section = doc_inline_oneline.group(1)
1624d966dc65SMauro Carvalho Chehab            self.entry.contents = doc_inline_oneline.group(2)
1625d966dc65SMauro Carvalho Chehab
1626d966dc65SMauro Carvalho Chehab            if self.entry.contents != "":
1627d966dc65SMauro Carvalho Chehab                self.entry.contents += "\n"
1628d966dc65SMauro Carvalho Chehab                self.dump_section(start_new=False)
1629d966dc65SMauro Carvalho Chehab
1630d966dc65SMauro Carvalho Chehab        elif doc_inline_start.search(line):
1631f9cdbc57SMauro Carvalho Chehab            self.state = state.INLINE
1632f9cdbc57SMauro Carvalho Chehab            self.inline_doc_state = state.INLINE_NAME
1633d966dc65SMauro Carvalho Chehab
1634d966dc65SMauro Carvalho Chehab        elif self.entry.decl_type == 'function':
1635d966dc65SMauro Carvalho Chehab            self.process_proto_function(ln, line)
1636d966dc65SMauro Carvalho Chehab
1637d966dc65SMauro Carvalho Chehab        else:
1638d966dc65SMauro Carvalho Chehab            self.process_proto_type(ln, line)
1639d966dc65SMauro Carvalho Chehab
1640d966dc65SMauro Carvalho Chehab    def process_docblock(self, ln, line):
1641d966dc65SMauro Carvalho Chehab        """STATE_DOCBLOCK: within a DOC: block."""
1642d966dc65SMauro Carvalho Chehab
1643d966dc65SMauro Carvalho Chehab        if doc_end.search(line):
1644d966dc65SMauro Carvalho Chehab            self.dump_section()
1645408269aeSMauro Carvalho Chehab            self.output_declaration("doc", self.entry.identifier,
1646d966dc65SMauro Carvalho Chehab                                    sectionlist=self.entry.sectionlist,
1647c3597ab2SMauro Carvalho Chehab                                    sections=self.entry.sections,
16482ab867a4SMauro Carvalho Chehab                                    section_start_lines=self.entry.section_start_lines)
1649d966dc65SMauro Carvalho Chehab            self.reset_state(ln)
1650d966dc65SMauro Carvalho Chehab
1651d966dc65SMauro Carvalho Chehab        elif doc_content.search(line):
1652d966dc65SMauro Carvalho Chehab            self.entry.contents += doc_content.group(1) + "\n"
1653d966dc65SMauro Carvalho Chehab
165416740c29SMauro Carvalho Chehab    def parse_export(self):
165516740c29SMauro Carvalho Chehab        """
165616740c29SMauro Carvalho Chehab        Parses EXPORT_SYMBOL* macros from a single Kernel source file.
165716740c29SMauro Carvalho Chehab        """
165816740c29SMauro Carvalho Chehab
165916740c29SMauro Carvalho Chehab        export_table = set()
166016740c29SMauro Carvalho Chehab
166116740c29SMauro Carvalho Chehab        try:
166216740c29SMauro Carvalho Chehab            with open(self.fname, "r", encoding="utf8",
166316740c29SMauro Carvalho Chehab                      errors="backslashreplace") as fp:
166416740c29SMauro Carvalho Chehab
166516740c29SMauro Carvalho Chehab                for line in fp:
166616740c29SMauro Carvalho Chehab                    self.process_export(export_table, line)
166716740c29SMauro Carvalho Chehab
166816740c29SMauro Carvalho Chehab        except IOError:
166916740c29SMauro Carvalho Chehab            return None
167016740c29SMauro Carvalho Chehab
167116740c29SMauro Carvalho Chehab        return export_table
167216740c29SMauro Carvalho Chehab
167316740c29SMauro Carvalho Chehab    def parse_kdoc(self):
1674d966dc65SMauro Carvalho Chehab        """
1675d966dc65SMauro Carvalho Chehab        Open and process each line of a C source file.
167616740c29SMauro Carvalho Chehab        The parsing is controlled via a state machine, and the line is passed
1677d966dc65SMauro Carvalho Chehab        to a different process function depending on the state. The process
1678d966dc65SMauro Carvalho Chehab        function may update the state as needed.
167916740c29SMauro Carvalho Chehab
168016740c29SMauro Carvalho Chehab        Besides parsing kernel-doc tags, it also parses export symbols.
1681d966dc65SMauro Carvalho Chehab        """
1682d966dc65SMauro Carvalho Chehab
1683d966dc65SMauro Carvalho Chehab        cont = False
1684d966dc65SMauro Carvalho Chehab        prev = ""
1685d966dc65SMauro Carvalho Chehab        prev_ln = None
168616740c29SMauro Carvalho Chehab        export_table = set()
1687d966dc65SMauro Carvalho Chehab
1688d966dc65SMauro Carvalho Chehab        try:
1689d966dc65SMauro Carvalho Chehab            with open(self.fname, "r", encoding="utf8",
1690d966dc65SMauro Carvalho Chehab                      errors="backslashreplace") as fp:
1691d966dc65SMauro Carvalho Chehab                for ln, line in enumerate(fp):
1692d966dc65SMauro Carvalho Chehab
1693d966dc65SMauro Carvalho Chehab                    line = line.expandtabs().strip("\n")
1694d966dc65SMauro Carvalho Chehab
1695d966dc65SMauro Carvalho Chehab                    # Group continuation lines on prototypes
1696f9cdbc57SMauro Carvalho Chehab                    if self.state == state.PROTO:
1697d966dc65SMauro Carvalho Chehab                        if line.endswith("\\"):
1698e4b2bd90SMauro Carvalho Chehab                            prev += line.rstrip("\\")
1699d966dc65SMauro Carvalho Chehab                            cont = True
1700d966dc65SMauro Carvalho Chehab
1701d966dc65SMauro Carvalho Chehab                            if not prev_ln:
1702d966dc65SMauro Carvalho Chehab                                prev_ln = ln
1703d966dc65SMauro Carvalho Chehab
1704d966dc65SMauro Carvalho Chehab                            continue
1705d966dc65SMauro Carvalho Chehab
1706d966dc65SMauro Carvalho Chehab                        if cont:
1707d966dc65SMauro Carvalho Chehab                            ln = prev_ln
1708d966dc65SMauro Carvalho Chehab                            line = prev + line
1709d966dc65SMauro Carvalho Chehab                            prev = ""
1710d966dc65SMauro Carvalho Chehab                            cont = False
1711d966dc65SMauro Carvalho Chehab                            prev_ln = None
1712d966dc65SMauro Carvalho Chehab
1713d966dc65SMauro Carvalho Chehab                    self.config.log.debug("%d %s%s: %s",
1714f9cdbc57SMauro Carvalho Chehab                                          ln, state.name[self.state],
1715f9cdbc57SMauro Carvalho Chehab                                          state.inline_name[self.inline_doc_state],
1716d966dc65SMauro Carvalho Chehab                                          line)
1717d966dc65SMauro Carvalho Chehab
171816740c29SMauro Carvalho Chehab                    # This is an optimization over the original script.
171916740c29SMauro Carvalho Chehab                    # There, when export_file was used for the same file,
172016740c29SMauro Carvalho Chehab                    # it was read twice. Here, we use the already-existing
172116740c29SMauro Carvalho Chehab                    # loop to parse exported symbols as well.
172216740c29SMauro Carvalho Chehab                    #
172316740c29SMauro Carvalho Chehab                    # TODO: It should be noticed that not all states are
172416740c29SMauro Carvalho Chehab                    # needed here. On a future cleanup, process export only
172516740c29SMauro Carvalho Chehab                    # at the states that aren't handling comment markups.
172616740c29SMauro Carvalho Chehab                    self.process_export(export_table, line)
172716740c29SMauro Carvalho Chehab
1728d966dc65SMauro Carvalho Chehab                    # Hand this line to the appropriate state handler
1729f9cdbc57SMauro Carvalho Chehab                    if self.state == state.NORMAL:
1730d966dc65SMauro Carvalho Chehab                        self.process_normal(ln, line)
1731f9cdbc57SMauro Carvalho Chehab                    elif self.state == state.NAME:
1732d966dc65SMauro Carvalho Chehab                        self.process_name(ln, line)
1733f9cdbc57SMauro Carvalho Chehab                    elif self.state in [state.BODY, state.BODY_MAYBE,
1734f9cdbc57SMauro Carvalho Chehab                                        state.BODY_WITH_BLANK_LINE]:
1735d966dc65SMauro Carvalho Chehab                        self.process_body(ln, line)
1736f9cdbc57SMauro Carvalho Chehab                    elif self.state == state.INLINE:  # scanning for inline parameters
1737d966dc65SMauro Carvalho Chehab                        self.process_inline(ln, line)
1738f9cdbc57SMauro Carvalho Chehab                    elif self.state == state.PROTO:
1739d966dc65SMauro Carvalho Chehab                        self.process_proto(ln, line)
1740f9cdbc57SMauro Carvalho Chehab                    elif self.state == state.DOCBLOCK:
1741d966dc65SMauro Carvalho Chehab                        self.process_docblock(ln, line)
1742d966dc65SMauro Carvalho Chehab        except OSError:
1743d966dc65SMauro Carvalho Chehab            self.config.log.error(f"Error: Cannot open file {self.fname}")
174416740c29SMauro Carvalho Chehab
174516740c29SMauro Carvalho Chehab        return export_table, self.entries
1746