Skip to content

fix(nargs): stop counting comments as parameters - #1207

Merged
dekobon merged 2 commits into
mainfrom
fix/1201-nargs-comment-params
Aug 4, 2026
Merged

fix(nargs): stop counting comments as parameters#1207
dekobon merged 2 commits into
mainfrom
fix/1201-nargs-comment-params

Conversation

@dekobon

@dekobon dekobon commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Fixes #1201.

tree-sitter attaches a comment written inside a parameter list as a
direct child of the parameter-list node, not inside the parameter it
documents. Every nargs filter listed punctuation only, so each comment
scored one: int h(int a /* one */, int b /* two */) reported 4, and
the C++ idiom for a deliberately unused parameter,
void f(int /*unused*/), reported 2.

Since #1196 the gate reads the per-callable own count, so a commented
signature could trip a bca check --threshold nargs=N its real arity
would pass.

The fix

One shared count_args<T> replaces four separate negative filters, so
the rule is stated once and the next language inherits it rather than
needing the same edit in twenty is_non_arg impls. Elixir's
count_elixir_args turned out to be identical to the shared helper once
fixed, and is deleted.

Repaired: C, C++, Mozcpp, Objective-C functions, JavaScript, MozJS,
TypeScript, TSX, Python, Rust, Java, C#, PHP, Ruby, Groovy functions,
Elixir, Kotlin lambdas.

Verified already correct, swept as regression guards: Go, Lua,
Objective-C methods and blocks, Kotlin functions, Groovy closures, Perl.
Bash has no formal parameter list.

Two corrections to the issue's scoping

Both established by AST dump rather than by reading the code.

Tcl had not "already solved this" — it has no problem to solve. The
issue proposed generalising Tcl's handling; compute_tcl_args filters
positively for Tcl::Argument and never encounters a comment, because
Tcl recognises one only where a command is expected. So

proc h {a
  # c
  b} { return $a }

genuinely declares four arguments named a, #, c and b, and the
grammar emits four argument nodes with no comment node. Tcl's reported
4 is correct and unchanged, now pinned by
tcl_has_no_comment_inside_a_parameter_list.

compute_kotlin_lambda_args was a fourth broken loop the issue does
not name.
It filtered != Kotlin::COMMA, so
{ a: Int, /* one */ b: Int -> a } reported 3. Kotlin functions were
fine (positive Parameter filter), which is why a function-only check
would have missed it.

Verification

  • Per-loop revert. Before the loops were consolidated each was
    reverted independently and failed only its own fixtures. After
    consolidation there is one line to revert; doing so fails 37 of 43
    fixtures, and the 6 survivors are exactly the positive-filter guards.
  • The guards are live, not decorative. Swapping Go's positive
    parameter_declaration filter for a negative one fails both Go rows
    and nothing else.
  • Kotlin widening is safe. { (a, b), c -> } = 2, { (a, b) -> } =
    1, { -> } = 0 — a destructuring pattern nests its parens inside a
    multi_variable_declaration, so no paren is ever a direct child of
    lambda_parameters.
  • is_bare_param is the one path the new predicate never runs on.
    bca dump shows why it is safe: in x /* c */ -> x the
    block_comment is a sibling of the bare identifier. The comment is
    therefore not a discriminating input, so that test is written as a
    parity assertion between the commented and bare spellings and says so
    in its doc comment rather than implying coverage it does not have.
  • Corpus. 854 recorded values move, every one downward, no other
    metric touched. Hand-checked against source: kenlm's
    DontBhiksha::DontBhiksha has 4 real parameters and 3 /*…*/
    comments (7 → 4), ReadBackoff has 2 and 1 (3 → 2).
  • make pre-commit passes. Patch coverage on the changed production
    lines is 100%, against a project line coverage of 94.04%.

Notes for review

tree-sitter attaches a comment written inside a parameter list as a
direct child of the parameter-*list* node, not inside the parameter it
documents. Every nargs filter listed punctuation only, so each comment
scored one: `int h(int a /* one */, int b /* two */)` reported 4, and
the C++ idiom for a deliberately unused parameter,
`void f(int /*unused*/)`, reported 2.

Excluded in one shared `count_args` rather than in twenty `is_non_arg`
impls, so the next language inherits it. Four independent loops
collapsed onto it: `compute_args` (C, C++, Mozcpp, Objective-C
functions, JavaScript, MozJS, TypeScript, TSX, Python, Rust, Java, C#,
PHP, Ruby, Groovy functions), Elixir's `count_elixir_args` — which the
shared helper now replaces outright — `compute_kotlin_lambda_args`, and
`compute_perl_args`, the last of which already excluded comments and is
unchanged in behaviour.

