Skip to contentSkip to conversation

Several kdoc fixes

PATCH In review Mauro Carvalho Chehab 2 participants 24 messages Mar 2 – Mar 3 Documentation

lore
Share & tools
lore
#0 lore raw

cover Several kdoc fixes

Hi Jon,

This series contains patches from:

	https://lore.kernel.org/linux-doc/[email protected]/T/#t

I rebased them to make the changes cleaner, fixing mostly on fixes.

I dropped stuff related to NestedMatch arguments parsing. I'll be sending you on a new patch series, likely tomorrow.

The changes here basically addresses several issues discoverd by Randy and by me and makes NestedMatch more similar to KernRe.

It also moves the transforms ruleset to a separate file, to make easier to maintain kernel-doc code.

Mauro Carvalho Chehab (16):
docs: kdoc_re: add support for groups()
docs: kdoc_re: don't go past the end of a line
docs: kdoc_parser: move var transformers to the beginning
docs: kdoc_parser: don't mangle with function defines
docs: kdoc_parser: fix variable regexes to work with size_t
docs: kdoc_parser: fix the default_value logic for variables
docs: kdoc_parser: don't exclude defaults from prototype
docs: kdoc_parser: fix parser to support multi-word types
docs: kdoc_parser: add support for LIST_HEAD
docs: kdoc_re: better show KernRe() at documentation
docs: kdoc_parser: move transform lists to a separate file
docs: kdoc_re: handle strings and escape chars on NextMatch
docs: kdoc_re: don't recompile NestedMatch regex every time
docs: kdoc_re: Change NestedMath args replacement to \0
docs: kdoc_re: make NestedMatch use KernRe
docs: kdoc_parser: move nested match transforms to xforms_lists.py

Randy Dunlap (2):
docs: kdoc_parser: handle struct member macro
VIRTIO_DECLARE_FEATURES(name)
docs: xforms_lists: ignore context analysis and lock attributes

 Documentation/tools/kdoc_parser.rst   |   8 ++
 tools/lib/python/kdoc/kdoc_files.py   |   3 +-
 tools/lib/python/kdoc/kdoc_parser.py  | 172 +++-----------------------
 tools/lib/python/kdoc/kdoc_re.py      | 115 +++++++++++++----
 tools/lib/python/kdoc/xforms_lists.py | 170 +++++++++++++++++++++++++
 5 files changed, 286 insertions(+), 182 deletions(-)
 create mode 100644 tools/lib/python/kdoc/xforms_lists.py
Signature · 1 line
2.52.0
#1 lore raw

PATCH 1/18 docs: kdoc_re: add support for groups()

Add an equivalent to re groups() method.
This is useful on debug messages.

---
 tools/lib/python/kdoc/kdoc_re.py | 7 +++++++
 1 file changed, 7 insertions(+)
tools/lib/python/kdoc/kdoc_re.py +7 -0
index 0bf9e01cdc57..774dd747ecb0 100644
@@ -106,6 +106,13 @@ class KernRe:
 
         return self.last_match.group(num)
 
