From ae527b46df14125651350c5ae4b384059e6cf205 Mon Sep 17 00:00:00 2001 From: Glyphack Date: Sat, 9 Sep 2023 22:44:51 +0200 Subject: [PATCH] add undefined error, line numbers and other stuff --- typechecker/src/build.rs | 13 ++++++- typechecker/src/type_check/checker.rs | 37 ++++++++++++++----- typechecker/src/type_check/type_inference.rs | 3 ++ .../testdata/inputs/type_check_undefined.py | 4 ++ ...hecker__build__tests__type_check_call.snap | 4 +- ...hecker__build__tests__type_check_list.snap | 4 +- ...r__build__tests__type_check_undefined.snap | 8 ++++ ...checker__build__tests__type_check_var.snap | 4 +- 8 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 typechecker/testdata/inputs/type_check_undefined.py create mode 100644 typechecker/testdata/output/typechecker__build__tests__type_check_undefined.snap diff --git a/typechecker/src/build.rs b/typechecker/src/build.rs index 05b14571..73d2ee21 100644 --- a/typechecker/src/build.rs +++ b/typechecker/src/build.rs @@ -83,7 +83,11 @@ impl BuildManager { for stmt in &state.1.file.body { checker.type_check(stmt); } - self.errors.append(&mut checker.errors); + for error in checker.errors { + let line = get_line_number_of_character_position(&state.1.file.source, error.start); + let error = format!("{} line {}: {}", state.0, line, error.msg); + self.errors.push(error); + } } } } @@ -99,7 +103,7 @@ fn get_line_number_of_character_position(source: &str, pos: usize) -> usize { } } line_number -} + } #[cfg(test)] mod tests { @@ -189,4 +193,9 @@ mod tests { test_type_check_list, "../testdata/inputs/type_check_list.py" ); + + snap_type!( + test_type_check_undefined, + "../testdata/inputs/type_check_undefined.py" + ); } diff --git a/typechecker/src/type_check/checker.rs b/typechecker/src/type_check/checker.rs index b5cb1e35..d5959b3b 100644 --- a/typechecker/src/type_check/checker.rs +++ b/typechecker/src/type_check/checker.rs @@ -10,13 +10,19 @@ use super::{type_evaluator::TypeEvaluator, type_inference::type_check_bin_op, ty // TODO: currently only supporting a single file pub struct TypeChecker<'a> { - pub errors: Vec, + pub errors: Vec, // The symbol table of the module being type checked symbol_table: SymbolTable, pub options: &'a Settings, type_evaluator: TypeEvaluator, } +pub struct TypeCheckError { + pub msg: String, + pub start: usize, + pub end: usize, +} + #[allow(unused)] impl<'a> TypeChecker<'a> { pub fn new(module: &'a State, options: &'a Settings) -> Self { @@ -38,8 +44,7 @@ impl<'a> TypeChecker<'a> { Ok(t) => t, Err(e) => { self.make_error( - &format!("Failed to infer type for expression: {}", e), - &format!("{:?}", expr), + e.to_string().as_str(), expr.get_node().start, expr.get_node().end, ); @@ -48,8 +53,12 @@ impl<'a> TypeChecker<'a> { } } - fn make_error(&mut self, msg: &str, code: &str, start: usize, end: usize) { - let error = format!("{}: {} at ({})", msg, code, start); + fn make_error(&mut self, msg: &str, start: usize, end: usize) { + let error = TypeCheckError { + msg: msg.to_string(), + start, + end, + }; self.errors.push(error); } } @@ -237,10 +246,15 @@ impl<'a> TraversalVisitor for TypeChecker<'a> { let r_type = self.infer_expr_type(&b.right); if !type_check_bin_op(&l_type, &r_type, &b.op) { - self.errors.push(format!( + let msg = format!( "Operator '{}' not supported for types '{}' and '{}'", b.op, l_type, r_type - )); + ); + self.errors.push(TypeCheckError { + msg, + start: b.node.start, + end: b.node.end, + }); } } @@ -300,10 +314,15 @@ impl<'a> TraversalVisitor for TypeChecker<'a> { .get_symbol_node_type(symbol, n.node.start).unwrap_or(Type::Unknown); let value_type = self.infer_expr_type(&_a.value); if !is_reassignment_valid(&prev_target_type, &value_type) { - self.errors.push(format!( + let msg = format!( "Cannot assign type '{}' to variable of type '{}'", value_type, prev_target_type - )); + ); + self.errors.push(TypeCheckError { + msg, + start: n.node.start, + end: n.node.end, + }); } } } diff --git a/typechecker/src/type_check/type_inference.rs b/typechecker/src/type_check/type_inference.rs index c562d132..0594f8bd 100644 --- a/typechecker/src/type_check/type_inference.rs +++ b/typechecker/src/type_check/type_inference.rs @@ -100,6 +100,9 @@ pub fn type_check_bin_op(t1: &Type, t2: &Type, op: &BinaryOperator) -> bool { }; for (t1_, t2_) in check_table { + if matches!(t1, Type::Unknown) || matches!(t2, Type::Unknown) { + return true; + } if type_equal(t1, &t1_) && type_equal(t2, &t2_) { return true; } diff --git a/typechecker/testdata/inputs/type_check_undefined.py b/typechecker/testdata/inputs/type_check_undefined.py new file mode 100644 index 00000000..a9b48820 --- /dev/null +++ b/typechecker/testdata/inputs/type_check_undefined.py @@ -0,0 +1,4 @@ +a = b + 1 + +a = c() + diff --git a/typechecker/testdata/output/typechecker__build__tests__type_check_call.snap b/typechecker/testdata/output/typechecker__build__tests__type_check_call.snap index f6432851..b93fc944 100644 --- a/typechecker/testdata/output/typechecker__build__tests__type_check_call.snap +++ b/typechecker/testdata/output/typechecker__build__tests__type_check_call.snap @@ -3,5 +3,5 @@ source: typechecker/src/build.rs description: "def function() -> int:\n return 1\n\na = function()\nb = function() + \"1\"\nc = a + 1\nd = function() + 1\n\nfunction + 1\n" expression: result --- -Operator '+' not supported for types 'Int' and 'Str' -Operator '+' not supported for types 'function' and 'Int' +test line 5: Operator '+' not supported for types 'Int' and 'Str' +test line 9: Operator '+' not supported for types 'function' and 'Int' diff --git a/typechecker/testdata/output/typechecker__build__tests__type_check_list.snap b/typechecker/testdata/output/typechecker__build__tests__type_check_list.snap index c9d0c915..7e00ca6f 100644 --- a/typechecker/testdata/output/typechecker__build__tests__type_check_list.snap +++ b/typechecker/testdata/output/typechecker__build__tests__type_check_list.snap @@ -3,5 +3,5 @@ source: typechecker/src/build.rs description: "a: list[int] = [1, 2, 3]\n\nb = a[0] + 1\n\nc = a[0] + a[1]\n\n# invalid usage of types\nd = a[0] + \"str\"\n\n# valid reassignment\na = [1]\n# invalid reassignment\na = [1, 2, \"str\"]\n" expression: result --- -Operator '+' not supported for types 'Int' and 'Str' -Cannot assign type 'builtins.list[Unknown]' to variable of type 'builtins.list[Int]' +test line 8: Operator '+' not supported for types 'Int' and 'Str' +test line 13: Cannot assign type 'builtins.list[Unknown]' to variable of type 'builtins.list[Int]' diff --git a/typechecker/testdata/output/typechecker__build__tests__type_check_undefined.snap b/typechecker/testdata/output/typechecker__build__tests__type_check_undefined.snap new file mode 100644 index 00000000..fb458aca --- /dev/null +++ b/typechecker/testdata/output/typechecker__build__tests__type_check_undefined.snap @@ -0,0 +1,8 @@ +--- +source: typechecker/src/build.rs +description: "a = b + 1\n\na = c()\n\n" +expression: result +--- +test line 1: symbol b is not defined +test line 1: symbol b is not defined +test line 3: symbol c is not defined diff --git a/typechecker/testdata/output/typechecker__build__tests__type_check_var.snap b/typechecker/testdata/output/typechecker__build__tests__type_check_var.snap index 757ec01d..711f86e8 100644 --- a/typechecker/testdata/output/typechecker__build__tests__type_check_var.snap +++ b/typechecker/testdata/output/typechecker__build__tests__type_check_var.snap @@ -1,6 +1,6 @@ --- source: typechecker/src/build.rs -description: "a: int = 1\n\na + \"str\"\n" +description: "a: int = 1\n\na + \"str\"\n\nb = a + 1\n\nc = b + b\n" expression: result --- -Operator '+' not supported for types 'Int' and 'Str' +test line 3: Operator '+' not supported for types 'Int' and 'Str'