From 7a4654f32bfa29fa56f4c5e0caafdff9a51b7b0a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 29 Oct 2025 10:34:24 -0700 Subject: [PATCH 1/2] tests: kdoc: fix parse failure logging Commit 0a1066f7b7dd ("tests: kdoc: fix handling file removal") added extra logging to the test intending to help debug parsing failures. Unfortunately, the newly added "" log line is added to every warning which doesn't end in ':', regardless of whether or not the KdocWarning class handles parsing of the line. This appears to have happened due to the confusion of the way skip and extra work. What we really want to do is convert the text into a warning object, then check if it cleanly parsed. We could check for the 'Unknown' kind. However, in the interest of being precise should new kinds ever be added, expose a new 'parsed' field of the warning object. Its set to true if we got a clean regex parse, and false otherwise. Additionally refactor the conditional checks and add comments for clarity. In particular, set extra to None unconditionally first, then perform the merging check. This should improve the readability and intended flow of this logic. Signed-off-by: Jacob Keller --- tests/patch/kdoc/test.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/patch/kdoc/test.py b/tests/patch/kdoc/test.py index e6d3216..1db3905 100644 --- a/tests/patch/kdoc/test.py +++ b/tests/patch/kdoc/test.py @@ -31,6 +31,7 @@ class KdocWarning: line : Optional[int] = dataclasses.field(repr=True, compare=False) # The content of the warning (excluding kind, file, line) content : str = dataclasses.field(repr=True, compare=True) + parsed : bool = dataclasses.field(repr=True, compare=True) @classmethod def from_text(self, line, extra=None): @@ -60,14 +61,12 @@ def from_text(self, line, extra=None): content = m['content'] if extra: content += '\n' + extra - else: - kind = 'Unknown' - file = None - line = None - content = message - return KdocWarning(message, kind=kind, file=file, line=line, - content=content) + return KdocWarning(message, kind=kind, file=file, line=line, + content=content, parsed=True) + + return KdocWarning(message, kind='Unknown', file=None, line=None, + content=line, parsed=False) def __str__(self): return self.message @@ -80,20 +79,28 @@ def parse_warnings(lines, logs) -> List[KdocWarning]: # Walk through lines and convert to warning objects for i, line in enumerate(lines): + # Skip lines already merged into previous warning if skip: skip = False continue + # Ignore blank lines + if not line.strip(): + continue + + # Check if we should merge the next line with this warning + extra = None if line.endswith(':') and i + 1 < length: extra = lines[i + 1] skip = True - elif not line.strip(): - continue - else: - logs += [": " + line.strip()] - extra = None - warnings.append(KdocWarning.from_text(line, extra)) + # Parse line into warning object + warning = KdocWarning.from_text(line, extra); + + if not warning.parsed: + logs += [f': {line.strip()}'] + + warnings.append(warning) return warnings From 948b6bb62d9ed7efdabca15bc7fe5bf1a14d972d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 30 Oct 2025 09:38:44 -0700 Subject: [PATCH 2/2] tests: kdoc: handle buggy "line number only" warnings Some versions of kernel-doc currently report certain warnings with only their line number. This breaks the logic of the kdoc test, as such lines do not pass the expected format defined by the KdocWarning class. Such warnings appear to indicate duplicate section headers in a kernel doc entry, but the text of the warning is not properly displayed. As a result, the kdoc test produces false positive failures for patches which touch files that have such warnings. The line contents are compared without ignoring the line numbers. This produces output failures: Warnings before: 7 after: 7 (add: 2 del: 2) Warnings removed: Warning: 174 Warning: 184 Per-file breakdown: 2 None New warnings added: Warning: 180 Warning: 196 Per-file breakdown: 2 None Since the warnings are compared directly, the changing line numbers are detected as differences. This is a bug in the kernel-doc script, and a proper fix requires updating the script to stop producing such warnings. In the mean time, the NIPA tests continue to fail. To fix that, use a separate regular expression to identify lines of the form 'Warning: '. Set the content of such warnings to None. This will make all such warnings compare as identical in the existing algorithm, and thus the changing line numbers will no longer cause such a false positive. To avoid introducing further regressions, extend the check to count the total number of unparsed warnings. If it increases, then also fail the kdoc test. This way, we will correctly fail when we see more empty warnings after applying the current patch. This also should help catch issues for other types of unrecognized warnings in the future. Note that all warnings still get compared in the usual way, where fully unrecognized warnings will be compare the lines as-is without eliding the line number comparison. This just adds an additional safety guard in addition to handling the empty warning case. Signed-off-by: Jacob Keller --- tests/patch/kdoc/test.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/patch/kdoc/test.py b/tests/patch/kdoc/test.py index 1db3905..e00307e 100644 --- a/tests/patch/kdoc/test.py +++ b/tests/patch/kdoc/test.py @@ -30,7 +30,7 @@ class KdocWarning: # Note: *not* part of comparison, or hash! line : Optional[int] = dataclasses.field(repr=True, compare=False) # The content of the warning (excluding kind, file, line) - content : str = dataclasses.field(repr=True, compare=True) + content : Optional[str] = dataclasses.field(repr=True, compare=True) parsed : bool = dataclasses.field(repr=True, compare=True) @classmethod @@ -65,6 +65,29 @@ def from_text(self, line, extra=None): return KdocWarning(message, kind=kind, file=file, line=line, content=content, parsed=True) + # Check for line-number only warnings produced by certain buggy + # versions of kernel-doc. These will get treated as unparsed but + # all such warnings will compare identically to each other. + line_only_parser = re.compile( + r""" + ^ # Start of string + (?Pwarning|error): # Severity + \s+ # Spacing + (?P[0-9]+) # Line number + $ # End of string + """, + re.VERBOSE | re.IGNORECASE) + + m = line_only_parser.match(line) + if m: + kind = m['kind'] + line = m['line'] + + return KdocWarning(message, kind=kind, file=None, line=line, + content=None, parsed=False) + + # The warning text didn't match a known format. Such warnings will be + # counted to ensure that we don't increase this count over time. return KdocWarning(message, kind='Unknown', file=None, line=None, content=line, parsed=False) @@ -188,6 +211,10 @@ def kdoc(tree, patch, _result_dir) -> Tuple[int, str, str]: new_count = len(new_warnings) rm_count = len(rm_warnings) + # Count the number of warnings which we failed to parse + current_unparsed = len([x for x in current_warnings if not x.parsed]) + incumbent_unparsed = len([x for x in incumbent_warnings if not x.parsed]) + desc = f'Warnings before: {incumbent_count} after: {current_count}' brac = [] if new_count: @@ -220,4 +247,8 @@ def kdoc(tree, patch, _result_dir) -> Tuple[int, str, str]: for f, count in file_breakdown.items(): log += [f'{count:6} {f}'] + if current_unparsed > incumbent_unparsed: + ret = 1 + log += ["", f'Number of parse failures increased from {incumbent_unparsed} to {current_unparsed}.'] + return ret, desc, "\n".join(log)