Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/orphan-modules.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Orphaned modules -- .rs files no crate root reaches.
#
# Rust compiles a file only if a `mod` declaration leads to it from the crate
# root. A file nothing declares is not an unused file: it is a file the compiler
# never opens. `cargo build` cannot error on it, `cargo clippy` never sees it,
# and its `#[test]` functions do not exist for `cargo test`.
#
# #2427 -- a PR about the Zig subset lexer -- removed `mod elab;` from
# cli/tri/src/main.rs in the same hunk as two other lines and left the 319-line
# file. `tri elab` stopped existing, the suite fell 358 -> 354, and nothing
# printed a word (#2900). A live script kept instructing its reader to run the
# command that was gone.
#
# NO `paths:` FILTER, deliberately. The change that strands a file is a change to
# the crate root, and the root is not the file that goes missing -- a filter on
# the stranded path would have been silent for exactly this defect.
#
# An exact-match ratchet, not a refusal: bootstrap carries 7 orphans today, none
# of them repaired here (#2905 is one). The number may not rise, and it may not
# sit below its ceiling either -- lower the ceiling in the commit that connects a
# file, so the next orphan cannot hide in the slack.

name: Orphaned modules

on:
pull_request:
# No `branches:` filter, matching the rest of this repository: a branch
# filter makes a gate silent on any PR whose base is not listed, and a
# stacked PR then shows a green check list a master-based PR would not get.
push:
branches: [master]
workflow_dispatch:

concurrency:
group: orphan-modules-${{ github.ref }}
cancel-in-progress: true

jobs:
orphans:
name: Every source file is reachable from its crate root
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- name: Build the tool
run: cargo build --release -p tri

# A gate that cannot fail is not a gate, and CI reads the exit code, not
# the prose. This plants an undeclared file in a temp crate and exits
# non-zero unless the walk reports exactly it.
- name: Negative control
run: ./target/release/tri mods orphan --self-check

- name: Orphan ceiling
run: ./target/release/tri mods orphan --gate
35 changes: 35 additions & 0 deletions cli/tri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ mod orphaned;
mod cibase;
mod fleet;
mod fpga;
mod elab;
mod modreach;
mod gates;
mod prose;
mod unparsed;
Expand Down Expand Up @@ -41,6 +43,19 @@ struct Cli {
command: Commands,
}

#[derive(Subcommand)]
pub enum ModsCmd {
/// List them.
Orphan {
/// Compare against docs/reports/orphan_modules.json and exit non-zero on a change.
#[arg(long)]
gate: bool,
/// Negative control: prove this gate can see a planted orphan.
#[arg(long)]
self_check: bool,
},
}

#[derive(Subcommand)]
enum Commands {
Status,
Expand Down Expand Up @@ -141,6 +156,16 @@ enum Commands {
#[command(subcommand)]
action: gates::GatesCmd,
},
/// Source files no crate root reaches, and so nothing compiles.
Mods {
#[command(subcommand)]
action: ModsCmd,
},
/// Classify a compiler's error output before quoting a number from it.
Elab {
#[command(subcommand)]
action: elab::ElabCmd,
},
/// The structural check t27.ai offers, run locally: five verdicts, the
/// yosys version beside the numbers, and no claim about correctness.
Rtl {
Expand Down Expand Up @@ -812,6 +837,16 @@ fn main() -> Result<()> {
Commands::Red { action } => red::run(action)?,
Commands::Gates { action } => gates::run(action)?,
Commands::Vectors { action } => vectors::run(action)?,
Commands::Mods { action } => match action {
ModsCmd::Orphan { gate, self_check } => {
if *self_check {
modreach::self_check()?
} else {
modreach::run(*gate)?
}
}
},
Commands::Elab { action } => elab::run(action)?,
Commands::Rtl { action } => rtl::run(action)?,
Commands::Abandoned { action } => abandoned::run(action)?,
Commands::Types { action } => types_dup::run(action)?,
Expand Down
Loading
Loading