+    def groups(self):
+        """
+        Returns the group results of the last match
+        """
+
+        return self.last_match.groups()
+
 
 class NestedMatch:
     """
Signature · 1 line
2.52.0
#2 lore raw

PATCH 2/18 docs: kdoc_re: don't go past the end of a line

In reply to Mauro Carvalho Chehab

The logic which checks if the line ends with ";" is currently
broken: it may try to read past the buffer.

Fix it by checking before trying to access line[pos].

---
 tools/lib/python/kdoc/kdoc_re.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/lib/python/kdoc/kdoc_re.py +1 -1
index 774dd747ecb0..6c44fcce0415 100644
@@ -269,7 +269,7 @@ class NestedMatch:
             out += new_sub
 
             # Drop end ';' if any
-            if line[pos] == ';':
+            if pos < len(line) and line[pos] == ';':
                 pos += 1
 
             cur_pos = pos
Signature · 1 line
2.52.0
#3 lore raw

PATCH 3/18 docs: kdoc_parser: move var transformers to the beginning

In reply to Mauro Carvalho Chehab

Just like functions and structs had their transform variables placed at the beginning, move variable transforms to there as well.

No functional changes.

---
 tools/lib/python/kdoc/kdoc_parser.py | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +13 -10
index ca00695b47b3..68a5aea9175d 100644
@@ -192,6 +192,18 @@ function_xforms  = [
     (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""),
 ]
 
+#
+# Transforms for variable prototypes
+#
+var_xforms = [
+    (KernRe(r"__read_mostly"), ""),
+    (KernRe(r"__ro_after_init"), ""),
+    (KernRe(r"(?://.*)$"), ""),
+    (KernRe(r"(?:/\*.*\*/)"), ""),
+    (KernRe(r";$"), ""),
+    (KernRe(r"=.*"), ""),
+]
+
 #
 # Ancillary functions
 #
@@ -972,15 +984,6 @@ class KernelDoc:
         ]
         OPTIONAL_VAR_ATTR = "^(?:" + "|".join(VAR_ATTRIBS) + ")?"
 
-        sub_prefixes = [
-            (KernRe(r"__read_mostly"), ""),
-            (KernRe(r"__ro_after_init"), ""),
-            (KernRe(r"(?://.*)$"), ""),
-            (KernRe(r"(?:/\*.*\*/)"), ""),
-            (KernRe(r";$"), ""),
-            (KernRe(r"=.*"), ""),
-        ]
-
         #
         # Store the full prototype before modifying it
         #
@@ -1004,7 +1007,7 @@ class KernelDoc:
         # Drop comments and macros to have a pure C prototype
         #
         if not declaration_name:
-            for r, sub in sub_prefixes:
+            for r, sub in var_xforms:
                 proto = r.sub(sub, proto)
 
         proto = proto.rstrip()
Signature · 1 line
2.52.0
#4 lore raw

PATCH 4/18 docs: kdoc_parser: don't mangle with function defines

In reply to Mauro Carvalho Chehab

Mangling with #defines is not nice, as we may end removing the macro names, preventing several macros from being properly documented.

Also, on defines, we have something like:

	#define foo(a1, a2, a3, ...)			 \
		/* some real implementation */

The prototype part (first line on this example) won't contain any macros, so no need to apply any regexes on it.

With that, move the apply_transforms() logic to ensure that it will be called only on functions.

---
 tools/lib/python/kdoc/kdoc_parser.py | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +7 -5
index 68a5aea9175d..9643ffb7584a 100644
@@ -163,7 +163,7 @@ struct_nested_prefixes = [
 #
 # Transforms for function prototypes
 #
-function_xforms  = [
+function_xforms = [
     (KernRe(r"^static +"), ""),
     (KernRe(r"^extern +"), ""),
     (KernRe(r"^asmlinkage +"), ""),
@@ -1066,10 +1066,7 @@ class KernelDoc:
         found = func_macro = False
         return_type = ''
         decl_type = 'function'
-        #
-        # Apply the initial transformations.
-        #
-        prototype = apply_transforms(function_xforms, prototype)
+
         #
         # If we have a macro, remove the "#define" at the front.
         #
@@ -1088,6 +1085,11 @@ class KernelDoc:
                 declaration_name = r.group(1)
                 func_macro = True
                 found = True
+        else:
+            #
+            # Apply the initial transformations.
+            #
+            prototype = apply_transforms(function_xforms, prototype)
 
         # Yes, this truly is vile.  We are looking for:
         # 1. Return type (may be nothing if we're looking at a macro)
Signature · 1 line
2.52.0
#5 lore raw

PATCH 5/18 docs: kdoc_parser: fix variable regexes to work with size_t

In reply to Mauro Carvalho Chehab

The regular expressions meant to pick variable types are too
naive: they forgot that the type word may contain underlines.

It also means that we need to change the regex which detects var attributes to handle "const".

---
 tools/lib/python/kdoc/kdoc_parser.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +4 -3
index 9643ffb7584a..9c9443281c40 100644
@@ -981,8 +981,9 @@ class KernelDoc:
         """
         VAR_ATTRIBS = [
             "extern",
+            "const",
         ]
-        OPTIONAL_VAR_ATTR = "^(?:" + "|".join(VAR_ATTRIBS) + ")?"
+        OPTIONAL_VAR_ATTR = r"^(?:\b(?:" +"|".join(VAR_ATTRIBS) +r")\b\s*)*"
 
         #
         # Store the full prototype before modifying it
@@ -1018,14 +1019,14 @@ class KernelDoc:
 
         default_val = None
 
