plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


69225da — Peter John 2026-11-01T17:06:45+05:30
fix: checker errors point at the failing call, not the fn header
plum-checker/src/lib.rs CHANGED
@@ -24,6 +24,9 @@ thread_local! {
24
24
  /// Position (line, column) of the innermost call expression being inferred, or of the
25
25
  /// declaration being checked outside any call: the "called at" site of recorded calls.
26
26
  static CUR_POS: Cell<(usize, usize)> = const { Cell::new((0, 0)) };
27
+ /// Position of the innermost call whose inference failed, so a statement's
28
+ /// error points at that call rather than the enclosing fn/test header.
29
+ static ERR_POS: Cell<Option<(usize, usize)>> = const { Cell::new(None) };
27
30
  /// Address of the expression `inferExprWith` is currently inferring (the innermost one).
28
31
  static CUR_EXPR: Cell<usize> = const { Cell::new(0) };
29
32
  /// While checking the last statement of a value-producing tail (an fn body's
@@ -2139,6 +2142,25 @@ fn checkStmt(
2139
2142
  fn_name: &str,
2140
2143
  fn_pos: (usize, usize),
2141
2144
  ctx: &CheckCtx,
2145
+ ) -> Vec<CheckError> {
2146
+ let saved = ERR_POS.with(|e| e.replace(None));
2147
+ let mut errors = checkStmtInner(stmt, env, declared_ret, fn_name, fn_pos, ctx);
2148
+ if let Some((l, c)) = ERR_POS.with(|e| e.replace(saved)) {
2149
+ for e in errors.iter_mut().filter(|e| (e.line, e.column) == fn_pos) {
2150
+ e.line = l;
2151
+ e.column = c;
2152
+ }
2153
+ }
2154
+ errors
2155
+ }
2156
+
2157
+ fn checkStmtInner(
2158
+ stmt: &ast::Stmt,
2159
+ env: &mut TypeEnv,
2160
+ declared_ret: &PlumType,
2161
+ fn_name: &str,
2162
+ fn_pos: (usize, usize),
2163
+ ctx: &CheckCtx,
2142
2164
  ) -> Vec<CheckError> {
2143
2165
  let mut errors = Vec::new();
2144
2166
  let tail = TAIL.with(|t| t.borrow_mut().take());
@@ -3475,6 +3497,9 @@ pub fn inferExprWith(
3475
3497
  CUR_POS.with(|p| p.set(pos));
3476
3498
  }
3477
3499
  let r = inferExprInner(expr, env, ctx, expected);
3500
+ if r.is_err() && call_pos.is_some_and(|p| p.0 != 0) && ERR_POS.with(|e| e.get()).is_none() {
3501
+ ERR_POS.with(|e| e.set(call_pos));
3502
+ }
3478
3503
  CUR_POS.with(|p| p.set(prev_pos));
3479
3504
  CUR_EXPR.with(|c| c.set(prev));
3480
3505
  if let Ok(t) = &r {
plum-checker/tests/checker_tests.rs CHANGED
@@ -2125,5 +2125,10 @@ fn unknown_method_on_heap_const_is_checker_error() {
2125
2125
  };
2126
2126
  let msg = errText(checkSrc(&src("Cfg.CELL.size()")));
2127
2127
  assert!(msg.contains("Box[Int] has no method 'size'"), "{}", msg);
2128
+ let errs = checkSrc(&src("Cfg.CELL.size()")).unwrap_err();
2129
+ let e = errs.iter().find(|e| e.message.contains("no method 'size'")).unwrap();
2130
+ assert_eq!(e.line, 13, "{:?}", e);
2131
+ let assign = checkSrc(&src("n := Cfg.CELL.size()\n n")).unwrap_err();
2132
+ assert_eq!(assign[0].line, 13, "{:?}", assign);
2128
2133
  assert!(checkSrc(&src("Cfg.CELL.get()")).is_ok());
2129
2134
  }