diff --git a/.bca-baseline.toml b/.bca-baseline.toml index c3637bca..ac054009 100644 --- a/.bca-baseline.toml +++ b/.bca-baseline.toml @@ -728,14 +728,14 @@ value = 16.0 [[entry]] path = "src/metrics/cognitive.rs" qualified = "tcl_switch_decision_arms" -start_line = 447 +start_line = 448 metric = "nargs" value = 7.0 [[entry]] path = "src/metrics/cognitive.rs" qualified = "tcl_switch_decision_arms" -start_line = 447 +start_line = 448 metric = "nexits" value = 6.0 @@ -968,7 +968,7 @@ path = "src/tools.rs" qualified = "" start_line = 1 metric = "loc.sloc" -value = 761.0 +value = 767.0 [[entry]] path = "src/tools.rs" diff --git a/Cargo.lock b/Cargo.lock index a2f1eadb..027665b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -364,7 +364,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bca-tree-sitter-ccomment" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -372,7 +372,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozcpp" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-cpp", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozjs" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-javascript", @@ -390,7 +390,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-preproc" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -398,7 +398,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-tcl" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -406,7 +406,7 @@ dependencies = [ [[package]] name = "big-code-analysis" -version = "2.0.0" +version = "2.0.1" dependencies = [ "aho-corasick", "bca-tree-sitter-ccomment", @@ -462,7 +462,7 @@ dependencies = [ [[package]] name = "big-code-analysis-cli" -version = "2.0.0" +version = "2.0.1" dependencies = [ "assert_cmd", "big-code-analysis", @@ -484,7 +484,7 @@ dependencies = [ [[package]] name = "big-code-analysis-py" -version = "2.0.0" +version = "2.0.1" dependencies = [ "big-code-analysis", "globset", @@ -498,7 +498,7 @@ dependencies = [ [[package]] name = "big-code-analysis-web" -version = "2.0.0" +version = "2.0.1" dependencies = [ "actix-rt", "actix-web", @@ -5082,7 +5082,7 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "xtask" -version = "2.0.0" +version = "2.0.1" dependencies = [ "big-code-analysis-cli", "big-code-analysis-web", diff --git a/Cargo.toml b/Cargo.toml index cdcd4dc7..0aef9378 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [ ] [workspace.package] -version = "2.0.0" +version = "2.0.1" edition = "2024" rust-version = "1.94" license = "MPL-2.0" @@ -76,11 +76,11 @@ tree-sitter-c = "=0.24.2" # Rationale and the rejected alternatives live on the umbrella # issue (dekobon/big-code-analysis#149); the operational details # (publish order, first-bootstrap workflow) live in `RELEASING.md`. -tree-sitter-ccomment = { package = "bca-tree-sitter-ccomment", path = "./tree-sitter-ccomment", version = "=2.0.0" } -tree-sitter-mozcpp = { package = "bca-tree-sitter-mozcpp", path = "./tree-sitter-mozcpp", version = "=2.0.0" } -tree-sitter-mozjs = { package = "bca-tree-sitter-mozjs", path = "./tree-sitter-mozjs", version = "=2.0.0" } -tree-sitter-preproc = { package = "bca-tree-sitter-preproc", path = "./tree-sitter-preproc", version = "=2.0.0" } -tree-sitter-tcl = { package = "bca-tree-sitter-tcl", path = "./tree-sitter-tcl", version = "=2.0.0" } +tree-sitter-ccomment = { package = "bca-tree-sitter-ccomment", path = "./tree-sitter-ccomment", version = "=2.0.1" } +tree-sitter-mozcpp = { package = "bca-tree-sitter-mozcpp", path = "./tree-sitter-mozcpp", version = "=2.0.1" } +tree-sitter-mozjs = { package = "bca-tree-sitter-mozjs", path = "./tree-sitter-mozjs", version = "=2.0.1" } +tree-sitter-preproc = { package = "bca-tree-sitter-preproc", path = "./tree-sitter-preproc", version = "=2.0.1" } +tree-sitter-tcl = { package = "bca-tree-sitter-tcl", path = "./tree-sitter-tcl", version = "=2.0.1" } # PyO3 powers the `big-code-analysis-py` cdylib. Pinned to a major # series; bumping is a deliberate, separate change because the Bound diff --git a/README.md b/README.md index 7d366a8c..6975c1b9 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Every tree-sitter grammar is gated behind a per-language Cargo feature. The default feature set is `all-languages`, so a bare ```toml -big-code-analysis = "2.0.0" +big-code-analysis = "2.0.1" ``` pulls every grammar in (matching the library's historical behaviour @@ -100,7 +100,7 @@ only need a subset of languages can opt out of the defaults and re-enable just the grammars they want: ```toml -big-code-analysis = { version = "2.0.0", default-features = false, features = ["rust", "typescript"] } +big-code-analysis = { version = "2.0.1", default-features = false, features = ["rust", "typescript"] } ``` Supported language features: `bash`, `c`, `cpp`, `csharp`, `elixir`, diff --git a/STABILITY.md b/STABILITY.md index ee7ba8c9..6f3daf0f 100644 --- a/STABILITY.md +++ b/STABILITY.md @@ -1,6 +1,6 @@ # Stability policy -`big-code-analysis` is on the `2.x` line (currently `2.0.0`). The +`big-code-analysis` is on the `2.x` line (currently `2.0.1`). The crate graduated from the rapid-iteration `0.x` window into a written stability contract and has now made its first major (`2.0`) break: callers can pin a minor version and expect a working tree against any @@ -31,7 +31,7 @@ pin bump (any `tree-sitter-*` crate in the root `Cargo.toml`) or a regression fix in a metric definition is by definition a value change but does not break the API surface. Callers who need bit-for-bit reproducibility should pin to an exact version -(`big-code-analysis = "= 2.0.0"`) and store that version alongside +(`big-code-analysis = "= 2.0.1"`) and store that version alongside their results — see [What is stable in value](#what-is-stable-in-value) below. @@ -399,7 +399,7 @@ versions, even within `2.x`.** Concretely: This is the one deliberate carve-out in the `2.x` contract. The *shape* of the data is stable; the *numbers in the cells* are not. If you need to compare metric runs across time, pin to an exact -version (`big-code-analysis = "= 2.0.0"`) and store the version +version (`big-code-analysis = "= 2.0.1"`) and store the version alongside the results. ### Float precision and non-finite values diff --git a/big-code-analysis-book/src/library/cargo-features.md b/big-code-analysis-book/src/library/cargo-features.md index bdec10b4..19c4f084 100644 --- a/big-code-analysis-book/src/library/cargo-features.md +++ b/big-code-analysis-book/src/library/cargo-features.md @@ -6,7 +6,7 @@ the default ```toml [dependencies] -big-code-analysis = "2.0.0" +big-code-analysis = "2.0.1" ``` pulls every grammar in — matching the library's historical @@ -24,7 +24,7 @@ A downstream service that only analyses Rust and TypeScript: ```toml [dependencies] -big-code-analysis = { version = "2.0.0", default-features = false, features = ["rust", "typescript"] } +big-code-analysis = { version = "2.0.1", default-features = false, features = ["rust", "typescript"] } ``` The library still compiles, the `LANG` enum still has every diff --git a/big-code-analysis-book/src/library/quick-start.md b/big-code-analysis-book/src/library/quick-start.md index c21da70f..5ca00d05 100644 --- a/big-code-analysis-book/src/library/quick-start.md +++ b/big-code-analysis-book/src/library/quick-start.md @@ -8,7 +8,7 @@ metrics from a string of source code. ```toml # Cargo.toml [dependencies] -big-code-analysis = "2.0.0" +big-code-analysis = "2.0.1" ``` The crate uses Rust edition 2024 and pins `rust-version = "1.94"`. diff --git a/big-code-analysis-book/src/library/stability.md b/big-code-analysis-book/src/library/stability.md index b52890a4..6ff48961 100644 --- a/big-code-analysis-book/src/library/stability.md +++ b/big-code-analysis-book/src/library/stability.md @@ -1,6 +1,6 @@ # Stability and versioning -`big-code-analysis` is on the `2.x` line (currently `2.0.0`). The +`big-code-analysis` is on the `2.x` line (currently `2.0.1`). The full stability contract lives in [`STABILITY.md`][stability] at the root of the repository — that file is the source of truth and is updated alongside the changelog at every release. @@ -22,7 +22,7 @@ The headlines for library consumers: bump or a bug fix in a metric definition can shift any metric value on any file in any direction, even across a patch bump. Each such drift is flagged in the changelog. Pin to an exact - version (`big-code-analysis = "= 2.0.0"`) if you need bit-for-bit + version (`big-code-analysis = "= 2.0.1"`) if you need bit-for-bit reproducibility across runs. - **MSRV is `1.94`.** Bumping the MSRV is treated as a minor-bump event and is flagged in the changelog under **(breaking)** — diff --git a/big-code-analysis-book/src/recipes/ci.md b/big-code-analysis-book/src/recipes/ci.md index a5b66057..5b994d84 100644 --- a/big-code-analysis-book/src/recipes/ci.md +++ b/big-code-analysis-book/src/recipes/ci.md @@ -103,7 +103,7 @@ so a green-path rerun skips the download entirely: ```yaml env: - BCA_VERSION: "2.0.0" + BCA_VERSION: "2.0.1" BCA_TARGET: "x86_64-unknown-linux-gnu" # sha256 of big-code-analysis-${BCA_VERSION}-${BCA_TARGET}.tar.gz from the # release's SHA256SUMS file. Bump together with BCA_VERSION. @@ -176,7 +176,7 @@ default for downstream adopters. - name: Install bca uses: taiki-e/install-action@v2 with: - tool: big-code-analysis-cli@2.0.0 + tool: big-code-analysis-cli@2.0.1 ``` ```yaml @@ -184,7 +184,7 @@ default for downstream adopters. - name: Install cargo-binstall uses: cargo-bins/cargo-binstall@main - name: Install bca - run: cargo binstall --no-confirm big-code-analysis-cli --version 2.0.0 + run: cargo binstall --no-confirm big-code-analysis-cli --version 2.0.1 ``` If either action falls back to compilation, cache the cargo registry + @@ -233,7 +233,7 @@ jobs: - name: Install bca uses: taiki-e/install-action@v2 with: - tool: big-code-analysis-cli@2.0.0 + tool: big-code-analysis-cli@2.0.1 - name: Generate report run: | bca \ @@ -662,7 +662,7 @@ stages: - quality variables: - BCA_VERSION: "2.0.0" # pin a published big-code-analysis-cli release + BCA_VERSION: "2.0.1" # pin a published big-code-analysis-cli release BCA_TARGET: "x86_64-unknown-linux-gnu" # sha256 of big-code-analysis-${BCA_VERSION}-${BCA_TARGET}.tar.gz from # the release's SHA256SUMS file. Bump together with BCA_VERSION. diff --git a/big-code-analysis-cli/Cargo.toml b/big-code-analysis-cli/Cargo.toml index a0be3648..79fc8e1e 100644 --- a/big-code-analysis-cli/Cargo.toml +++ b/big-code-analysis-cli/Cargo.toml @@ -20,7 +20,7 @@ ignore = "^0.4" # The CLI exposes every language `bca` advertises, so it pins the # library's full feature set explicitly. Disabling default features # here would silently drop grammars from the CLI build. See #252. -big-code-analysis = { path = "..", version = "=2.0.0", default-features = false, features = [ +big-code-analysis = { path = "..", version = "=2.0.1", default-features = false, features = [ "all-languages", # End-user binary ships every backend that exists; `vcs` is the # umbrella turning on `vcs-git` (issue #328). diff --git a/big-code-analysis-web/Cargo.toml b/big-code-analysis-web/Cargo.toml index 65c718f7..7ab0d472 100644 --- a/big-code-analysis-web/Cargo.toml +++ b/big-code-analysis-web/Cargo.toml @@ -18,7 +18,7 @@ futures = "^0.3" # pins the library's full feature set explicitly. Disabling default # features here would silently drop grammars from the daemon build. # See #252. -big-code-analysis = { path = "..", version = "=2.0.0", default-features = false, features = [ +big-code-analysis = { path = "..", version = "=2.0.1", default-features = false, features = [ "all-languages", # `POST /vcs` change-history endpoint (issue #328). "vcs", diff --git a/codecov.yml b/codecov.yml index b2acc7b1..e13c365b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -27,6 +27,17 @@ comment: # always runs) and `python` (pytest-cov, only on PRs that touch the # bindings). carryforward keeps the python number from vanishing on # Rust-only PRs, where the python-test job is skipped. +# +# Measurement gap (intentional): the `python` flag measures only the pure- +# Python surface via pytest-cov. The PyO3 *Rust* layer in +# big-code-analysis-py/src/ (lib.rs, vcs.rs, node.rs, ast.rs, sarif.rs) is +# exercised by those same pytest suites, but the crate has no `#[test]`s, so +# `cargo llvm-cov nextest` (which only runs cargo tests) never instruments +# it. Its standalone Rust-line coverage therefore reads far lower than it is +# actually tested — do not "fix" that number by adding redundant cargo tests +# that re-cover behaviour pytest already drives. Closing it for real means +# instrumenting the maturin/pytest run and merging that profdata, tracked +# separately. flag_management: default_rules: carryforward: true diff --git a/enums/Cargo.lock b/enums/Cargo.lock index 8be1b84e..1477db74 100644 --- a/enums/Cargo.lock +++ b/enums/Cargo.lock @@ -125,7 +125,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-ccomment" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozcpp" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-cpp", @@ -142,7 +142,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozjs" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-javascript", @@ -151,7 +151,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-preproc" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -159,7 +159,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-tcl" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter-language", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "enums" -version = "2.0.0" +version = "2.0.1" dependencies = [ "askama", "bca-tree-sitter-ccomment", diff --git a/enums/Cargo.toml b/enums/Cargo.toml index 1e7a3f9d..dfabfe18 100644 --- a/enums/Cargo.toml +++ b/enums/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "enums" -version = "2.0.0" +version = "2.0.1" publish = false authors = ["Calixte Denizet "] edition = "2024" @@ -37,11 +37,11 @@ tree-sitter-c = "=0.24.2" # Vendored grammar forks — see ../Cargo.toml for rationale on the # `package = ...` alias trick that preserves the `tree_sitter_` # import path while the published crate name is `bca-tree-sitter-`. -tree-sitter-tcl = { package = "bca-tree-sitter-tcl", path = "../tree-sitter-tcl", version = "=2.0.0" } -tree-sitter-preproc = { package = "bca-tree-sitter-preproc", path = "../tree-sitter-preproc", version = "=2.0.0" } -tree-sitter-ccomment = { package = "bca-tree-sitter-ccomment", path = "../tree-sitter-ccomment", version = "=2.0.0" } -tree-sitter-mozcpp = { package = "bca-tree-sitter-mozcpp", path = "../tree-sitter-mozcpp", version = "=2.0.0" } -tree-sitter-mozjs = { package = "bca-tree-sitter-mozjs", path = "../tree-sitter-mozjs", version = "=2.0.0" } +tree-sitter-tcl = { package = "bca-tree-sitter-tcl", path = "../tree-sitter-tcl", version = "=2.0.1" } +tree-sitter-preproc = { package = "bca-tree-sitter-preproc", path = "../tree-sitter-preproc", version = "=2.0.1" } +tree-sitter-ccomment = { package = "bca-tree-sitter-ccomment", path = "../tree-sitter-ccomment", version = "=2.0.1" } +tree-sitter-mozcpp = { package = "bca-tree-sitter-mozcpp", path = "../tree-sitter-mozcpp", version = "=2.0.1" } +tree-sitter-mozjs = { package = "bca-tree-sitter-mozjs", path = "../tree-sitter-mozjs", version = "=2.0.1" } [profile.release] debug = "line-tables-only" diff --git a/man/bca-check.1 b/man/bca-check.1 index b4386311..05605749 100644 --- a/man/bca-check.1 +++ b/man/bca-check.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-CHECK 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-CHECK 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-check \- Check per\-function metrics against thresholds. Exits 2 when any threshold is exceeded; reserve exit 1 for tool errors so CI can distinguish "metric regression" from "tool crashed". `\-\-strict\-exit\-codes` opts into tiered codes (2\-5) that split the violation case by severity .SH SYNOPSIS diff --git a/man/bca-count.1 b/man/bca-count.1 index e75837d6..928ee249 100644 --- a/man/bca-count.1 +++ b/man/bca-count.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-COUNT 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-COUNT 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-count \- Count nodes of one or more types .SH SYNOPSIS diff --git a/man/bca-diff-baseline.1 b/man/bca-diff-baseline.1 index 705ac140..8f253364 100644 --- a/man/bca-diff-baseline.1 +++ b/man/bca-diff-baseline.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-DIFF-BASELINE 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-DIFF-BASELINE 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-diff\-baseline \- Diff two `.bca\-baseline.toml` files and report what was added, removed, worsened, or improved. Replaces the in\-the\-head TOML diff parsing the book\*(Aqs PR\-review recipe used to walk through. Exits 0 on success by default — the diff is informational, not a gate. With `\-\-exit\-code`, exits 2 when the filtered diff is non\-empty .SH SYNOPSIS diff --git a/man/bca-diff.1 b/man/bca-diff.1 index 359b1bf9..d40f0789 100644 --- a/man/bca-diff.1 +++ b/man/bca-diff.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-DIFF 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-DIFF 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-diff \- Compare two metric\-output runs and report, per metric, which files changed (old to new), plus files added/removed between the two sets. Each side is a per\-file JSON file or a directory tree of them (the form `bca metrics \-O json \-\-output\-dir DIR` writes). Replaces the grammar\-bump glue chain — the external `json\-minimal\-tests` binary plus `split\-minimal\-tests.py` — with one native command. Exits 0 on success by default; the diff is informational, not a gate. With `\-\-exit\-code`, exits 2 when the filtered diff is non\-empty .SH SYNOPSIS diff --git a/man/bca-dump.1 b/man/bca-dump.1 index 504b02f5..abb346de 100644 --- a/man/bca-dump.1 +++ b/man/bca-dump.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-DUMP 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-DUMP 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-dump \- Dump the AST to stdout. Each file\*(Aqs tree is prefixed with a `== ==` banner so a multi\-file dump is attributable. Requires an explicit path — unlike the other walking subcommands, bare `bca dump` errors instead of dumping the whole current directory (a whole\-tree AST dump has no plausible use) .SH SYNOPSIS diff --git a/man/bca-exemptions.1 b/man/bca-exemptions.1 index 0cce204e..407d1772 100644 --- a/man/bca-exemptions.1 +++ b/man/bca-exemptions.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-EXEMPTIONS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-EXEMPTIONS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-exemptions \- Audit everything the `bca check` gate skips in one view: in\-source suppression markers (`bca: suppress`, `#lizard forgives`, …), `[check.exclude]` globs, and `.bca\-baseline.toml` entries. Read\-only; always exits 0 on success .SH SYNOPSIS diff --git a/man/bca-find.1 b/man/bca-find.1 index 78ad907d..c51294b3 100644 --- a/man/bca-find.1 +++ b/man/bca-find.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-FIND 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-FIND 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-find \- Find nodes of one or more types .SH SYNOPSIS diff --git a/man/bca-functions.1 b/man/bca-functions.1 index 1e954511..bee39c15 100644 --- a/man/bca-functions.1 +++ b/man/bca-functions.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-FUNCTIONS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-FUNCTIONS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-functions \- List functions/methods and their spans .SH SYNOPSIS diff --git a/man/bca-init.1 b/man/bca-init.1 index fedecc38..8e123aa6 100644 --- a/man/bca-init.1 +++ b/man/bca-init.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-INIT 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-INIT 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-init \- Scaffold the canonical adoption files (`bca.toml` manifest, `.bcaignore`, `.bca\-baseline.toml`) in the current directory. Replaces the six\-step copy\-paste flow from the book\*(Aqs adoption recipe. Refuses to overwrite existing files without `\-\-force` .SH SYNOPSIS diff --git a/man/bca-list-metrics.1 b/man/bca-list-metrics.1 index fa766c25..00a6a360 100644 --- a/man/bca-list-metrics.1 +++ b/man/bca-list-metrics.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-LIST-METRICS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-LIST-METRICS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-list\-metrics \- List the metrics this tool can compute and exit .SH SYNOPSIS diff --git a/man/bca-metrics.1 b/man/bca-metrics.1 index 7daf1c6d..0eb992d0 100644 --- a/man/bca-metrics.1 +++ b/man/bca-metrics.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-METRICS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-METRICS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-metrics \- Compute per\-file metrics and emit them in a structured format .SH SYNOPSIS diff --git a/man/bca-ops.1 b/man/bca-ops.1 index 2442e126..f373a170 100644 --- a/man/bca-ops.1 +++ b/man/bca-ops.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-OPS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-OPS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-ops \- Extract per\-file operands and operators .SH SYNOPSIS diff --git a/man/bca-preproc.1 b/man/bca-preproc.1 index dea5edcc..fa860464 100644 --- a/man/bca-preproc.1 +++ b/man/bca-preproc.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-PREPROC 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-PREPROC 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-preproc \- Generate preprocessor\-data JSON for C/C++ analysis .SH SYNOPSIS diff --git a/man/bca-report.1 b/man/bca-report.1 index 0fd5b0fc..038c42b7 100644 --- a/man/bca-report.1 +++ b/man/bca-report.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-REPORT 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-REPORT 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-report \- Generate an aggregated report across the analyzed source .SH SYNOPSIS diff --git a/man/bca-strip-comments.1 b/man/bca-strip-comments.1 index 6e3e33f4..94474aef 100644 --- a/man/bca-strip-comments.1 +++ b/man/bca-strip-comments.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-STRIP-COMMENTS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-STRIP-COMMENTS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-strip\-comments \- Remove comments from source files .SH SYNOPSIS diff --git a/man/bca-vcs-commit.1 b/man/bca-vcs-commit.1 index 283e88f0..b76869a0 100644 --- a/man/bca-vcs-commit.1 +++ b/man/bca-vcs-commit.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-VCS-COMMIT 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-VCS-COMMIT 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-vcs\-commit \- Score a single commit for defect\-induction risk — the just\-in\-time (JIT) defect\-prediction unit a CI gate reviews at check\-in. Emits a JSON breakdown of size / diffusion / history / experience / purpose features, their contributions, and an ordinal composite score. Window / `\-\-ref` / bot / merge / rename behaviour comes from the parent `vcs` flags. The old `jit` spelling stays a hidden alias for one release cycle .SH SYNOPSIS diff --git a/man/bca-vcs-trend.1 b/man/bca-vcs-trend.1 index e5516c97..17701c2b 100644 --- a/man/bca-vcs-trend.1 +++ b/man/bca-vcs-trend.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-VCS-TREND 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-VCS-TREND 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-vcs\-trend \- Sample the change\-history metrics at several points in time and emit a per\-file time series, surfacing whether code is improving or degrading over the project\*(Aqs life. Each point re\-anchors at the mainline tip of that moment, so it is a faithful historical snapshot. Window / `\-\-ref` / bot / merge / rename / as\-of (the most\-recent anchor) and `\-\-top` (files kept) come from the parent `vcs` flags .SH SYNOPSIS diff --git a/man/bca-vcs.1 b/man/bca-vcs.1 index 4d527f31..14807e49 100644 --- a/man/bca-vcs.1 +++ b/man/bca-vcs.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-VCS 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-VCS 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-vcs \- Rank files by change\-history (VCS) risk: churn, commit and author counts, ownership dilution, and bug\- / security\-fix history over a git working tree. Errors clearly outside a repo .SH SYNOPSIS diff --git a/man/bca-web.1 b/man/bca-web.1 index 3ce15cbf..68288cb3 100644 --- a/man/bca-web.1 +++ b/man/bca-web.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA-WEB 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA-WEB 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca\-web \- Run a web server. .SH SYNOPSIS @@ -34,6 +34,6 @@ Print help (see a summary with \*(Aq\-h\*(Aq) \fB\-V\fR, \fB\-\-version\fR Print version .SH VERSION -v2.0.0 +v2.0.1 .SH AUTHORS Calixte Denizet , Elijah Zupancic diff --git a/man/bca.1 b/man/bca.1 index 8415fd16..a23823f2 100644 --- a/man/bca.1 +++ b/man/bca.1 @@ -1,6 +1,6 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH BCA 1 "big-code-analysis 2.0.0" "big-code-analysis Manual" +.TH BCA 1 "big-code-analysis 2.0.1" "big-code-analysis Manual" .SH NAME bca \- Analyze source code. .SH SYNOPSIS @@ -88,6 +88,6 @@ Every other subcommand exits 0 on success and 1 on error. Migrating from the flag\-style CLI? See the migration guide: https://dekobon.github.io/big\-code\-analysis/migration.html .SH VERSION -v2.0.0 +v2.0.1 .SH AUTHORS Calixte Denizet , Elijah Zupancic diff --git a/src/error.rs b/src/error.rs index c7b435b1..8ba34306 100644 --- a/src/error.rs +++ b/src/error.rs @@ -178,3 +178,78 @@ impl From for FromPathError { Self::Parse(e) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::error::Error as _; + use std::io::{Error as IoError, ErrorKind}; + + // `FromPathError`'s `Display` / `source` impls and its `From` + // conversion are part of the stable error surface but are only + // reached on `from_path` failure paths that no other test drives. + // Pin the message shape (substring, not exact wording) and the + // `source` chaining contract, which is what `?`-propagating callers + // and `anyhow`-style reporters rely on. + + #[test] + fn from_path_error_display_covers_every_variant() { + let io = FromPathError::Io(IoError::new(ErrorKind::PermissionDenied, "denied")); + assert!(io.to_string().contains("could not read file")); + assert!(io.to_string().contains("denied")); + + assert!( + FromPathError::NonUtf8Path + .to_string() + .contains("not valid UTF-8") + ); + assert!( + FromPathError::Unreadable + .to_string() + .contains("empty, binary") + ); + assert!( + FromPathError::UnknownLanguage + .to_string() + .contains("no language is registered") + ); + + // `Parse` delegates to the wrapped `MetricsError`'s `Display`. + let parse = FromPathError::Parse(MetricsError::LanguageDisabled(LANG::Rust)); + assert_eq!( + parse.to_string(), + MetricsError::LanguageDisabled(LANG::Rust).to_string() + ); + } + + #[test] + fn from_path_error_source_chains_only_for_wrapping_variants() { + // `Io` and `Parse` wrap an underlying error and must expose it; + // the leaf variants must report no source. + let io = FromPathError::Io(IoError::new(ErrorKind::NotFound, "missing")); + assert!(io.source().is_some(), "Io must chain to the io::Error"); + + let parse = FromPathError::Parse(MetricsError::EmptyRoot); + assert!(parse.source().is_some(), "Parse must chain to MetricsError"); + + for leaf in [ + FromPathError::NonUtf8Path, + FromPathError::Unreadable, + FromPathError::UnknownLanguage, + ] { + assert!(leaf.source().is_none(), "{leaf:?} must report no source"); + } + } + + #[test] + fn metrics_error_converts_into_parse_variant() { + let converted: FromPathError = MetricsError::LanguageDisabled(LANG::Cpp).into(); + assert!( + matches!( + converted, + FromPathError::Parse(MetricsError::LanguageDisabled(LANG::Cpp)) + ), + "From must wrap into Parse" + ); + } +} diff --git a/src/langs.rs b/src/langs.rs index c4ca44a0..17c4d382 100644 --- a/src/langs.rs +++ b/src/langs.rs @@ -76,7 +76,7 @@ mk_langs!( tree_sitter_mozjs, [jsm], [], - "2.0.0" + "2.0.1" ), ( "java", @@ -148,7 +148,7 @@ mk_langs!( tree_sitter_tcl, [tcl, tk, tm], ["tcl"], - "2.0.0" + "2.0.1" ), ( "irules", @@ -210,7 +210,7 @@ mk_langs!( tree_sitter_mozcpp, [], [], - "2.0.0" + "2.0.1" ), ( "objc", @@ -311,7 +311,7 @@ mk_langs!( tree_sitter_ccomment, [], [], - "2.0.0" + "2.0.1" ), ( "c-family-helpers", @@ -323,7 +323,7 @@ mk_langs!( tree_sitter_preproc, [], [], - "2.0.0" + "2.0.1" ), ( "perl", diff --git a/src/language_enum_roundtrip.rs b/src/language_enum_roundtrip.rs new file mode 100644 index 00000000..8e305103 --- /dev/null +++ b/src/language_enum_roundtrip.rs @@ -0,0 +1,78 @@ +//! Drift guard for the generated per-language token enums. +//! +//! This module lives at the crate root rather than under `src/languages/` +//! because that directory is wholly owned by the `enums/` codegen — the +//! `enums-codegen-drift` gate flags any hand-written file there as stale. +//! +//! Each `language_.rs` exposes two generated conversion tables — +//! `From` (node-kind id → enum variant) and `From for +//! &'static str` (variant → kind name). They are pure lookup tables, so +//! nothing exercises most of their arms unless a fixture happens to +//! contain a node of that exact kind; on a typical run they sit at a few +//! percent line coverage and a silent disagreement with the live grammar +//! (e.g. after a grammar bump renumbers node kinds) goes unnoticed. +//! +//! For every supported language we walk all `0..node_kind_count` ids the +//! grammar defines, round-trip each through both tables, and assert the +//! enum's name string agrees with the grammar for every *visible* kind. +//! This both pins the mapping against drift (the same fear behind the +//! `grammar_version` guard in `src/langs.rs`) and drives the generated +//! tables to near-full coverage. + +use crate::langs::LANG; +use crate::languages::*; + +/// Round-trips every node-kind id the grammar behind `lang` defines +/// through the generated `From` and `From for &'static str` +/// tables, asserting the enum name matches the grammar for each visible +/// kind. Languages whose grammar feature is disabled in the current +/// build are skipped (the enum surface is always compiled, but +/// `tree_sitter_language` hands back `Err(LanguageDisabled)`). +fn check(lang: LANG) +where + E: From + Into<&'static str>, +{ + let Ok(grammar) = lang.tree_sitter_language() else { + return; + }; + let count = grammar.node_kind_count(); + for id in 0..count { + // Node-kind ids are u16 in tree-sitter; `node_kind_count` can + // never exceed that range, so the cast is lossless. + let id = u16::try_from(id).expect("node-kind id exceeds u16"); + let variant: E = id.into(); + let name: &'static str = variant.into(); + // Hidden / supertype kinds carry grammar-internal names that the + // enum deliberately does not mirror; only assert on the visible + // surface, but still convert above so every arm is exercised. + if grammar.node_kind_is_visible(id) { + assert_eq!( + Some(name), + grammar.node_kind_for_id(id), + "{lang:?} kind id {id}: enum name {name:?} disagrees with grammar" + ); + } + } +} + +macro_rules! roundtrip_tests { + ($($lang:ident),* $(,)?) => { + $( + // The token enum and the `LANG` variant share a name; the + // type position resolves to the re-exported token enum, the + // value position to `LANG::$lang`. + #[test] + #[allow(non_snake_case)] + fn $lang() { + check::<$lang>(LANG::$lang); + } + )* + }; +} + +// One arm per `mk_langs!` entry in `src/langs.rs`. Keep in sync: a new +// language must gain a round-trip test here too. +roundtrip_tests!( + Javascript, Mozjs, Java, Go, Kotlin, Lua, Rust, Tcl, Irules, C, Cpp, Mozcpp, Objc, Csharp, + Elixir, Python, Tsx, Typescript, Bash, Ccomment, Preproc, Perl, Php, Ruby, Groovy, +); diff --git a/src/lib.rs b/src/lib.rs index 3d1bd404..ce3f9b0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,6 +122,8 @@ mod c_macro; mod cfg_predicate; mod checker; mod getter; +#[cfg(test)] +mod language_enum_roundtrip; mod languages; mod macros; diff --git a/src/metrics/npa.rs b/src/metrics/npa.rs index b5338bb0..8aca34a1 100644 --- a/src/metrics/npa.rs +++ b/src/metrics/npa.rs @@ -550,7 +550,7 @@ implement_metric_trait!( clippy::too_many_lines )] mod tests { - use crate::tools::{assert_child_space_kind, check_func_space, check_metrics}; + use crate::tools::{assert_child_space_kind, check_func_space, check_metrics, child_space}; use super::*; @@ -4011,4 +4011,46 @@ mod tests { check_metrics::("interface Foo {}", "foo.java", assert_zero); check_metrics::("interface Foo {}", "foo.cs", assert_zero); } + + // Rounds out `npa`'s public surface — the `Display` impl and the + // per-space `class_npa` / `class_na` / `interface_*` accessors — + // mirroring the `Display` tests the sibling metrics carry. + #[test] + fn stats_display_and_per_space_accessors() { + check_func_space::( + "public interface I {\n int K = 1;\n}\n\ + public class C {\n public int a;\n private int b;\n}\n", + "X.java", + |unit| { + // Class C: a public, b private → 1 public of 2 attributes. + // Interface I: one constant K. + assert_eq!(unit.metrics.npa.class_npa_sum(), 1); + assert_eq!(unit.metrics.npa.class_na_sum(), 2); + let rendered = unit.metrics.npa.to_string(); + for fragment in [ + "classes: 1, interfaces: 1", + "class_attributes: 2", + "interface_attributes: 1", + "total: 2, total_attributes: 3", + ] { + assert!( + rendered.contains(fragment), + "missing {fragment:?} in {rendered}" + ); + } + // Singular accessors populate only on the owning class / + // interface space (0 on the file-unit root); assert them where + // they are nonzero so an always-zero or wrong-field accessor + // would fail. + let class = child_space(&unit, "C"); + assert_eq!(class.kind, SpaceKind::Class); + assert_eq!(class.metrics.npa.class_npa(), 1); + assert_eq!(class.metrics.npa.class_na(), 2); + let iface = child_space(&unit, "I"); + assert_eq!(iface.kind, SpaceKind::Interface); + assert_eq!(iface.metrics.npa.interface_npa(), 1); + assert_eq!(iface.metrics.npa.interface_na(), 1); + }, + ); + } } diff --git a/src/metrics/npm.rs b/src/metrics/npm.rs index 35307e97..85116854 100644 --- a/src/metrics/npm.rs +++ b/src/metrics/npm.rs @@ -478,7 +478,7 @@ implement_metric_trait!( clippy::too_many_lines )] mod tests { - use crate::tools::{assert_child_space_kind, check_func_space, check_metrics}; + use crate::tools::{assert_child_space_kind, check_func_space, check_metrics, child_space}; use super::*; @@ -3446,4 +3446,46 @@ class C { check_metrics::("interface Foo {}", "foo.java", assert_zero); check_metrics::("interface Foo {}", "foo.cs", assert_zero); } + + // Rounds out `npm`'s public surface — the `Display` impl and the + // per-space `class_npm` / `class_nm` / `interface_*` accessors — + // mirroring the `Display` tests the sibling metrics carry. + #[test] + fn stats_display_and_per_space_accessors() { + check_func_space::( + "public interface I {\n void p();\n}\n\ + public class C {\n public void m() {}\n private void n() {}\n}\n", + "X.java", + |unit| { + // Class C: m public, n private → 1 public of 2 methods. + // Interface I: one method p. + assert_eq!(unit.metrics.npm.class_npm_sum(), 1); + assert_eq!(unit.metrics.npm.class_nm_sum(), 2); + let rendered = unit.metrics.npm.to_string(); + for fragment in [ + "classes: 1, interfaces: 1", + "class_methods: 2", + "interface_methods: 1", + "total: 2, total_methods: 3", + ] { + assert!( + rendered.contains(fragment), + "missing {fragment:?} in {rendered}" + ); + } + // Singular accessors populate only on the owning class / + // interface space (0 on the file-unit root); assert them where + // they are nonzero so an always-zero or wrong-field accessor + // would fail. + let class = child_space(&unit, "C"); + assert_eq!(class.kind, SpaceKind::Class); + assert_eq!(class.metrics.npm.class_npm(), 1); + assert_eq!(class.metrics.npm.class_nm(), 2); + let iface = child_space(&unit, "I"); + assert_eq!(iface.kind, SpaceKind::Interface); + assert_eq!(iface.metrics.npm.interface_npm(), 1); + assert_eq!(iface.metrics.npm.interface_nm(), 1); + }, + ); + } } diff --git a/src/metrics/wmc.rs b/src/metrics/wmc.rs index 86ae8dbd..d98ac995 100644 --- a/src/metrics/wmc.rs +++ b/src/metrics/wmc.rs @@ -440,7 +440,7 @@ implement_metric_trait!( clippy::too_many_lines )] mod tests { - use crate::tools::{assert_child_space_kind, check_func_space, check_metrics}; + use crate::tools::{assert_child_space_kind, check_func_space, check_metrics, child_space}; use super::*; @@ -3736,4 +3736,35 @@ mod tests { }, ); } + + // Rounds out `wmc`'s public surface — the `Display` impl and the + // per-space `class_wmc` / `interface_wmc` accessors — mirroring the + // `Display` tests the sibling metrics (nom, nargs, halstead) carry. + #[test] + fn stats_display_and_per_space_accessors() { + check_func_space::( + "public interface I {\n void p();\n}\n\ + public class C {\n public void m() {}\n private void n() {}\n}\n", + "X.java", + |unit| { + // Root rollup: class C contributes WMC 2, interface I contributes 1. + assert_eq!(unit.metrics.wmc.class_wmc_sum(), 2); + assert_eq!(unit.metrics.wmc.interface_wmc_sum(), 1); + assert_eq!( + unit.metrics.wmc.to_string(), + "classes: 2, interfaces: 1, total: 3" + ); + // The singular accessors are per-space and populate only on the + // owning class / interface space (0 on the file-unit root), so + // assert them where they are nonzero — an accessor that always + // returned 0 or read the wrong field would fail here. + let class = child_space(&unit, "C"); + assert_eq!(class.kind, SpaceKind::Class); + assert_eq!(class.metrics.wmc.class_wmc(), 2); + let iface = child_space(&unit, "I"); + assert_eq!(iface.kind, SpaceKind::Interface); + assert_eq!(iface.metrics.wmc.interface_wmc(), 1); + }, + ); + } } diff --git a/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_empty.snap b/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_empty.snap index 4218bcf7..c33040b5 100644 --- a/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_empty.snap +++ b/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_empty.snap @@ -10,7 +10,7 @@ expression: "render(&[])" "tool": { "driver": { "name": "big-code-analysis", - "version": "2.0.0", + "version": "2.0.1", "rules": [] } }, diff --git a/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_multi.snap b/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_multi.snap index cad46fb0..06344347 100644 --- a/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_multi.snap +++ b/src/output/snapshots/big_code_analysis__output__sarif__tests__sarif_multi.snap @@ -10,7 +10,7 @@ expression: render(&offenders) "tool": { "driver": { "name": "big-code-analysis", - "version": "2.0.0", + "version": "2.0.1", "rules": [ { "id": "cognitive", diff --git a/src/tools.rs b/src/tools.rs index 4dc3a7ea..008e6bff 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -800,6 +800,22 @@ pub(crate) fn assert_child_space_kind( ); } +/// Returns the direct child [`FuncSpace`][crate::FuncSpace] named `name`, +/// panicking if absent. Lets per-space-metric tests assert accessors on the +/// owning class / interface space (where the value is nonzero) rather than on +/// the always-zero file-unit root. +#[cfg(test)] +pub(crate) fn child_space<'a>( + func_space: &'a crate::FuncSpace, + name: &str, +) -> &'a crate::FuncSpace { + func_space + .spaces + .iter() + .find(|s| s.name.as_deref() == Some(name)) + .unwrap_or_else(|| panic!("expected a child FuncSpace named {name:?}")) +} + #[cfg(test)] #[path = "tools_tests.rs"] mod tests; diff --git a/tests/alterator_string_flattening.rs b/tests/alterator_string_flattening.rs new file mode 100644 index 00000000..2939c5ef --- /dev/null +++ b/tests/alterator_string_flattening.rs @@ -0,0 +1,82 @@ +//! Behavioural coverage for the per-language `Alterator::alterate` +//! string-flattening arms in `src/alterator.rs`. +//! +//! `alterate` collapses a string-like literal into a single leaf +//! [`AstNode`] holding its verbatim source text, so the AST dump (and the +//! REST `/ast` endpoint that consumes it) renders `"hi"` as one node +//! rather than exposing the grammar's delimiter / `string_content` +//! children. Eight languages — Objective-C, Mozilla C++, C#, Lua, Tcl, +//! iRules, Ruby, and Elixir — had no dump test reaching this arm, leaving +//! their `alterate` impls entirely uncovered. Each case parses a minimal +//! snippet, dumps it through the public [`Ast::dump`] API, and asserts the +//! literal survives as a verbatim leaf. +//! +//! The assertion is non-vacuous: without flattening the literal's quotes +//! and body land in separate child leaves (`"`, `hi`, `"`), so no single +//! leaf carries the full quoted text — only the `alterate` arm produces a +//! childless node whose value is `"hi"`. +#![allow(missing_docs)] + +use big_code_analysis::{Ast, AstCfg, AstNode, LANG, Source}; + +/// Dumps `code` for `lang` and returns the root AST node. Runs only under +/// builds where `lang`'s grammar feature is enabled (the `--all-features` +/// test leg); the `feature-matrix` legs merely `cargo check` this file. +fn dump_root(lang: LANG, code: &str, file_name: &str) -> AstNode { + let cfg = AstCfg { + id: String::new(), + language: String::new(), + comment: false, + span: false, + }; + Ast::parse(Source::new(lang, code.as_bytes()).with_name(Some(file_name.to_owned()))) + .expect("language feature enabled under --all-features") + .dump(cfg) + .root + .expect("source parses to a root AST node") +} + +/// True if the tree contains a leaf node (no children) whose verbatim +/// value equals `literal` — the signature of a flattened string arm. +fn has_verbatim_leaf(node: &AstNode, literal: &str) -> bool { + (node.children.is_empty() && node.value == literal) + || node.children.iter().any(|c| has_verbatim_leaf(c, literal)) +} + +#[track_caller] +fn assert_flattened(lang: LANG, code: &str, file_name: &str, literal: &str) { + let root = dump_root(lang, code, file_name); + assert!( + has_verbatim_leaf(&root, literal), + "{lang:?}: expected {literal:?} to survive as a single verbatim leaf in the AST dump" + ); +} + +/// Emits one `#[test]` per `name: lang, code, file, literal;` case, each +/// asserting the literal survives `alterate` as a single verbatim leaf. +/// Mirrors the `roundtrip_tests!` pattern in `src/language_enum_roundtrip.rs`. +macro_rules! flatten_cases { + ($($name:ident: $lang:expr, $code:expr, $file:expr, $lit:expr;)*) => { + $( + #[test] + fn $name() { + assert_flattened($lang, $code, $file, $lit); + } + )* + }; +} + +flatten_cases! { + objc_flattens_string_literal: LANG::Objc, "int f(void) { const char *s = \"hi\"; return 0; }", "f.m", "\"hi\""; + mozcpp_flattens_string_literal: LANG::Mozcpp, "int f() { const char *s = \"hi\"; return 0; }", "f.cpp", "\"hi\""; + csharp_flattens_string_literal: LANG::Csharp, "class C { void M() { string s = \"hi\"; } }", "f.cs", "\"hi\""; + lua_flattens_string_literal: LANG::Lua, "local s = \"hi\"", "f.lua", "\"hi\""; + tcl_flattens_quoted_word: LANG::Tcl, "set s \"hi\"", "f.tcl", "\"hi\""; + // In valid iRules a quoted word only appears inside an event handler's + // `{ … }` body, which `alterate` flattens as a single `braced_word` leaf + // (the same match arm that handles `quoted_word`), so the verbatim text + // to look for is the whole brace block. + irules_flattens_braced_word: LANG::Irules, "when HTTP_REQUEST { set s \"hi\" }", "f.irul", "{ set s \"hi\" }"; + ruby_flattens_string_literal: LANG::Ruby, "s = \"hi\"\n", "f.rb", "\"hi\""; + elixir_flattens_string_literal: LANG::Elixir, "s = \"hi\"\n", "f.ex", "\"hi\""; +} diff --git a/tests/snapshots/sarif_test__sarif_multi_offender.snap b/tests/snapshots/sarif_test__sarif_multi_offender.snap index db2d8301..a01319b5 100644 --- a/tests/snapshots/sarif_test__sarif_multi_offender.snap +++ b/tests/snapshots/sarif_test__sarif_multi_offender.snap @@ -10,7 +10,7 @@ expression: out "tool": { "driver": { "name": "big-code-analysis", - "version": "2.0.0", + "version": "2.0.1", "rules": [ { "id": "cyclomatic", diff --git a/tests/snapshots/sarif_test__sarif_zero_offenders.snap b/tests/snapshots/sarif_test__sarif_zero_offenders.snap index 2bfb8148..35ef5b22 100644 --- a/tests/snapshots/sarif_test__sarif_zero_offenders.snap +++ b/tests/snapshots/sarif_test__sarif_zero_offenders.snap @@ -10,7 +10,7 @@ expression: out "tool": { "driver": { "name": "big-code-analysis", - "version": "2.0.0", + "version": "2.0.1", "rules": [] } }, diff --git a/tree-sitter-ccomment/Cargo.lock b/tree-sitter-ccomment/Cargo.lock index e3b5ff66..542883d9 100644 --- a/tree-sitter-ccomment/Cargo.lock +++ b/tree-sitter-ccomment/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-ccomment" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter", diff --git a/tree-sitter-ccomment/Cargo.toml b/tree-sitter-ccomment/Cargo.toml index 88aaf313..cc36024f 100644 --- a/tree-sitter-ccomment/Cargo.toml +++ b/tree-sitter-ccomment/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bca-tree-sitter-ccomment" description = "Ccomment grammar for the tree-sitter parsing library (big-code-analysis fork)" -version = "2.0.0" +version = "2.0.1" authors = [ "Calixte Denizet ", "Elijah Zupancic ", diff --git a/tree-sitter-mozcpp/Cargo.lock b/tree-sitter-mozcpp/Cargo.lock index c07948bc..d1bcca84 100644 --- a/tree-sitter-mozcpp/Cargo.lock +++ b/tree-sitter-mozcpp/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozcpp" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter", diff --git a/tree-sitter-mozcpp/Cargo.toml b/tree-sitter-mozcpp/Cargo.toml index e965dc29..42ef620c 100644 --- a/tree-sitter-mozcpp/Cargo.toml +++ b/tree-sitter-mozcpp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bca-tree-sitter-mozcpp" description = "Mozcpp grammar for the tree-sitter parsing library (big-code-analysis fork)" -version = "2.0.0" +version = "2.0.1" authors = [ "Calixte Denizet ", "Elijah Zupancic ", diff --git a/tree-sitter-mozjs/Cargo.lock b/tree-sitter-mozjs/Cargo.lock index f2f1c3e6..34b94d13 100644 --- a/tree-sitter-mozjs/Cargo.lock +++ b/tree-sitter-mozjs/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-mozjs" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter", diff --git a/tree-sitter-mozjs/Cargo.toml b/tree-sitter-mozjs/Cargo.toml index ce3bf537..567b2d5b 100644 --- a/tree-sitter-mozjs/Cargo.toml +++ b/tree-sitter-mozjs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bca-tree-sitter-mozjs" description = "Mozjs grammar for the tree-sitter parsing library (big-code-analysis fork)" -version = "2.0.0" +version = "2.0.1" authors = [ "Calixte Denizet ", "Elijah Zupancic ", diff --git a/tree-sitter-preproc/Cargo.lock b/tree-sitter-preproc/Cargo.lock index d7618b5e..68bcc529 100644 --- a/tree-sitter-preproc/Cargo.lock +++ b/tree-sitter-preproc/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-preproc" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter", diff --git a/tree-sitter-preproc/Cargo.toml b/tree-sitter-preproc/Cargo.toml index eb58e3c1..dff3c2d6 100644 --- a/tree-sitter-preproc/Cargo.toml +++ b/tree-sitter-preproc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bca-tree-sitter-preproc" description = "Preproc grammar for the tree-sitter parsing library (big-code-analysis fork)" -version = "2.0.0" +version = "2.0.1" authors = [ "Calixte Denizet ", "Elijah Zupancic ", diff --git a/tree-sitter-tcl/Cargo.lock b/tree-sitter-tcl/Cargo.lock index 2f4c9489..c805041a 100644 --- a/tree-sitter-tcl/Cargo.lock +++ b/tree-sitter-tcl/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "bca-tree-sitter-tcl" -version = "2.0.0" +version = "2.0.1" dependencies = [ "cc", "tree-sitter", diff --git a/tree-sitter-tcl/Cargo.toml b/tree-sitter-tcl/Cargo.toml index c148af10..6717dac8 100644 --- a/tree-sitter-tcl/Cargo.toml +++ b/tree-sitter-tcl/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bca-tree-sitter-tcl" description = "Tcl grammar for the tree-sitter parsing library (big-code-analysis fork)" -version = "2.0.0" +version = "2.0.1" authors = [ "Lewis Russell", "Elijah Zupancic ",