Skip to content

chore: add SPDX license headers to all hand-written source files - #340

Merged
tinovyatkin merged 3 commits into
mainfrom
chore/spdx-license-headers
Aug 12, 2026
Merged

chore: add SPDX license headers to all hand-written source files#340
tinovyatkin merged 3 commits into
mainfrom
chore/spdx-license-headers

Conversation

@tinovyatkin

@tinovyatkin tinovyatkin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a two-line license header to every hand-written source file (.rs, .py, .sh):

// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Konstantin Vyatkin

Scope was narrowed during review — the header is only applied to files this project actually owns:

Excluded Why
*/generated/*.rs, *.inc.rs Already carry // @generated; regenerated from grammars, so a header would be overwritten
crates/antlr-rust-codegen/tests/fixtures/.../rust-support/** Pinned byte-level reproductions of grammars-v4 (each documents its upstream commit + blob). The tree is sha256-fingerprinted to catch content drift
third_party/** Upstream grammars under their own licenses

crates/antlr-rust-toml-parser/src/*.rs uses SPDX-License-Identifier: Apache-2.0 instead — that crate publishes as Apache-2.0 because its recognizers derive from the ASF-licensed grammars-v4 TOML grammar, and it ships its own LICENSE/NOTICE. Every other workspace member takes license.workspace = true (BSD-3-Clause).

Shell and Python files place the header after the shebang so execve still resolves the interpreter.

Review fixes (ac76faf)

All three Codex findings addressed:

  • P1 — headers broke noninteractive_bundle_requires_its_exact_fingerprint. Fixed by reverting the fixture tree, not by re-recording the fingerprint: accepting a new hash would normalize away exactly the drift the snapshot exists to detect.
  • P2 — BSD header contradicted antlr-rust-toml-parser's Apache-2.0 metadata. Now Apache-2.0.
  • P2 — the pre-commit hook strips executable bits from files it rewrites; my restore pass ran a blanket chmod +x and over-corrected three never-executable modules (test_run.py, dump_python.py, bench.py). All modes now diffed against the base commit and matching.

Test plan

  • rust_support::* green locally (6/6) — the fingerprint suite that caught the P1
  • Every file mode diffed against base 9d2a8c11; no unintended mode changes
  • No generated or third_party/ file touched
  • Runtime Testsuite (357-case conformance sweep) passes
  • Clippy and Unit Tests pass

Reviewer note

CodeRabbit skipped this PR (186 files vs. its 100-file limit, plus a credits/capacity message), so it has not reviewed the diff. Splitting the sweep would add churn without much benefit for a uniform two-line header change — but happy to split if a CodeRabbit pass is wanted.

Every .rs, .py, and .sh source file now opens with:

  // SPDX-License-Identifier: BSD-3-Clause
  // Copyright (c) 2026 Konstantin Vyatkin

Generated files (*/generated/*.rs, *.inc.rs) are left untouched —
they already carry a @generated marker and are regenerated from
grammars, so a license header would be overwritten on the next regen.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 186 files, which is 86 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 04d5c485-2d1e-46db-a101-544869de9a2e

📥 Commits

Reviewing files that changed from the base of the PR and between 9d2a8c1 and ac76faf.

📒 Files selected for processing (186)
  • benches/benches/char_stream.rs
  • benches/benches/grammar_frontend.rs
  • crates/antlr-rust-codegen/src/artifact.rs
  • crates/antlr-rust-codegen/src/bin/antlr4-rust-gen.rs
  • crates/antlr-rust-codegen/src/bin/antlr4-rust-testrig.rs
  • crates/antlr-rust-codegen/src/builder.rs
  • crates/antlr-rust-codegen/src/cli.rs
  • crates/antlr-rust-codegen/src/cli_report.rs
  • crates/antlr-rust-codegen/src/config.rs
  • crates/antlr-rust-codegen/src/driver.rs
  • crates/antlr-rust-codegen/src/embedded/antlr4rust/aliases.rs
  • crates/antlr-rust-codegen/src/embedded/antlr4rust/macros.rs
  • crates/antlr-rust-codegen/src/embedded/antlr4rust/mod.rs
  • crates/antlr-rust-codegen/src/embedded/antlr4rust/scopes.rs
  • crates/antlr-rust-codegen/src/embedded/members.rs
  • crates/antlr-rust-codegen/src/embedded/mod.rs
  • crates/antlr-rust-codegen/src/embedded/model.rs
  • crates/antlr-rust-codegen/src/embedded/translate.rs
  • crates/antlr-rust-codegen/src/error.rs
  • crates/antlr-rust-codegen/src/generator/tests.rs
  • crates/antlr-rust-codegen/src/grammar/action.rs
  • crates/antlr-rust-codegen/src/grammar/atn/analysis.rs
  • crates/antlr-rust-codegen/src/grammar/atn/build.rs
  • crates/antlr-rust-codegen/src/grammar/atn/general_bug_test.rs
  • crates/antlr-rust-codegen/src/grammar/atn/interp_test.rs
  • crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
  • crates/antlr-rust-codegen/src/grammar/atn/mod.rs
  • crates/antlr-rust-codegen/src/grammar/atn/optimize.rs
  • crates/antlr-rust-codegen/src/grammar/atn/parser.rs
  • crates/antlr-rust-codegen/src/grammar/char_support.rs
  • crates/antlr-rust-codegen/src/grammar/compiler.rs
  • crates/antlr-rust-codegen/src/grammar/diagnostic.rs
  • crates/antlr-rust-codegen/src/grammar/escape_sequence.rs
  • crates/antlr-rust-codegen/src/grammar/integration.rs
  • crates/antlr-rust-codegen/src/grammar/left_recursion.rs
  • crates/antlr-rust-codegen/src/grammar/loader.rs
  • crates/antlr-rust-codegen/src/grammar/mod.rs
  • crates/antlr-rust-codegen/src/grammar/model.rs
  • crates/antlr-rust-codegen/src/grammar/mutual_recursion.rs
  • crates/antlr-rust-codegen/src/grammar/provenance.rs
  • crates/antlr-rust-codegen/src/grammar/rule_reachability.rs
  • crates/antlr-rust-codegen/src/grammar/semantics.rs
  • crates/antlr-rust-codegen/src/grammar/semantics/attributes.rs
  • crates/antlr-rust-codegen/src/grammar/semantics/bindings.rs
  • crates/antlr-rust-codegen/src/grammar/source.rs
  • crates/antlr-rust-codegen/src/grammar/syntax.rs
  • crates/antlr-rust-codegen/src/grammar/transform/analysis.rs
  • crates/antlr-rust-codegen/src/grammar/transform/artifact.rs
  • crates/antlr-rust-codegen/src/grammar/transform/mod.rs
  • crates/antlr-rust-codegen/src/grammar/transform/passes/precedence_ladder.rs
  • crates/antlr-rust-codegen/src/grammar/transform/passes/prune_unreachable.rs
  • crates/antlr-rust-codegen/src/grammar/transform/registry.rs
  • crates/antlr-rust-codegen/src/grammar/unicode.rs
  • crates/antlr-rust-codegen/src/grammar/unicode_escape.rs
  • crates/antlr-rust-codegen/src/grammar/unicode_icu_tests.rs
  • crates/antlr-rust-codegen/src/grammar/validation.rs
  • crates/antlr-rust-codegen/src/json.rs
  • crates/antlr-rust-codegen/src/lexer/mod.rs
  • crates/antlr-rust-codegen/src/lexer/render.rs
  • crates/antlr-rust-codegen/src/lexer/render_model.rs
  • crates/antlr-rust-codegen/src/lib.rs
  • crates/antlr-rust-codegen/src/optimization/config.rs
  • crates/antlr-rust-codegen/src/optimization/descriptor.rs
  • crates/antlr-rust-codegen/src/optimization/metrics.rs
  • crates/antlr-rust-codegen/src/optimization/mod.rs
  • crates/antlr-rust-codegen/src/optimization/report.rs
  • crates/antlr-rust-codegen/src/parser/decision.rs
  • crates/antlr-rust-codegen/src/parser/ir/lower.rs
  • crates/antlr-rust-codegen/src/parser/ir/mod.rs
  • crates/antlr-rust-codegen/src/parser/ir/optimize.rs
  • crates/antlr-rust-codegen/src/parser/mod.rs
  • crates/antlr-rust-codegen/src/parser/render/decisions.rs
  • crates/antlr-rust-codegen/src/parser/render/fallback.rs
  • crates/antlr-rust-codegen/src/parser/render/loops.rs
  • crates/antlr-rust-codegen/src/parser/render/mod.rs
  • crates/antlr-rust-codegen/src/parser/render/rules.rs
  • crates/antlr-rust-codegen/src/parser/render_model.rs
  • crates/antlr-rust-codegen/src/parser/routing.rs
  • crates/antlr-rust-codegen/src/parser/surface/accessors.rs
  • crates/antlr-rust-codegen/src/parser/surface/contexts.rs
  • crates/antlr-rust-codegen/src/parser/surface/facade.rs
  • crates/antlr-rust-codegen/src/parser/surface/model.rs
  • crates/antlr-rust-codegen/src/parser/surface/names.rs
  • crates/antlr-rust-codegen/src/parser/surface/support_abi.rs
  • crates/antlr-rust-codegen/src/parser/surface/traversal.rs
  • crates/antlr-rust-codegen/src/pipeline.rs
  • crates/antlr-rust-codegen/src/rust_output.rs
  • crates/antlr-rust-codegen/src/rust_support/identity.rs
  • crates/antlr-rust-codegen/src/rust_support/mod.rs
  • crates/antlr-rust-codegen/src/rust_support/prompt.rs
  • crates/antlr-rust-codegen/src/rust_support/python.rs
  • crates/antlr-rust-codegen/src/rust_support/stage.rs
  • crates/antlr-rust-codegen/src/semantics/hooks.rs
  • crates/antlr-rust-codegen/src/semantics/inventory.rs
  • crates/antlr-rust-codegen/src/semantics/manifest.rs
  • crates/antlr-rust-codegen/src/semantics/mod.rs
  • crates/antlr-rust-codegen/src/semantics/model.rs
  • crates/antlr-rust-codegen/src/semantics/patterns.rs
  • crates/antlr-rust-codegen/src/semantics/semir.rs
  • crates/antlr-rust-codegen/src/semantics/stack_member.rs
  • crates/antlr-rust-codegen/src/semantics/template_syntax.rs
  • crates/antlr-rust-codegen/src/semantics/templates.rs
  • crates/antlr-rust-codegen/src/structural/contexts.rs
  • crates/antlr-rust-codegen/src/structural/mod.rs
  • crates/antlr-rust-codegen/src/test_rig.rs
  • crates/antlr-rust-codegen/src/testrig_cli.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/cli.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/compatibility.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/diagnostics.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/lexer.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/multi_recognizer.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/optimizations.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/parser.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/rust_support.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/semantics.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/support.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/testrig.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/transforms.rs
  • crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/typed_tree.rs
  • crates/antlr-rust-codegen/tests/fixtures/antlr4-rust-gen/midi-binary/make_fixture.py
  • crates/antlr-rust-g4-parser/src/frontend.rs
  • crates/antlr-rust-g4-parser/src/lexer_adaptor.rs
  • crates/antlr-rust-g4-parser/src/lib.rs
  • crates/antlr-rust-rs-parser/src/cfg_syntax.rs
  • crates/antlr-rust-rs-parser/src/lib.rs
  • crates/antlr-rust-runtime/src/atn/ascii_range.rs
  • crates/antlr-rust-runtime/src/atn/bypass.rs
  • crates/antlr-rust-runtime/src/atn/lexer.rs
  • crates/antlr-rust-runtime/src/atn/lexer_dfa.rs
  • crates/antlr-rust-runtime/src/atn/mod.rs
  • crates/antlr-rust-runtime/src/atn/parser.rs
  • crates/antlr-rust-runtime/src/atn/parser_atn.rs
  • crates/antlr-rust-runtime/src/atn/serialized.rs
  • crates/antlr-rust-runtime/src/byte_stream.rs
  • crates/antlr-rust-runtime/src/char_stream.rs
  • crates/antlr-rust-runtime/src/dfa.rs
  • crates/antlr-rust-runtime/src/errors.rs
  • crates/antlr-rust-runtime/src/generated.rs
  • crates/antlr-rust-runtime/src/int_stream.rs
  • crates/antlr-rust-runtime/src/lexer.rs
  • crates/antlr-rust-runtime/src/lib.rs
  • crates/antlr-rust-runtime/src/parser.rs
  • crates/antlr-rust-runtime/src/perf.rs
  • crates/antlr-rust-runtime/src/prediction.rs
  • crates/antlr-rust-runtime/src/recognizer.rs
  • crates/antlr-rust-runtime/src/semir.rs
  • crates/antlr-rust-runtime/src/token.rs
  • crates/antlr-rust-runtime/src/token_stream.rs
  • crates/antlr-rust-runtime/src/tree.rs
  • crates/antlr-rust-runtime/src/tree_pattern.rs
  • crates/antlr-rust-runtime/src/validated.rs
  • crates/antlr-rust-runtime/src/vocabulary.rs
  • crates/antlr-rust-runtime/src/xpath.rs
  • crates/antlr-rust-toml-parser/src/ast.rs
  • crates/antlr-rust-toml-parser/src/decode.rs
  • crates/antlr-rust-toml-parser/src/lib.rs
  • crates/antlr-rust-toml-parser/src/string.rs
  • tests/antlr-rust-runtime-testsuite/src/main.rs
  • tests/antlr-rust-runtime-testsuite/src/rust_names.rs
  • tests/javascript-parity/dump_python.py
  • tests/javascript-parity/dumper/src/javascript_lexer_base.rs
  • tests/javascript-parity/dumper/src/javascript_parser_base.rs
  • tests/javascript-parity/dumper/src/main.rs
  • tests/javascript-parity/run.sh
  • tests/kotlin-parity/dump_python.py
  • tests/kotlin-parity/dumper/src/main.rs
  • tests/kotlin-parity/run.sh
  • tests/typescript-parity/dumper/src/main.rs
  • tests/typescript-parity/dumper/src/typescript_lexer_base.rs
  • tests/typescript-parity/dumper/src/typescript_parser_base.rs
  • tests/typescript-parity/run.sh
  • tools/fixed-lookahead-bench/bench.py
  • tools/fixed-lookahead-bench/run.sh
  • tools/grammar-frontend/update-interp-fixtures.sh
  • tools/grammar-frontend/update-stage0.sh
  • tools/parse-bench/compare.py
  • tools/parse-bench/run.py
  • tools/parse-bench/rust-support/csharp_lexer_base.rs
  • tools/parse-bench/test_run.py
  • tools/release/check-workspace-version.sh
  • tools/release/preflight-package-archives.sh
  • tools/release/registry-smoke.sh
  • tools/release/verify-package-contents.sh
  • tools/rust-syntax/update-generated.sh
  • tools/toml-syntax/update-generated.sh

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Copy/Paste Detection

Found 101 duplication(s) across 167 changed non-generated Rust file(s) (threshold: 100 tokens).

Show duplications

Found a 132 line (611 tokens) duplication in the following files:

  • Starting at line 77 of crates/antlr-rust-codegen/src/rust_output.rs
  • Starting at line 63 of tests/antlr-rust-runtime-testsuite/src/rust_names.rs
}

/// Splits mixed-case, snake-case, and punctuation-heavy grammar identifiers
/// into words for Rust identifier rendering.
pub(crate) fn split_identifier_words(name: &str) -> Vec<String> {
    let mut words = Vec::new();
    let mut current = String::new();

    let chars: Vec<char> = name.chars().collect();
    for (index, ch) in chars.iter().copied().enumerate() {
        if !ch.is_ascii_alphanumeric() {
            if !current.is_empty() {
                words.push(ascii_lowercase(&current));
                current.clear();
            }
            continue;
        }

        let previous = index.checked_sub(1).and_then(|i| chars.get(i)).copied();
        let next = chars.get(index + 1).copied();
        let starts_new_word = !current.is_empty()
            && ch.is_ascii_uppercase()
            && (previous.is_some_and(|prev| prev.is_ascii_lowercase() || prev.is_ascii_digit())
                || (previous.is_some_and(|prev| prev.is_ascii_uppercase())
                    && next.is_some_and(|next| next.is_ascii_lowercase())));

        if starts_new_word {
            words.push(ascii_lowercase(&current));
            current.clear();
        }
        current.push(ch);
    }
    if !current.is_empty() {
        words.push(ascii_lowercase(&current));
    }
    words
}

/// Produces a legal Rust identifier and leaves keyword handling to callers that
/// know whether raw identifiers are valid at the target position.
pub(crate) fn sanitize_identifier(value: &str) -> String {
    let mut out = String::new();
    for (index, ch) in value.chars().enumerate() {
        if ch == '_' || ch.is_ascii_alphanumeric() {
            if index == 0 && ch.is_ascii_digit() {
                out.push('_');
            }
            out.push(ch);
        } else {
            out.push('_');
        }
    }
    if out.is_empty() { "_".to_owned() } else { out }
}

/// Produces a legal Rust identifier, using raw syntax only for keywords that
/// permit it. Path keywords cannot be raw identifiers, so suffix those names.
pub(crate) fn rust_identifier(value: &str) -> String {
    let identifier = sanitize_identifier(value);
    if matches!(identifier.as_str(), "crate" | "self" | "Self" | "super") {
        format!("{identifier}_")
    } else if is_rust_keyword(&identifier) {
        format!("r#{identifier}")
    } else {
        identifier
    }
}

/// Returns true for Rust reserved and contextual keywords that cannot be used
/// directly as generated identifiers.
pub(crate) fn is_rust_keyword(value: &str) -> bool {
    matches!(
        value,
        "as" | "async"
            | "await"
            | "break"
            | "const"
            | "continue"
            | "crate"
            | "dyn"
            | "else"
            | "enum"
            | "extern"
            | "false"
            | "fn"
            | "for"
            | "gen"
            | "if"
            | "impl"
            | "in"
            | "let"
            | "loop"
            | "match"
            | "mod"
            | "move"
            | "mut"
            | "pub"
            | "ref"
            | "return"
            | "Self"
            | "self"
            | "static"
            | "struct"
            | "super"
            | "trait"
            | "true"
            | "type"
            | "unsafe"
            | "use"
            | "where"
            | "while"
            | "abstract"
            | "become"
            | "box"
            | "do"
            | "final"
            | "macro"
            | "override"
            | "priv"
            | "try"
            | "typeof"
            | "unsized"
            | "virtual"
            | "yield"
    )
}

/// Converts ASCII letters to lower case without using allocation-hiding string
/// case helpers disallowed by the strict Clippy policy.
fn ascii_lowercase(value: &str) -> String {
    value.chars().map(|ch| ch.to_ascii_lowercase()).collect()
}
```rust

---

Found a 65 line (392 tokens) duplication in the following files:
* Starting at line 3 of crates/antlr-rust-codegen/src/rust_output.rs
* Starting at line 3 of tests/antlr-rust-runtime-testsuite/src/rust_names.rs

```rust
use icu_properties::{CodePointSetData, CodePointSetDataBorrowed, props};

/// Returns the byte end of a Rust identifier beginning at `start`.
#[allow(dead_code)] // This shared module is also compiled by the conformance harness.
pub(crate) fn rust_identifier_end(value: &str, start: usize) -> Option<usize> {
    static XID_START: CodePointSetDataBorrowed<'static> =
        CodePointSetData::new::<props::XidStart>();
    static XID_CONTINUE: CodePointSetDataBorrowed<'static> =
        CodePointSetData::new::<props::XidContinue>();

    let mut chars = value.get(start..)?.char_indices();
    let (_, first) = chars.next()?;
    if first != '_' && !XID_START.contains(first) {
        return None;
    }
    let mut end = start + first.len_utf8();
    for (relative, ch) in chars {
        if !XID_CONTINUE.contains(ch) {
            break;
        }
        end = start + relative + ch.len_utf8();
    }
    Some(end)
}

/// Converts a grammar type name into a snake-case module file name.
pub(crate) fn module_name(name: &str) -> String {
    split_identifier_words(name).join("_")
}

/// Converts an ANTLR grammar name into a Rust type name.
pub(crate) fn rust_type_name(name: &str) -> String {
    split_identifier_words(name)
        .into_iter()
        .map(|part| {
            let mut chars = part.chars();
            chars.next().map_or_else(String::new, |first| {
                let mut out = String::with_capacity(part.len());
                out.push(first.to_ascii_uppercase());
                out.push_str(chars.as_str());
                out
            })
        })
        .collect()
}

/// Converts an ANTLR rule name into a snake-case Rust method name.
pub(crate) fn rust_function_name(name: &str) -> String {
    let words = split_identifier_words(name);
    let ident = if words.is_empty() {
        "rule".to_owned()
    } else {
        words.join("_")
    };
    rust_identifier(&ident)
}

/// Escapes a Rust string literal using explicit ASCII escape forms.
pub(crate) fn rust_string(value: &str) -> String {
    value.escape_default().to_string()
}

/// Replaces every non-overlapping occurrence without relying on the
/// allocation-hiding `str::replace` helper prohibited by the workspace lints.
pub(crate) fn replace_all(text: &str, needle: &str, replacement: &str) -> String {

Found a 66 line (387 tokens) duplication in the following files:

  • Starting at line 26 of tests/javascript-parity/dumper/src/main.rs
  • Starting at line 26 of tests/typescript-parity/dumper/src/main.rs
use javascript_parser_base::JavaScriptParserBase;

fn dump_tree<S: AsRef<str>>(
    out: &mut dyn Write,
    tree: Node<'_>,
    rule_names: &[S],
    depth: usize,
) -> io::Result<()> {
    let pad = "  ".repeat(depth);
    match tree.kind() {
        NodeKind::Rule => {
            let rule = tree.as_rule().expect("rule node kind checked");
            let name = rule_names
                .get(rule.rule_index())
                .map_or("<?>", AsRef::as_ref);
            writeln!(
                out,
                "{pad}Rule({name}, children={})",
                rule.child_count()
            )?;
            for child in rule.children() {
                dump_tree(out, child, rule_names, depth + 1)?;
            }
        }
        NodeKind::Terminal => writeln!(
            out,
            "{pad}Term({:?})",
            tree.as_terminal().expect("terminal node kind checked").text()
        )?,
        NodeKind::Error => writeln!(
            out,
            "{pad}Err({:?})",
            tree.as_error().expect("error node kind checked").text()
        )?,
    }
    Ok(())
}

fn main() -> ExitCode {
    let mut args = env::args().skip(1);
    let mut input: Option<PathBuf> = None;
    let mut tokens_only = false;
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "--input" => input = args.next().map(PathBuf::from),
            "--tokens" => tokens_only = true,
            other => {
                eprintln!("unknown argument: {other}");
                return ExitCode::from(2);
            }
        }
    }
    let Some(input) = input else {
        eprintln!("missing --input <path>");
        return ExitCode::from(2);
    };
    let source = match fs::read_to_string(&input) {
        Ok(source) => source,
        Err(error) => {
            eprintln!("failed to read {}: {error}", input.display());
            return ExitCode::FAILURE;
        }
    };

    if tokens_only {
        let lexer = JavaScriptLexer::with_typed_hooks(
```rust

---

Found a 54 line (320 tokens) duplication in the following files:
* Starting at line 608 of crates/antlr-rust-runtime/src/atn/lexer.rs
* Starting at line 679 of crates/antlr-rust-runtime/src/atn/lexer.rs

```rust
    atn: &LexerAtn,
    hooks: &mut H,
    mut generated_action: A,
    mut generated_predicate: P,
    unknown_policy: UnknownSemanticPolicy,
    mut accept_adjuster: E,
) -> Result<TokenId, TokenStoreError>
where
    I: CharStream,
    H: SemanticHooks,
    A: FnMut(&mut BaseLexer<I>, LexerCustomAction) -> bool,
    P: FnMut(&BaseLexer<I>, LexerPredicate) -> Option<bool>,
    E: FnMut(&mut BaseLexer<I>, i32, usize),
{
    let hooks = RefCell::new(hooks);
    let token = next_token_with_hooks_impl(
        lexer,
        sink,
        atn,
        &mut |lexer, action| {
            if !generated_action(lexer, action)
                && !dispatch_lexer_action_hook(&hooks, lexer, action)
                && unknown_policy == UnknownSemanticPolicy::Error
                && let (Ok(rule), Ok(index)) = (
                    usize::try_from(action.rule_index()),
                    usize::try_from(action.action_index()),
                )
            {
                lexer.record_semantic_error(true, rule, index);
            }
        },
        &mut |lexer, predicate| {
            generated_predicate(lexer, predicate)
                .or_else(|| dispatch_lexer_predicate_hook(&hooks, lexer, predicate))
                .unwrap_or_else(|| match unknown_policy {
                    UnknownSemanticPolicy::AssumeTrue => true,
                    UnknownSemanticPolicy::AssumeFalse => false,
                    UnknownSemanticPolicy::Error => {
                        lexer.record_semantic_error(
                            false,
                            predicate.rule_index(),
                            predicate.pred_index(),
                        );
                        false
                    }
                })
        },
        &mut |lexer| dispatch_lexer_before_token_hook(&hooks, lexer),
        &mut accept_adjuster,
        &mut |lexer, accept_position| {
            dispatch_lexer_after_accept_hook(&hooks, lexer, accept_position);
        },
        LexerMatchStrategy {
            compiled: None,

Found a 44 line (283 tokens) duplication in the following files:

  • Starting at line 201 of crates/antlr-rust-codegen/src/grammar/atn/analysis.rs
  • Starting at line 1390 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
}

fn nullable_rule_indices(graph: &FinalizedAtnGraph) -> BTreeSet<usize> {
    let transitions = transitions_by_id(graph);
    let mut nullable = BTreeSet::new();
    loop {
        let previous = nullable.len();
        for (rule, (&start, &stop)) in graph.rule_starts.iter().zip(&graph.rule_stops).enumerate() {
            if epsilon_reaches(graph, &transitions, start, stop, &nullable) {
                nullable.insert(rule);
            }
        }
        if nullable.len() == previous {
            return nullable;
        }
    }
}

fn epsilon_reaches(
    graph: &FinalizedAtnGraph,
    transitions: &BTreeMap<super::super::model::BuildTransitionId, &FinalizedTransition>,
    start: usize,
    stop: usize,
    nullable_rules: &BTreeSet<usize>,
) -> bool {
    let mut pending = vec![start];
    let mut visited = BTreeSet::new();
    while let Some(state) = pending.pop() {
        if state == stop {
            return true;
        }
        if !visited.insert(state) {
            continue;
        }
        for transition in graph.states[state]
            .transitions
            .iter()
            .filter_map(|transition| transitions.get(transition).copied())
        {
            match &transition.kind {
                FinalizedTransitionKind::Rule {
                    rule_index, follow, ..
                } if nullable_rules.contains(rule_index) => pending.push(*follow),
                kind if kind.is_epsilon() => pending.push(transition.target),
```rust

---

Found a 44 line (265 tokens) duplication in the following files:
* Starting at line 13 of tests/javascript-parity/dumper/src/javascript_parser_base.rs
* Starting at line 13 of tests/typescript-parity/dumper/src/typescript_parser_base.rs

```rust
impl JavaScriptParserBase {
    fn raw_token<S>(ctx: &mut ParserSemCtx<'_, S>, index: usize) -> Option<(i32, i32, String)>
    where
        S: TokenSource,
    {
        ctx.token_at(index).map(|token| {
            (
                token.channel(),
                token.token_type(),
                token.text_or_empty().to_owned(),
            )
        })
    }

    fn has_line_terminator_ahead<S>(ctx: &mut ParserSemCtx<'_, S>) -> bool
    where
        S: TokenSource,
    {
        let current = ctx.input_index();
        let Some(previous) = current.checked_sub(1) else {
            return false;
        };
        let Some((channel, mut token_type, mut text)) = Self::raw_token(ctx, previous) else {
            return false;
        };
        if channel != HIDDEN_CHANNEL {
            return false;
        }
        if token_type == LINE_TERMINATOR {
            return true;
        }
        if token_type == WHITE_SPACES {
            let Some(before_whitespace) = previous.checked_sub(1) else {
                return false;
            };
            let Some((_, next_type, next_text)) = Self::raw_token(ctx, before_whitespace) else {
                return false;
            };
            token_type = next_type;
            text = next_text;
        }
        token_type == LINE_TERMINATOR
            || (token_type == MULTI_LINE_COMMENT && (text.contains('\r') || text.contains('\n')))
    }

Found a 41 line (250 tokens) duplication in the following files:

  • Starting at line 1226 of crates/antlr-rust-codegen/src/embedded/translate.rs
  • Starting at line 195 of crates/antlr-rust-rs-parser/src/cfg_syntax.rs
            offset = skip_while(bytes, offset, is_ascii_whitespace);
            RustLexemeKind::Trivia
        } else if body[offset..].starts_with("//") {
            offset = body[offset..]
                .find('\n')
                .map_or(body.len(), |newline| offset + newline);
            RustLexemeKind::Trivia
        } else if body[offset..].starts_with("/*") {
            offset = block_comment_end(body, offset);
            RustLexemeKind::Trivia
        } else if let Some(end) = raw_literal_end(body, offset) {
            offset = end;
            RustLexemeKind::Literal
        } else if let Some(end) = quoted_literal_end(body, offset) {
            offset = end;
            RustLexemeKind::Literal
        } else if let Some(end) = raw_identifier_end(body, offset) {
            offset = end;
            RustLexemeKind::Identifier
        } else if let Some(end) = rust_identifier_end(body, offset) {
            offset = end;
            RustLexemeKind::Identifier
        } else if bytes[offset].is_ascii_punctuation() {
            offset += 1;
            RustLexemeKind::Punctuation(bytes[offset - 1])
        } else {
            offset += body[offset..]
                .chars()
                .next()
                .expect("offset is within the string")
                .len_utf8();
            RustLexemeKind::Other
        };
        lexemes.push(RustLexeme {
            kind,
            start,
            end: offset,
        });
    }
    lexemes
}
```rust

---

Found a 45 line (233 tokens) duplication in the following files:
* Starting at line 910 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 488 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
        let (rule, _) = self.current_rule.expect("building a lexer rule");
        self.epsilon_closures.push((rule.id, pair.left, pair.right));
        let entry = self.synthetic_state(
            AtnStateKind::StarLoopEntry,
            SyntheticReason::LoopBoundary,
            owner,
        );
        self.graph.state_mut(entry).non_greedy = !greedy;
        self.graph.add_decision(entry);
        let end = self.synthetic_state(AtnStateKind::LoopEnd, SyntheticReason::LoopBoundary, owner);
        let loop_state = self.synthetic_state(
            AtnStateKind::StarLoopBack,
            SyntheticReason::LoopBoundary,
            owner,
        );
        self.graph.state_mut(end).loop_back_state = Some(loop_state);
        for target in if greedy {
            [pair.left, end]
        } else {
            [end, pair.left]
        } {
            self.synthetic_epsilon(entry, target, SyntheticReason::LoopBoundary, owner, false);
        }
        self.synthetic_epsilon(
            pair.right,
            loop_state,
            SyntheticReason::LoopBoundary,
            owner,
            false,
        );
        self.synthetic_epsilon(
            loop_state,
            entry,
            SyntheticReason::LoopBoundary,
            owner,
            false,
        );
        StatePair {
            left: entry,
            right: end,
        }
    }

    fn element_list(&mut self, elements: &[StatePair], owner: ModelNodeId) -> StatePair {
        for pair in elements.windows(2) {

Found a 21 line (226 tokens) duplication in the following files:

  • Starting at line 15289 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 16694 of crates/antlr-rust-runtime/src/parser.rs
            (9, AtnStateKind::RuleStop),
        ] {
            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
        }
        atn.set_left_recursive_rule(0)
            .expect("left-recursive rule start");
        atn.set_precedence_rule_decision(2)
            .expect("precedence decision");
        atn.set_loop_back_state(8, 7).expect("loop-back state");
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![9])
            .expect("rule stop states");
        for state in [1, 2, 3] {
            atn.add_decision_state(state).expect("decision state");
        }
        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
                .expect("epsilon transition");
        }
        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
```rust

---

Found a 37 line (221 tokens) duplication in the following files:
* Starting at line 1237 of crates/antlr-rust-codegen/src/grammar/mutual_recursion.rs
* Starting at line 132 of crates/antlr-rust-codegen/src/grammar/transform/analysis.rs

```rust
    let rules = rules_by_id(unit);
    let mut nullable = BTreeSet::new();
    loop {
        let previous = nullable.len();
        for (id, rule) in &rules {
            if rule.block.alternatives.iter().any(|alternative| {
                alternative
                    .elements
                    .iter()
                    .all(|element| element_nullable(element, names, &nullable))
            }) {
                nullable.insert(*id);
            }
        }
        if nullable.len() == previous {
            return nullable;
        }
    }
}

fn element_nullable(
    element: &Element,
    names: &BTreeMap<String, RuleId>,
    nullable: &BTreeSet<RuleId>,
) -> bool {
    if matches!(
        element.quantifier,
        Quantifier::Optional { .. } | Quantifier::ZeroOrMore { .. }
    ) {
        return true;
    }
    match &element.kind {
        ElementKind::Epsilon | ElementKind::Action { .. } | ElementKind::Predicate { .. } => true,
        ElementKind::RuleCall(call) => names
            .get(&call.name)
            .is_some_and(|target| nullable.contains(target)),
        ElementKind::Block(block) => block_is_nullable(block, names, nullable),

Found a 44 line (215 tokens) duplication in the following files:

  • Starting at line 4279 of crates/antlr-rust-codegen/src/generator/tests.rs
  • Starting at line 17035 of crates/antlr-rust-runtime/src/parser.rs
fn plus_loop_atn() -> ParserAtn {
    let mut atn = ParserAtnBuilder::new(2);
    assert_eq!(
        atn.add_state(AtnStateKind::RuleStart, Some(0))
            .expect("state")
            .index(),
        0
    );
    assert_eq!(
        atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
            .expect("state")
            .index(),
        1
    );
    assert_eq!(
        atn.add_state(AtnStateKind::Basic, Some(0))
            .expect("state")
            .index(),
        2
    );
    assert_eq!(
        atn.add_state(AtnStateKind::BlockEnd, Some(0))
            .expect("state")
            .index(),
        3
    );
    assert_eq!(
        atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
            .expect("state")
            .index(),
        4
    );
    assert_eq!(
        atn.add_state(AtnStateKind::LoopEnd, Some(0))
            .expect("state")
            .index(),
        5
    );
    assert_eq!(
        atn.add_state(AtnStateKind::RuleStop, Some(0))
            .expect("state")
            .index(),
        6
    );
```rust

---

Found a 27 line (212 tokens) duplication in the following files:
* Starting at line 1331 of crates/antlr-rust-codegen/src/embedded/translate.rs
* Starting at line 312 of crates/antlr-rust-rs-parser/src/cfg_syntax.rs

```rust
pub(crate) fn quoted_literal_end(body: &str, start: usize) -> Option<usize> {
    let bytes = body.as_bytes();
    let (quote, content) = match bytes.get(start..start + 2) {
        Some([b'b' | b'c', b'"']) => (b'"', start + 2),
        Some([b'b', b'\'']) => (b'\'', start + 2),
        _ if bytes[start] == b'"' => (b'"', start + 1),
        _ if bytes[start] == b'\'' => (b'\'', start + 1),
        _ => return None,
    };
    if quote == b'\'' {
        return char_literal_end(body, content);
    }

    let mut offset = content;
    let mut escaped = false;
    while offset < bytes.len() {
        if escaped {
            escaped = false;
        } else if bytes[offset] == b'\\' {
            escaped = true;
        } else if bytes[offset] == quote {
            return Some(offset + 1);
        }
        offset += 1;
    }
    Some(body.len())
}

Found a 42 line (210 tokens) duplication in the following files:

  • Starting at line 551 of crates/antlr-rust-codegen/src/grammar/left_recursion.rs
  • Starting at line 1323 of crates/antlr-rust-codegen/src/grammar/transform/passes/precedence_ladder.rs
                    .map(|alternative| self.alternative(alternative, 0))
                    .collect(),
                options: block.options.clone(),
                syntax: block.syntax,
                span: block.span.clone(),
            }),
            ElementKind::Action { id, body } => {
                let cloned_id = self.ids.action();
                self.record(ModelNodeId::Action(cloned_id), ModelNodeId::Action(*id));
                ElementKind::Action {
                    id: cloned_id,
                    body: body.clone(),
                }
            }
            ElementKind::Predicate {
                id,
                body,
                fail,
                precedence,
            } => {
                let cloned_id = self.ids.predicate();
                self.record(
                    ModelNodeId::Predicate(cloned_id),
                    ModelNodeId::Predicate(*id),
                );
                ElementKind::Predicate {
                    id: cloned_id,
                    body: body.clone(),
                    fail: fail.clone(),
                    precedence: *precedence,
                }
            }
            kind => kind.clone(),
        };
        self.record(
            ModelNodeId::Element(cloned.id),
            ModelNodeId::Element(source.id),
        );
        cloned
    }

    fn record(&mut self, destination: ModelNodeId, source: ModelNodeId) {
```rust

---

Found a 25 line (193 tokens) duplication in the following files:
* Starting at line 16623 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 17590 of crates/antlr-rust-runtime/src/parser.rs

```rust
        let mut atn = ParserAtnBuilder::new(1);
        for (state_number, kind) in [
            (0, AtnStateKind::RuleStart),
            (1, AtnStateKind::StarLoopEntry),
            (2, AtnStateKind::Basic),
            (3, AtnStateKind::Basic),
            (4, AtnStateKind::StarLoopBack),
            (5, AtnStateKind::LoopEnd),
            (6, AtnStateKind::RuleStop),
        ] {
            assert_eq!(
                atn.add_state(kind, Some(0)).expect("state").index(),
                state_number
            );
        }
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![6])
            .expect("rule stop states");
        atn.add_decision_state(1).expect("decision state");
        atn.set_loop_back_state(5, 4).expect("loop back state");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("entry transition");
        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
            .expect("loop body");

Found a 21 line (188 tokens) duplication in the following files:

  • Starting at line 1302 of crates/antlr-rust-codegen/src/embedded/translate.rs
  • Starting at line 267 of crates/antlr-rust-rs-parser/src/cfg_syntax.rs
pub(crate) fn raw_literal_end(body: &str, start: usize) -> Option<usize> {
    let rest = &body[start..];
    let prefix = ["br", "cr", "r"]
        .into_iter()
        .find(|prefix| rest.starts_with(prefix))?;
    let mut quote = start + prefix.len();
    while body.as_bytes().get(quote) == Some(&b'#') {
        quote += 1;
    }
    if body.as_bytes().get(quote) != Some(&b'"') {
        return None;
    }
    let hashes = quote - start - prefix.len();
    let closing = format!("\"{}", "#".repeat(hashes));
    let content = quote + 1;
    Some(
        body[content..]
            .find(&closing)
            .map_or(body.len(), |end| content + end + closing.len()),
    )
}
```rust

---

Found a 39 line (188 tokens) duplication in the following files:
* Starting at line 4143 of crates/antlr-rust-codegen/src/generator/tests.rs
* Starting at line 16743 of crates/antlr-rust-runtime/src/parser.rs

```rust
fn block_decision_atn() -> ParserAtn {
    let mut atn = ParserAtnBuilder::new(2);
    assert_eq!(
        atn.add_state(AtnStateKind::RuleStart, Some(0))
            .expect("state")
            .index(),
        0
    );
    assert_eq!(
        atn.add_state(AtnStateKind::BlockStart, Some(0))
            .expect("state")
            .index(),
        1
    );
    assert_eq!(
        atn.add_state(AtnStateKind::Basic, Some(0))
            .expect("state")
            .index(),
        2
    );
    assert_eq!(
        atn.add_state(AtnStateKind::Basic, Some(0))
            .expect("state")
            .index(),
        3
    );
    assert_eq!(
        atn.add_state(AtnStateKind::BlockEnd, Some(0))
            .expect("state")
            .index(),
        4
    );
    assert_eq!(
        atn.add_state(AtnStateKind::RuleStop, Some(0))
            .expect("state")
            .index(),
        5
    );
    atn.set_end_state(1, 4).expect("block end state");

Found a 20 line (186 tokens) duplication in the following files:

  • Starting at line 1359 of crates/antlr-rust-codegen/src/embedded/translate.rs
  • Starting at line 340 of crates/antlr-rust-rs-parser/src/cfg_syntax.rs
pub(crate) fn char_literal_end(body: &str, content: usize) -> Option<usize> {
    let bytes = body.as_bytes();
    let end = if bytes.get(content) == Some(&b'\\') {
        match bytes.get(content + 1).copied()? {
            b'x' => content.checked_add(4)?,
            b'u' if bytes.get(content + 2) == Some(&b'{') => {
                content + 3 + body[content + 3..].find('}')? + 1
            }
            _ => content.checked_add(2)?,
        }
    } else {
        content
            + body[content..]
                .chars()
                .next()
                .filter(|ch| *ch != '\'' && *ch != '\n' && *ch != '\r')?
                .len_utf8()
    };
    (bytes.get(end) == Some(&b'\'')).then_some(end + 1)
}
```rust

---

Found a 25 line (182 tokens) duplication in the following files:
* Starting at line 576 of crates/antlr-rust-codegen/src/embedded/antlr4rust/scopes.rs
* Starting at line 27 of crates/antlr-rust-rs-parser/src/cfg_syntax.rs

```rust
    pub(crate) fn new(lexemes: &[RustLexeme]) -> Self {
        let mut pairs = vec![None; lexemes.len()];
        let mut stack = Vec::new();
        for (position, lexeme) in lexemes.iter().enumerate() {
            match lexeme.kind {
                RustLexemeKind::Punctuation(open @ (b'(' | b'[' | b'{')) => {
                    let close = match open {
                        b'(' => b')',
                        b'[' => b']',
                        b'{' => b'}',
                        _ => unreachable!("matched opening delimiter"),
                    };
                    stack.push((position, close));
                }
                RustLexemeKind::Punctuation(close @ (b')' | b']' | b'}')) => {
                    if let Some((open, expected)) = stack.pop()
                        && close == expected
                    {
                        pairs[open] = Some(position);
                        pairs[position] = Some(open);
                    }
                }
                _ => {}
            }
        }

Found a 37 line (176 tokens) duplication in the following files:

  • Starting at line 506 of crates/antlr-rust-codegen/src/grammar/integration.rs
  • Starting at line 556 of crates/antlr-rust-codegen/src/grammar/left_recursion.rs
  • Starting at line 1328 of crates/antlr-rust-codegen/src/grammar/transform/passes/precedence_ladder.rs
            ElementKind::Block(block) => ElementKind::Block(self.block(block)),
            ElementKind::Action { id, body } => {
                let cloned_id = self.ids.action();
                self.record(ModelNodeId::Action(cloned_id), ModelNodeId::Action(*id));
                ElementKind::Action {
                    id: cloned_id,
                    body: body.clone(),
                }
            }
            ElementKind::Predicate {
                id,
                body,
                fail,
                precedence,
            } => {
                let cloned_id = self.ids.predicate();
                self.record(
                    ModelNodeId::Predicate(cloned_id),
                    ModelNodeId::Predicate(*id),
                );
                ElementKind::Predicate {
                    id: cloned_id,
                    body: body.clone(),
                    fail: fail.clone(),
                    precedence: *precedence,
                }
            }
            kind => kind.clone(),
        };
        self.record(
            ModelNodeId::Element(cloned.id),
            ModelNodeId::Element(source.id),
        );
        cloned
    }

    fn label(&mut self, source: &Label) -> Label {
```rust

---

Found a 17 line (163 tokens) duplication in the following files:
* Starting at line 956 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 534 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
            let next = pair[1];
            let state = self.graph.state(element.left);
            let transition = (state.kind == AtnStateKind::Basic
                && self.graph.state(element.right).kind == AtnStateKind::Basic
                && state.transitions.len() == 1)
                .then(|| state.transitions[0]);
            let can_inline = transition.is_some_and(|transition| {
                let transition = self.graph.transition(transition);
                match &transition.kind {
                    BuildTransitionKind::Rule { follow, .. } => *follow == element.right,
                    _ => transition.target == element.right,
                }
            });
            if can_inline {
                let transition = transition.expect("checked above");
                match &mut self.graph.transition_mut(transition).kind {
                    BuildTransitionKind::Rule { follow, .. } => *follow = next.left,

Found a 26 line (160 tokens) duplication in the following files:

  • Starting at line 279 of crates/antlr-rust-codegen/src/embedded/translate.rs
  • Starting at line 370 of crates/antlr-rust-codegen/src/embedded/translate.rs
                    };
                }
            }
        }
        // Deepest choices first, so an inner result rolls up into its parent branch.
        // The list is rebuilt from `per_branch` each pass, because folding an inner
        // choice *creates* an entry for its parent that must then fold in turn.
        let mut processed: BTreeSet<usize> = BTreeSet::new();
        // Deepest unprocessed choice still holding entries. Folding one creates an
        // entry for its parent, so the candidate set is re-examined every pass.
        while let Some(choice) = per_branch
            .keys()
            .map(|(choice, _)| *choice)
            .filter(|choice| !processed.contains(choice))
            .max_by_key(|choice| depth_of_choice.get(choice).copied().unwrap_or(0))
        {
            processed.insert(choice);
            let counts = per_branch
                .iter()
                .filter(|((candidate, _), _)| *candidate == choice)
                .map(|((_, branch), count)| (*branch, *count))
                .collect::<Vec<_>>();
            if counts.is_empty() {
                continue;
            }
            let agreed = if restricted_to_one_path {
```rust

---

Found a 28 line (160 tokens) duplication in the following files:
* Starting at line 993 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 641 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
        self.current_rule.expect("building a lexer rule").1
    }

    fn basic_pair(&mut self, owner: ModelNodeId) -> StatePair {
        StatePair {
            left: self.authored_state(AtnStateKind::Basic, owner),
            right: self.authored_state(AtnStateKind::Basic, owner),
        }
    }

    fn epsilon_pair(&mut self, owner: ModelNodeId) -> StatePair {
        let pair = self.basic_pair(owner);
        self.authored_transition(pair.left, pair.right, BuildTransitionKind::Epsilon, owner);
        pair
    }

    fn atom_pair(&mut self, owner: ModelNodeId, label: i32) -> StatePair {
        let pair = self.basic_pair(owner);
        self.authored_transition(
            pair.left,
            pair.right,
            BuildTransitionKind::Atom(label),
            owner,
        );
        pair
    }

    fn authored_state(&mut self, kind: AtnStateKind, owner: ModelNodeId) -> BuildStateId {

Found a 26 line (153 tokens) duplication in the following files:

  • Starting at line 319 of crates/antlr-rust-runtime/src/atn/lexer.rs
  • Starting at line 744 of crates/antlr-rust-runtime/src/atn/lexer.rs
pub fn next_token_with_hooks<I, A, P, E>(
    lexer: &mut BaseLexer<I>,
    sink: &mut TokenSink<'_>,
    atn: &LexerAtn,
    mut custom_action: A,
    mut semantic_predicate: P,
    mut accept_adjuster: E,
) -> Result<TokenId, TokenStoreError>
where
    I: CharStream,
    A: FnMut(&mut BaseLexer<I>, LexerCustomAction),
    P: FnMut(&BaseLexer<I>, LexerPredicate) -> bool,
    E: FnMut(&mut BaseLexer<I>, i32, usize),
{
    next_token_with_hooks_impl(
        lexer,
        sink,
        atn,
        &mut custom_action,
        &mut semantic_predicate,
        &mut |_| {},
        &mut accept_adjuster,
        &mut |_, _| {},
        LexerMatchStrategy {
            compiled: None,
            use_cache: false,
```rust

---

Found a 22 line (149 tokens) duplication in the following files:
* Starting at line 156 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 107 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
        self.create_rule_boundaries();
        for (rule_index, rule) in self.grammar.unit.rules.iter().enumerate() {
            self.current_rule = Some((rule, rule_index));
            let pair = self.block(&rule.block, Quantifier::One, ModelNodeId::Rule(rule.id));
            let start = self.graph.rule_starts[rule_index];
            let stop = self.graph.rule_stops[rule_index];
            self.synthetic_epsilon(
                start,
                pair.left,
                SyntheticReason::RuleBoundary,
                ModelNodeId::Rule(rule.id),
                false,
            );
            self.synthetic_epsilon(
                pair.right,
                stop,
                SyntheticReason::RuleBoundary,
                ModelNodeId::Rule(rule.id),
                false,
            );
        }
        self.current_rule = None;

Found a 23 line (148 tokens) duplication in the following files:

  • Starting at line 872 of crates/antlr-rust-codegen/src/embedded/mod.rs
  • Starting at line 1479 of crates/antlr-rust-codegen/src/embedded/mod.rs
    fn unscoped_reads_reject_alternatives_that_would_satisfy_them_unbound() {
        let token_ref = |label: Option<&str>, target: &str, token_type| ElementRef {
            label: label.map(ToOwned::to_owned),
            target: target.to_owned(),
            token_types: vec![token_type],
            is_block: false,
            is_list: false,
            cardinality: ChildCardinality {
                min: 1,
                max: Some(1),
            },
            stable_accessor: true,
            choice_branch: Vec::new(),
            choice_arity: Vec::new(),
            choice_spans: Vec::new(),
            group_spans: Vec::new(),
            branch_spans: Vec::new(),
            leading_terminal: true,
            span: None,
            branch_local_cardinality: ChildCardinality::ONE,
            group_local_cardinality: ChildCardinality::ONE,
        };
        let translate = |second: ElementRef| {
```rust

---

Found a 21 line (146 tokens) duplication in the following files:
* Starting at line 256 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 152 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
            );
        }
    }

    fn block(&mut self, block: &Block, quantifier: Quantifier, owner: ModelNodeId) -> StatePair {
        let alternatives = block
            .alternatives
            .iter()
            .map(|alternative| self.alternative(alternative))
            .collect::<Vec<_>>();
        if quantifier == Quantifier::One && alternatives.len() == 1 {
            return alternatives[0];
        }

        let start_kind = match quantifier {
            Quantifier::One | Quantifier::Optional { .. } => AtnStateKind::BlockStart,
            Quantifier::ZeroOrMore { .. } => AtnStateKind::StarBlockStart,
            Quantifier::OneOrMore { .. } => AtnStateKind::PlusBlockStart,
        };
        let start = self.synthetic_state(start_kind, SyntheticReason::BlockBoundary, owner);
        if (quantifier == Quantifier::One && alternatives.len() > 1)

Found a 27 line (145 tokens) duplication in the following files:

  • Starting at line 18111 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 18245 of crates/antlr-rust-runtime/src/parser.rs
    fn generated_match_token_recovers_missing_token_from_context_follow() {
        let atn = generated_match_recovery_atn();
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new(
                [None, Some("'X'"), Some("'Y'")],
                [None, Some("X"), Some("Y")],
                [None::<&str>, None, None],
            ),
        );
        let mut parser = BaseParser::new(
            CommonTokenStream::new(Source {
                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
                index: 0,
            }),
            data,
        );
        parser.rule_context_stack = vec![
            RuleContextFrame {
                rule_index: 0,
                invoking_state: 0,
            },
            RuleContextFrame {
                rule_index: 1,
                invoking_state: 1,
            },
        ];
```rust

---

Found a 26 line (142 tokens) duplication in the following files:
* Starting at line 4181 of crates/antlr-rust-codegen/src/generator/tests.rs
* Starting at line 4404 of crates/antlr-rust-codegen/src/generator/tests.rs

```rust
    atn.set_end_state(1, 4).expect("block end state");
    atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
        .expect("transition");
    atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
        .expect("transition");
    atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
        .expect("transition");
    atn.add_transition(
        2,
        ParserTransitionSpec::Atom {
            target: 4,
            label: 1,
        },
    )
    .expect("transition");
    atn.add_transition(
        3,
        ParserTransitionSpec::Atom {
            target: 4,
            label: 2,
        },
    )
    .expect("transition");
    atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
        .expect("transition");
    atn.add_decision_state(1).expect("decision state");

Found a 25 line (142 tokens) duplication in the following files:

  • Starting at line 452 of crates/antlr-rust-runtime/src/atn/lexer.rs
  • Starting at line 561 of crates/antlr-rust-runtime/src/atn/lexer.rs
    atn: &LexerAtn,
    hooks: &mut H,
) -> Result<TokenId, TokenStoreError>
where
    I: CharStream,
    H: SemanticHooks,
{
    let hooks = RefCell::new(hooks);
    let token = next_token_with_hooks_impl(
        lexer,
        sink,
        atn,
        &mut |lexer, action| {
            let _ = dispatch_lexer_action_hook(&hooks, lexer, action);
        },
        &mut |lexer, predicate| {
            dispatch_lexer_predicate_hook(&hooks, lexer, predicate).unwrap_or(true)
        },
        &mut |lexer| dispatch_lexer_before_token_hook(&hooks, lexer),
        &mut |_, _, _| {},
        &mut |lexer, accept_position| {
            dispatch_lexer_after_accept_hook(&hooks, lexer, accept_position);
        },
        LexerMatchStrategy {
            compiled: None,
```rust

---

Found a 23 line (139 tokens) duplication in the following files:
* Starting at line 1027 of crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/compatibility.rs
* Starting at line 1086 of crates/antlr-rust-codegen/tests/antlr4_rust_gen_cli/compatibility.rs

```rust
         ID: [a-z]+;\n",
    )
    .expect("delegate grammar should be writable");

    let output = run_antlr4_rust_gen(&[
        root.as_os_str(),
        OsStr::new("-I"),
        temp.path().as_os_str(),
        OsStr::new("--actions"),
        OsStr::new("embedded"),
        OsStr::new("--sem-unknown"),
        OsStr::new("error"),
        OsStr::new("--require-full-semantics"),
        OsStr::new("--out-dir"),
        out.as_os_str(),
    ]);
    assert!(
        output.status.success(),
        "stdout: {}\nstderr: {}",
        utf8(&output.stdout),
        utf8(&output.stderr)
    );
    let parser = fs::read_to_string(out.join("root_parser.rs")).expect("parser should be emitted");

Found a 33 line (137 tokens) duplication in the following files:

  • Starting at line 826 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
  • Starting at line 400 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs
        alternatives: &[StatePair],
        owner: ModelNodeId,
    ) -> StatePair {
        let end = self.synthetic_state(
            AtnStateKind::BlockEnd,
            SyntheticReason::BlockBoundary,
            owner,
        );
        self.graph.state_mut(start).end_state = Some(end);
        for alternative in alternatives {
            self.synthetic_epsilon(
                start,
                alternative.left,
                SyntheticReason::BlockBoundary,
                owner,
                false,
            );
            self.synthetic_epsilon(
                alternative.right,
                end,
                SyntheticReason::BlockBoundary,
                owner,
                false,
            );
            remove_tail_epsilons(&mut self.graph, alternative.left);
        }
        StatePair {
            left: start,
            right: end,
        }
    }

    fn optional(&mut self, pair: StatePair, greedy: bool, owner: ModelNodeId) -> StatePair {
```rust

---

Found a 19 line (137 tokens) duplication in the following files:
* Starting at line 871 of crates/antlr-rust-codegen/src/grammar/atn/lexer.rs
* Starting at line 448 of crates/antlr-rust-codegen/src/grammar/atn/parser.rs

```rust
        let (rule, _) = self.current_rule.expect("building a lexer rule");
        self.epsilon_closures.push((rule.id, pair.left, pair.right));
        let loop_state = self.synthetic_state(
            AtnStateKind::PlusLoopBack,
            SyntheticReason::LoopBoundary,
            owner,
        );
        self.graph.state_mut(loop_state).non_greedy = !greedy;
        self.graph.add_decision(loop_state);
        let end = self.synthetic_state(AtnStateKind::LoopEnd, SyntheticReason::LoopBoundary, owner);
        self.graph.state_mut(pair.left).loop_back_state = Some(loop_state);
        self.graph.state_mut(end).loop_back_st

(report preview truncated from 124640 bytes)

@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown

Claude finished @tinovyatkin's task in 22m 11s —— View job


Code review: SPDX license headers

  • Gather context (diff, crate license metadata, file modes)
  • Verify per-file SPDX vs. each crate's license field
  • Check fingerprinted/hashed directories for stale snapshots
  • Check exec-bit and shebang handling
  • Verify generated files were left untouched
  • cargo test --locked --workspace --all-featurespasses
  • Post consolidated review

No merge blockers. All three Codex findings are resolved by ac76fafd, and I confirmed that empirically rather than by reading the commit message. Four non-blocking items below, one of which I'd fix before merge.


Verification of the prior review round

Codex finding Status Evidence
BSD-3-Clause header conflicts with toml-parser's Apache-2.0 metadata ✅ fixed All 4 files now read Apache-2.0, matching crates/antlr-rust-toml-parser/Cargo.toml:8 and its packaged LICENSE/NOTICE
Stale rust-support fingerprint snapshot ✅ fixed git diff origin/main...HEAD -- .../rust-support/ is empty (byte-identical), snapshot still pins sha256:38f5f9c3, and noninteractive_bundle_requires_its_exact_fingerprint passes
test_run.py made executable without a shebang ✅ fixed Back to 100644; the entire diff has zero mode changes vs origin/main

Test evidence:

  • cargo test --locked --workspace --all-features ran through to the final doc-test target (cargo aborts at the first failing target, so reaching the end means nothing failed).
  • antlr4_rust_gen_cli suite — the one owning untrusted_rust_support_diagnostic95 passed; 0 failed.

Mechanical correctness (verified file-by-file, not spot-checked)

  • No */generated/*, *.inc.rs, third_party/, or @generated-marked file touched.
  • All 18 shebang scripts insert the header after #!. The one shebang-less .py (tools/parse-bench/test_run.py) correctly starts with the comment.
  • Placement is syntactically safe everywhere: 22 files now have //! inner docs and 16 have #![…] inner attributes at line 3. Both are legal after leading comments.
  • The heavy include!-splicing in semantics/mod.rs, parser/mod.rs, lexer/mod.rs is unaffected — comments splice harmlessly.
  • crates/antlr-rust-runtime/src/generated.rs:2460 does include_str!("generated.rs") on itself, but driver_macro_body() locates the region with .find(…) offsets, so the 2-line prefix doesn't perturb parser_driver_entry_ordering_invariants. Confirmed passing.
  • Coverage is complete: exactly one tracked non-generated .rs lacks a header (c_parser_base.rs, deliberately) plus the two fixture transformGrammar.py.

Findings

1. The invariant has no enforcement and its exclusions live only in a commit message · P1

grep -rniE 'spdx|reuse lint|licensure|addlicense' .github/ tools/ finds only the headers themselves. There is no CI check, no .pre-commit-config.yaml, no reuse/licensure config, and the sweep script isn't checked in. So the two hard-won exclusion rules exist nowhere durable:

  1. */generated/* and *.inc.rs (overwritten on regen)
  2. crates/antlr-rust-codegen/tests/fixtures/antlr4-rust-gen/rust-support/** (sha256-fingerprinted — headers there break noninteractive_bundle_requires_its_exact_fingerprint)

Neither CLAUDE.md nor AGENTS.md mentions rule 2. The concrete failure mode is the one this PR just spent a commit fixing: the next contributor — or the next agent-driven header sweep — re-adds headers to the fixture tree and re-breaks the fingerprint test, with nothing upstream of CI to catch it. Worth a tools/lint/check-license-headers.sh encoding both exclusions, wired into CI, plus a short section in CLAUDE.md and AGENTS.md (the repo's own rule says keep them in sync).

Fix this →

2. Grammars-v4-derived *Base ports are treated inconsistently with the reverted fixture tree · P2

ac76fafd stripped headers from the fixture tree on the stated grounds that a sole-copyright notice "misstates their provenance". But the same class of file kept a sole-copyright BSD-3-Clause header:

  • tests/javascript-parity/dumper/src/javascript_lexer_base.rs:1, javascript_parser_base.rs:1
  • tests/typescript-parity/dumper/src/typescript_lexer_base.rs:1, typescript_parser_base.rs:1
  • tools/parse-bench/rust-support/csharp_lexer_base.rs:1

tests/typescript-parity/README.md:9 describes these as standing in for "grammar-specific Java base classes shipped by grammars-v4", whose upstream originals carry their own copyrights.

I don't think this is a licensing defect — README.md:519 states the project is "written from scratch as a clean-room implementation", which supports keeping the headers. The problem is that nothing in these five files records that, so the next reader can't distinguish them from the fixture tree that was just stripped. A one-line // Clean-room reimplementation of grammars-v4 <X>; no upstream code copied. above each header would close the gap permanently.

Related nit on the commit message: it says the reverted fixture files "each document [their] upstream commit and blob", but only the two transformGrammar.py files do — c_parser_base.rs has no provenance note at all. The operative reason that tree must stay header-free is the sha256 fingerprint, which is worth stating plainly since it's the part that's load-bearing for CI.

3. Copyright holder disagrees with LICENSE · P2

LICENSE:3 reads Copyright (c) 2026, Ophidiarium contributors, while all 186 new headers read Copyright (c) 2026 Konstantin Vyatkin. An SPDX/REUSE scanner will report two different holders for the same work. Not wrong if you're the sole copyright owner, but the two should agree — either align the headers to the LICENSE wording or add the individual holder to LICENSE.

4. PR body overstates the scope · P3

"every hand-written .rs, .py, and .sh source file" is accurate; the summary line "all hand-written source files" is not. Still uncovered: tests/antlr-rust-runtime-testsuite/java/RenderGrammar.java, tests/antlr-rust-runtime-testsuite/templates/Rust.test.stg, tests/typescript-parity/TypeScriptParityDumper.java, tools/grammar-frontend/oracle/GenerateUnicodeData.java.

Fine to defer, but note tool/src/org/antlr/v4/codegen/target/RustTarget.java and tool/resources/org/antlr/v4/tool/templates/codegen/Rust/Rust.stg are upstream-ANTLR-tool-shaped files that would want ANTLR's header rather than this one — another reason the exclusion list in finding 1 should be explicit rather than inferred by extension.


Since the squash body becomes permanent history here, item 2's correction to the ac76fafd rationale is worth folding into the final commit message.
· branch chore/spdx-license-headers

@codspeed-hq

codspeed-hq Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing chore/spdx-license-headers (ac76faf) with main (9d2a8c1)

Open in CodSpeed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c104b0f49b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1,3 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the TOML parser files under Apache-2.0

When antlr-rust-toml-parser is published or scanned for license compliance, this per-file BSD-3-Clause identifier conflicts with the crate's license = "Apache-2.0" metadata and its packaged Apache LICENSE/NOTICE. The same incorrect header was added to ast.rs, decode.rs, and string.rs; use an Apache-2.0 header for these files, or deliberately update the package metadata and license materials if they are intended to be separately licensed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ac76faflib.rs, ast.rs, decode.rs, and string.rs now carry SPDX-License-Identifier: Apache-2.0, matching the crate metadata and its packaged LICENSE/NOTICE.

Took the first option (match the declared license) rather than re-licensing the package: the crate is Apache-2.0 precisely because its recognizers derive from the ASF-licensed grammars-v4 TOML grammar, and include ships /LICENSE + /NOTICE alongside /src/**, so the per-file identifier has to agree with them.

Verified this is the only crate in the workspace that diverges — every other member takes license.workspace = true (BSD-3-Clause).

@@ -1,3 +1,5 @@
# SPDX-License-Identifier: BSD-3-Clause

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refresh the Rust-support fingerprint snapshot

When the noninteractive_bundle_requires_its_exact_fingerprint integration test runs, this comment changes the Java support bundle fingerprint because fingerprint_directory hashes every file byte, from the snapshotted sha256:38f5... to sha256:6909.... The corresponding untrusted_rust_support_diagnostic snapshot was not updated, so the workspace test suite will fail until the new diagnostic is reviewed and accepted.

AGENTS.md reference: AGENTS.md:L251-L253

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ac76faf — but by dropping the headers rather than re-recording the fingerprint.

The snapshot is doing its job here: this tree is a pinned byte-level reproduction of grammars-v4 (each file documents its upstream commit and blob, e.g. Source blob: 9547fea6...), so accepting a new fingerprint would have silently normalized exactly the drift it exists to catch. A sole-copyright notice also misstates the provenance of files that are deliberately not ours. The whole rust-support/ tree is now reverted to its base content and mode.

rust_support::* is green locally (6/6), and fingerprint_directory has no other call site over a checked-in tree.

@@ -1,3 +1,5 @@
# SPDX-License-Identifier: BSD-3-Clause

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the executable bit or add a Python shebang

When this newly executable test module is invoked as ./tools/parse-bench/test_run.py, it still begins with a comment rather than a shebang, so execve cannot select Python and shells may instead try to interpret the Python source as shell commands. This file was 100644 before the commit; either retain that mode or add a valid Python shebang if direct execution is intended.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ac76faf — restored to 100644.

Correct diagnosis: the pre-commit hook stripped the executable bit from every script it rewrote, and my restore pass ran a blanket chmod +x instead of replaying the original modes. test_run.py is an importable pytest module, so shebang-less and non-executable is right.

Two other files were caught by the same over-correction and are also back to 100644: tests/kotlin-parity/dump_python.py and tools/fixed-lookahead-bench/bench.py. Every mode in the PR is now diffed against the base commit and matches.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Three corrections from review of the initial header sweep:

- Drop headers from the rust-support fixture tree. Those files are pinned
  byte-level reproductions of grammars-v4 (each documents its upstream commit
  and blob), so a sole-copyright notice misstates their provenance. The
  bundle is sha256-fingerprinted precisely to catch content drift, and the
  headers broke noninteractive_bundle_requires_its_exact_fingerprint.

- antlr-rust-toml-parser publishes as Apache-2.0 (its recognizers derive from
  the ASF-licensed grammars-v4 TOML grammar), so its sources carry an
  Apache-2.0 identifier. A BSD-3-Clause header there would contradict the
  crate's own metadata and packaged LICENSE/NOTICE.

- Restore 100644 on dump_python.py, bench.py, and test_run.py. Re-adding the
  executable bit stripped by the pre-commit hook over-corrected these three,
  which were never executable; test_run.py in particular is an importable
  pytest module with no shebang.
@github-actions

Copy link
Copy Markdown

📊 Source Code Metrics (this PR vs main)

File Cyclomatic Cognitive Functions LLOC MI
crates/antlr-rust-runtime/src/recognizer.rs 53 ⚪ 2 ⚪ 39 ⚪ 77 ⚪ 6.83 (main: 6.87) 🔴
crates/antlr-rust-runtime/src/char_stream.rs 82 ⚪ 29 ⚪ 37 ⚪ 117 ⚪ 2.69 (main: 2.73) 🔴
crates/antlr-rust-runtime/src/atn/mod.rs 55 ⚪ 13 ⚪ 34 ⚪ 57 ⚪ 7.23 (main: 7.28) 🔴
crates/antlr-rust-codegen/src/grammar/escape_sequence.rs 52 ⚪ 16 ⚪ 25 ⚪ 71 ⚪ 12.70 (main: 12.76) 🔴
crates/antlr-rust-codegen/src/builder.rs 46 ⚪ 10 ⚪ 24 ⚪ 55 ⚪ 13.52 (main: 13.59) 🔴
crates/antlr-rust-codegen/src/grammar/char_support.rs 102 ⚪ 59 ⚪ 23 ⚪ 165 ⚪ 0.07 (main: 0.12) 🔴
crates/antlr-rust-runtime/src/byte_stream.rs 44 ⚪ 15 ⚪ 22 ⚪ 98 ⚪ 11.24 (main: 11.30) 🔴
crates/antlr-rust-codegen/src/testrig_cli.rs 75 ⚪ 34 ⚪ 21 ⚪ 88 ⚪ 2.37 (main: 2.42) 🔴
crates/antlr-rust-runtime/src/validated.rs 30 ⚪ 2 ⚪ 20 ⚪ 31 ⚪ 14.48 (main: 14.54) 🔴
crates/antlr-rust-codegen/src/grammar/compiler.rs 62 ⚪ 22 ⚪ 19 ⚪ 86 ⚪ 5.30 (main: 5.34) 🔴
crates/antlr-rust-codegen/src/embedded/antlr4rust/aliases.rs 109 ⚪ 65 ⚪ 18 ⚪ 110 ⚪ 0.79 (main: 0.85) 🔴
crates/antlr-rust-codegen/src/error.rs 26 ⚪ 2 ⚪ 16 ⚪ 20 ⚪ 22.86 (main: 22.97) 🔴
crates/antlr-rust-codegen/src/grammar/atn/build.rs 55 ⚪ 9 ⚪ 16 ⚪ 59 ⚪ 5.08 (main: 5.12) 🔴
crates/antlr-rust-codegen/src/parser/surface/names.rs 65 ⚪ 40 ⚪ 16 ⚪ 108 ⚪ 5.04 (main: 5.08) 🔴
crates/antlr-rust-codegen/src/test_rig.rs 43 ⚪ 14 ⚪ 16 ⚪ 61 ⚪ 3.78 (main: 3.80) 🔴
crates/antlr-rust-rs-parser/src/cfg_syntax.rs 111 ⚪ 84 ⚪ 16 ⚪ 150 ⚪ 0 (main: 0.02) 🔴
crates/antlr-rust-codegen/src/grammar/action.rs 48 ⚪ 29 ⚪ 15 ⚪ 78 ⚪ 11.23 (main: 11.29) 🔴
crates/antlr-rust-runtime/src/atn/ascii_range.rs 61 ⚪ 58 ⚪ 15 ⚪ 116 ⚪ 9.36 (main: 9.42) 🔴
crates/antlr-rust-codegen/src/grammar/model.rs 20 ⚪ 0 ⚪ 14 ⚪ 34 ⚪ 4.95 (main: 4.98) 🔴
crates/antlr-rust-codegen/src/optimization/config.rs 42 ⚪ 33 ⚪ 14 ⚪ 62 ⚪ 13.34 (main: 13.40) 🔴
crates/antlr-rust-codegen/src/pipeline.rs 31 ⚪ 13 ⚪ 14 ⚪ 48 ⚪ 13.13 (main: 13.20) 🔴
crates/antlr-rust-codegen/src/semantics/model.rs 62 ⚪ 10 ⚪ 14 ⚪ 14 ⚪ 7.41 (main: 7.46) 🔴
crates/antlr-rust-codegen/src/cli_report.rs 43 ⚪ 8 ⚪ 13 ⚪ 40 ⚪ 18.28 (main: 18.39) 🔴
crates/antlr-rust-codegen/src/grammar/source.rs 25 ⚪ 5 ⚪ 13 ⚪ 37 ⚪ 24.94 (main: 25.09) 🔴
crates/antlr-rust-g4-parser/src/lexer_adaptor.rs 39 ⚪ 24 ⚪ 13 ⚪ 43 ⚪ 17.84 (main: 17.93) 🔴
crates/antlr-rust-toml-parser/src/ast.rs 18 ⚪ 0 ⚪ 13 ⚪ 15 ⚪ 29.93 (main: 30.11) 🔴
crates/antlr-rust-toml-parser/src/lib.rs 15 ⚪ 1 ⚪ 13 ⚪ 33 ⚪ 24.14 (main: 24.23) 🔴
crates/antlr-rust-codegen/src/embedded/model.rs 44 ⚪ 12 ⚪ 12 ⚪ 31 ⚪ 9.77 (main: 9.82) 🔴
crates/antlr-rust-codegen/src/grammar/provenance.rs 27 ⚪ 12 ⚪ 12 ⚪ 26 ⚪ 20.02 (main: 20.12) 🔴
crates/antlr-rust-codegen/src/grammar/transform/artifact.rs 26 ⚪ 2 ⚪ 12 ⚪ 14 ⚪ 14.05 (main: 14.11) 🔴
crates/antlr-rust-codegen/src/semantics/template_syntax.rs 68 ⚪ 48 ⚪ 12 ⚪ 91 ⚪ 13.03 (main: 13.11) 🔴
crates/antlr-rust-codegen/src/semantics/templates.rs 57 ⚪ 35 ⚪ 12 ⚪ 92 ⚪ 10.37 (main: 10.43) 🔴
crates/antlr-rust-codegen/src/grammar/atn/optimize.rs 57 ⚪ 44 ⚪ 11 ⚪ 79 ⚪ 12.49 (main: 12.57) 🔴
crates/antlr-rust-codegen/src/grammar/diagnostic.rs 21 ⚪ 4 ⚪ 11 ⚪ 22 ⚪ 22.85 (main: 22.96) 🔴
crates/antlr-rust-codegen/src/rust_output.rs 45 ⚪ 37 ⚪ 11 ⚪ 68 ⚪ 17.10 (main: 17.19) 🔴
crates/antlr-rust-runtime/src/vocabulary.rs 34 ⚪ 3 ⚪ 11 ⚪ 38 ⚪ 18.92 (main: 19.01) 🔴
crates/antlr-rust-codegen/src/artifact.rs 33 ⚪ 18 ⚪ 10 ⚪ 35 ⚪ 23.17 (main: 23.31) 🔴
crates/antlr-rust-codegen/src/grammar/atn/general_bug_test.rs 33 ⚪ 21 ⚪ 10 ⚪ 52 ⚪ 19.00 (main: 19.10) 🔴
crates/antlr-rust-codegen/src/grammar/unicode_escape.rs 16 ⚪ 1 ⚪ 10 ⚪ 16 ⚪ 32.51 (main: 32.69) 🔴
crates/antlr-rust-codegen/src/parser/routing.rs 37 ⚪ 25 ⚪ 10 ⚪ 94 ⚪ 8.56 (main: 8.60) 🔴
crates/antlr-rust-codegen/src/embedded/antlr4rust/macros.rs 97 ⚪ 99 ⚪ 9 ⚪ 142 ⚪ 3.50 (main: 3.56) 🔴
crates/antlr-rust-codegen/src/grammar/transform/analysis.rs 52 ⚪ 38 ⚪ 9 ⚪ 67 ⚪ 14.71 (main: 14.80) 🔴
crates/antlr-rust-codegen/src/grammar/transform/passes/prune_unreachable.rs 28 ⚪ 22 ⚪ 9 ⚪ 39 ⚪ 24.43 (main: 24.57) 🔴
crates/antlr-rust-codegen/src/lexer/render_model.rs 20 ⚪ 1 ⚪ 9 ⚪ 22 ⚪ 25.36 (main: 25.50) 🔴
crates/antlr-rust-codegen/src/rust_support/stage.rs 51 ⚪ 39 ⚪ 9 ⚪ 61 ⚪ 17.82 (main: 17.93) 🔴
crates/antlr-rust-codegen/src/semantics/semir.rs 64 ⚪ 18 ⚪ 9 ⚪ 71 ⚪ 8.65 (main: 8.70) 🔴
crates/antlr-rust-toml-parser/src/string.rs 70 ⚪ 42 ⚪ 9 ⚪ 54 ⚪ 15.76 (main: 15.88) 🔴
crates/antlr-rust-codegen/src/grammar/rule_reachability.rs 64 ⚪ 35 ⚪ 8 ⚪ 61 ⚪ 12.67 (main: 12.75) 🔴
crates/antlr-rust-codegen/src/grammar/unicode_icu_tests.rs 27 ⚪ 18 ⚪ 8 ⚪ 52 ⚪ 23.69 (main: 23.82) 🔴
crates/antlr-rust-codegen/src/structural/mod.rs 69 ⚪ 39 ⚪ 8 ⚪ 86 ⚪ 6.41 (main: 6.47) 🔴
crates/antlr-rust-codegen/src/grammar/transform/registry.rs 16 ⚪ 6 ⚪ 7 ⚪ 22 ⚪ 27.80 (main: 27.97) 🔴
crates/antlr-rust-codegen/src/semantics/manifest.rs 21 ⚪ 4 ⚪ 6 ⚪ 13 ⚪ 18.55 (main: 18.64) 🔴
crates/antlr-rust-codegen/src/cli.rs 23 ⚪ 13 ⚪ 5 ⚪ 27 ⚪ 17.12 (main: 17.20) 🔴
crates/antlr-rust-codegen/src/parser/render/rules.rs 45 ⚪ 52 ⚪ 5 ⚪ 113 ⚪ 7.79 (main: 7.83) 🔴
crates/antlr-rust-codegen/src/parser/surface/model.rs 7 ⚪ 0 ⚪ 5 ⚪ 5 ⚪ 36.56 (main: 36.84) 🔴
crates/antlr-rust-codegen/src/rust_support/prompt.rs 26 ⚪ 18 ⚪ 5 ⚪ 31 ⚪ 27.62 (main: 27.80) 🔴
crates/antlr-rust-codegen/src/parser/render/loops.rs 33 ⚪ 22 ⚪ 4 ⚪ 120 ⚪ 10.04 (main: 10.09) 🔴
crates/antlr-rust-codegen/src/parser/surface/contexts.rs 25 ⚪ 22 ⚪ 4 ⚪ 69 ⚪ 16.57 (main: 16.63) 🔴
crates/antlr-rust-codegen/src/grammar/validation.rs 44 ⚪ 50 ⚪ 3 ⚪ 41 ⚪ 20.95 (main: 21.08) 🔴
crates/antlr-rust-codegen/src/optimization/report.rs 18 ⚪ 1 ⚪ 3 ⚪ 12 ⚪ 33.83 (main: 34.10) 🔴
crates/antlr-rust-codegen/src/parser/ir/optimize.rs 11 ⚪ 5 ⚪ 3 ⚪ 15 ⚪ 39.52 (main: 39.92) 🔴
crates/antlr-rust-codegen/src/parser/render/mod.rs 43 ⚪ 41 ⚪ 3 ⚪ 90 ⚪ 9.33 (main: 9.38) 🔴
crates/antlr-rust-codegen/src/parser/surface/traversal.rs 6 ⚪ 2 ⚪ 3 ⚪ 36 ⚪ 17.73 (main: 17.77) 🔴
crates/antlr-rust-runtime/src/int_stream.rs 5 ⚪ 0 ⚪ 3 ⚪ 2 ⚪ 52.45 (main: 53.51) 🔴
crates/antlr-rust-codegen/src/embedded/antlr4rust/mod.rs 66 ⚪ 62 ⚪ 2 ⚪ 93 ⚪ 6.18 (main: 6.23) 🔴
crates/antlr-rust-codegen/src/lib.rs 3 ⚪ 0 ⚪ 2 ⚪ 4 ⚪ 38.06 (main: 38.36) 🔴
crates/antlr-rust-codegen/src/optimization/metrics.rs 8 ⚪ 9 ⚪ 2 ⚪ 12 ⚪ 44.40 (main: 44.97) 🔴
crates/antlr-rust-runtime/src/errors.rs 6 ⚪ 0 ⚪ 2 ⚪ 2 ⚪ 32.85 (main: 33.06) 🔴
crates/antlr-rust-codegen/src/bin/antlr4-rust-gen.rs 2 ⚪ 0 ⚪ 1 ⚪ 1 ⚪ 72.37 (main: 77.21) 🔴
crates/antlr-rust-codegen/src/bin/antlr4-rust-testrig.rs 2 ⚪ 0 ⚪ 1 ⚪ 1 ⚪ 71.39 (main: 76.23) 🔴
crates/antlr-rust-codegen/src/json.rs 2 ⚪ 0 ⚪ 1 ⚪ 3 ⚪ 61.57 (main: 63.69) 🔴
crates/antlr-rust-codegen/src/parser/render/fallback.rs 7 ⚪ 6 ⚪ 1 ⚪ 9 ⚪ 40.45 (main: 40.80) 🔴
crates/antlr-rust-codegen/src/parser/surface/facade.rs 6 ⚪ 1 ⚪ 1 ⚪ 11 ⚪ 38.53 (main: 38.82) 🔴
crates/antlr-rust-codegen/src/rust_support/python.rs 11 ⚪ 7 ⚪ 1 ⚪ 27 ⚪ 36.26 (main: 36.57) 🔴
crates/antlr-rust-runtime/src/lib.rs 4 ⚪ 3 ⚪ 1 ⚪ 7 ⚪ 29.52 (main: 29.69) 🔴
crates/antlr-rust-codegen/src/config.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 41.91 (main: 42.33) 🔴
crates/antlr-rust-codegen/src/grammar/atn/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 57.13 (main: 58.48) 🔴
crates/antlr-rust-codegen/src/grammar/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 43.03 (main: 43.49) 🔴
crates/antlr-rust-codegen/src/grammar/transform/mod.rs 2 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 27.49 (main: 27.64) 🔴
crates/antlr-rust-codegen/src/lexer/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 54.55 (main: 55.66) 🔴
crates/antlr-rust-codegen/src/optimization/descriptor.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 43.87 (main: 44.38) 🔴
crates/antlr-rust-codegen/src/optimization/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 66.75 (main: 69.47) 🔴
crates/antlr-rust-codegen/src/parser/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 40.54 (main: 40.87) 🔴
crates/antlr-rust-codegen/src/semantics/mod.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 50.14 (main: 50.84) 🔴
crates/antlr-rust-g4-parser/src/lib.rs 1 ⚪ 0 ⚪ 0 ⚪ 0 ⚪ 51.75 (main: 52.57) 🔴

Generated by mehen v1.8.1 — the code quality watcher.

@tinovyatkin
tinovyatkin merged commit c732098 into main Aug 12, 2026
20 checks passed
@tinovyatkin
tinovyatkin deleted the chore/spdx-license-headers branch August 12, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant