Skip to content

grow/capacity/rate arg-type errors are positionless → editors can't squiggle the bad argument #164

Description

@dvhthomas

Summary

Runtime argument-type errors from the growth / capacity / rate builtins (e.g. grow) are returned as bare fmt.Errorf values with no source position. They reach the document as a Diagnostic carrying a line but no Column / EndColumn, so editor/LSP consumers can show the message in the gutter but cannot underline the offending argument inline — the way semantic-checker diagnostics (e.g. undefined variable) are underlined.

Repro

sx_grown = grow(100, 20%, 5)

grow's increment (2nd arg) must be a number / quantity / currency; 20% is a percentage and is correctly rejected:

grow: increment must be a number, quantity, or currency — got percentage

The diagnostic is reported, but with no character range. (Concrete consumer check: calcmark-web's POST /api/eval returns this diagnostic with range: undefined.)

Root cause

impl/interpreter/growth_functions.go (and the sibling capacity_functions.go, rate_eval.go, which share the error shape) reject arguments with a positionless error:

return decZero, fmt.Errorf(
    "%s: %s must be a number, quantity, or currency — got percentage",
    funcName, paramName)

spec/document.Diagnostic already has the fields to carry a span (Line, Column, EndLine, EndColumn), but these errors never populate Column / EndColumn — no AST position is attached, so the diagnostic is line-only downstream.

Impact

Editor/LSP consumers (e.g. calcmark-web) can render the gutter message but can't place an inline squiggle under the bad argument. Users see "there's an error on this line" but not "this token is the problem" — a noticeable gap vs. semantic-checker errors, which underline the exact token.

Suggested fix (either is sufficient)

  1. Interpreter — when these builtins reject an argument, attach that argument's AST Range to the emitted Diagnostic (the call node knows each argument's position). Consumers already map a diagnostic Range → inline underline, so this lights up with zero consumer changes.

  2. Semantic checker (cleaner long-term) — teach the checker the parameter-type contracts for these builtins (e.g. grow's increment ∈ {number, quantity, currency}) so invalid arguments are caught at check time with a Range, the same path that already produces ranged diagnostics for undefined variables. This also surfaces the error earlier, before evaluation.

Notes

Found during a fit-and-finish pass on calcmark-web. The same positionless-error shape affects other runtime type errors too (e.g. currency ÷ currency), which share the identical "gutter yes, squiggle no" symptom.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions