Skip to content

Commit 1e1de80

Browse files
committed
Fix line length violations and improve error messages
- Wrap long lines to stay under 100 chars for tidy check - Distinguish "already up-to-date" from "pattern not found" cases - Implement code review suggestions
1 parent e2b6930 commit 1e1de80

File tree

1 file changed

+131
-46
lines changed

1 file changed

+131
-46
lines changed

src/tools/update-rustc-man-opt-level.py

Lines changed: 131 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
Update the rustc manpage "-O" description to match `rustc --help`.
55
66
Usage (dry-run by default):
7-
./src/tools/update-rustc-man-opt-level.py --man-file src/doc/man/rustc.1
7+
./src/tools/update-rustc-man-opt-level.py \
8+
--man-file src/doc/man/rustc.1
89
910
Apply changes (creates a timestamped backup):
10-
./src/tools/update-rustc-man-opt-level.py --man-file src/doc/man/rustc.1 --apply
11+
./src/tools/update-rustc-man-opt-level.py \
12+
--man-file src/doc/man/rustc.1 --apply
1113
1214
Force a level instead of querying rustc:
13-
./src/tools/update-rustc-man-opt-level.py --man-file ... --expected-level 3 --apply
15+
./src/tools/update-rustc-man-opt-level.py \
16+
--man-file ... --expected-level 3 --apply
1417
"""
18+
1519
from __future__ import annotations
1620

1721
import argparse
@@ -22,7 +26,6 @@
2226
import sys
2327
import re
2428
from pathlib import Path
25-
from typing import Tuple
2629

2730
DEFAULT_RUSTC = "rustc"
2831

@@ -45,105 +48,172 @@ def colorize(line: str, color: str, enabled: bool) -> str:
4548
def get_rustc_opt_level(rustc_cmd: str = DEFAULT_RUSTC) -> int:
4649
"""Query `rustc --help` and parse the opt-level mapped to -O."""
4750
try:
48-
proc = subprocess.run([rustc_cmd, "--help"], capture_output=True, text=True, check=True)
51+
proc = subprocess.run(
52+
[rustc_cmd, "--help"], capture_output=True, text=True, check=True
53+
)
4954
except FileNotFoundError:
5055
raise RuntimeError(f"rustc not found at '{rustc_cmd}'")
5156
except subprocess.CalledProcessError as e:
5257
stderr = (e.stderr or "").strip()
5358
raise RuntimeError(f"rustc --help failed: {stderr or e}") from e
5459

5560
help_text = (proc.stdout or "") + "\n" + (proc.stderr or "")
56-
m = re.search(r'-O[^\n]*opt(?:\\-)?level\s*=\s*(\d+)', help_text, flags=re.IGNORECASE)
61+
# Fixed: removed unnecessary \\ escape before the hyphen
62+
m = re.search(r"-O[^\n]*opt(?:-)?level\s*=\s*(\d+)", help_text, flags=re.IGNORECASE)
5763
if not m:
58-
m2 = re.search(r'Equivalent to\s+-C\s+opt(?:-)?level\s*=\s*(\d+)', help_text, flags=re.IGNORECASE)
64+
m2 = re.search(
65+
r"Equivalent to\s+-C\s+opt(?:-)?level\s*=\s*(\d+)",
66+
help_text,
67+
flags=re.IGNORECASE,
68+
)
5969
if not m2:
60-
raise RuntimeError("Could not find '-O' opt-level mapping in rustc --help output")
70+
raise RuntimeError(
71+
"Could not find '-O' opt-level mapping in rustc --help output"
72+
)
6173
return int(m2.group(1))
6274
return int(m.group(1))
6375

6476

65-
def find_and_replace_manpage_content(content: str, new_level: int) -> Tuple[str, int]:
66-
"""
67-
Replace opt-level numbers in 'Equivalent to ... opt-level=N.' sentences tied to -O.
77+
def find_and_replace_manpage_content(
78+
content: str, new_level: int
79+
) -> tuple[str, int, bool]:
80+
r"""
81+
Replace opt-level numbers in 'Equivalent to ... opt-level=N.'
82+
sentences tied to -O.
6883
6984
Conservative heuristic:
70-
- Locate sentences starting with 'Equivalent to' up to the next period.
71-
- Ensure the sentence mentions opt-level (accepting escaped '\-').
72-
- Confirm a -O header appears within a lookback window before the sentence.
85+
- Locate sentences starting with 'Equivalent to' up to the
86+
next period.
87+
- Ensure the sentence mentions opt-level (accepting escaped
88+
'\-').
89+
- Confirm a -O header appears within a lookback window before
90+
the sentence.
91+
92+
Returns:
93+
tuple of (new_content, replacements_made, found_any_patterns)
7394
"""
7495
replacements = 0
96+
found_patterns = False
7597
out_parts = []
7698
last_index = 0
7799

78-
sentence_pattern = re.compile(r'Equivalent to([^\n\.]{0,800}?)\.', flags=re.IGNORECASE)
100+
# More conservative limit of 200 chars instead of 800
101+
sentence_pattern = re.compile(
102+
r"Equivalent to([^\n\.]{0,200}?)\.", flags=re.IGNORECASE
103+
)
79104

80105
for m in sentence_pattern.finditer(content):
81106
start, end = m.span()
82107
sentence = m.group(0)
83108

84-
if not re.search(r'opt(?:\\-)?level', sentence, flags=re.IGNORECASE):
109+
if not re.search(r"opt(?:\\-)?level", sentence, flags=re.IGNORECASE):
85110
continue
86111

87-
num_match = re.search(r'(\d+)', sentence)
112+
num_match = re.search(r"(\d+)", sentence)
88113
if not num_match:
89114
continue
90115
old_level = int(num_match.group(1))
91-
if old_level == new_level:
92-
continue
93116

94117
window_start = max(0, start - 1200)
95118
window = content[window_start:start]
96119

97-
if not (
98-
re.search(r'(^|\n)\s*-O\b', window)
99-
or re.search(r'\\fB\\-?O\\fR', window)
100-
or re.search(r'\\-O\b', window)
101-
or re.search(r'\.B\s+-O\b', window)
102-
or re.search(r'\\fB-?O\\fP', window)
120+
# Use any() for better readability
121+
if not any(
122+
[
123+
re.search(r"(^|\n)\s*-O\b", window),
124+
re.search(r"\\fB\\-?O\\fR", window),
125+
re.search(r"\\-O\b", window),
126+
re.search(r"\.B\s+-O\b", window),
127+
re.search(r"\\fB-?O\\fP", window),
128+
]
103129
):
104130
continue
105131

106-
new_sentence = re.sub(r'(\d+)', str(new_level), sentence, count=1)
132+
# We found at least one -O entry with opt-level
133+
found_patterns = True
134+
135+
if old_level == new_level:
136+
continue
137+
138+
# More robust: replace only the number after opt-level=
139+
new_sentence = re.sub(
140+
r"(opt(?:\\-)?level\s*=\s*)\d+",
141+
rf"\g<1>{new_level}",
142+
sentence,
143+
count=1,
144+
)
107145
out_parts.append(content[last_index:start])
108146
out_parts.append(new_sentence)
109147
last_index = end
110148
replacements += 1
111149

112150
out_parts.append(content[last_index:])
113-
return "".join(out_parts), replacements
151+
return "".join(out_parts), replacements, found_patterns
114152

115153

116154
def show_colored_diff(old: str, new: str, filename: str, color: bool) -> None:
117-
old_lines = old.splitlines(keepends=True)
118-
new_lines = new.splitlines(keepends=True)
119-
diff_iter = difflib.unified_diff(old_lines, new_lines, fromfile=filename, tofile=filename + " (updated)", lineterm="")
155+
# Use keepends=False and lineterm="\n" for consistency
156+
old_lines = old.splitlines(keepends=False)
157+
new_lines = new.splitlines(keepends=False)
158+
diff_iter = difflib.unified_diff(
159+
old_lines,
160+
new_lines,
161+
fromfile=filename,
162+
tofile=filename + " (updated)",
163+
lineterm="\n",
164+
)
120165
for line in diff_iter:
121166
if line.startswith("---") or line.startswith("+++"):
122-
print(colorize(line.rstrip("\n"), "bold", color))
167+
print(colorize(line, "bold", color))
123168
elif line.startswith("@@"):
124-
print(colorize(line.rstrip("\n"), "yellow", color))
169+
print(colorize(line, "yellow", color))
125170
elif line.startswith("+"):
126-
# avoid coloring the file header lines that also start with +++
127-
print(colorize(line.rstrip("\n"), "green", color))
171+
print(colorize(line, "green", color))
128172
elif line.startswith("-"):
129-
print(colorize(line.rstrip("\n"), "red", color))
173+
print(colorize(line, "red", color))
130174
else:
131-
print(line.rstrip("\n"))
175+
print(line)
132176

133177

134178
def backup_file(path: Path) -> Path:
135-
ts = datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
179+
# Added microseconds to avoid collision if run multiple times
180+
# per second
181+
ts = datetime.datetime.now().strftime("%Y%m%dT%H%M%S%f")
136182
backup = path.with_name(path.name + f".bak.{ts}")
137183
shutil.copy2(path, backup)
138184
return backup
139185

140186

141187
def parse_args() -> argparse.Namespace:
142-
p = argparse.ArgumentParser(description="Update rustc man page -O opt-level to match rustc --help")
143-
p.add_argument("--man-file", "-m", required=True, help="Path to rustc man page file to update (e.g. src/doc/man/rustc.1)")
144-
p.add_argument("--rustc-cmd", default=DEFAULT_RUSTC, help="rustc binary to query (default: rustc)")
145-
p.add_argument("--expected-level", "-e", type=int, help="Use this level instead of querying rustc")
146-
p.add_argument("--apply", action="store_true", help="Write changes to the man file (creates a backup). Without this flag runs in dry-run mode and prints a diff.")
188+
p = argparse.ArgumentParser(
189+
description=("Update rustc man page -O opt-level to match rustc --help")
190+
)
191+
p.add_argument(
192+
"--man-file",
193+
"-m",
194+
required=True,
195+
help=("Path to rustc man page file to update (e.g. src/doc/man/rustc.1)"),
196+
)
197+
p.add_argument(
198+
"--rustc-cmd",
199+
default=DEFAULT_RUSTC,
200+
help="rustc binary to query (default: rustc)",
201+
)
202+
p.add_argument(
203+
"--expected-level",
204+
"-e",
205+
type=int,
206+
help="Use this level instead of querying rustc",
207+
)
208+
p.add_argument(
209+
"--apply",
210+
action="store_true",
211+
help=(
212+
"Write changes to the man file (creates a backup). "
213+
"Without this flag runs in dry-run mode and prints "
214+
"a diff."
215+
),
216+
)
147217
p.add_argument("--no-color", action="store_true", help="Disable colored output")
148218
return p.parse_args()
149219

@@ -158,7 +228,11 @@ def main() -> int:
158228
return 2
159229

160230
try:
161-
new_level = args.expected_level if args.expected_level is not None else get_rustc_opt_level(args.rustc_cmd)
231+
new_level = (
232+
args.expected_level
233+
if args.expected_level is not None
234+
else get_rustc_opt_level(args.rustc_cmd)
235+
)
162236
except RuntimeError as e:
163237
print(f"Error determining rustc opt-level: {e}", file=sys.stderr)
164238
return 3
@@ -169,10 +243,20 @@ def main() -> int:
169243
print(f"Error reading man file {man_path}: {e}", file=sys.stderr)
170244
return 4
171245

172-
new_content, replacements = find_and_replace_manpage_content(content, new_level)
246+
new_content, replacements, found_patterns = find_and_replace_manpage_content(
247+
content, new_level
248+
)
173249

174250
if replacements == 0:
175-
print("No replacements necessary (either already up-to-date or -O entry not found near an 'Equivalent to -C opt-level=' line).")
251+
if found_patterns:
252+
print(f"✓ Manpage is already up-to-date (opt-level={new_level}).")
253+
else:
254+
print(
255+
"Warning: Could not find -O entry with "
256+
"'Equivalent to -C opt-level=' pattern in manpage.",
257+
file=sys.stderr,
258+
)
259+
return 6
176260
return 0
177261

178262
header = f"Found {replacements} replacement(s). Proposed changes:"
@@ -183,7 +267,8 @@ def main() -> int:
183267
try:
184268
backup = backup_file(man_path)
185269
man_path.write_text(new_content, encoding="utf-8")
186-
print(colorize(f"\nApplied changes to {man_path}. Backup saved to {backup}", "green", color))
270+
msg = f"\nApplied changes to {man_path}. Backup saved to {backup}"
271+
print(colorize(msg, "green", color))
187272
except Exception as e:
188273
print(f"Error writing updated man file: {e}", file=sys.stderr)
189274
return 5

0 commit comments

Comments
 (0)