Two corrections to the issue's scoping, both confirmed by AST dump.
Tcl needed no fix and had solved nothing: it recognises a comment only
where a command is expected, so `proc h {a\n# c\n b}` really does
declare four arguments and the grammar emits four `argument` nodes with
no comment node. And `compute_kotlin_lambda_args` was a fourth broken
loop the issue did not name.

Each of the four loops is covered by a fixture that fails when that
loop alone is reverted. The corpus snapshots move 854 values, all
downward, including kenlm's `DontBhiksha::DontBhiksha` (7 -> 4) and
`ReadBackoff` (3 -> 2).

Folding four counting loops into one drops `src/metrics/nargs.rs` from
547 to 536 loc.ploc, so `.bca-baseline.toml` ratchets down; the refresh
also drops four entries left stale by #1203.

Fixes #1201
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.81421% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.27%. Comparing base (8e40738) to head (d5cd307).

Files with missing lines Patch % Lines
src/metrics/nargs.rs 97.81% 2 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##             main    #1207    +/-   ##
========================================
  Coverage   98.27%   98.27%            
========================================
  Files         276      276            
  Lines       71501    71658   +157     
  Branches    71071    71228   +157     
========================================
+ Hits        70266    70423   +157     
+ Misses        820      818     -2     
- Partials      415      417     +2     
Flag Coverage Δ
rust 98.26% <97.81%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
src/metrics/nargs.rs 99.28% <97.81%> (-0.10%) ⬇️

... and 1 file 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.

Codecov put the PR's patch coverage at 92.31% against a project 98.26%,
with all 13 missing lines in this file's test module. None were
production: the changed production lines measure 100%. They were the
lines that run only when the test fails — the `format!` inside the
failure branch, and the `assert!` arguments, which are evaluated at
panic time.

Three changes, each of which also reads better than what it replaces:

- The failure branch pushes the raw `(lang, source, expected, got)`
  tuple and the report is built once, by `{failures:#?}` in the
  assertion message. The "collect every failure rather than stopping at
  the alphabetically first" property is preserved — reverting the fix
  still reports `37/43` with every language named.
- The assertion's two computed arguments are bound eagerly and
  interpolated by name.
- The two `if !lang.is_enabled() { continue; }` guards become an
  iterator `filter`.

Patch coverage measured locally with the CI command
(`cargo llvm-cov --no-report nextest --all-features --workspace
--locked`): 92.31% -> 98.00%, production lines 100%.

The five that remain are not reachable by any test. Four are comment
and doc-comment lines that cargo-llvm-cov's codecov output assigns a
region to, because a multi-line region contributes to every line it
spans. The fifth is the `failures.push` itself, which no passing run can
execute.

Refs #1201
@dekobon

dekobon commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Coverage follow-up (d5cd307)

Codecov flagged patch coverage at 92.31% against a project 98.26%. I
reproduced it locally with the CI command
(cargo llvm-cov --no-report nextest --all-features --workspace --locked)
and split the patch by module:

instrumented missing patch %
production 20 0 100%
test module 228 13 94.3%

None of the shortfall was production code. All 13 were lines that run
only when the test fails — the format! inside the failure branch,
the assert! arguments (evaluated at panic time), and the
if !lang.is_enabled() { continue; } guards' untaken arm.

Three changes, each of which also reads better than what it replaced:
the failure branch now pushes the raw (lang, source, expected, got)
tuple with the report built once by {failures:#?}; the assertion's
computed arguments are bound eagerly and interpolated by name; the two
if/continue guards became an iterator filter. The "collect every
failure rather than stopping at the alphabetically first" property is
preserved — reverting the fix still reports 37/43 naming every
language.

Result: patch 92.31% → 97.81%, and the project dip is gone (Misses -2, previously +9).

The 4 that remain are not reachable by any test

Three are comment and doc-comment lines. cargo-llvm-cov's codecov
output assigns a region value to them because a multi-line region
contributes to every line it spans — e.g. line 4205 is a bare ///
scoring 0/1. No test can execute a comment.

The fourth is failures.push(...) itself, which no passing run can
reach by construction.

Worth knowing for future PRs

codecov.yml's ignore list has **/tests/**, but this repo keeps
most of its tests in #[cfg(test)] modules inside src/, which no
path-based ignore can exclude. So a test-heavy patch is always measured
on its own test code, including that code's failure paths. Nothing to
change here — just the reason patch coverage reads below project
coverage on PRs like this one.

@dekobon
dekobon merged commit dbc24cf into main Aug 4, 2026
40 checks passed
@dekobon
dekobon deleted the fix/1201-nargs-comment-params branch August 4, 2026 21:48
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(nargs): comments inside parameter lists are counted as parameters

1 participant