Skip to content

perry compile drops the source span on lowering errors — bare message, no file:line:col (perry check shows it) #5249

Description

@proggeramlug

Summary

Lowering errors carry a source span (LowerError { message, span: Option<Span> }), and perry check renders it as a proper error[CODE] --> file:line:col diagnostic. But perry compile prints only the bare message with no location. On large/minified/generated inputs this makes any lowering wall effectively undiagnosable — you get a one-line error with no way to find the offending construct.

Repro

Same input, two front-ends:

// t.ts
for (var { a, b } = { a: 1, b: 2 }, i = 0; i < 1; i++) console.log(a, b, i);
$ perry check t.ts
error[U006]: Unsupported binding pattern
  --> t.ts:1:11
1 | for (var { a, b } = { a: 1, b: 2 }, i = 0; i < 1; i++) console.log(a, b, i);
  |          ^

$ perry compile t.ts -o /tmp/out
Error: Unsupported binding pattern        # <- no code, no file, no line:col

On a 13 MB single-file bundle the difference is stark: check pinpoints …:551:5336; compile gives you Error: Unsupported binding pattern and nothing else to go on.

Root cause

lower_bail! (crates/perry-hir/src/error.rs:46) correctly constructs a span-tagged LowerError:

macro_rules! lower_bail {
    ($span:expr, $($arg:tt)*) => {
        return Err(anyhow::Error::new(LowerError::new(format!($($arg)*), $span)))
    };
}

…but LowerError's Display (error.rs:34) emits only the message:

impl std::fmt::Display for LowerError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)   // span is dropped
    }
}

perry check downcasts to LowerError and resolves span against the source map to print file:line:col + a code. The compile error path just prints the anyhow Display chain, so the captured span is silently discarded. (The module doc-comment even notes the span exists "so downstream tooling (notably perry check) can downcast and produce a diagnostic with a proper span instead of a locationless message" — compile just doesn't do that downcast.)

Suggested fix

Have the compile error path downcast to LowerError (as check already does) and render the span — error[CODE] --> file:line:col — falling back to the bare message only when span is None. Ideally share the diagnostic renderer between check and compile so both stay consistent.

Why it matters

A locationless lowering error is fine on a 5-line test but useless on generated/minified/bundled code, which is exactly where un-handled syntactic corners surface. The span is already captured; it's purely a rendering gap on the compile side.

Environment

  • perry 0.5.1175, built from main @ a5d7b3e64

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

    bugConfirmed defect or regressiontoolingDeveloper tooling, CI, tests, or release infrastructure

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions