]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112301: Add warning count to warning check tooling (#122711)
authorNate Ohlson <nohlson@purdue.edu>
Wed, 14 Aug 2024 21:03:53 +0000 (16:03 -0500)
committerGitHub <noreply@github.com>
Wed, 14 Aug 2024 21:03:53 +0000 (00:03 +0300)
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Misc/NEWS.d/next/Security/2024-08-06-00-06-23.gh-issue-112301.4k4lw6.rst [new file with mode: 0644]
Tools/build/.warningignore_macos
Tools/build/.warningignore_ubuntu
Tools/build/check_warnings.py

diff --git a/Misc/NEWS.d/next/Security/2024-08-06-00-06-23.gh-issue-112301.4k4lw6.rst b/Misc/NEWS.d/next/Security/2024-08-06-00-06-23.gh-issue-112301.4k4lw6.rst
new file mode 100644 (file)
index 0000000..0bd2f4d
--- /dev/null
@@ -0,0 +1,2 @@
+Add ability to ignore warnings per file with warning count in warning checking tooling.
+Patch by Nate Ohlson.
index 1b504dfc54000f691a7cb2c488b3e757e2a80426..67f50119db73100656057532a6012873682f8ad0 100644 (file)
@@ -1,3 +1,5 @@
 # Files listed will be ignored by the compiler warning checker
 # for the macOS/build and test job.
 # Keep lines sorted lexicographically to help avoid merge conflicts.
+# Format example:
+# /path/to/file (number of warnings in file)
index 8242c8d17c89fba69387976553eedaf8972a8ed5..469c727abfb11c066bf70d180a0630967f823baf 100644 (file)
@@ -1,3 +1,5 @@
 # Files listed will be ignored by the compiler warning checker
 # for the Ubuntu/build and test job.
 # Keep lines sorted lexicographically to help avoid merge conflicts.
+# Format example:
+# /path/to/file (number of warnings in file)
index 31258932dbd4ca682de8708077cf9eae8f360747..1ed83447b6b6684ab014551468c15bcb04a47a82 100644 (file)
@@ -9,6 +9,11 @@ import json
 import re
 import sys
 from pathlib import Path
+from typing import NamedTuple
+
+class FileWarnings(NamedTuple):
+    name: str
+    count: int
 
 
 def extract_warnings_from_compiler_output_clang(
@@ -19,7 +24,8 @@ def extract_warnings_from_compiler_output_clang(
     """
     # Regex to find warnings in the compiler output
     clang_warning_regex = re.compile(
-        r"(?P<file>.*):(?P<line>\d+):(?P<column>\d+): warning: (?P<message>.*)"
+        r"(?P<file>.*):(?P<line>\d+):(?P<column>\d+): warning: "
+        r"(?P<message>.*) (?P<option>\[-[^\]]+\])$"
     )
     compiler_warnings = []
     for line in compiler_output.splitlines():
@@ -30,6 +36,7 @@ def extract_warnings_from_compiler_output_clang(
                     "line": match.group("line"),
                     "column": match.group("column"),
                     "message": match.group("message"),
+                    "option": match.group("option").lstrip("[").rstrip("]"),
                 }
             )
 
@@ -49,9 +56,7 @@ def extract_warnings_from_compiler_output_json(
     """
     # Regex to find json arrays at the top level of the file
     # in the compiler output
-    json_arrays = re.findall(
-        r"\[(?:[^[\]]|\[[^\]]*\])*\]", compiler_output
-    )
+    json_arrays = re.findall(r"\[(?:[^[\]]|\[[^]]*])*]", compiler_output)
     compiler_warnings = []
     for array in json_arrays:
         try:
@@ -74,6 +79,7 @@ def extract_warnings_from_compiler_output_json(
                                     "line": location[key]["line"],
                                     "column": location[key]["column"],
                                     "message": warning["message"],
+                                    "option": warning["option"],
                                 }
                             )
                             # Found a caret, start, or end in location so
@@ -92,18 +98,26 @@ def extract_warnings_from_compiler_output_json(
 def get_warnings_by_file(warnings: list[dict]) -> dict[str, list[dict]]:
     """
     Returns a dictionary where the key is the file and the data is the warnings
-    in that file
+    in that file. Does not include duplicate warnings for a file from list of
+    provided warnings.
     """
     warnings_by_file = defaultdict(list)
+    warnings_added = set()
     for warning in warnings:
-        warnings_by_file[warning["file"]].append(warning)
+        warning_key = (
+            f"{warning['file']}-{warning['line']}-"
+            f"{warning['column']}-{warning['option']}"
+        )
+        if warning_key not in warnings_added:
+            warnings_added.add(warning_key)
+            warnings_by_file[warning["file"]].append(warning)
 
     return warnings_by_file
 
 
 def get_unexpected_warnings(
-    files_with_expected_warnings: set[str],
-    files_with_warnings: dict[str, list[dict]],
+    files_with_expected_warnings: set[FileWarnings],
+    files_with_warnings: set[FileWarnings],
 ) -> int:
     """
     Returns failure status if warnings discovered in list of warnings
@@ -112,7 +126,14 @@ def get_unexpected_warnings(
     """
     unexpected_warnings = []
     for file in files_with_warnings.keys():
-        if file not in files_with_expected_warnings:
+        found_file_in_ignore_list = False
+        for ignore_file in files_with_expected_warnings:
+            if file == ignore_file.name:
+                if len(files_with_warnings[file]) > ignore_file.count:
+                    unexpected_warnings.extend(files_with_warnings[file])
+                found_file_in_ignore_list = True
+                break
+        if not found_file_in_ignore_list:
             unexpected_warnings.extend(files_with_warnings[file])
 
     if unexpected_warnings:
@@ -125,8 +146,8 @@ def get_unexpected_warnings(
 
 
 def get_unexpected_improvements(
-    files_with_expected_warnings: set[str],
-    files_with_warnings: dict[str, list[dict]],
+    files_with_expected_warnings: set[FileWarnings],
+    files_with_warnings: set[FileWarnings],
 ) -> int:
     """
     Returns failure status if there are no warnings in the list of warnings
@@ -134,13 +155,15 @@ def get_unexpected_improvements(
     """
     unexpected_improvements = []
     for file in files_with_expected_warnings:
-        if file not in files_with_warnings.keys():
+        if file.name not in files_with_warnings.keys():
             unexpected_improvements.append(file)
+        elif len(files_with_warnings[file.name]) < file.count:
+                unexpected_improvements.append(file)
 
     if unexpected_improvements:
         print("Unexpected improvements:")
         for file in unexpected_improvements:
-            print(file)
+            print(file.name)
         return 1
 
     return 0
@@ -214,8 +237,11 @@ def main(argv: list[str] | None = None) -> int:
         with Path(args.warning_ignore_file_path).open(
             encoding="UTF-8"
         ) as clean_files:
+            # Files with expected warnings are stored as a set of tuples
+            # where the first element is the file name and the second element
+            # is the number of warnings expected in that file
             files_with_expected_warnings = {
-                file.strip()
+                FileWarnings(file.strip().split()[0], int(file.strip().split()[1]))
                 for file in clean_files
                 if file.strip() and not file.startswith("#")
             }