plum

#treesitter#compiler#wasm

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

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


729d7cbPeter John 2026-07-19T19:17:31+05:30
test(plum-checker): add error-case tests for type checker
plum-checker/Cargo.toml CHANGED
@@ -5,3 +5,7 @@ edition = "2021"
5
5
 
6
6
  [dependencies]
7
7
  plum-core = { path = "../plum-core" }
8
+
9
+ [dev-dependencies]
10
+ tree-sitter = "0.26"
11
+ tree-sitter-plum = { path = "../tooling/tree-sitter-plum" }
plum-checker/src/lib.rs CHANGED
@@ -111,6 +111,17 @@ fn check_fn(f: &ast::Fn, global_env: &TypeEnv) -> Vec<CheckError> {
111
111
  ast::FnBody::Block(block) => {
112
112
  let mut block_errors = check_block(block, &mut env, &declared_ret, &f.name);
113
113
  errors.append(&mut block_errors);
114
+ // Check the type of the last expression statement against the declared return type
115
+ if let Some(ast::Stmt::Expr(last_expr)) = block.stmts.last() {
116
+ match infer_expr(last_expr, &env) {
117
+ Ok(t) => {
118
+ if let Err(msg) = unify(&declared_ret, &t) {
119
+ errors.push(CheckError { message: format!("fn '{}': return type mismatch: {}", f.name, msg) });
120
+ }
121
+ }
122
+ Err(_) => {} // already reported by check_block
123
+ }
124
+ }
114
125
  }
115
126
  }
116
127
  errors
plum-checker/tests/checker_tests.rs CHANGED
@@ -1,6 +1,15 @@
1
1
  use plum_checker::types::*;
2
- use plum_checker::{plum_type_from_ast, unify};
2
+ use plum_checker::{check_source, plum_type_from_ast, unify};
3
3
  use plum_core::ast::Type as AstType;
4
+ use plum_core::{ast::*, AstParser};
5
+
6
+ fn parse(src: &str) -> Source {
7
+ let mut parser = tree_sitter::Parser::new();
8
+ parser.set_language(&tree_sitter_plum::LANGUAGE.into()).unwrap();
9
+ let tree = parser.parse(src, None).unwrap();
10
+ let ap = AstParser::new(src);
11
+ ap.parse_source(tree.root_node())
12
+ }
4
13
 
5
14
  #[test]
6
15
  fn ast_type_int_maps_to_tint() {
@@ -41,3 +50,36 @@ fn mono_scheme() {
41
50
  assert!(scheme.vars.is_empty());
42
51
  assert_eq!(*scheme.body, PlumType::TInt);
43
52
  }
53
+
54
+ #[test]
55
+ fn valid_add_fn_passes() {
56
+ let src = "add(a: Int, b: Int) -> Int =\n a + b\n";
57
+ let source = parse(src);
58
+ assert!(check_source(&source).is_ok(), "expected Ok");
59
+ }
60
+
61
+ #[test]
62
+ fn wrong_return_type_is_error() {
63
+ let src = "bad() -> Int =\n True\n";
64
+ let source = parse(src);
65
+ let result = check_source(&source);
66
+ assert!(result.is_err());
67
+ let errs = result.unwrap_err();
68
+ assert!(errs[0].message.contains("return type mismatch"), "got: {}", errs[0].message);
69
+ }
70
+
71
+ #[test]
72
+ fn undeclared_var_is_error() {
73
+ let src = "bad() -> Int =\n x\n";
74
+ let source = parse(src);
75
+ let result = check_source(&source);
76
+ assert!(result.is_err());
77
+ }
78
+
79
+ #[test]
80
+ fn type_mismatch_in_binary_op_is_error() {
81
+ let src = "bad() -> Int =\n 1 + 2.0\n";
82
+ let source = parse(src);
83
+ let result = check_source(&source);
84
+ assert!(result.is_err());
85
+ }