-        r= KernRe(OPTIONAL_VAR_ATTR + r"\w.*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+        r= KernRe(OPTIONAL_VAR_ATTR + r"[\w_]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
         if r.match(proto):
             if not declaration_name:
                 declaration_name = r.group(1)
 
             default_val = r.group(2)
         else:
-            r= KernRe(OPTIONAL_VAR_ATTR + r"(?:\w.*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+            r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
         if r.match(proto):
             default_val = r.group(1)
 
Signature · 1 line
2.52.0
#6 lore raw

PATCH 6/18 docs: kdoc_parser: fix the default_value logic for variables

In reply to Mauro Carvalho Chehab

The indentation is wrong for the second regex, which causes problems on variables with defaults.

---
 tools/lib/python/kdoc/kdoc_parser.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +2 -2
index 9c9443281c40..4bf55244870f 100644
@@ -1027,9 +1027,9 @@ class KernelDoc:
             default_val = r.group(2)
         else:
             r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
-        if r.match(proto):
-            default_val = r.group(1)
 
+            if r.match(proto):
+                default_val = r.group(1)
         if not declaration_name:
            self.emit_msg(ln,f"{proto}: can't parse variable")
            return
Signature · 1 line
2.52.0
#7 lore raw

PATCH 7/18 docs: kdoc_parser: don't exclude defaults from prototype

In reply to Mauro Carvalho Chehab

If we do that, the defaults won't be parsed.

---
 tools/lib/python/kdoc/kdoc_parser.py | 1 -
 1 file changed, 1 deletion(-)
tools/lib/python/kdoc/kdoc_parser.py +0 -1
index 4bf55244870f..39ff27d421eb 100644
@@ -201,7 +201,6 @@ var_xforms = [
     (KernRe(r"(?://.*)$"), ""),
     (KernRe(r"(?:/\*.*\*/)"), ""),
     (KernRe(r";$"), ""),
-    (KernRe(r"=.*"), ""),
 ]
 
 #
Signature · 1 line
2.52.0
#8 lore raw

PATCH 8/18 docs: kdoc_parser: fix parser to support multi-word types

In reply to Mauro Carvalho Chehab

The regular expression currently expects a single word for the type, but it may be something like "struct foo".

Add support for it.

---
 tools/lib/python/kdoc/kdoc_parser.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +2 -2
index 39ff27d421eb..22a820d33dc8 100644
@@ -1018,14 +1018,14 @@ class KernelDoc:
 
         default_val = None
 
-        r= KernRe(OPTIONAL_VAR_ATTR + r"[\w_]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+        r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
         if r.match(proto):
             if not declaration_name:
                 declaration_name = r.group(1)
 
             default_val = r.group(2)
         else:
-            r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+            r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_\s]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")
 
             if r.match(proto):
                 default_val = r.group(1)
Signature · 1 line
2.52.0
maintainer
#9 lore raw

Re: [PATCH 08/18] docs: kdoc_parser: fix parser to support multi-word types

Quoted 18 lines · Mauro Carvalho Chehab

The regular expression currently expects a single word for the type, but it may be something like "struct foo".

Add support for it.

---
 tools/lib/python/kdoc/kdoc_parser.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +0 -0
index 39ff27d421eb..22a820d33dc8 100644

tools/lib/python/kdoc/kdoc_parser.py class KernelDoc: · line 1021

Quoted 5 lines
tools/lib/python/kdoc/kdoc_parser.py +1 -1
@@ -1018,14 +1018,14 @@ class KernelDoc:
 
         default_val = None
 
-        r= KernRe(OPTIONAL_VAR_ATTR + r"[\w_]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+        r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")

Just for future reference...I *really* think that the code is improved by breaking up and commenting gnarly regexes like this. They are really unreadable in this form. (And yes, I know the code has been full of these forever, but we can always try to make it better :)

Anyway, just grumbling.

Thanks,

jon

#10 lore raw

Re: [PATCH 08/18] docs: kdoc_parser: fix parser to support multi-word types

Quoted 23 lines · On Tue, 03 Mar 2026 10:34:48 -0700 Jonathan Corbet <[email protected]> wrote:
Quoted 21 lines · Mauro Carvalho Chehab

The regular expression currently expects a single word for the type, but it may be something like "struct foo".

Add support for it.

---
 tools/lib/python/kdoc/kdoc_parser.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +0 -0
index 39ff27d421eb..22a820d33dc8 100644
@@ -1018,14 +1018,14 @@ class KernelDoc:
 
         default_val = None
 
Quoted 7 lines

tools/lib/python/kdoc/kdoc_parser.py class KernelDoc: · line 1021

Quoted 2 lines
tools/lib/python/kdoc/kdoc_parser.py +1 -1
-        r= KernRe(OPTIONAL_VAR_ATTR + r"[\w_]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
+        r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")  

Just for future reference...I *really* think that the code is improved by breaking up and commenting gnarly regexes like this. They are really unreadable in this form. (And yes, I know the code has been full of these forever, but we can always try to make it better :)

Heh, you're right: this could be better.

Quoted 1 line

Anyway, just grumbling.

Heh, if we start using a code like the tokenizer I'm experimenting here:

	https://lore.kernel.org/linux-doc/20260303155310.5235b367@localhost/

we could probably get rid of regexes in the future, using instead a loop that would be picking "ID" tokens, e.g. basically we would have something similar to this completely untested code snippet:

self.tokenizer = CTokenizer()

...

ids = []
get_default = False

while kind, value in self.tokenizer(proto):
if kind == "ID":
ids.append(value)

if kind == "OP" and value == "=":
get_default = True
break

if get_default:
while kind, value in self.tokenizer(proto):
if kind in ["CHAR", "STRING", "NUMBER"]:
default_val = value break

declaration_name = ids[-1]

Thanks,
Mauro

maintainer
#11 lore raw

Re: [PATCH 08/18] docs: kdoc_parser: fix parser to support multi-word types

Quoted 8 lines · Mauro Carvalho Chehab

Heh, if we start using a code like the tokenizer I'm experimenting here:

	https://lore.kernel.org/linux-doc/20260303155310.5235b367@localhost/

we could probably get rid of regexes in the future, using instead a loop that would be picking "ID" tokens, e.g. basically we would have something similar to this completely untested code snippet:

...which has some appeal, but I will confess to being leery of another massive kernel-doc thrash; it would be nice if things settled for a bit.

One can always dream :)

jon

#12 lore raw

Re: [PATCH 08/18] docs: kdoc_parser: fix parser to support multi-word types

Quoted 5 lines · On Tue, 03 Mar 2026 13:24:55 -0700 Jonathan Corbet <[email protected]> wrote:
Quoted 3 lines · Mauro Carvalho Chehab

Heh, if we start using a code like the tokenizer I'm experimenting here:

Quoted 8 lines
Quoted 5 lines
	https://lore.kernel.org/linux-doc/20260303155310.5235b367@localhost/

we could probably get rid of regexes in the future, using instead a loop that would be picking "ID" tokens, e.g. basically we would have something similar to this completely untested code snippet:

...which has some appeal, but I will confess to being leery of another massive kernel-doc thrash; it would be nice if things settled for a bit.

Yeah, I feel your pain. The idea is not to simply rewrite the entire kernel-doc. Just to use it at the places that have known hard to solve bugs.

Quoted 1 line

One can always dream :)

The thing is that there are some issues at kernel-doc that can only be solved with a better parser, and a plain regex logic won't fix, even with really complex expressions.

I'm not talking about variable handling like on this specific patch. For it, the current pure regex approach works fine, at least for the cases we already mapped.

However, kernel-doc, even after this series, do a crap job on several places:

1. Macros used to build structs and function prototypes.

During the conversion I wrote a half-baked NestedMatch class to be able of properly handling struct_group*(), which is the best example of the involved complexity. It works, but it requires parsing the code twice.

Also, It probably will fail with nested struct_group;

2. Nested structs.

Current logic just transform them on an un-nested struct-like pseudo-code to re-use the structs regex-based parser;

3. Nested struct identifiers handling.

Spec says that if one has:

	struct {
		struct {
			int foo;
		} a;
	} b;

kernel-doc should document "foo" as "a.foo", but this is not always the case, due to bugs at the parser. So, on some places, you need to use "foo"; on others, "a.foo".

4. Public/private handling.

Code almost works, but when it finds a unmatched private inside a nested struct, it will can hide close brackets. This prevents fixing (2).

5. Comments strip.

Code kinda works if you don't touch it, but when trying to solve the previous issues, I ended discovering some hidden problems related to the way it does.

(That's basically when I ended opting to try a different approach:
too much changes to try to live with a plain regex approach, plus all stuff needed for NestedMatch to do a better job)

6. Proper whitespace and alignment at the output.

The current way we parse things mean that little changes end mangling with whitespaces, line breaks and/or indentation.

Perhaps we could use some token-based formatter for man pages to properly handle open/close brackets.

For rst output, we're relying at the C domain to handle it for us. Still, perhaps a tokenizer-based approach can just add a single whitespace everywhere, which would help us to check before/after differences on kernel-doc changes.

I attempted fixing this at the /38 patch series and afterwards (good news that that we have 18 less patches after you merged this one), but my current pending patch pile has stlll +40 patches to address issues and add unit tests.

Among them, I have changes:

- adding support for "\1", "\2", "\3"... group matches at NestedMatch sub. It works. The code itself is small, but very complex;

- writing a different logic to address comment stripping;

- write a logic to pick struct IDs on nested cases that will be using an approach similar to NestedMatch.

- some of those new logic are recursive, which makes them more complex to be understood and tested.

Such approach works, but, as Jani pointed, this ends adding lots of complexity, and the main reason is that we're acquainted to use regexes - or perhaps too afraid to handle it on a different way. Also, they ended introducing extra bugs.

I had to confess that I also a little reticent on trying to use a tokenizer, as I was afraid that this would require extra libraries to have something similar to what flex/bison would be doing. Then, I realized that perhaps the already internal libraries might just have what we needed. So, after some research, looking at:

https://docs.python.org/3/library/re.html#writing-a-tokenizer

and doing some (so far simple) tests, I'm starting to think about modifying my pending patches to use a code similar to it.

Note that, at this point, I didn't try yet to use the tokenizer, so for now this is still mostly a brainstorm.

I intend to try to use the tokenizer to handle comments e.g. touching the logic at trim_private_members() and see how it behaves. This is self-contained, checking the results would likely be easy, and I don't expect big surprises.

Depending on such tests, I may try to modify NestedMatch and my patches using it to use the same approach.

So, if all ends well, the changes will so far be confined to the code used by dump_struct().

Let's see.

Thanks,
Mauro

#13 lore raw

PATCH 9/18 docs: kdoc_parser: add support for LIST_HEAD

In reply to Mauro Carvalho Chehab

Convert LIST_HEAD into struct list_head when handling its prototype.

---
 tools/lib/python/kdoc/kdoc_parser.py | 1 +
 1 file changed, 1 insertion(+)
tools/lib/python/kdoc/kdoc_parser.py +1 -0
index 22a820d33dc8..1df869061bf3 100644
@@ -198,6 +198,7 @@ function_xforms = [
 var_xforms = [
     (KernRe(r"__read_mostly"), ""),
     (KernRe(r"__ro_after_init"), ""),
+    (KernRe(r"LIST_HEAD\(([\w_]+)\)"), r"struct list_head \1"),
     (KernRe(r"(?://.*)$"), ""),
     (KernRe(r"(?:/\*.*\*/)"), ""),
     (KernRe(r";$"), ""),
Signature · 1 line
2.52.0
#14 lore raw

PATCH 10/18 docs: kdoc_parser: handle struct member macro VIRTIO_DECLARE_FEATURES(name)

In reply to Mauro Carvalho Chehab

From: Randy Dunlap <[email protected]>

Parse the macro VIRTIO_DECLARE_FEATURES(name) and expand it to its definition. These prevents one build warning:

WARNING: include/linux/virtio.h:188 struct member 'VIRTIO_DECLARE_FEATURES(features' not described in 'virtio_device'
---
 tools/lib/python/kdoc/kdoc_parser.py | 1 +
 1 file changed, 1 insertion(+)
tools/lib/python/kdoc/kdoc_parser.py +1 -0
index 1df869061bf3..917e4528bfbf 100644
@@ -150,6 +150,7 @@ struct_xforms = [
             struct_args_pattern + r'\)', re.S), r'\1 \2[]'),
     (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'),
     (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'),
+    (KernRe(r'VIRTIO_DECLARE_FEATURES\(([\w_]+)\)'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'),
 ]
 #
 # Regexes here are guaranteed to have the end delimiter matching
Signature · 1 line
2.52.0
#15 lore raw

PATCH 11/18 docs: kdoc_re: better show KernRe() at documentation

In reply to Mauro Carvalho Chehab

the __repr__() function is used by autodoc to document macro initialization.

Add a better representation for them.

---
 tools/lib/python/kdoc/kdoc_re.py | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
tools/lib/python/kdoc/kdoc_re.py +22 -1
index 6c44fcce0415..664c04c8cc9f 100644
@@ -52,7 +52,28 @@ class KernRe:
         return self.regex.pattern
 
     def __repr__(self):
-        return f're.compile("{self.regex.pattern}")'
+        """
+        Returns a displayable version of the class init.
+        """
+
+        flag_map = {
+            re.IGNORECASE: "re.I",
+            re.MULTILINE: "re.M",
+            re.DOTALL: "re.S",
+            re.VERBOSE: "re.X",
+        }
+
+        flags = []
+        for flag, name in flag_map.items():
+            if self.regex.flags & flag:
+                flags.append(name)
+
+        flags_name = " | ".join(flags)
+
+        if flags_name:
+            return f'KernRe("{self.regex.pattern}", {flags_name})'
+        else:
+            return f'KernRe("{self.regex.pattern}")'
 
     def __add__(self, other):
         """
Signature · 1 line
2.52.0
#16 lore raw

PATCH 12/18 docs: kdoc_parser: move transform lists to a separate file

In reply to Mauro Carvalho Chehab

Over the time, most of the changes at kernel-doc are related to maintaining a list of transforms to convert macros into pure C code.

Place such transforms on a separate module, to cleanup the parser module.

There is an advantage on that: QEMU also uses our own kernel-doc, but the xforms list there is different. By placing it on a separate module, we can minimize the differences and make it easier to keep QEMU in sync with Kernel upstream.

No functional changes.

---
 Documentation/tools/kdoc_parser.rst   |   8 ++
 tools/lib/python/kdoc/kdoc_files.py   |   3 +-
 tools/lib/python/kdoc/kdoc_parser.py  | 145 +-----------------------
 tools/lib/python/kdoc/xforms_lists.py | 153 ++++++++++++++++++++++++++
 4 files changed, 168 insertions(+), 141 deletions(-)
 create mode 100644 tools/lib/python/kdoc/xforms_lists.py
Documentation/tools/kdoc_parser.rst +8 -0
index 03ee54a1b1cc..55b202173195 100644
@@ -4,6 +4,14 @@
 Kernel-doc parser stage
 =======================
 
+C replacement rules used by the parser
+======================================
+
+.. automodule:: lib.python.kdoc.xforms_lists
+   :members:
+   :show-inheritance:
+   :undoc-members:
+
 File handler classes
 ====================
 
tools/lib/python/kdoc/kdoc_files.py +2 -1
index 022487ea2cc6..33618c6abec2 100644
@@ -15,6 +15,7 @@ import os
 import re
 
 from kdoc.kdoc_parser import KernelDoc
+from kdoc.xforms_lists import CTransforms
 from kdoc.kdoc_output import OutputFormat
 
 
@@ -117,7 +118,7 @@ class KernelFiles():
         if fname in self.files:
             return
 
-        doc = KernelDoc(self.config, fname)
+        doc = KernelDoc(self.config, fname, CTransforms())
         export_table, entries = doc.parse_kdoc()
 
         self.export_table[fname] = export_table
tools/lib/python/kdoc/kdoc_parser.py +5 -140

189 lines not on this page · raw message

tools/lib/python/kdoc/xforms_lists.py new file +153 -0

153 lines not on this page · raw message

Signature · 1 line
2.52.0
#17 lore raw

PATCH 13/18 docs: xforms_lists: ignore context analysis and lock attributes

In reply to Mauro Carvalho Chehab

From: Randy Dunlap <[email protected]>

Drop context analysis and lock (tracking) attributes to avoid kernel-doc warnings.

There are now lots of warnings like these:

Documentation/core-api/kref:328: ../include/linux/kref.h:72: WARNING: Invalid C declaration: Expected end of definition. [error at 96]
int kref_put_mutex (struct kref *kref, void (*release)(struct kref *kref), struct mutex *mutex) __cond_acquires(true# mutex) ------------------------------------------------------------------------------------------------^
Documentation/core-api/kref:328: ../include/linux/kref.h:94: WARNING: Invalid C declaration: Expected end of definition. [error at 92]
int kref_put_lock (struct kref *kref, void (*release)(struct kref *kref), spinlock_t *lock) __cond_acquires(true# lock) --------------------------------------------------------------------------------------------^

The regex is suggested by Mauro; mine was too greedy. Thanks. Updated context analysis and lock macros list provided by PeterZ. Thanks.

[mchehab: modified to be applied after xforms_lists split]

---
 tools/lib/python/kdoc/kdoc_parser.py  | 10 ++++++++++
 tools/lib/python/kdoc/xforms_lists.py |  5 +++++
 2 files changed, 15 insertions(+)
tools/lib/python/kdoc/kdoc_parser.py +10 -0
index d7daf658e9d2..503a18212747 100644
@@ -75,6 +75,16 @@ doc_begin_func = KernRe(str(doc_com) +			# initial " * '
 # is allowed.
 #
 struct_nested_prefixes = [
+    (re.compile(r"__cond_acquires\s*\("), ""),
+    (re.compile(r"__cond_releases\s*\("), ""),
+    (re.compile(r"__acquires\s*\("), ""),
+    (re.compile(r"__releases\s*\("), ""),
+    (re.compile(r"__must_hold\s*\("), ""),
+    (re.compile(r"__must_not_hold\s*\("), ""),
+    (re.compile(r"__must_hold_shared\s*\("), ""),
+    (re.compile(r"__cond_acquires_shared\s*\("), ""),
+    (re.compile(r"__acquires_shared\s*\("), ""),
+    (re.compile(r"__releases_shared\s*\("), ""),
     (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
 ]
 
tools/lib/python/kdoc/xforms_lists.py +5 -0
index e6e0302e5dd0..1bda7c4634c3 100644
@@ -22,6 +22,8 @@ class CTransforms:
         (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
         (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
         (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
+        (KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ' '),
+        (KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ' '),
         (KernRe(r'\s*__packed\s*', re.S), ' '),
         (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
         (KernRe(r'\s*__private', re.S), ' '),
@@ -120,6 +122,7 @@ class CTransforms:
         (KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""),
         (KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""),
         (KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"),
+        (KernRe(r"__no_context_analysis\s*"), ""),
         (KernRe(r"__attribute_const__ +"), ""),
         (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""),
     ]
@@ -128,6 +131,8 @@ class CTransforms:
     var_xforms = [
         (KernRe(r"__read_mostly"), ""),
         (KernRe(r"__ro_after_init"), ""),
+        (KernRe(r'\s*__guarded_by\s*\([^\)]*\)', re.S), ""),
+        (KernRe(r'\s*__pt_guarded_by\s*\([^\)]*\)', re.S), ""),
         (KernRe(r"LIST_HEAD\(([\w_]+)\)"), r"struct list_head \1"),
         (KernRe(r"(?://.*)$"), ""),
         (KernRe(r"(?:/\*.*\*/)"), ""),
Signature · 1 line
2.52.0
#18 lore raw

PATCH 14/18 docs: kdoc_re: handle strings and escape chars on NextMatch

In reply to Mauro Carvalho Chehab

The logic inside NestedMatch currently doesn't consider that function arguments may have chars and strings, which may eventually contain delimiters.

Add logic to handle strings and escape characters on them.

---
 tools/lib/python/kdoc/kdoc_re.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
tools/lib/python/kdoc/kdoc_re.py +18 -0
index 664c04c8cc9f..0a7f12616f9f 100644
@@ -216,6 +216,8 @@ class NestedMatch:
         for match_re in regex.finditer(line):
             start = match_re.start()
             offset = match_re.end()
+            string_char = None
+            escape = False
 
             d = line[offset - 1]
             if d not in self.DELIMITER_PAIRS:
@@ -229,6 +231,22 @@ class NestedMatch:
 
                 d = line[pos]
 
+                if escape:
+                    escape = False
+                    continue
+
+                if string_char:
+                    if d == '\\':
+                        escape = True
+                    elif d == string_char:
+                        string_char = None
+
+                    continue
+
+                if d in ('"', "'"):
+                    string_char = d
+                    continue
+
                 if d in self.DELIMITER_PAIRS:
                     end = self.DELIMITER_PAIRS[d]
 
Signature · 1 line
2.52.0
#19 lore raw

PATCH 15/18 docs: kdoc_re: don't recompile NestedMatch regex every time

In reply to Mauro Carvalho Chehab

Store delimiters and its regex-compiled version as const vars.

---
 tools/lib/python/kdoc/kdoc_re.py | 35 ++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 13 deletions(-)
tools/lib/python/kdoc/kdoc_re.py +22 -13
index 0a7f12616f9f..00afa5bccd6d 100644
@@ -99,6 +99,13 @@ class KernRe:
         self.last_match = self.regex.search(string)
         return self.last_match
 
+    def finditer(self,  string):
+        """
+        Alias to re.finditer.
+        """
+
+        return self.regex.finditer(string)
+
     def findall(self, string):
         """
         Alias to re.findall.
@@ -134,6 +141,16 @@ class KernRe:
 
         return self.last_match.groups()
 
+#: Nested delimited pairs (brackets and parenthesis)
+DELIMITER_PAIRS = {
+    '{': '}',
+    '(': ')',
+    '[': ']',
+}
+
+#: compiled delimiters
+RE_DELIM = KernRe(r'[\{\}\[\]\(\)]')
+
 
 class NestedMatch:
     """
@@ -183,14 +200,6 @@ class NestedMatch:
     #
     #   FOO(arg1, arg2, arg3)
 
-    DELIMITER_PAIRS = {
-        '{': '}',
-        '(': ')',
-        '[': ']',
-    }
-
-    RE_DELIM = re.compile(r'[\{\}\[\]\(\)]')
-
     def _search(self, regex, line):
         """
         Finds paired blocks for a regex that ends with a delimiter.
@@ -220,13 +229,13 @@ class NestedMatch:
             escape = False
 
             d = line[offset - 1]
-            if d not in self.DELIMITER_PAIRS:
+            if d not in DELIMITER_PAIRS:
                 continue
 
-            end = self.DELIMITER_PAIRS[d]
+            end = DELIMITER_PAIRS[d]
             stack.append(end)
 
-            for match in self.RE_DELIM.finditer(line[offset:]):
+            for match in RE_DELIM.finditer(line[offset:]):
                 pos = match.start() + offset
 
                 d = line[pos]
@@ -247,8 +256,8 @@ class NestedMatch:
                     string_char = d
                     continue
 
-                if d in self.DELIMITER_PAIRS:
-                    end = self.DELIMITER_PAIRS[d]
+                if d in DELIMITER_PAIRS:
+                    end = DELIMITER_PAIRS[d]
 
                     stack.append(end)
                     continue
Signature · 1 line
2.52.0
#20 lore raw

PATCH 16/18 docs: kdoc_re: Change NestedMath args replacement to \0

In reply to Mauro Carvalho Chehab

Future patches will allow parsing each argument instead of the hole set. Prepare for it by changing the replace all args from \1 to \0.

No functional changes.

---
 tools/lib/python/kdoc/kdoc_parser.py | 2 +-
 tools/lib/python/kdoc/kdoc_re.py     | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +1 -1
index 503a18212747..0f90c16cb51a 100644
@@ -85,7 +85,7 @@ struct_nested_prefixes = [
     (re.compile(r"__cond_acquires_shared\s*\("), ""),
     (re.compile(r"__acquires_shared\s*\("), ""),
     (re.compile(r"__releases_shared\s*\("), ""),
-    (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
+    (re.compile(r'\bSTRUCT_GROUP\('), r'\0'),
 ]
 
 #
tools/lib/python/kdoc/kdoc_re.py +4 -4
index 00afa5bccd6d..ea4f6f3d9e42 100644
@@ -188,7 +188,7 @@ class NestedMatch:
     # except that the content inside the match group is delimiter-aligned.
     #
     # The content inside parentheses is converted into a single replace
-    # group (e.g. r`\1').
+    # group (e.g. r`\0').
     #
     # It would be nice to change such definition to support multiple
     # match groups, allowing a regex equivalent to:
@@ -291,7 +291,7 @@ class NestedMatch:
 
         if the sub argument contains::
 
-            r'\1'
+            r'\0'
 
         it will work just like re: it places there the matched paired data
         with the delimiter stripped.
@@ -310,9 +310,9 @@ class NestedMatch:
             # Value, ignoring start/end delimiters
             value = line[end:pos - 1]
 
-            # replaces \1 at the sub string, if \1 is used there
+            # replaces \0 at the sub string, if \0 is used there
             new_sub = sub
-            new_sub = new_sub.replace(r'\1', value)
+            new_sub = new_sub.replace(r'\0', value)
 
             out += new_sub
 
Signature · 1 line
2.52.0
#21 lore raw

PATCH 17/18 docs: kdoc_re: make NestedMatch use KernRe

In reply to Mauro Carvalho Chehab

Instead of using re_compile, let's create the class with the regex and use KernRe to keep it cached.

---
 tools/lib/python/kdoc/kdoc_parser.py | 25 ++++++++++++-------------
 tools/lib/python/kdoc/kdoc_re.py     | 24 +++++++++++++++++-------
 2 files changed, 29 insertions(+), 20 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +12 -13
index 0f90c16cb51a..cd9857906a2b 100644
@@ -75,17 +75,17 @@ doc_begin_func = KernRe(str(doc_com) +			# initial " * '
 # is allowed.
 #
 struct_nested_prefixes = [
-    (re.compile(r"__cond_acquires\s*\("), ""),
-    (re.compile(r"__cond_releases\s*\("), ""),
-    (re.compile(r"__acquires\s*\("), ""),
-    (re.compile(r"__releases\s*\("), ""),
-    (re.compile(r"__must_hold\s*\("), ""),
-    (re.compile(r"__must_not_hold\s*\("), ""),
-    (re.compile(r"__must_hold_shared\s*\("), ""),
-    (re.compile(r"__cond_acquires_shared\s*\("), ""),
-    (re.compile(r"__acquires_shared\s*\("), ""),
-    (re.compile(r"__releases_shared\s*\("), ""),
-    (re.compile(r'\bSTRUCT_GROUP\('), r'\0'),
+    (NestedMatch(r"__cond_acquires\s*\("), ""),
+    (NestedMatch(r"__cond_releases\s*\("), ""),
+    (NestedMatch(r"__acquires\s*\("), ""),
+    (NestedMatch(r"__releases\s*\("), ""),
+    (NestedMatch(r"__must_hold\s*\("), ""),
+    (NestedMatch(r"__must_not_hold\s*\("), ""),
+    (NestedMatch(r"__must_hold_shared\s*\("), ""),
+    (NestedMatch(r"__cond_acquires_shared\s*\("), ""),
+    (NestedMatch(r"__acquires_shared\s*\("), ""),
+    (NestedMatch(r"__releases_shared\s*\("), ""),
+    (NestedMatch(r'\bSTRUCT_GROUP\('), r'\0'),
 ]
 
 #
@@ -761,9 +761,8 @@ class KernelDoc:
         members = trim_private_members(members)
         members = self.xforms.apply("struct", members)
 
-        nested = NestedMatch()
         for search, sub in struct_nested_prefixes:
-            members = nested.sub(search, sub, members)
+            members = search.sub(search, sub, members)
         #
         # Deal with embedded struct and union members, and drop enums entirely.
         #
tools/lib/python/kdoc/kdoc_re.py +17 -7
index ea4f6f3d9e42..085b89a4547c 100644
@@ -200,7 +200,10 @@ class NestedMatch:
     #
     #   FOO(arg1, arg2, arg3)
 
-    def _search(self, regex, line):
+    def __init__(self, regex):
+        self.regex = KernRe(regex)
+
+    def _search(self, line):
         """
         Finds paired blocks for a regex that ends with a delimiter.
 
@@ -222,7 +225,7 @@ class NestedMatch:
 
         stack = []
 
-        for match_re in regex.finditer(line):
+        for match_re in self.regex.finditer(line):
             start = match_re.start()
             offset = match_re.end()
             string_char = None
@@ -270,7 +273,7 @@ class NestedMatch:
                         yield start, offset, pos + 1
                         break
 
-    def search(self, regex, line):
+    def search(self, line):
         """
         This is similar to re.search:
 
@@ -278,12 +281,12 @@ class NestedMatch:
         returning occurrences only if all delimiters are paired.
         """
 
-        for t in self._search(regex, line):
+        for t in self._search(line):
 
             yield line[t[0]:t[2]]
 
-    def sub(self, regex, sub, line, count=0):
-        r"""
+    def sub(self, sub, line, count=0):
+        """
         This is similar to re.sub:
 
         It matches a regex that it is followed by a delimiter,
@@ -304,7 +307,7 @@ class NestedMatch:
         cur_pos = 0
         n = 0
 
-        for start, end, pos in self._search(regex, line):
+        for start, end, pos in self._search(line):
             out += line[cur_pos:start]
 
             # Value, ignoring start/end delimiters
@@ -331,3 +334,10 @@ class NestedMatch:
         out += line[cur_pos:l]
 
         return out
+
+    def __repr__(self):
+        """
+        Returns a displayable version of the class init.
+        """
+
+        return f'NestedMatch("{self.regex.regex.pattern}")'
Signature · 1 line
2.52.0
#22 lore raw

PATCH 18/18 docs: kdoc_parser: move nested match transforms to xforms_lists.py

In reply to Mauro Carvalho Chehab

As NestedMatch now has a sub method and a declaration close to what KernRe does, we can move the rules to xforms_lists and simplify kdoc_parser a little bit.

No functional changes.

---
 tools/lib/python/kdoc/kdoc_parser.py  | 21 ---------------------
 tools/lib/python/kdoc/xforms_lists.py | 14 +++++++++++++-
 2 files changed, 13 insertions(+), 22 deletions(-)
tools/lib/python/kdoc/kdoc_parser.py +0 -21
index cd9857906a2b..edf70ba139a5 100644
@@ -69,25 +69,6 @@ doc_begin_func = KernRe(str(doc_com) +			# initial " * '
                         r'(?:[-:].*)?$',		# description (not captured)
                         cache = False)
 
-#
-# Regexes here are guaranteed to have the end delimiter matching
-# the start delimiter. Yet, right now, only one replace group
-# is allowed.
-#
-struct_nested_prefixes = [
-    (NestedMatch(r"__cond_acquires\s*\("), ""),
-    (NestedMatch(r"__cond_releases\s*\("), ""),
-    (NestedMatch(r"__acquires\s*\("), ""),
-    (NestedMatch(r"__releases\s*\("), ""),
-    (NestedMatch(r"__must_hold\s*\("), ""),
-    (NestedMatch(r"__must_not_hold\s*\("), ""),
-    (NestedMatch(r"__must_hold_shared\s*\("), ""),
-    (NestedMatch(r"__cond_acquires_shared\s*\("), ""),
-    (NestedMatch(r"__acquires_shared\s*\("), ""),
-    (NestedMatch(r"__releases_shared\s*\("), ""),
-    (NestedMatch(r'\bSTRUCT_GROUP\('), r'\0'),
-]
-
 #
 # Ancillary functions
 #
@@ -761,8 +742,6 @@ class KernelDoc:
         members = trim_private_members(members)
         members = self.xforms.apply("struct", members)
 
-        for search, sub in struct_nested_prefixes:
-            members = search.sub(search, sub, members)
         #
         # Deal with embedded struct and union members, and drop enums entirely.
         #
tools/lib/python/kdoc/xforms_lists.py +13 -1
index 1bda7c4634c3..c07cbe1e6349 100644
@@ -4,7 +4,7 @@
 
 import re
 
-from kdoc.kdoc_re import KernRe
+from kdoc.kdoc_re import KernRe, NestedMatch
 
 struct_args_pattern = r'([^,)]+)'
 
@@ -94,6 +94,18 @@ class CTransforms:
         (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'),
         (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'),
         (KernRe(r'VIRTIO_DECLARE_FEATURES\(([\w_]+)\)'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'),
+
+        (NestedMatch(r"__cond_acquires\s*\("), ""),
+        (NestedMatch(r"__cond_releases\s*\("), ""),
+        (NestedMatch(r"__acquires\s*\("), ""),
+        (NestedMatch(r"__releases\s*\("), ""),
+        (NestedMatch(r"__must_hold\s*\("), ""),
+        (NestedMatch(r"__must_not_hold\s*\("), ""),
+        (NestedMatch(r"__must_hold_shared\s*\("), ""),
+        (NestedMatch(r"__cond_acquires_shared\s*\("), ""),
+        (NestedMatch(r"__acquires_shared\s*\("), ""),
+        (NestedMatch(r"__releases_shared\s*\("), ""),
+        (NestedMatch(r'\bSTRUCT_GROUP\('), r'\0'),
     ]
 
     #: Transforms for function prototypes.
Signature · 1 line
2.52.0
maintainer
#23 lore raw
In reply to Mauro Carvalho Chehab
Quoted 7 lines · Mauro Carvalho Chehab

Hi Jon,

This series contains patches from:

	https://lore.kernel.org/linux-doc/[email protected]/T/#t

I rebased them to make the changes cleaner, fixing mostly on fixes.

Quoted 8 lines

I dropped stuff related to NestedMatch arguments parsing. I'll be sending you on a new patch series, likely tomorrow.

The changes here basically addresses several issues discoverd by Randy and by me and makes NestedMatch more similar to KernRe.

It also moves the transforms ruleset to a separate file, to make easier to maintain kernel-doc code.

OK, I have applied this set - it all seems to work as advertised as far as I can tell.

Thanks,

jon