Skip to content

UpdateSourcePositions leaves JS-only nodes and their subtrees unpositioned #8536

Description

@eoliphan

UpdateSourcePositions leaves JS-only nodes and their subtrees unpositioned

Component: rewrite-javascript / rewrite-java (UpdateSourcePositions)
Versions: rewrite-javascript 8.90.0, Moderne CLI 4.6.1, TypeScript 5.9.3, Node 26.7.0

What happens

UpdateSourcePositions builds its position map with a JavaPrinter. A JavaPrinter cannot print
JS-only node types, so those nodes — and everything beneath them — never enter the position map
and receive no Range marker.

The JavaScript printer does implement these nodes (visitBindingElement, print.ts L57);
UpdateSourcePositions simply does not use it. There is no JS-side SourcePositionService
override, which looks like the natural fix location.

Minimal reproduction

// d-binding-element.ts
function pick(): number { return 1; }
export function destructure({ a = pick() }: { a?: number } = {}): number {
  return a;
}

Run UpdateSourcePositions over the compilation unit, then walk it. The J.MethodInvocation for
pick() — reached through a JS.BindingElement — has no Range marker, while ordinary
invocations elsewhere in the same file do.

A consumer that treats a missing Range after UpdateSourcePositions as a broken precondition
throws here:

java.lang.IllegalStateException: no Range marker on MethodInvocation in d-binding-element.ts
  ... LstCallIndex.visitMethodInvocation
  ... JavaScriptVisitor.visitBindingElement

Expected

Every node in a positioned compilation unit carries a Range, or the gap is documented so consumers
know a missing Range is a legitimate outcome for JS rather than a bug in their own pipeline.

Why this matters downstream

The failure is silent and partial, which is worse than loud. In our emitter the exception aborted
the compilation unit mid-scan: methods and calls had already been recorded, types had not. One
unpositioned node cost a 1,053-line file every type row it should have produced, the artifact looked
complete, and the run reported success. It surfaced only because an unrelated threshold could not
reach zero.

Any consumer that indexes a file in more than one pass is exposed to the same shape.

Timing / related

  • Open PR Report the line number of each search result in TypeUses and MethodCalls #8454 (line numbers in TypeUses/MethodCalls) is JavaPrinter-based via
    SourcePositionService and will inherit this gap for JS sources. Filing now, while that work is
    in flight, so the two can be considered together.
  • The fix likely belongs in a JS SourcePositionService override rather than in
    UpdateSourcePositions itself, which may make it larger than a drive-by change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions