Skip to content

Commit 65a6733

Browse files
committed
add feature flag --test-rust-blocks
1 parent ae11438 commit 65a6733

File tree

7 files changed

+116
-120
lines changed

7 files changed

+116
-120
lines changed

builder/build_cli.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def build_docs(
2929
debug: bool,
3030
offline: bool,
3131
spec_lock_consistency_check: bool,
32+
test_rust_blocks: bool,
3233
) -> Path:
3334
"""
3435
Builds the Sphinx documentation with the specified options.
@@ -72,6 +73,8 @@ def build_docs(
7273
conf_opt_values.append("offline=1")
7374
if debug:
7475
conf_opt_values.append("debug=1")
76+
if test_rust_blocks:
77+
conf_opt_values.append("test_rust_blocks=1")
7578

7679
# Only add the --define argument if there are options to define
7780
if conf_opt_values:
@@ -151,6 +154,14 @@ def main(root):
151154
help="build in offline mode",
152155
action="store_true",
153156
)
157+
158+
parser.add_argument(
159+
"--test-rust-blocks",
160+
help="Test extracted rust code blocks using rustc",
161+
default=False,
162+
action="store_true"
163+
)
164+
154165
group = parser.add_mutually_exclusive_group()
155166
parser.add_argument(
156167
"--ignore-spec-lock-diff",
@@ -192,6 +203,6 @@ def main(root):
192203
update_spec_lockfile(SPEC_CHECKSUM_URL, root / "src" / SPEC_LOCKFILE)
193204

194205
rendered = build_docs(
195-
root, "xml" if args.xml else "html", args.clear, args.serve, args.debug, args.offline, not args.ignore_spec_lock_diff,
206+
root, "xml" if args.xml else "html", args.clear, args.serve, args.debug, args.offline, not args.ignore_spec_lock_diff, args.test_rust_blocks
196207
)
197208

exts/coding_guidelines/__init__.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ def on_build_finished(app, exception):
4848
def setup(app):
4949

5050
app.add_domain(CodingGuidelinesDomain)
51+
52+
app.add_config_value(
53+
name='test_rust_blocks',
54+
default=False,
55+
rebuild='env'
56+
)
57+
5158
app.add_config_value(
5259
name = "offline",
5360
default=False,
@@ -79,12 +86,14 @@ def setup(app):
7986
logger.setLevel(logging.INFO)
8087
common.disable_tqdm = True
8188

82-
app.connect('env-check-consistency', guidelines_checks.validate_required_fields)
83-
app.connect('env-check-consistency', fls_checks.check_fls)
84-
app.connect('build-finished', write_guidelines_ids.build_finished)
85-
app.connect('build-finished', fls_linking.build_finished)
86-
app.connect('build-finished', on_build_finished)
87-
89+
# Ignore builds while testing code blocks
90+
if not app.config.test_rust_blocks:
91+
app.connect('env-check-consistency', guidelines_checks.validate_required_fields)
92+
app.connect('env-check-consistency', fls_checks.check_fls)
93+
app.connect('build-finished', write_guidelines_ids.build_finished)
94+
app.connect('build-finished', fls_linking.build_finished)
95+
app.connect('build-finished', on_build_finished)
96+
8897
return {
8998
'version': '0.1',
9099
'parallel_read_safe': True,

exts/rust-code-runner/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
21
from . import rust_examples_aggregate
32
from . import rustc
43
import os
4+
55
def setup(app):
6-
app.output_rust_file = "exts/rust-code-runner/generated.rs"
6+
7+
app.output_rust_file = "build/rust-code-blocks/generated.rs"
78
if os.path.isfile(app.output_rust_file):
89
with open(app.output_rust_file, 'w'):
910
pass
@@ -12,7 +13,9 @@ def setup(app):
1213
# and it also makes this extension indepandant from `needs`.
1314
#
1415
app.connect('source-read', rust_examples_aggregate.preprocess_rst_for_rust_code)
15-
app.connect('build-finished', rustc.check_rust_test_errors)
16+
17+
if app.config.test_rust_blocks:
18+
app.connect('build-finished', rustc.check_rust_test_errors)
1619
return {
1720
'version': '0.1',
1821
'parallel_read_safe': False,

exts/rust-code-runner/generated.rs

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,3 @@
1-
// ==== Code Block 1 ====
2-
#[test]
3-
fn test_block_coding_guidelines_concurrency_1() {
4-
test ga
5-
}
6-
7-
// ==== Code Block 2 ====
8-
#[test]
9-
fn test_block_coding_guidelines_concurrency_2() {
10-
test ga
11-
}
12-
13-
// ==== Code Block 3 ====
14-
#[test]
15-
fn test_block_coding_guidelines_concurrency_3() {
16-
.. compliant_example::
17-
:id: compl_ex_yp7aQuEi3Sag
18-
:status: draft
19-
}
20-
21-
// ==== Code Block 4 ====
22-
#[test]
23-
fn test_block_coding_guidelines_concurrency_4() {
24-
test ga
25-
}
26-
27-
// ==== Code Block 5 ====
28-
#[test]
29-
fn test_block_coding_guidelines_concurrency_5() {
30-
.. compliant_example::
31-
:id: compl_ex_gqeLAg0YBu9P
32-
:status: draft
33-
}
34-
35-
// ==== Code Block 6 ====
36-
#[test]
37-
fn test_block_coding_guidelines_concurrency_6() {
38-
test ga
39-
}
40-
411
// ==== Code Block 1 ====
422
#[test]
433
fn test_block_coding_guidelines_expressions_1() {
@@ -87,9 +47,6 @@ fn test_block_coding_guidelines_macros_4() {
8747
// ==== Code Block 5 ====
8848
#[test]
8949
fn test_block_coding_guidelines_macros_5() {
90-
// HIDDEN START
91-
use std::vec::*;
92-
// HIDDEN END
9350
macro_rules! increment_and_double {
9451
($x:expr) => {
9552
{
@@ -99,7 +56,6 @@ fn test_block_coding_guidelines_macros_5() {
9956
};
10057
}
10158
let mut num = 5;
102-
let vv = Vec![];
10359
let result = increment_and_double!(num);
10460
println!("Result: {}, Num: {}", result, num);
10561
// Result: 12, Num: 6
@@ -109,7 +65,7 @@ fn test_block_coding_guidelines_macros_5() {
10965
#[test]
11066
fn test_block_coding_guidelines_macros_6() {
11167
fn increment_and_double(x: &mut i32) -> i32 {
112-
*x += 1; // mutation is explicit
68+
*x += 1; // mutation is explicit
11369
*x * 2
11470
}
11571
let mut num = 5;
@@ -186,9 +142,7 @@ fn test_block_coding_guidelines_macros_14() {
186142
#[test]
187143
fn test_block_coding_guidelines_macros_15() {
188144
#[tokio::main] // non-compliant
189-
async fn maind() {
190-
//dd
191-
}
145+
async fn main() {
192146
}
193147

194148
// ==== Code Block 16 ====
@@ -210,10 +164,43 @@ fn test_block_coding_guidelines_macros_17() {
210164
// ==== Code Block 18 ====
211165
#[test]
212166
fn test_block_coding_guidelines_macros_18() {
213-
// HIDDEN START
214-
use std::fs;
215-
use std::io::{self, Read};
216-
// HIDDEN END
167+
fn example_function() {
168+
// Compliant implementation
169+
}
170+
}
171+
172+
// ==== Code Block 19 ====
173+
#[test]
174+
fn test_block_coding_guidelines_macros_19() {
175+
#[macro_export]
176+
macro_rules! vec {
177+
( $( $x:expr ),* ) => {
178+
{
179+
let mut temp_vec = Vec::new(); // non-global path
180+
$(
181+
temp_vec.push($x);
182+
)*
183+
temp_vec
184+
}
185+
};
186+
}
187+
}
188+
189+
// ==== Code Block 20 ====
190+
#[test]
191+
fn test_block_coding_guidelines_macros_20() {
192+
#[macro_export]
193+
macro_rules! vec {
194+
( $( $x:expr ),* ) => {
195+
{
196+
let mut temp_vec = ::std::vec::Vec::new(); // global path
197+
$(
198+
temp_vec.push($x);
199+
)*
200+
temp_vec
201+
}
202+
};
203+
}
217204
}
218205

219206
// ==== Code Block 1 ====

exts/rust-code-runner/rust_examples_aggregate.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ def replacer(match):
6060
prefix = match.group(1)
6161
code_content = match.group(2)
6262
cleaned_code, hidden_code = strip_hidden(code_content)
63-
print("============")
64-
print(hidden_code)
65-
print("============")
63+
# print("============")
64+
# print(hidden_code)
65+
# print("============")
6666
return prefix + cleaned_code
6767

6868
modified_text = code_block_re.sub(replacer, source_text)
@@ -76,12 +76,11 @@ def preprocess_rst_for_rust_code(app, docname, source):
7676
modified_content = remove_hidden_blocks_from_document(original_content)
7777
source[0] = modified_content
7878

79-
print(f"Original content length: {len(original_content)}")
80-
print(f"Extracted {len(code_blocks)} code blocks")
79+
# print(f"Original content length: {len(original_content)}")
80+
# print(f"Extracted {len(code_blocks)} code blocks")
8181

82-
output_path = "exts/rust-code-runner/generated.rs"
8382
safe_docname = docname.replace("/", "_").replace("-", "_")
84-
with open(output_path, "a", encoding="utf-8") as f:
83+
with open(app.output_rust_file, "a", encoding="utf-8") as f:
8584
for i, block in enumerate(code_blocks, start=1):
8685
f.write(f"// ==== Code Block {i} ====\n")
8786
f.write("#[test]\n")

exts/rust-code-runner/rustc.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,54 +31,56 @@ def print_code_snippet(file_path, line_num, context=3):
3131
print(f"Could not read file {file_path}: {e}")
3232

3333

34-
def parse_rustc_json(stderr: str, file):
34+
def parse_rustc_json(stderr: str, file_path):
3535
"""
36-
Parses the JSON diagnostics output from `rustc`.
37-
38-
This function takes the standard error output (in JSON format) from the Rust compiler (`rustc`)
39-
and processes it, possibly filtering or reporting diagnostics relevant to the specified file.
36+
Parses rustc's JSON output and prints only the first error with a single snippet.
4037
4138
Args:
42-
stderr (str): The JSON-formatted stderr output from `rustc`.
43-
file: The file object or path that the diagnostics should relate to.
39+
stderr (str): JSON-formatted stderr output from rustc.
40+
file_path: Path to the Rust file.
4441
4542
Returns:
46-
Any
43+
None
4744
"""
4845
for line in stderr.splitlines():
4946
line = line.strip()
5047
if not line:
5148
continue
49+
5250
try:
53-
obj = json.loads(line)
51+
diagnostic = json.loads(line)
5452
except json.JSONDecodeError:
5553
continue
56-
if obj.get("$message_type") != "diagnostic":
54+
55+
if diagnostic.get("$message_type") != "diagnostic":
5756
continue
5857

59-
level = obj.get("level")
60-
msg = obj.get("message")
61-
spans = obj.get("spans", [])
58+
if diagnostic.get("level") != "error":
59+
continue # skip warnings and notes
6260

63-
line_num = None
64-
for span in spans:
65-
if span.get("is_primary"):
66-
line_num = span.get("line_start")
67-
break
68-
69-
if line_num is not None:
70-
print(f"{level}: line {line_num}: {msg}")
71-
diag_f = print_code_snippet(file, line_num)
72-
if level == "error":
73-
print("=========================")
74-
print(f" {diag_f}")
75-
print("=========================\n\n")
76-
else:
77-
print(f"{level}: {msg}")
61+
message = diagnostic.get("message", "")
62+
spans = diagnostic.get("spans", [])
7863

64+
# Try to find a span in the current file
65+
for span in spans:
66+
if span["file_name"] == file_path:
67+
line_num = span["line_start"]
68+
label = span.get("label", "")
69+
print(f"error: line {line_num}: {message}")
70+
if label:
71+
print(f"--> {label}")
72+
print("=" * 25)
73+
snippet = print_code_snippet(file_path, line_num, context=3)
74+
print(snippet)
75+
print("=" * 25)
76+
return # we return because we only print the first error--in json format there can be multiple error messages for 1 error-- if you want to see them comment this line.
77+
78+
# If no span in the file, still print the error
79+
print(f"error: {message}")
80+
return
7981

8082
def check_rust_test_errors(app, exception):
81-
"""
83+
"""
8284
Sphinx 'build-finished' event handler that compiles the generated Rust file in test mode.
8385
8486
This function is connected to the Sphinx build lifecycle and is executed after the build finishes.
@@ -89,13 +91,13 @@ def check_rust_test_errors(app, exception):
8991
app: The Sphinx application object. Must have an `output_rust_file` attribute containing
9092
the path to the generated Rust source file.
9193
exception: Exception raised during the build process, or None if the build completed successfully.
92-
9394
"""
9495
rs_path = app.output_rust_file
9596
# Run the Rust compiler in test mode with JSON error output format.
9697
# capturing stdout and stderr as text.
9798
result = subprocess.run(
98-
["rustc", "--test", "--edition=2024", "--error-format=json", rs_path],
99+
["rustc", "--test", "--edition=2024", "--error-format=json", "--emit=metadata", rs_path],
100+
# --emit=metadata or else rustc will produce a binary ./generated
99101
capture_output=True,
100102
text=True
101103
)

0 commit comments

Comments
 (0)