Skip to content

fix: metric value semantics, MI formula, and tool-bound doc comments - #1650

Merged
dekobon merged 20 commits into
mainfrom
fix/batch-2026-10-17
Oct 20, 2026
Merged

dekobon merged 20 commits into
mainfrom
fix/batch-2026-10-17

Conversation

@dekobon

@dekobon dekobon commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Summary

Two threads, 18 commits, each gate-clean on its own.

Metric value semantics (#1621 and its reports). Every mi.* decision that used to be a scattered lower_is_worse / starts_with("mi") branch now has one owner: a validated metric_catalog::Threshold with a Scale { Ratio, Interval { min, max, unit } } on each catalog row. The CLI gate, baseline, Code Climate and SARIF writers, the Python to_sarif, and the reports all consume it. Negative mi.* baseline values round-trip, an MI regression reads in points ([regr +10.0 pts]) rather than a meaningless percentage, one severity key ranks mixed-metric offenders, the soft tier is additive for interval metrics and clamped so it is never looser than the hard tier, and a space with no code to measure is skipped by the gate as the report already skipped it. lower_is_worse is #[deprecated] and banned in clippy.toml. (#1611, #1614, #1616, #1617, #1620, #1621)

MI formula (#1607). All three mi.* variants size on ploc; mi.sei is original + 50·sin(√(2.4·CM)) with CM the 0–1 comment ratio and ln throughout. --write-baseline stamps mi_formula = 2; a baseline from another revision warns on every read. This moves mi.* for any space with comments or blank rows — review mi.* thresholds.

Doc-comment attachment (#1608, #1610, #1612, #1613, #1615). dloc now counts the rows each language's documentation tool actually attaches, with every rule read from the tool's source and, where a toolchain could run, confirmed by running it: rustdoc, javac, Kotlin's findDocComment, Groovydoc, Roslyn, phpDocumentor + Zend, JSDoc, tsc, Doxygen (C/C++/ObjC/mozcpp), Pod::Simple. The mechanism is one declarative DocBinding / KeptDocs per language and one generic stretch walker, memoised through FoldLane so each comment costs O(1); make bench-scaling pins the exponent on 16 new probes. Perl POD after __END__ counts; a parenthesized Python docstring is one. The 23 deliberate tool-vs-bca divergences that remain are tracked in #1646.

Closes

Fixes #1607, #1608, #1610, #1611, #1612, #1613, #1614, #1615, #1616, #1617, #1620, #1621.
Filed, not fixed: #1622, #1623, #1624, #1643, #1644, #1645, #1646, #1648, #1649.

Review guide

The two threads are independent; review them as two series. Commit order within each is dependency order.

Decisions worth a reviewer's eye, each recorded in the CHANGELOG and on its issue:

Verification

  • Every commit: make pre-commit → BCA_GATE: pass; per-commit clippy -D warnings + targeted tests sweep, 18/18.
  • Tip: make bench-scaling (74 probes), make chain-audit, make check-test-lang-gates-compare COMPARE_REF=origin/main all clean.
  • Coverage over the series: patch 98.6 %, project 98.2 %.
  • Three independent review rounds (five reviewers in the last one), findings verified against source before adoption; every regression test verified by revert or perturbation.
  • Integration snapshots: submodule big-code-analysis-output at 9076c554 (two commits, pushed); gitlink bumped in the commits that moved metrics.

Public API (STABILITY.md updated)

Additive, minor bump: metric_catalog::{Threshold, Checked, LimitError, LimitWarning, Change, Scale}, MetricInfo::scale, mi::Stats::is_empty, wire::Loc::dloc, MetricsOptions::with_mi_charges_docs, Python metrics= accepts CLI names. Deprecated: metric_catalog::lower_is_worse. No removals.

The Python metrics= kwarg took only the 13 family names, while
bca metrics --metrics also takes the list-metrics row names (sloc,
dloc, ...) and the dotted threshold ids (cyclomatic.modified), so a
name copied from the CLI raised ValueError in Python.

Move the CLI's resolution into the library as
metric_catalog::resolve_selector, with selector_names() for the
advertised vocabulary and UnknownSelectorError carrying the input
and the did-you-mean candidates. Both front ends now resolve through
it; selection stays family-granular, so metrics=["dloc"] returns the
whole loc block, as --metrics dloc does.

The edit-distance suggester moves from the CLI into the library with
its unit tests. The CLI reaches it through one #[doc(hidden)]
format_suggestion re-export, and its --metrics error text is
unchanged byte for byte (pinned by a new unit test).

Python's unknown-name error keeps its "unknown metric: <name>"
prefix and now ends with the CLI's hint and known-name list instead
of "; valid: <families>". Docs, including the analyze() docstring,
gain the alias vocabulary and the nom dependency of cognitive, nargs
and nexits.

Fixes #1614
The catalog recorded only a metric's direction, so every surface that
asks "breached, how much worse, how much changed" re-derived the
answer with ratio or percentage arithmetic. That assumes a true zero.
It holds for counts and fails for the Maintainability Index family,
which is interval-scale: mi.original and mi.sei have an arbitrary
zero and go negative, and mi.visual_studio is clamped to [0, 100]. A
0.0107-point MI drop rendered as "+1069%".

Add the value-semantics layer the callers will migrate onto:

- Scale { Ratio, Interval { min, max, unit } } on MetricInfo, with a
  scale(id) lookup. The three mi.* rows are Interval; one Visual
  Studio band (10 points) is the unit, 17.1 on the 171-point scale.
- MetricInfo::cmp_worse, a direction-oriented total order with NaN
  sorting worst.
- Threshold, a validated limit. Threshold::check rejects an unknown
  id, a non-finite limit and a negative limit on a non-interval
  metric, and accepts an out-of-domain or unfireable interval limit
  with a warning, since 2.x already parses those. It returns
  Checked { threshold, warning }; Threshold::warning re-asks a
  threshold in hand for the same advisory. Threshold provides
  breaches, excess, one severity key (bitwise value / limit for
  counts, saturating at a zero limit, 1 + excess / unit for interval
  metrics), cmp_severity and cmp_breach, soft_limit with its
  is_soft_ratio band rule, change (percent or points) and drift.

No caller is migrated here; the CLI, Code Climate and the Python
binding move onto Threshold in later commits.

Refs #1621
mi.original and mi.sei are unclamped and fall below zero on large
functions, but the baseline reader and writer both dropped every
negative value on the premise that extractors never emit one. A
refreshed baseline therefore never covered those offenders: they
reported [new] on every run and the suggested refresh could not quiet
them.

Both sides now go through one `recordable` helper that accepts every
finite value and refuses only NaN and infinity, which cannot ratchet.
A negative zero is written as 0.0 so the file never spells -0.0; the
two compare equal, so no classification changes.

The regression tag is now rendered from Threshold::change. Counts keep
[regr +N%] byte for byte. The mi.* family reads in index points,
[regr +N.N pts]: MI has an arbitrary zero, so a percentage of its
record means nothing. Dividing by a signed record printed [regr +-100%]
for a drop from -10 to -20, and a 0.01-point drop from a record of
0.001 read [regr +1069%]; they now read +10.0 pts and +0.0 pts. A
violation that cannot form a Threshold keeps the old arithmetic.

Also correct the comments that claimed values are never negative and
that toml rejects nan/inf (it parses both), and the relative_drift doc,
whose [0, 1] bound no longer holds once a record can cross zero. The
--exit-codes help and the book name the tag shape-neutrally.

Fixes #1611
Every surface that read a limit re-derived "breached, how badly, which
is worse" with ratio arithmetic, which assumes a true zero. That fails
for the interval-scale Maintainability Index. The CLI ranked any MI at
or below zero as infinitely bad and broke the tie on the milder value,
Code Climate banded the same breach as the mildest, the baseline
tie-break preferred the stricter record for mi.*, and the soft tier
divided an MI floor, which would loosen a negative one.

Move them all onto the catalog's Threshold at once, since negative MI
limits become legal here and a half-migrated tree would let the CLI
and Code Climate disagree again:

- Violation carries a Threshold instead of limit + lower_is_worse.
  Every layer validates through Threshold::check. mi.original and
  mi.sei floors may be negative; a mi.visual_studio limit that cannot
  fire or fires on everything still parses and warns once. The
  advisories are read off the resolved gate
  (ThresholdSet::gated_thresholds), so under --tier=soft they judge
  the raised floor that actually gates, not the configured one.
- Ranking (footer, step-summary rollup, top-offenders table) uses
  Threshold::cmp_breach. The table heading now says "by severity".
- Code Climate bands Threshold::severity with the same cutoffs; a
  zero-limit breach is blocker; unknown or non-finite records keep the
  declared floor.
- The soft tier calls soft_limit: an MI floor rises by (1 - r) of one
  maintainability band, rounded up off the sig-fig grid. A limit the
  ratio does not move is used as written, so ratio 1 is the identity
  STABILITY.md promises, and a rounded one is held on the hard
  limit's side in the tier's direction. Count rounding is unchanged.
  --explain-threshold names the additive step the resolver took, and
  says "clamped" when the domain edge cut it short.
- The baseline ratchet, stale tally and diff-baseline buckets use
  cmp_worse and drift; an equidistant tie keeps the more lenient record
  in the metric's direction.
- --min-change rejects a non-finite value: inf hid every delta, and
  NaN and -inf reported every one. A negative one, which releases
  before 2.4 accepted, still reports every delta and now warns that
  it reads as 0. check --help says "breaches" rather than "at or
  over".

Fixes #1617
Fixes #1616
Fixes #1620
Move the last direction-bool consumer, the Python binding's
to_sarif(thresholds=), onto metric_catalog::Threshold. It copied the
CLI's old rules: any negative limit was rejected, so no mi.original or
mi.sei floor could sit under a large function's value, and the breach
test was open-coded. Limits now validate and breach exactly as
bca check does. A negative MI floor is accepted, and a limit that can
never fire (mi.visual_studio=0) raises a UserWarning carrying the
library's message.

With no caller left, deprecate metric_catalog::lower_is_worse (kept
working until 3.0) and ban it in a root clippy.toml, so a new direction
encoding cannot creep back in. The CLI's test-only
Violation::lower_is_worse goes too; its tests read the threshold's
direction instead.

Refs #1621
A space with no code to measure (zero Halstead volume or size term)
reports MI 0.0, an in-band sentinel. bca report folded it into its
SLOC-weighted average anyway, numerator and denominator alike, so an
count: arc.h alone read 11.9 (MODERATE), arc.h plus fstlib.h 8.4
(LOW).

Expose the predicate as mi::Stats::is_empty() (additive; the private
inputs_are_empty now calls it), project it into FunctionSummary, and
fold every report MI average through one MiAverage accumulator that
skips empty units in both its numerator and its own SLOC. All four
sites use it: the Markdown summary row and language overview, and the
HTML overview row and per-language summary. Displayed SLOC, file
counts and the comment ratio still count the empty file. The MI
hotspot keep reads the same flag instead of a hand-copied predicate.

A language whose every unit is empty has no average at all: the
summary reads n/a rather than rating a 0.0 LOW, and the overview
leaves its Avg MI cell empty, which the HTML sorter reads as no
value.

The mi.* gate skips such a space too. A bodiless Ruby block
(`lambda {}`, `[1, 2].each { }`) is a function space with zero
volume, and bca check reported its placeholder as the worst MI in
the file, banded critical in Code Climate. bca check and the Python
to_sarif(thresholds=) now leave it out, as bca report does.

mi_rating takes its 10/20 edges from mi.visual_studio's catalog unit,
so the report's bands and the gate's MI scale are one number.

The serialized MI of an empty space stays 0.0 in 2.x; emitting null
is deferred to 3.0.

Refs #1621
The HTML report carried a line-for-line copy of the Markdown
per-language overview row, and each report summed a language's units
and formed the comment ratio on its own, the Markdown global totals a
third time. One copy of each now lives in markdown_report:
lang_overview_row, LanguageTotals and comment_ratio_percent, so the
two reports cannot drift apart. The rendered output is byte-identical;
every sum is added in the same order as before.
Consolidate the [Unreleased] entries for the five commits that give
the catalog a Scale and a validated Threshold and move the CLI gate,
the baseline, Code Climate, the Python bindings and the report
averages onto it: the additions, the changed limit and soft-tier
rules, the deprecation of lower_is_worse, the MI null-on-the-wire
change deferred to 3.0, and the five fixes it folds in.

Fixes #1621
Three changes to the maintainability index, all in Mi::compute and
Stats::sei:

- Every variant sizes a space on ploc, the rows that carry code.
  Comment, documentation and blank rows no longer enter the size term,
  in either documentation mode; a row shared by documentation and code
  stays in.
- mi.sei takes the comment share as a 0-1 ratio rather than a 0-100
  percentage. On the percentage scale 50*sin(sqrt(2.4*CM)) swung
  between -50 and +50 several times over ordinary densities; on the
  ratio it rises monotonically to about 50, so sei >= original, with
  equality when a space has no comment. Blank rows stay in the
  denominator, so they still dilute a live comment share.
- mi.sei uses ln, not log2, for volume and size; it is now original
  plus the comment term.

mi_charges_docs therefore moves only mi.sei's comment share and can
only raise it; original and visual_studio are identical in both modes.

Every mi.* value moves for unchanged code, so a written baseline now
stamps [provenance] mi_formula = 2. An unstamped file reads as revision
1, and bca check --baseline warns when a file holding any mi.* entry
records another revision, in place of the mode warning; bca
diff-baseline warns the same way when its two files disagree and
either holds an mi.* entry. The mode warning itself now compares
only mi.sei, the one metric the mode moves. No
baseline version bump, so an older bca still reads the file but
cannot know the stamp: STABILITY.md says to pin bca >= 2.4 wherever a
2.4-written baseline is read.

visual_studio() is now original() rescaled rather than a second copy
of the formula.

Fixes #1607
Checker::holds_doc_comment_in_body carried a comment and the source
only for Rust's inner-doc exception, so every other override bound
them as unused parameters and the hook's contract depended on depth
(the comment was passed for the two nearest ancestors, None above).
The hook is now (ancestor, ancestors, below), and the exception moves
to a dedicated Checker::doc_comment_opens_body, false by default,
which documents_where_it_sits asks of the two nearest ancestors only.
Both the //! and the /*! arm reach it through that one call.

python_documentation_string_run walked siblings_before twice: once
for the run's first string and once, inside
python_is_documentation_string, for the statement before the run.
One backward scan now finds both, since the first non-string,
non-comment sibling ends the run and is the statement documented_by
tests.

No metric moves: loc JSON over the corpora and this tree is
byte-identical between the pre- and post-change binaries. The
baseline is refreshed for checker.rs's file-level loc and drops the
six nargs entries the old hook signature held.

Fixes #1615
CPython drops the parentheses around an expression, so ("""doc""")
as a body's first statement is its docstring. Every bare-string test
read the string's parent as the statement, and a
parenthesized_expression in between made the string, at any nesting
depth, a code row and an operand, and kept it out of dloc.

The bare-string predicates now climb through parenthesized_expression
to the statement, and the docstring-run scan unwraps a statement's
value the same way, skipping comments inside the parentheses. Both
walks stop at CPython's 200-level paren limit. The ( and ) rows are
billed as the string's comment rows, and as dloc only when the string
is documentation, so dloc stays within cloc. Halstead drops the ( as
it drops the string. A tuple, a parenthesized value and (f"x") keep
their old roles.

Every parenthesis of such a statement asks whether it wraps the
string, and answering by a climb and an unwrap per parenthesis made
a statement d deep cost O(d^2): 3.8 s for 80 KB at depth 200. A
walk that lends a FoldCache, now including the bca ops walk, reads
the answer off a memoised fold of the ancestry instead. Probes
loc/python-paren-bare-strings and ops/python-paren-bare-strings
hold it under the cap that halstead/python-nested-paren cannot
see.

Fixes #1610
The in-body test for a doc comment scanned the children of the
function that holds it, past every other comment there, so the LOC
walk was quadratic in doc comments in one C# accessor list or one PHP
or Kotlin signature (exponent 1.93-1.98 on the new probes).

Each hook now decides from the node on the comment's path: a C#
property or indexer holds every comment but those in its accessor
list, a PHP body is the compound_statement or an arrow function's
last child, and a Kotlin body is the function_body or block child.
csharp_member_has_accessors stops at the first accessor. The
probes loc/csharp-accessor-docs, loc/php-signature-docs and
loc/kotlin-signature-docs now fit 0.95-0.98.

loc JSON over every C#, PHP, Kotlin, Java and Groovy file in the
corpora (247 files) is byte-identical. One placement moves: a C# doc
comment in the header or initializer of a property with accessors
was dloc and is now a plain comment, as Roslyn reads it and as the
same comment after a method's attribute already was. The baseline is
refreshed for checker.rs's file-level loc.

Fixes #1612
tree-sitter-perl holds everything after __END__ or __DATA__ as one
data_not_for_compiler leaf, which the Perl LOC walk did not bill, so
POD in the h2xs layout fell through to blank while the same POD
before the marker was cloc and dloc.

A one-pass scan of the leaf's bytes now reads POD blocks as
Pod::Simple does (^=[a-zA-Z][a-zA-Z0-9]* opens, ^=cut closes, EOF
ends an open block) and bills them as comment rows, and as
documentation unless pod_comment_region says the block is only
comment regions. Every other non-blank row is PLOC; blank rows stay
blank. A bench-scaling probe, loc/perl-data-pod, fits 1.01.

Splitting the first match arm removes every rustfmt bail in the file,
so its rustfmt-bail baseline entry is dropped. The new arm moves the
baselined halstead.effort of PerlCode::compute; the baseline is
refreshed.

Fixes #1608
A marker doc comment outside a function body counted as dloc wherever
it sat, so a Javadoc licence header above `package`, a `///` above a
macro call or an `extern` block, and the superseded first of two
Javadocs all inflated dloc and the MI comment share.

Rust, Java, Kotlin and Groovy now count a doc comment only when their
own tool attaches it to the declaration after it, read from each
tool's source and confirmed by running it:

- Rust (rustc, rustdoc): every outer doc comment above an item, across
  blank rows, plain comments and attributes. An extern block, a macro
  call, a statement or a generic parameter takes none. `//!` and doc
  comments in macro token trees keep the old reading.
- Java (javac `Token::docComment`, `-Xlint:dangling-doc-comments`):
  the last doc comment before a declaration's first token; consecutive
  `///` lines are one. The package takes one only in a file with no
  type after it, the shape of package-info.java.
- Kotlin (`PrecedingDocCommentsBinder`): the last KDoc before a
  declaration; a KDoc inside its signature stays as #1612 decided,
  and one nested deeper, in an argument or a type parameter list,
  documents nothing, as javac's does.
- Groovy (`GroovydocManager`): only the comment nearest the
  declaration, read past block comments the lexer hides.

The choice is made once per stretch of comments, at its last member,
which walks back over the stretch: O(k) for k members, held by the new
loc/java-dangling-docs and loc/rust-attributed-docs probes.

No committed corpus snapshot moves: the corpus snapshots cover C++,
JavaScript, Rust (serde), C# and PHP only, and every serde doc comment
is attached. Submodule unchanged at f34ec062.

Refs #1613
C#, PHP and the JS family counted a doc comment as dloc wherever it
sat outside a function body, so a /** */ above a call, a licence
header above a plain comment, and a doc comment above a namespace or
a `using` inflated dloc and the MI comment share.

Each now counts only what its own tool attaches, read from the tool's
source and confirmed by running it:

- C# (Roslyn GetDocumentationCommentTriviaFromSyntaxNode, dotnet
  build): the nearest run of doc comments only blank rows separate,
  read back past plain comments and directives. A namespace, a
  `using`, a statement and an accessor take none (CS1587), which
  reverses #1612's accessor-list reading.
- PHP (phpDocumentor 3): the last DocBlock before an element, and
  phpDocumentor's file DocBlock, which documents the file.
- JavaScript (JSDoc 4): the nearest block comment before a declaration
  or an assignment; a virtual comment (@file, @module, a naming tag)
  stands alone. @license does not.
- TypeScript and TSX (tsc, TypeDoc): the last /** before a
  declaration, past every plain comment; none after a decorator.
  @module and @packageDocumentation document the file.

A new KeptDocs::Leading models the reverse trivia scan Roslyn, JSDoc
and tsc share. In-body JSDoc stays documentation. Probes
loc/csharp-dangling-docs, loc/php-file-docblock and
loc/js-standalone-tags hold the decision at O(k).

Three pdf.js corpus snapshots move, on loc.dloc* and mi.sei only,
each matching jsdoc -X: submodule f34ec062 -> 9076c554.

Refs #1613
C, C++, Mozilla C++ and Objective-C counted a Doxygen block as dloc
wherever it sat outside a function body, so a block above the end of
a file, a closing brace or a forward declaration inflated dloc and the
MI comment share.

Each now counts what Doxygen 1.13.2 documents, read from commentscan.l
and confirmed by running it with EXTRACT_ALL=NO over ~200 fixtures:

- Doxygen keeps every doc block before an entry and concatenates
  them: a declaration, definition, macro, typedef, alias, namespace,
  template, enumerator, macro call, or Objective-C container,
  property or method. It reads past plain comments, #include,
  #import, #pragma, an empty `;` and an empty conditional, and into a
  conditional that declares something.
- A block above a forward declaration, `}`, `@end`, `@synthesize`,
  an explicit instantiation or the end of the file documents nothing.
- A structural block (\file, \defgroup, \page, \name, or a naming
  command with a name: \class X, \def M) stands alone and counts
  wherever it sits; the doc blocks above a \file block count with it.
- Member-after /**< blocks and in-body blocks keep counting (#1609).

A new DocBinding::gathers models the \file case for a stretch that
ends at no target. The rule lives in lang_helpers/doxygen.rs and one
macro the four Checker impls share. Probes loc/c-dangling-docs and
loc/cpp-structural-docs hold it at O(k).

Two rules the other stretch bindings share change with it. A
stretch reads past a parse error that is no doc target and holds
none, as Doxygen's binding already does, so the error
tree-sitter-rust leaves for a Rust 2024 `safe fn` no longer drops
its doc comment; an error holding a declaration, such as a Kotlin
class-body context(...), stands in for it. And KeptDocs::Leading
asks whether a doc comment after code on its row still documents:
tsc's leading-comment scan starts at the line break after the token
before, so in TS and TSX one does only before a parameter, while
Roslyn, JSDoc and PHP-Parser read it.

No committed corpus snapshot moves: every C++ doc block in DeepSpeech
native_client is attached. Submodule unchanged at 9076c554.

Fixes #1613
Consolidate the [Unreleased] entries for the six LOC commits: the
parenthesized Python docstring (#1610), Perl POD after __END__ (#1608),
the linear doc-placement test and the C# header-comment reading
(#1612), and the three-part doc-comment attachment rule with its
per-tool behaviour and its probes (#1613). The #1615 refactor has no
user-visible change; its entry was a rewording of the #1597 hook names
made in place.
Lesson 99: a bench-scaling probe guards only the axis it grows, so a
walk that descends needs a depth probe — the #1613 nested-conditional
walk was O(depth²) under three green gates whose probes grew width.
Lesson 100: a float tolerance is measured in the value's own ulps,
never in f64::EPSILON, and a rounded limit is clamped to the hard
limit's side — the soft-tier snap of #1621 was wrong twice before the
clamp made the invariant hold regardless of the tolerance. Lesson 98
gains the #1613 sub-example: premises read from a tool's source and
refuted only by running the tool.

The interrupted-sweep case goes to .claude/rules/testing.md as a rule
rather than a lesson: the driver writes its backup to disk before the
first edit, and recovery is cmp against it.

Refs #1613, #1621
@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.91723% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.26%. Comparing base (ac28b7b) to head (1149499).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
src/metrics/mi.rs 95.27% 9 Missing and 5 partials ⚠️
big-code-analysis-cli/src/markdown_report.rs 96.83% 2 Missing and 3 partials ⚠️
src/metric_catalog/threshold.rs 97.59% 3 Missing and 1 partial ⚠️
...-code-analysis-ast/src/lang_helpers/doc_comment.rs 99.13% 1 Missing and 2 partials ⚠️
big-code-analysis-ast/src/lang_helpers/python.rs 98.02% 2 Missing and 1 partial ⚠️
big-code-analysis-cli/src/thresholds.rs 96.20% 1 Missing and 2 partials ⚠️
big-code-analysis-ast/src/test_support.rs 66.66% 0 Missing and 2 partials ⚠️
big-code-analysis-cli/src/baseline_diff.rs 81.81% 2 Missing ⚠️
...ig-code-analysis-cli/src/commands/check/explain.rs 95.00% 1 Missing and 1 partial ⚠️
big-code-analysis-cli/src/threshold_soft.rs 94.44% 1 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1650      +/-   ##
==========================================
+ Coverage   98.22%   98.26%   +0.03%     
==========================================
  Files         367      371       +4     
  Lines      104922   108453    +3531     
  Branches   104487   108018    +3531     
==========================================
+ Hits       103055   106566    +3511     
- Misses       1205     1211       +6     
- Partials      662      676      +14     
Flag Coverage Δ
python 100.00% <ø> (ø)
rust 98.25% <98.91%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
big-code-analysis-ast/src/checker.rs 96.95% <100.00%> (+0.01%) ⬆️
big-code-analysis-ast/src/checker/c.rs 100.00% <ø> (ø)
big-code-analysis-ast/src/checker/cpp.rs 100.00% <ø> (ø)
...de-analysis-ast/src/checker/doxygen_doc_binding.rs 100.00% <100.00%> (ø)
big-code-analysis-ast/src/checker/groovy.rs 100.00% <100.00%> (ø)
big-code-analysis-ast/src/checker/java.rs 94.73% <100.00%> (+1.87%) ⬆️
big-code-analysis-ast/src/checker/javascript.rs 82.35% <100.00%> (+2.35%) ⬆️
...ig-code-analysis-ast/src/checker/js_doc_binding.rs 100.00% <100.00%> (ø)
big-code-analysis-ast/src/checker/kotlin.rs 92.59% <100.00%> (-1.41%) ⬇️
big-code-analysis-ast/src/checker/mozcpp.rs 100.00% <ø> (ø)
... and 56 more

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

RUSTSEC-2026-0383 (webpki-root-certs < 1.0.10 bundles the emSign ECC
Root CA - G3, whose distrusted intermediates' private keys are now
public) and RUSTSEC-2026-0382 (rustls-webpki < 0.103.16 may accept a
path through those intermediates). Both reach the workspace only
through the dev-dependency chain jsonschema → reqwest → rustls, so no
shipped binary changes. `cargo deny check` is clean again.
Codecov listed the expression-bodied arrow arm of the JS family's
holds_doc_comment_in_body, the InternalModule arm of tsdoc_target and
DocBinding::new as never run. Rows for a doc comment in an
expression-bodied arrow (JSDoc attaches it to nothing), in an arrow's
object literal (a member JSDoc documents, so a doc row where it sits)
and above a top-level `namespace N {}` (tsc's ModuleDeclaration carries
it), each verified against jsdoc 4 / tsc 5.9 and by deleting the
comment from the fixture. DocBinding::new is a const fn every language
evaluates at compile time, so one runtime call pins its defaults.

Refs #1613
@dekobon
dekobon merged commit 1149499 into main Oct 20, 2026
64 checks passed
@dekobon
dekobon deleted the fix/batch-2026-10-17 branch October 20, 2026 21:49

This branch was successfully deployed

1 active deployment
github-pages — 1149499f Deployed Oct 20, 2026 by dekobon via deploy to GitHub Pages #828
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.

fix(vcs): --fail-above trips at the threshold, check trips past it feat(mi): size term and SEI comment term for every language

1 participant