tools/lib/python/kdoc/kdoc_parser.py | 51 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-)
Recent check_sections() change added a warning if a documentation tag
member name does not match the detected struct/union member names. Since
the checker knows all possible names, we can suggest known names, so
that it's more obvious how to deal with the warning.
Signed-off-by: Ryszard Knop <ryszard.knop@intel.com>
---
Changes in v1:
- Added the suggestion hint in the warning, with basic name substring checks
- Link: https://lore.kernel.org/linux-doc/20260714111208.323108-1-ryszard.knop@intel.com/
Changes in v2:
- Strip trailing whitespace from the warning when the generated hint is empty
- Link: https://lore.kernel.org/linux-doc/20260715111726.394565-1-ryszard.knop@intel.com/
v3:
- Use difflib to generate suggestions even if the tag member is mistyped
- Suggest names based on the nested struct members too
---
tools/lib/python/kdoc/kdoc_parser.py | 51 ++++++++++++++++++++++++++--
1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
index 2dedda215c22..884f42584667 100644
--- a/tools/lib/python/kdoc/kdoc_parser.py
+++ b/tools/lib/python/kdoc/kdoc_parser.py
@@ -11,6 +11,7 @@ and extract embedded documentation comments from it.
import sys
import re
+import difflib
from pprint import pformat
from kdoc.c_lex import CTokenizer, tokenizer_set_log
@@ -558,6 +559,50 @@ class KernelDoc:
self.push_parameter(ln, decl_type, param, dtype,
arg, declaration_name)
+ def get_suggestions_hint(self, decl_name, possible_names):
+ # For decl name 'flags' or 'flgas', suggests 'substruct.flags'
+ submember_exact = []
+ submember_substrings = []
+ submember_suggestions = []
+ for possible_name in possible_names:
+ parts = possible_name.strip().split('.')
+ if len(parts) < 2:
+ continue
+
+ final_part = parts[-1]
+ if decl_name == final_part:
+ submember_exact.append(possible_name)
+ elif decl_name in final_part:
+ submember_substrings.append(possible_name)
+ elif difflib.get_close_matches(decl_name, [final_part]):
+ submember_suggestions.append(possible_name)
+
+ # For decl name 'flgas', suggests 'flags'
+ full_suggestions = difflib.get_close_matches(decl_name, possible_names)
+
+ # For decl name 'member', suggests 'longer_member'
+ full_substrings = [name for name in possible_names if decl_name in name]
+
+ ordered_lists = [
+ submember_exact,
+ submember_substrings,
+ submember_suggestions,
+ full_suggestions,
+ full_substrings,
+ ]
+
+ # Deduplicate but maintain order from most to least likely:
+ unique_suggestions = {}
+ for suggestion_list in ordered_lists:
+ for suggestion in suggestion_list:
+ unique_suggestions[suggestion] = None
+
+ suggestions = list(unique_suggestions.keys())
+ if not suggestions:
+ return ""
+
+ return f"(did you mean one of: '{"', '".join(suggestions)}')"
+
def check_sections(self, ln, decl_name, decl_type):
"""
Check for errors inside sections, emitting warnings if not found
@@ -566,12 +611,13 @@ class KernelDoc:
for section in self.entry.sections:
if section not in self.entry.parameterlist and \
not known_sections.search(section):
+ hint = self.get_suggestions_hint(section, self.entry.parameterlist)
if decl_type == 'function':
dname = f"{decl_type} parameter"
else:
dname = f"{decl_type} member"
self.emit_msg(ln,
- f"Excess {dname} '{section}' description in '{decl_name}'")
+ f"Excess {dname} '{section}' description in '{decl_name}' {hint}".strip())
#
# Check that documented parameter names (from doc comments, including
@@ -591,12 +637,13 @@ class KernelDoc:
if param_name in self.entry.parameterlist:
continue
+ hint = self.get_suggestions_hint(param_name, self.entry.parameterlist)
if decl_type == 'function':
dname = f"{decl_type} parameter"
else:
dname = f"{decl_type} member"
self.emit_msg(ln,
- f"Excess {dname} '{param_name}' description in '{decl_name}'")
+ f"Excess {dname} '{param_name}' description in '{decl_name}' {hint}".strip())
def check_return_section(self, ln, declaration_name, return_type):
"""
--
2.55.0
Ryszard Knop <ryszard.knop@intel.com> writes: > Recent check_sections() change added a warning if a documentation tag > member name does not match the detected struct/union member names. Since > the checker knows all possible names, we can suggest known names, so > that it's more obvious how to deal with the warning. > > Signed-off-by: Ryszard Knop <ryszard.knop@intel.com> > --- > Changes in v1: > - Added the suggestion hint in the warning, with basic name substring checks > - Link: https://lore.kernel.org/linux-doc/20260714111208.323108-1-ryszard.knop@intel.com/ > > Changes in v2: > - Strip trailing whitespace from the warning when the generated hint is empty > - Link: https://lore.kernel.org/linux-doc/20260715111726.394565-1-ryszard.knop@intel.com/ > > v3: > - Use difflib to generate suggestions even if the tag member is mistyped > - Suggest names based on the nested struct members too > --- > tools/lib/python/kdoc/kdoc_parser.py | 51 ++++++++++++++++++++++++++-- > 1 file changed, 49 insertions(+), 2 deletions(-) Applied, thanks. jon
On 7/17/26 5:57 AM, Ryszard Knop wrote:
> Recent check_sections() change added a warning if a documentation tag
> member name does not match the detected struct/union member names. Since
> the checker knows all possible names, we can suggest known names, so
> that it's more obvious how to deal with the warning.
>
> Signed-off-by: Ryszard Knop <ryszard.knop@intel.com>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.
> ---
> Changes in v1:
> - Added the suggestion hint in the warning, with basic name substring checks
> - Link: https://lore.kernel.org/linux-doc/20260714111208.323108-1-ryszard.knop@intel.com/
>
> Changes in v2:
> - Strip trailing whitespace from the warning when the generated hint is empty
> - Link: https://lore.kernel.org/linux-doc/20260715111726.394565-1-ryszard.knop@intel.com/
>
> v3:
> - Use difflib to generate suggestions even if the tag member is mistyped
> - Suggest names based on the nested struct members too
> ---
> tools/lib/python/kdoc/kdoc_parser.py | 51 ++++++++++++++++++++++++++--
> 1 file changed, 49 insertions(+), 2 deletions(-)
>
> diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
> index 2dedda215c22..884f42584667 100644
> --- a/tools/lib/python/kdoc/kdoc_parser.py
> +++ b/tools/lib/python/kdoc/kdoc_parser.py
> @@ -11,6 +11,7 @@ and extract embedded documentation comments from it.
>
> import sys
> import re
> +import difflib
> from pprint import pformat
>
> from kdoc.c_lex import CTokenizer, tokenizer_set_log
> @@ -558,6 +559,50 @@ class KernelDoc:
> self.push_parameter(ln, decl_type, param, dtype,
> arg, declaration_name)
>
> + def get_suggestions_hint(self, decl_name, possible_names):
> + # For decl name 'flags' or 'flgas', suggests 'substruct.flags'
> + submember_exact = []
> + submember_substrings = []
> + submember_suggestions = []
> + for possible_name in possible_names:
> + parts = possible_name.strip().split('.')
> + if len(parts) < 2:
> + continue
> +
> + final_part = parts[-1]
> + if decl_name == final_part:
> + submember_exact.append(possible_name)
> + elif decl_name in final_part:
> + submember_substrings.append(possible_name)
> + elif difflib.get_close_matches(decl_name, [final_part]):
> + submember_suggestions.append(possible_name)
> +
> + # For decl name 'flgas', suggests 'flags'
> + full_suggestions = difflib.get_close_matches(decl_name, possible_names)
> +
> + # For decl name 'member', suggests 'longer_member'
> + full_substrings = [name for name in possible_names if decl_name in name]
> +
> + ordered_lists = [
> + submember_exact,
> + submember_substrings,
> + submember_suggestions,
> + full_suggestions,
> + full_substrings,
> + ]
> +
> + # Deduplicate but maintain order from most to least likely:
> + unique_suggestions = {}
> + for suggestion_list in ordered_lists:
> + for suggestion in suggestion_list:
> + unique_suggestions[suggestion] = None
> +
> + suggestions = list(unique_suggestions.keys())
> + if not suggestions:
> + return ""
> +
> + return f"(did you mean one of: '{"', '".join(suggestions)}')"
> +
> def check_sections(self, ln, decl_name, decl_type):
> """
> Check for errors inside sections, emitting warnings if not found
> @@ -566,12 +611,13 @@ class KernelDoc:
> for section in self.entry.sections:
> if section not in self.entry.parameterlist and \
> not known_sections.search(section):
> + hint = self.get_suggestions_hint(section, self.entry.parameterlist)
> if decl_type == 'function':
> dname = f"{decl_type} parameter"
> else:
> dname = f"{decl_type} member"
> self.emit_msg(ln,
> - f"Excess {dname} '{section}' description in '{decl_name}'")
> + f"Excess {dname} '{section}' description in '{decl_name}' {hint}".strip())
>
> #
> # Check that documented parameter names (from doc comments, including
> @@ -591,12 +637,13 @@ class KernelDoc:
> if param_name in self.entry.parameterlist:
> continue
>
> + hint = self.get_suggestions_hint(param_name, self.entry.parameterlist)
> if decl_type == 'function':
> dname = f"{decl_type} parameter"
> else:
> dname = f"{decl_type} member"
> self.emit_msg(ln,
> - f"Excess {dname} '{param_name}' description in '{decl_name}'")
> + f"Excess {dname} '{param_name}' description in '{decl_name}' {hint}".strip())
>
> def check_return_section(self, ln, declaration_name, return_type):
> """
--
~Randy
© 2016 - 2026 Red Hat, Inc.