From 29097fd633a6a03ba3a152e31986caca3803af85 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Sat, 1 Aug 2026 18:39:24 +0800 Subject: [PATCH] docs(common): discipline comments per comment spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - removed 12 divider banners (§5.4): 7 top-level section banners and 5 in-test step banners in tests/tests.rs - removed 7 narrations of the obvious (§5.1): 2 early-exit comments and 1 target-resolution comment in src/main.rs, 4 "Step N" comments in test_ir in tests/tests.rs - removed 2 floating section-header lines (§9.3) in tests/tests.rs - rewrote 3 comments for form: optimizer-scope narration to the borrow-lifetime constraint in src/main.rs, from_nodes step narration to the forward-branch-target invariant in src/common/graph.rs, and the dead_code rationale in tests/tests.rs stripped of future framing (§3.1) - fixed 3 imprecise docs (§4.1/§8.1) in src/main.rs: module doc says the pipeline (not the output stage) stops early; EmitTarget and the --emit field no longer call a stage an "intermediate representation" - trimmed 1 annotation block duplicating the asmt_tests doc (§2.2) in tests/tests.rs; kept the per-feature cargo invocations --- src/common/graph.rs | 6 ++--- src/main.rs | 20 ++++++--------- tests/tests.rs | 61 +++------------------------------------------ 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/src/common/graph.rs b/src/common/graph.rs index fdf9d52..be1ff47 100644 --- a/src/common/graph.rs +++ b/src/common/graph.rs @@ -64,9 +64,9 @@ impl Graph { /// Builds a [`Graph`] from a slice of [`CfgNode`] implementors. /// - /// This method first collects all node labels into a name-to-index map, - /// then calls [`CfgNode::successors`] on each node to compute the full - /// successor adjacency list, and finally delegates to [`Graph::new`]. + /// All node labels are collected into a name-to-index map before any + /// [`CfgNode::successors`] call runs, so a node may name a branch target + /// that appears later in `nodes`. pub fn from_nodes(nodes: &[N]) -> Self { let n = nodes.len(); let label_map: HashMap = nodes diff --git a/src/main.rs b/src/main.rs index 6506296..e856ee0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ //! //! This binary ties together all compiler stages in order: //! parsing → IR generation → optimization → assembly emission. -//! The output stage can be stopped early (via `--emit`) to inspect +//! The pipeline can be stopped early (via `--emit`) to inspect //! the AST, IR, or final AArch64 assembly. mod asm; @@ -22,8 +22,8 @@ use std::{ path::{Path, PathBuf}, }; -/// Controls which intermediate representation the compiler writes to the output. -/// The pipeline always runs up to (and including) the chosen stage, then exits. +/// Selects which pipeline stage's output is written. +/// The pipeline always runs up to (and including) that stage, then exits. #[derive(Copy, Clone, Debug, PartialEq, ValueEnum)] enum EmitTarget { /// Stop after parsing and emit the Abstract Syntax Tree. @@ -43,7 +43,7 @@ struct Cli { #[clap(value_name = "FILE")] input: String, - /// Which IR stage to emit as output (default: `asm`). + /// Which pipeline stage to emit as output (default: `asm`). #[arg(long, value_enum, ignore_case = true, default_value = "asm")] emit: EmitTarget, @@ -108,7 +108,6 @@ fn run() -> Result<()> { .generate() .with_context(|| format!("failed to parse '{}'", cli.input))?; - // Early exit: the user only wants the AST dump. if cli.emit == EmitTarget::Ast { return parser .output(&mut writer) @@ -130,12 +129,10 @@ fn run() -> Result<()> { let mut ir_gen = ir::IrGenerator::with_default_passes(ast, source_dir); ir_gen.generate().context("failed to generate IR")?; - // Optimization stage: run the default function-pass pipeline over - // the freshly generated IR. `Optimizer::with_default_passes` takes - // `&mut ir_gen.module`, and the assembly generator below needs to - // reborrow the module immutably. Confining the optimizer to its own - // scope forces Rust to drop that mutable borrow before the asm stage - // starts. + // `Optimizer::with_default_passes` takes `&mut ir_gen.module`, and the + // assembly generator below reborrows the module immutably. Confining the + // optimizer to its own scope forces Rust to drop that mutable borrow + // before the asm stage starts. { let mut optimizer = opt::Optimizer::with_default_passes(&mut ir_gen.module, &ir_gen.registry); @@ -143,7 +140,6 @@ fn run() -> Result<()> { .generate() .context("failed to run optimization passes")?; - // Early exit: the user only wants the (optimized) IR dump. if cli.emit == EmitTarget::Ir { return optimizer .output(&mut writer) diff --git a/tests/tests.rs b/tests/tests.rs index eba5c23..8295484 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -18,10 +18,6 @@ use regex::Regex; static INIT: Once = Once::new(); -// --------------------------------------------------------------------------- -// Platform detection -// --------------------------------------------------------------------------- - fn is_native_macos() -> bool { cfg!(all(target_os = "macos", target_arch = "aarch64")) } @@ -94,10 +90,6 @@ fn ensure_cross_tools() { } } -// --------------------------------------------------------------------------- -// stdlib object (tests/std/std.o) build -// --------------------------------------------------------------------------- - /// Platform-specific path for the compiled stdlib object: /// - macOS AArch64 → `tests/std/std-macos.o` /// - Docker macOS / cross-compile Linux → `tests/std/std-linux.o` @@ -206,10 +198,6 @@ fn ensure_std() { }); } -// --------------------------------------------------------------------------- -// Compile / link / run primitives -// --------------------------------------------------------------------------- - /// Invokes `teac --emit asm` from `dir` to compile `input_file` into /// `output_file`. `input_file` must be a bare filename so `teac`'s /// `source_dir` resolves to `dir` and `use std;` finds `./std.teah`. @@ -370,10 +358,6 @@ fn run_with_qemu(exe: &Path, input: Option<&Path>) -> io::Result<(i32, Vec, run_with_optional_stdin(&mut cmd, input) } -// --------------------------------------------------------------------------- -// Output comparison helpers -// --------------------------------------------------------------------------- - /// Whitespace-insensitive normalisation: collapses runs of whitespace /// within each line to a single space, drops blank lines, appends a /// trailing newline. @@ -412,10 +396,6 @@ fn append_line>(path: P, line: &str) { writeln!(f, "{line}").expect("Failed to append line"); } -// --------------------------------------------------------------------------- -// Test drivers -// --------------------------------------------------------------------------- - /// Returns every function name declared in `source`, in source order. /// Matches any line whose first non-whitespace token is the `fn` /// keyword followed by an identifier and an opening parenthesis; @@ -439,9 +419,8 @@ fn extract_fn_names(source: &str) -> Vec { /// receives the absolute source path, so `source_dir` resolves to the /// test-case directory without a `current_dir` override. // -// `#[allow(dead_code)]` because every in-tree caller is under a -// not-enabled-by-default `#[cfg(feature = ...)]` for a future language -// feature (float / for-loop / struct-method / multi-dim-array). +// `#[allow(dead_code)]`: every in-tree caller is behind a non-default +// `#[cfg(feature = ...)]` (float / for-loop / struct-method / multi-dim-array). #[allow(dead_code)] fn test_ast_parse(test_name: &str) { let base_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); @@ -526,7 +505,6 @@ fn test_ir(test_name: &str) { let build_dir = case_dir.join("build"); fs::create_dir_all(&build_dir).expect("Failed to create build dir"); - // Step 1: Generate IR let tool = Path::new(env!("CARGO_BIN_EXE_teac")); let ir_output = Command::new(tool) .arg(&tea) @@ -550,7 +528,6 @@ fn test_ir(test_name: &str) { fs::write(&ll_path, &ir_output.stdout) .unwrap_or_else(|e| panic!("Failed to write {}: {e}", ll_path.display())); - // Step 2: Compile IR + std.c → executable let std_c = std_dir.join("std.c"); let exe = build_dir.join(format!("{test_name}_ir")); @@ -570,7 +547,6 @@ fn test_ir(test_name: &str) { clang_output.status.code().unwrap_or(-1) ); - // Step 3: Run the executable let input = case_dir.join(format!("{test_name}.in")); let input_path = if input.is_file() { Some(input.as_path()) @@ -588,7 +564,6 @@ fn test_ir(test_name: &str) { } } - // Step 4: Compare output against golden .out file let expected_out = case_dir.join(format!("{test_name}.out")); let actual_out = build_dir.join(format!("{test_name}_ir.out")); @@ -698,9 +673,6 @@ fn test_single(test_name: &str) { tea.display() ); - // ----------------------------------------------------------------------- - // Step 1: Compile TeaLang source to assembly - // ----------------------------------------------------------------------- let output_name = format!("{test_name}.s"); let output_path = out_dir.join(&output_name); let output = launch( @@ -725,9 +697,6 @@ fn test_single(test_name: &str) { output_path.display() ); - // ----------------------------------------------------------------------- - // Step 2: Locate the pre-built stdlib object file - // ----------------------------------------------------------------------- let stdlib = get_std_o_path(); assert!( stdlib.is_file(), @@ -745,9 +714,6 @@ fn test_single(test_name: &str) { None }; - // ----------------------------------------------------------------------- - // Step 3: Link assembly + stdlib → executable and run (platform-specific) - // ----------------------------------------------------------------------- let (run_code, run_stdout, run_stderr) = if is_native_macos() { let exe = out_dir.join(test_name); let (link_code, link_err) = @@ -799,16 +765,10 @@ fn test_single(test_name: &str) { } } - // ----------------------------------------------------------------------- - // Step 4: Write actual output (stdout + exit code) to file - // ----------------------------------------------------------------------- fs::write(&actual_out, &run_stdout) .unwrap_or_else(|e| panic!("Failed to write {}: {e}", actual_out.display())); append_line(&actual_out, &run_code.to_string()); - // ----------------------------------------------------------------------- - // Step 5: Compare actual output against the golden .out file - // ----------------------------------------------------------------------- match read_to_string_if_exists(&expected_out).expect("Failed to read expected output file") { Some(exp) => { let got = fs::read_to_string(&actual_out) @@ -833,10 +793,6 @@ fn test_single(test_name: &str) { } } -// --------------------------------------------------------------------------- -// Test declaration macros -// --------------------------------------------------------------------------- - /// Declares a batch of `test_single` tests from test-case names. macro_rules! full_tests { ($($name:ident),* $(,)?) => { @@ -888,10 +844,6 @@ macro_rules! asmt_tests { }; } -// ----------------------------------------------------------------------- -// Full compile-link-run tests -// ----------------------------------------------------------------------- - full_tests! { dfs, bfs, @@ -925,7 +877,6 @@ full_tests! { type_infer_basic, } -// Return-type-inference tests (feature-gated) // type_infer_1..5 exercise the return-type-inference pass (every `fn` // omits its `-> T` clause). Without the feature the baseline treats // omitted returns as `-> void`, so these tests would fail spuriously. @@ -945,13 +896,7 @@ fn type_infer_5() { test_compile_error("type_infer_5"); } -// Assignment tests -// -// Each test runs `asmt_tests!`, which always performs AST parsing and -// then dispatches to one deeper stage based on the `asmt-tests-*` -// feature: `ast` stops after AST, `ir` runs `test_ir`, and `asm` (the -// default when no `asmt-tests-*` is set) runs `test_single` end-to-end. -// Per-assignment feature flags select which batch is compiled: +// Run per assignment feature: // cargo test --features float // cargo test --features for-loop // cargo test --features struct-method