Skip to content

Add Perl language support (.pl, .pm, .t) — grammar available, package scoping needs care #1738

Description

@netbrah

Context

CodeGraph has no Perl support today — .pl / .pm files aren't detected, so a Perl codebase indexes to zero symbols. Perl is item #9 on the umbrella language-coverage request in #1533; this is a focused issue for just that item, with a PR attached.

Perl is still the backbone of a lot of long-lived infrastructure: test harnesses, build and release tooling, DevOps automation, and large in-house systems that have accumulated for decades. Those are exactly the codebases where nobody remembers how anything connects, and where a graph is worth the most.

Why Perl is worth doing

Adding it turned out to be cheap. The tree-sitter-perl project publishes an official prebuilt tree-sitter-perl.wasm on its v2.0.0 release, and it's ABI 15 — so it drops straight into the existing vendored-grammar path with no emscripten or Docker build, and none of the ABI-13 shared-heap corruption that has bitten other grammars.

What makes Perl interesting to model

Two things don't fall out of generic tree-sitter handling, and getting them wrong quietly produces a useless graph:

1. Package scoping. The dominant idiom scopes by following sibling, not by containment:

package Sample::Widget;

sub new  { ... }
sub name { ... }

new and name are siblings of the package node, not children. Generic parent/child containment attaches them to the file and loses the Sample::Widget::new qualified name entirely.

And a package name is always absolute — this declares Inner, never Outer::Inner:

package Outer;
package Inner { sub b { ... } }

2. Method calls. $obj->method() parses as method_call_expression with invocant / method fields — none of the function / object / name fields the generic call path looks for. Left alone, it takes the invocant as the callee, so $obj->configure() records a call to $obj and configure's callers are invisible.

Where Perl can't be answered statically

Perl resolves a lot at run time, and this is where a naive implementation actively hurts. Each of these was measured emitting wrong edges on a real 913-file Perl codebase:

  • Chained invocant$self->service_db()->attribute_add(%opts) dispatches on whatever service_db() returned. Emitting the bare name attribute_add let it exact-match the enclosing same-named sub, producing hundreds of wrong self-referential call edges.
  • Dynamic method$obj->$method() emitted $method, which name-matching bound to the variable of that name — an edge to something that isn't callable.
  • SUPER:: — dispatches to a parent's method, but with no inheritance/MRO model the bare name binds to the child's own override, i.e. precisely the method Perl will not run.
  • Builtinspush, die, print, join parse as ordinary calls and can never resolve to a project symbol.

Consistent with the project's "silent beats wrong" principle, none of these should emit an edge.

Ambiguous extensions

.t is Perl's standard test suffix but is also Raku/Terra/Turing; .cgi is as often Python or shell. A shebang naming another interpreter should win over the extension — the same content-based disambiguation .h → C/C++/Objective-C already does.

Proposal

Wire Perl through the existing vendored-wasm path: .pl, .pm, .t, .pod, .psgi, .cgi, with packages as namespaces, subroutines with qualified names, use/require imports (pragmas filtered), use parent/use base inheritance including qw() lists, use constant, package-level variables, and call/instantiation edges — with the run-time-only cases above deliberately left silent.

Projects using non-standard Perl extensions can opt in through codegraph.json's extensions map, so nothing unusual needs to be hardcoded.

I have this working and will open a PR against this issue.

Related: #1533 (item 9).

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions