From eda65ad6609f0a7bee576a1e4209f86ecbd24615 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 19 Oct 2022 18:08:10 +0300 Subject: [PATCH] parser,checker,builder: make the checker more robust with `-check` over files with syntax/parsing errors --- vlib/v/builder/builder.v | 4 +-- vlib/v/builder/cbuilder/cbuilder.v | 6 ++-- vlib/v/checker/checker.v | 29 ++++++++++++------- vlib/v/checker/return.v | 4 +++ .../tests/with_check_option/v_tictactoe.out | 7 +++++ .../tests/with_check_option/v_tictactoe.vv | 27 +++++++++++++++++ .../v_tictactoe_fixed_syntax_error.out | 7 +++++ .../v_tictactoe_fixed_syntax_error.vv | 27 +++++++++++++++++ vlib/v/compiler_errors_test.v | 7 ++++- vlib/v/parser/parser.v | 4 +-- 10 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 vlib/v/checker/tests/with_check_option/v_tictactoe.out create mode 100644 vlib/v/checker/tests/with_check_option/v_tictactoe.vv create mode 100644 vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.out create mode 100644 vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.vv diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index a6f967ff03..fa822b1f7a 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -102,7 +102,7 @@ pub fn (mut b Builder) front_stages(v_files []string) ? { timers.show('PARSE') timers.show_if_exists('PARSE stmt') if b.pref.only_check_syntax { - return error_with_code('stop_after_parser', 9999) + return error_with_code('stop_after_parser', 7001) } } @@ -120,7 +120,7 @@ pub fn (mut b Builder) middle_stages() ? { return error('too many errors/warnings/notices') } if b.pref.check_only { - return error_with_code('stop_after_checker', 9999) + return error_with_code('stop_after_checker', 8001) } util.timing_start('TRANSFORM') b.transformer.transform_files(b.parsed_files) diff --git a/vlib/v/builder/cbuilder/cbuilder.v b/vlib/v/builder/cbuilder/cbuilder.v index 580c98e5e6..6bbb84bafa 100644 --- a/vlib/v/builder/cbuilder/cbuilder.v +++ b/vlib/v/builder/cbuilder/cbuilder.v @@ -38,10 +38,10 @@ pub fn compile_c(mut b builder.Builder) { pub fn gen_c(mut b builder.Builder, v_files []string) string { b.front_and_middle_stages(v_files) or { - if err.code() != 9999 { - builder.verror(err.msg()) + if err.code() > 7000 { + return '' } - return '' + builder.verror(err.msg()) } util.timing_start('C GEN') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4529f8e8b2..59303e9e3d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -582,10 +582,10 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { match mut expr { ast.CastExpr { // TODO - return '', pos + return '', expr.pos } ast.ComptimeSelector { - return '', pos + return '', expr.pos } ast.Ident { if mut expr.obj is ast.Var { @@ -619,6 +619,9 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { } } ast.IndexExpr { + if expr.left_type == 0 { + return to_lock, pos + } left_sym := c.table.sym(expr.left_type) mut elem_type := ast.Type(0) mut kind := '' @@ -651,10 +654,10 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { } ast.SelectorExpr { if expr.expr_type == 0 { - return '', pos + return '', expr.pos } // retrieve ast.Field - c.ensure_type_exists(expr.expr_type, expr.pos) or { return '', pos } + c.ensure_type_exists(expr.expr_type, expr.pos) or { return '', expr.pos } mut typ_sym := c.table.final_sym(c.unwrap_generic(expr.expr_type)) match typ_sym.kind { .struct_ { @@ -666,7 +669,7 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { if !has_field { type_str := c.table.type_to_str(expr.expr_type) c.error('unknown field `${type_str}.$expr.field_name`', expr.pos) - return '', pos + return '', expr.pos } if field_info.typ.has_flag(.shared_f) { expr_name := '${expr.expr}.$expr.field_name' @@ -702,7 +705,7 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { mut field_info := interface_info.find_field(expr.field_name) or { type_str := c.table.type_to_str(expr.expr_type) c.error('unknown field `${type_str}.$expr.field_name`', expr.pos) - return '', pos + return '', expr.pos } if !field_info.is_mut { type_str := c.table.type_to_str(expr.expr_type) @@ -717,7 +720,7 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { mut field_info := sumtype_info.find_field(expr.field_name) or { type_str := c.table.type_to_str(expr.expr_type) c.error('unknown field `${type_str}.$expr.field_name`', expr.pos) - return '', pos + return '', expr.pos } if !field_info.is_mut { type_str := c.table.type_to_str(expr.expr_type) @@ -756,18 +759,18 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) { } ast.ArrayInit { c.error('array literal can not be modified', expr.pos) - return '', pos + return '', expr.pos } ast.StructInit { - return '', pos + return '', expr.pos } ast.InfixExpr { - return '', pos + return '', expr.pos } else { if !expr.is_pure_literal() { c.error('unexpected expression `$expr.type_name()`', expr.pos()) - return '', pos + return '', expr.pos() } } } @@ -3495,6 +3498,10 @@ fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_ty pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { mut typ := c.expr(node.left) + if typ == 0 { + c.error('unknown type for expression `$node.left`', node.pos) + return typ + } mut typ_sym := c.table.final_sym(typ) node.left_type = typ match typ_sym.kind { diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index 7080096378..114c010f6a 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -39,8 +39,12 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) { mut expr_idxs := []int{} for i, expr in node.exprs { mut typ := c.expr(expr) + if typ == 0 { + return + } if typ == ast.void_type { c.error('`$expr` used as value', node.pos) + return } // Unpack multi return types sym := c.table.sym(typ) diff --git a/vlib/v/checker/tests/with_check_option/v_tictactoe.out b/vlib/v/checker/tests/with_check_option/v_tictactoe.out new file mode 100644 index 0000000000..917c7605a4 --- /dev/null +++ b/vlib/v/checker/tests/with_check_option/v_tictactoe.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/with_check_option/v_tictactoe.vv:4:31: error: expected `init:`, not `len` + 2 | + 3 | fn new_board() [][]string { + 4 | mut board := [3][]string{ len: 3, init: []string{ len: 3, init: '' } } + | ~~~ + 5 | for i in 0..9 { + 6 | board[i / 3][i % 3] = (i + 1).str() diff --git a/vlib/v/checker/tests/with_check_option/v_tictactoe.vv b/vlib/v/checker/tests/with_check_option/v_tictactoe.vv new file mode 100644 index 0000000000..5de8b26c24 --- /dev/null +++ b/vlib/v/checker/tests/with_check_option/v_tictactoe.vv @@ -0,0 +1,27 @@ +module main + +fn new_board() [][]string { + mut board := [3][]string{ len: 3, init: []string{ len: 3, init: '' } } + for i in 0..9 { + board[i / 3][i % 3] = (i + 1).str() + } + return board +} + +fn (mut b [][]string) place(player string, y int, x int) ? { + if b[y][x] in ['a'] { + error("position is already occupied") + } + b[y][x] = player +} + +fn prompt(player string) (int, int) { + return 0, 0 +} + +fn main() { + mut board := new_board() + mut player := 'X' + println(board) + println(player) +} diff --git a/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.out b/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.out new file mode 100644 index 0000000000..2e37853ff3 --- /dev/null +++ b/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.vv:8:9: error: cannot use `[3][]string` as type `[][]string` in return argument + 6 | board[i / 3][i % 3] = (i + 1).str() + 7 | } + 8 | return board + | ~~~~~ + 9 | } + 10 | diff --git a/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.vv b/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.vv new file mode 100644 index 0000000000..258cbbb6cd --- /dev/null +++ b/vlib/v/checker/tests/with_check_option/v_tictactoe_fixed_syntax_error.vv @@ -0,0 +1,27 @@ +module main + +fn new_board() [][]string { + mut board := [3][]string{init: []string{len: 3, init: ''}} + for i in 0 .. 9 { + board[i / 3][i % 3] = (i + 1).str() + } + return board +} + +fn (mut b [][]string) place(player string, y int, x int) ? { + if b[y][x] == 'a' { + error('position is already occupied') + } + b[y][x] = player +} + +fn prompt(player string) (int, int) { + return 0, 0 +} + +fn main() { + mut board := new_board() + mut player := 'X' + println(board) + println(player) +} diff --git a/vlib/v/compiler_errors_test.v b/vlib/v/compiler_errors_test.v index 97c5ce641f..383660255f 100644 --- a/vlib/v/compiler_errors_test.v +++ b/vlib/v/compiler_errors_test.v @@ -77,6 +77,7 @@ fn test_all() { vroot := os.dir(vexe) os.chdir(vroot) or {} checker_dir := 'vlib/v/checker/tests' + checker_with_check_option_dir := 'vlib/v/checker/tests/with_check_option' parser_dir := 'vlib/v/parser/tests' scanner_dir := 'vlib/v/scanner/tests' module_dir := '$checker_dir/modules' @@ -86,7 +87,7 @@ fn test_all() { skip_unused_dir := 'vlib/v/tests/skip_unused' trace_calls_dir := 'vlib/v/tests/trace_calls' // - checker_tests := get_tests_in_dir(checker_dir, false) + checker_tests := get_tests_in_dir(checker_dir, false).filter(!it.contains('with_check_option')) parser_tests := get_tests_in_dir(parser_dir, false) scanner_tests := get_tests_in_dir(scanner_dir, false) global_tests := get_tests_in_dir(global_dir, false) @@ -95,6 +96,8 @@ fn test_all() { run_tests := get_tests_in_dir(run_dir, false) skip_unused_dir_tests := get_tests_in_dir(skip_unused_dir, false) trace_calls_dir_tests := get_tests_in_dir(trace_calls_dir, false) + checker_with_check_option_tests := get_tests_in_dir(checker_with_check_option_dir, + false) mut tasks := Tasks{ vexe: vexe label: 'all tests' @@ -109,6 +112,8 @@ fn test_all() { tasks.add('', global_dir, '-enable-globals', '.out', global_tests, false) tasks.add('', module_dir, '-prod run', '.out', module_tests, true) tasks.add('', run_dir, 'run', '.run.out', run_tests, false) + tasks.add('', checker_with_check_option_dir, '-check', '.out', checker_with_check_option_tests, + false) tasks.run() // if os.user_os() == 'linux' { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 583e988f23..ad715ce362 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1906,7 +1906,7 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Pos) ast.NodeError { util.show_compiler_message(kind, pos: pos, file_path: p.file_name, message: s) exit(1) } - if p.pref.output_mode == .stdout && !p.pref.check_only { + if p.pref.output_mode == .stdout { if p.pref.is_verbose { print_backtrace() kind = 'parser error:' @@ -1923,7 +1923,7 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Pos) ast.NodeError { // To avoid getting stuck after an error, the parser // will proceed to the next token. - if p.pref.check_only { + if p.pref.check_only || p.pref.only_check_syntax { p.next() } }