pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/8ac5565be2e5a11fad643c2fe9cbf16d2ddb95cd

ylesheet" href="https://github.githubassets.com/assets/global-d18f184ea1a06a2c.css" /> gh-112301: Compiler warning management tooling (#121730) · python/cpython@8ac5565 · GitHub
Skip to content

Commit 8ac5565

Browse files
nohlsonblurb-it[bot]hugovk
authored
gh-112301: Compiler warning management tooling (#121730)
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>
1 parent bb09ba6 commit 8ac5565

5 files changed

Lines changed: 205 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ jobs:
348348
with:
349349
save: false
350350
- name: Configure CPython
351-
run: ./configure --config-cache --enable-slower-safety --with-pydebug --with-openssl=$OPENSSL_DIR
351+
run: ./configure CFLAGS="-fdiagnostics-format=json" --config-cache --enable-slower-safety --with-pydebug --with-openssl=$OPENSSL_DIR
352352
- name: Build CPython
353353
run: make -j4
354354
- name: Display build info

.github/workflows/reusable-ubuntu.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,20 @@ jobs:
6767
working-directory: ${{ env.CPYTHON_BUILDDIR }}
6868
run: >-
6969
../cpython-ro-srcdir/configure
70+
CFLAGS="-fdiagnostics-format=json"
7071
--config-cache
7172
--with-pydebug
7273
--enable-slower-safety
7374
--with-openssl=$OPENSSL_DIR
7475
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
7576
- name: Build CPython out-of-tree
7677
working-directory: ${{ env.CPYTHON_BUILDDIR }}
77-
run: make -j4
78+
run: make -j4 &> compiler_output.txt
7879
- name: Display build info
7980
working-directory: ${{ env.CPYTHON_BUILDDIR }}
8081
run: make pythoninfo
82+
- name: Check compiler warnings
83+
run: python Tools/build/check_warnings.py --compiler-output-file-path=${{ env.CPYTHON_BUILDDIR }}/compiler_output.txt --warning-ignore-file-path ${GITHUB_WORKSPACE}/Tools/build/.warningignore_ubuntu
8184
- name: Remount sources writable for tests
8285
# some tests write to srcdir, lack of pyc files slows down testing
8386
run: sudo mount $CPYTHON_RO_SRCDIR -oremount,rw
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add tooling to check for changes in compiler warnings.
2+
Patch by Nate Ohlson.

Tools/build/.warningignore_ubuntu

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Files listed will be ignored by the compiler warning checker
2+
# for the Ubuntu/build and test job.
3+
# Keep lines sorted lexicographically to help avoid merge conflicts.

Tools/build/check_warnings.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Parses compiler output with -fdiagnostics-format=json and checks that warnings
4+
exist only in files that are expected to have warnings.
5+
"""
6+
import argparse
7+
import json
8+
import re
9+
import sys
10+
from pathlib import Path
11+
12+
13+
def extract_warnings_from_compiler_output(compiler_output: str) -> list[dict]:
14+
"""
15+
Extracts warnings from the compiler output when using
16+
-fdiagnostics-format=json
17+
18+
Compiler output as a whole is not a valid json document, but includes many
19+
json objects and may include other output that is not json.
20+
"""
21+
22+
# Regex to find json arrays at the top level of the file
23+
# in the compiler output
24+
json_arrays = re.findall(
25+
r"\[(?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*\]", compiler_output
26+
)
27+
compiler_warnings = []
28+
for array in json_arrays:
29+
try:
30+
json_data = json.loads(array)
31+
json_objects_in_array = [entry for entry in json_data]
32+
compiler_warnings.extend(
33+
[
34+
entry
35+
for entry in json_objects_in_array
36+
if entry.get("kind") == "warning"
37+
]
38+
)
39+
except json.JSONDecodeError:
40+
continue # Skip malformed JSON
41+
42+
return compiler_warnings
43+
44+
45+
def get_warnings_by_file(warnings: list[dict]) -> dict[str, list[dict]]:
46+
"""
47+
Returns a dictionary where the key is the file and the data is the warnings
48+
in that file
49+
"""
50+
warnings_by_file = {}
51+
for warning in warnings:
52+
locations = warning["locations"]
53+
for location in locations:
54+
for key in ["caret", "start", "end"]:
55+
if key in location:
56+
file = location[key]["file"]
57+
file = file.lstrip(
58+
"./"
59+
) # Remove leading current directory if present
60+
if file not in warnings_by_file:
61+
warnings_by_file[file] = []
62+
warnings_by_file[file].append(warning)
63+
64+
return warnings_by_file
65+
66+
67+
def get_unexpected_warnings(
68+
warnings: list[dict],
69+
files_with_expected_warnings: set[str],
70+
files_with_warnings: set[str],
71+
) -> int:
72+
"""
73+
Returns failure status if warnings discovered in list of warnings
74+
are associated with a file that is not found in the list of files
75+
with expected warnings
76+
"""
77+
unexpected_warnings = []
78+
for file in files_with_warnings.keys():
79+
if file not in files_with_expected_warnings:
80+
unexpected_warnings.extend(files_with_warnings[file])
81+
82+
if unexpected_warnings:
83+
print("Unexpected warnings:")
84+
for warning in unexpected_warnings:
85+
print(warning)
86+
return 1
87+
88+
return 0
89+
90+
91+
def get_unexpected_improvements(
92+
warnings: list[dict],
93+
files_with_expected_warnings: set[str],
94+
files_with_warnings: set[str],
95+
) -> int:
96+
"""
97+
Returns failure status if there are no warnings in the list of warnings for
98+
a file that is in the list of files with expected warnings
99+
"""
100+
unexpected_improvements = []
101+
for file in files_with_expected_warnings:
102+
if file not in files_with_warnings.keys():
103+
unexpected_improvements.append(file)
104+
105+
if unexpected_improvements:
106+
print("Unexpected improvements:")
107+
for file in unexpected_improvements:
108+
print(file)
109+
return 1
110+
111+
return 0
112+
113+
114+
def main(argv: list[str] | None = None) -> int:
115+
parser = argparse.ArgumentParser()
116+
parser.add_argument(
117+
"--compiler-output-file-path",
118+
type=str,
119+
required=True,
120+
help="Path to the compiler output file",
121+
)
122+
parser.add_argument(
123+
"--warning-ignore-file-path",
124+
type=str,
125+
required=True,
126+
help="Path to the warning ignore file",
127+
)
128+
parser.add_argument(
129+
"--fail-on-regression",
130+
action="store_true",
131+
default=False,
132+
help="Flag to fail if new warnings are found",
133+
)
134+
parser.add_argument(
135+
"--fail-on-improvement",
136+
action="store_true",
137+
default=False,
138+
help="Flag to fail if files that were expected "
139+
"to have warnings have no warnings",
140+
)
141+
142+
args = parser.parse_args(argv)
143+
144+
exit_code = 0
145+
146+
# Check that the compiler output file is a valid path
147+
if not Path(args.compiler_output_file_path).is_file():
148+
print(
149+
"Compiler output file does not exist: "
150+
f"{args.compiler_output_file_path}"
151+
)
152+
return 1
153+
154+
# Check that the warning ignore file is a valid path
155+
if not Path(args.warning_ignore_file_path).is_file():
156+
print(
157+
"Warning ignore file does not exist: "
158+
f"{args.warning_ignore_file_path}"
159+
)
160+
return 1
161+
162+
with Path(args.compiler_output_file_path).open(encoding="UTF-8") as f:
163+
compiler_output_file_contents = f.read()
164+
165+
with Path(args.warning_ignore_file_path).open(
166+
encoding="UTF-8"
167+
) as clean_files:
168+
files_with_expected_warnings = {
169+
file.strip()
170+
for file in clean_files
171+
if file.strip() and not file.startswith("#")
172+
}
173+
174+
warnings = extract_warnings_from_compiler_output(
175+
compiler_output_file_contents
176+
)
177+
files_with_warnings = get_warnings_by_file(warnings)
178+
179+
status = get_unexpected_warnings(
180+
warnings, files_with_expected_warnings, files_with_warnings
181+
)
182+
if args.fail_on_regression:
183+
exit_code |= status
184+
185+
status = get_unexpected_improvements(
186+
warnings, files_with_expected_warnings, files_with_warnings
187+
)
188+
if args.fail_on_improvement:
189+
exit_code |= status
190+
191+
return exit_code
192+
193+
194+
if __name__ == "__main__":
195+
sys.exit(main())

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy