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)
-
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.
-
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.
Summary
Runtime argument-type errors from the growth / capacity / rate builtins (e.g.
grow) are returned as barefmt.Errorfvalues with no source position. They reach the document as aDiagnosticcarrying a line but noColumn/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
grow's increment (2nd arg) must be a number / quantity / currency;20%is a percentage and is correctly rejected:The diagnostic is reported, but with no character range. (Concrete consumer check: calcmark-web's
POST /api/evalreturns this diagnostic withrange: undefined.)Root cause
impl/interpreter/growth_functions.go(and the siblingcapacity_functions.go,rate_eval.go, which share the error shape) reject arguments with a positionless error:spec/document.Diagnosticalready has the fields to carry a span (Line,Column,EndLine,EndColumn), but these errors never populateColumn/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)
Interpreter — when these builtins reject an argument, attach that argument's AST
Rangeto the emittedDiagnostic(the call node knows each argument's position). Consumers already map a diagnosticRange→ inline underline, so this lights up with zero consumer changes.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 aRange, 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.