1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 5import re 6 7from kdoc.kdoc_re import KernRe 8 9struct_args_pattern = r'([^,)]+)' 10 11class CTransforms: 12 """ 13 Data class containing a long set of transformations to turn 14 structure member prefixes, and macro invocations and variables 15 into something we can parse and generate kdoc for. 16 """ 17 18 #: Transforms for structs and unions. 19 struct_xforms = [ 20 # Strip attributes 21 (KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '), 22 (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), 23 (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), 24 (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), 25 (KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ' '), 26 (KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ' '), 27 (KernRe(r'\s*__packed\s*', re.S), ' '), 28 (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), 29 (KernRe(r'\s*__private', re.S), ' '), 30 (KernRe(r'\s*__rcu', re.S), ' '), 31 (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), 32 (KernRe(r'\s*____cacheline_aligned', re.S), ' '), 33 (KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''), 34 # 35 # Unwrap struct_group macros based on this definition: 36 # __struct_group(TAG, NAME, ATTRS, MEMBERS...) 37 # which has variants like: struct_group(NAME, MEMBERS...) 38 # Only MEMBERS arguments require documentation. 39 # 40 # Parsing them happens on two steps: 41 # 42 # 1. drop struct group arguments that aren't at MEMBERS, 43 # storing them as STRUCT_GROUP(MEMBERS) 44 # 45 # 2. remove STRUCT_GROUP() ancillary macro. 46 # 47 # The original logic used to remove STRUCT_GROUP() using an 48 # advanced regex: 49 # 50 # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; 51 # 52 # with two patterns that are incompatible with 53 # Python re module, as it has: 54 # 55 # - a recursive pattern: (?1) 56 # - an atomic grouping: (?>...) 57 # 58 # I tried a simpler version: but it didn't work either: 59 # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; 60 # 61 # As it doesn't properly match the end parenthesis on some cases. 62 # 63 # So, a better solution was crafted: there's now a NestedMatch 64 # class that ensures that delimiters after a search are properly 65 # matched. So, the implementation to drop STRUCT_GROUP() will be 66 # handled in separate. 67 # 68 (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), 69 (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), 70 (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), 71 (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), 72 # 73 # Replace macros 74 # 75 # TODO: use NestedMatch for FOO($1, $2, ...) matches 76 # 77 # it is better to also move those to the NestedMatch logic, 78 # to ensure that parentheses will be properly matched. 79 # 80 (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), 81 r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), 82 (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), 83 r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), 84 (KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', 85 re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), 86 (KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', 87 re.S), r'unsigned long \1[1 << ((\2) - 1)]'), 88 (KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + 89 r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'), 90 (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' + 91 struct_args_pattern + r'\)', re.S), r'\2 *\1'), 92 (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' + 93 struct_args_pattern + r'\)', re.S), r'\1 \2[]'), 94 (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'), 95 (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'), 96 (KernRe(r'VIRTIO_DECLARE_FEATURES\(([\w_]+)\)'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'), 97 ] 98 99 #: Transforms for function prototypes. 100 function_xforms = [ 101 (KernRe(r"^static +"), ""), 102 (KernRe(r"^extern +"), ""), 103 (KernRe(r"^asmlinkage +"), ""), 104 (KernRe(r"^inline +"), ""), 105 (KernRe(r"^__inline__ +"), ""), 106 (KernRe(r"^__inline +"), ""), 107 (KernRe(r"^__always_inline +"), ""), 108 (KernRe(r"^noinline +"), ""), 109 (KernRe(r"^__FORTIFY_INLINE +"), ""), 110 (KernRe(r"__init +"), ""), 111 (KernRe(r"__init_or_module +"), ""), 112 (KernRe(r"__exit +"), ""), 113 (KernRe(r"__deprecated +"), ""), 114 (KernRe(r"__flatten +"), ""), 115 (KernRe(r"__meminit +"), ""), 116 (KernRe(r"__must_check +"), ""), 117 (KernRe(r"__weak +"), ""), 118 (KernRe(r"__sched +"), ""), 119 (KernRe(r"_noprof"), ""), 120 (KernRe(r"__always_unused *"), ""), 121 (KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""), 122 (KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""), 123 (KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""), 124 (KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"), 125 (KernRe(r"__no_context_analysis\s*"), ""), 126 (KernRe(r"__attribute_const__ +"), ""), 127 (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""), 128 ] 129 130 #: Transforms for variable prototypes. 131 var_xforms = [ 132 (KernRe(r"__read_mostly"), ""), 133 (KernRe(r"__ro_after_init"), ""), 134 (KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ""), 135 (KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ""), 136 (KernRe(r"LIST_HEAD\(([\w_]+)\)"), r"struct list_head \1"), 137 (KernRe(r"(?://.*)$"), ""), 138 (KernRe(r"(?:/\*.*\*/)"), ""), 139 (KernRe(r";$"), ""), 140 ] 141 142 #: Transforms main dictionary used at apply_transforms(). 143 xforms = { 144 "struct": struct_xforms, 145 "func": function_xforms, 146 "var": var_xforms, 147 } 148 149 def apply(self, xforms_type, text): 150 """ 151 Apply a set of transforms to a block of text. 152 """ 153 if xforms_type not in self.xforms: 154 return text 155 156 for search, subst in self.xforms[xforms_type]: 157 text = search.sub(subst, text) 158 return text 159