Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

scripts/kernel-doc.py: move KernelDoc class to a separate file

In preparation for letting kerneldoc Sphinx extension to import
Python libraries, move regex ancillary classes to a separate
file.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/c76df228504e711c6b4bcd23d5a0ea1fda678cda.1744106241.git.mchehab+huawei@kernel.org

authored by

Mauro Carvalho Chehab and committed by
Jonathan Corbet
d966dc65 e31fd36d

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