Skip to content

perl args: traditional subs have no formal signature -- ground truth only checked the declaration, and GitGalaxy's own shift/my@_ counting only saw the first statement #1519

Closed

Description

@squid-protocol

Summary

args_exact_match for perl measured 137/937 (14.6%) in docs/self_scan/tree_sitter_accuracy_history.csv -- second-worst score in the table, reading as "GitGalaxy's perl args regex is badly broken." As with shell (#1518), it wasn't: confirmed all ~800 mismatches were real=0 got=N, where traditional Perl subs (sub foo { my ($x,$y) = @_; ... }) have NO formal parameter list in the declaration at all -- args arrive via body-level idioms (my (...) = @_, my $x = shift, bare shift), and both the ground-truth measurement and GitGalaxy's own engine had real, fixable gaps in reading those idioms.

Root cause 1 (measurement): ground truth only checked the declaration

_get_param_count had no perl branch at all -- it checked for a formal parameters field, found nothing (traditional Perl has none), and returned 0 regardless of the body's real idiom.

Root cause 2 (engine): single match only ever saw the first arg-unpacking statement

Confirmed via bugzilla/Bug.pm::ValidateDependencies ($fields->{dependson} = shift; $fields->{blocked} = shift; my $id = shift || 0; -- three separate shift statements, real arity 3) and many subs mixing my $class = shift; (invocant) with a later my ($a, $b) = @_; (the rest) -- GitGalaxy's regex-based counting took one .search() per function and only ever counted the first matching statement.

Root cause 3 (engine, real false-positive): bare shift matched shift @other_array/shift(@other_array)

The old (\bshift\b) alternative matched the literal word "shift" anywhere, including calls that shift a COMPLETELY DIFFERENT array (shift @some_queue;), nothing to do with unpacking @_.

Root cause 4 (engine gap): my @statuses = @_; (bare array catch-all) wasn't recognized at all

Distinct from my ($x, $y) = @_ (fixed-size destructure) -- a parenless my @array = @_ catches ALL remaining args as a variadic "rest", extremely common after shifting off a fixed prefix (e.g. my $class = shift; my @statuses = @_;). The regex only recognized the parenthesized form.

Fix (implemented, see PR)

  • tests/tools/tree_sitter_accuracy_audit.py: new _count_perl_real_args, preferring a real Perl 5.20+ signature node if present, else recursively walking the sub's body (via tree-sitter's real parse, not descending into a nested sub's own body) summing every my (...) = @_ (including a bare undef placeholder slot used to skip an unwanted positional arg, e.g. my ($a, undef, $c) = @_; -- confirmed via bugzilla/Bug.pm::_check_component, still a real consumed argument) and every bare shift call found anywhere in the body.
  • gitgalaxy/core/detector.py: new opt-in _args_findall_sum_groups routing flag -- scans the whole block and SUMS every matching statement's contribution instead of taking the first.
  • gitgalaxy/standards/language_standards.py: tightened the shift alternative to exclude shift @other/shift(@other); added a new alternative for the bare my @array = @_ catch-all idiom.

args_exact_match: 137/937 (14.6%) -> 769/937 (82.1%).

Known remaining gap (documented, not fixed)

A nested anonymous sub's own my (...) = @_ (a closure/callback defined inside another sub, e.g. bugzilla/Bug.pm::_cf_accessor) gets summed into the OUTER sub's count too, since GitGalaxy's flat regex scan has no real nested-block-boundary awareness the way the ground-truth walker (which explicitly stops at a nested sub boundary) does. Fixing this on the engine side would need real block-boundary detection within the text-scanning architecture -- noted as a real, if uncommon, source of overcounting rather than chased further here.

Why this isn't "GitGalaxy loses to tree-sitter here"

For this one specific signal (parameter count), in this one specific circumstance (no formal parameter-list syntax exists), a plain AST/tree-sitter-only tool has nothing to read at the declaration and can only ever report 0 -- correct, but useless for coupling/complexity scoring. GitGalaxy's body-aware regex is the more informative measurement here. Full writeup: docs/why_gitgalaxy_beats_ast_here.md.

This only affects extraction-pillar args-counting accuracy (epic #813) and the args_exact_match measurement (epic #1227/#1261), not risk scoring beyond the signal's own coupling number.

Activity

  1. added
    bugUnintended behavior or logic failure in the engine
    testingUnit, integration, and E2E pipeline verification
    core-engineModifications to the central physics and parsing engine
    threat: false-negativeAccuracy tuning: Engine missed a bypass or vulnerability
    on Aug 14, 2026
  2. squid-protocol commented on Aug 15, 2026

    @squid-protocol
    OwnerAuthor

    This was already fixed by PR #1520 (fix(shell,perl): args-counting for languages with no formal parameter-list syntax, merged 2026-08-14) -- that PR's own body said "Fixes #1518, #1519" but GitHub only linked #1518 via closing-issue-references, so this one never auto-closed despite being resolved.

    Independently re-verified against current main: _count_perl_real_args (ground-truth counter in tests/tools/tree_sitter_accuracy_audit.py), _args_findall_sum_groups (gitgalaxy/standards/language_standards.py), and the summing routing logic (gitgalaxy/core/detector.py) are all present and cover all 3 root causes described here (plus a 4th this issue didn't originally enumerate: the bare my @array = @_ catch-all). args_exact_match for perl now measures 923/938 = 98.4%.

    Closing as already resolved.

  3. added this to the v2.4.8 milestone on Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUnintended behavior or logic failure in the enginecore-engineModifications to the central physics and parsing enginetestingUnit, integration, and E2E pipeline verificationthreat: false-negativeAccuracy tuning: Engine missed a bypass or vulnerability

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions