From a31f559d0a63c6373f4496f7553197342869a434 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 00:33:27 +0300 Subject: [PATCH] parser: fix assert without comma but with message confusion with vfmt (fixes #25005) --- cmd/tools/vfmt.v | 13 ++++++++-- cmd/tools/vfmt_test.v | 25 +++++++++++++++++++ vlib/v/parser/parser.v | 8 ++++++ .../assert_missing_comma_message_err.out | 5 ++++ .../tests/assert_missing_comma_message_err.vv | 3 +++ 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 cmd/tools/vfmt_test.v create mode 100644 vlib/v/parser/tests/assert_missing_comma_message_err.out create mode 100644 vlib/v/parser/tests/assert_missing_comma_message_err.vv diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index 769436b4f..498cd7741 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -166,7 +166,7 @@ fn main() { } fn (foptions &FormatOptions) verify_file(prefs &pref.Preferences, fpath string) bool { - fcontent := foptions.formated_content_from_file(prefs, fpath) + fcontent := foptions.formated_content_from_file(prefs, fpath) or { return false } content := os.read_file(fpath) or { return false } return fcontent == content } @@ -188,9 +188,12 @@ fn (foptions &FormatOptions) vlog(msg string) { } } -fn (foptions &FormatOptions) formated_content_from_file(prefs &pref.Preferences, file string) string { +fn (foptions &FormatOptions) formated_content_from_file(prefs &pref.Preferences, file string) !string { mut table := ast.new_table() file_ast := parser.parse_file(file, mut table, .parse_comments, prefs) + if file_ast.errors.len > 0 { + return error('the file contains parser errors') + } table.new_int = foptions.is_new_int formated_content := fmt.fmt(file_ast, mut table, prefs, foptions.is_debug) return formated_content @@ -209,6 +212,9 @@ fn (foptions &FormatOptions) format_file(file string) { foptions.vlog('vfmt2 running fmt.fmt over file: ${file}') prefs, mut table := setup_preferences_and_table() file_ast := parser.parse_file(file, mut table, .parse_comments, prefs) + if file_ast.errors.len > 0 { + exit(2) + } // checker.new_checker(table, prefs).check(file_ast) table.new_int = foptions.is_new_int formatted_content := fmt.fmt(file_ast, mut table, prefs, foptions.is_debug) @@ -222,6 +228,9 @@ fn (foptions &FormatOptions) format_pipe() { prefs, mut table := setup_preferences_and_table() input_text := os.get_raw_lines_joined() file_ast := parser.parse_text(input_text, '', mut table, .parse_comments, prefs) + if file_ast.errors.len > 0 { + exit(1) + } // checker.new_checker(table, prefs).check(file_ast) table.new_int = foptions.is_new_int formatted_content := fmt.fmt(file_ast, mut table, prefs, foptions.is_debug, diff --git a/cmd/tools/vfmt_test.v b/cmd/tools/vfmt_test.v new file mode 100644 index 000000000..872cbd6cb --- /dev/null +++ b/cmd/tools/vfmt_test.v @@ -0,0 +1,25 @@ +import os + +const vexe = @VEXE +const vfmt_test_tdir = os.join_path(os.vtmp_dir(), 'vfmt_test_25005') + +fn testsuite_begin() { + os.rmdir_all(vfmt_test_tdir) or {} + os.mkdir_all(vfmt_test_tdir)! +} + +fn testsuite_end() { + os.rmdir_all(vfmt_test_tdir) or {} +} + +fn test_fmt_keeps_invalid_assert_source_unchanged() { + source_path := os.join_path(vfmt_test_tdir, 'invalid_assert_message.v') + original := "fn main() {\n\tassert false 'bye'\n}\n" + os.write_file(source_path, original)! + + res := os.execute('${os.quoted_path(vexe)} fmt -w ${os.quoted_path(source_path)}') + + assert res.exit_code != 0, res.output + assert res.output.contains('unexpected string `bye`, expecting `,`'), res.output + assert os.read_file(source_path)! == original +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 51f5fc13e..65bae2e4a 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1217,6 +1217,14 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { extra = p.expr(0) // dump(extra) extra_pos = extra_pos.extend(p.tok.pos()) + } else if p.tok.line_nr == p.prev_tok.line_nr + p.prev_tok.lit.count('\n') + && p.tok.kind !in [.comment, .semicolon, .rcbr, .eof] { + line_nr := p.tok.line_nr + err := p.unexpected(got: p.tok.str(), expecting: '`,`') + for p.tok.kind != .eof && p.tok.line_nr == line_nr { + p.next() + } + return err } return ast.AssertStmt{ expr: expr diff --git a/vlib/v/parser/tests/assert_missing_comma_message_err.out b/vlib/v/parser/tests/assert_missing_comma_message_err.out new file mode 100644 index 000000000..3eea19faa --- /dev/null +++ b/vlib/v/parser/tests/assert_missing_comma_message_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/assert_missing_comma_message_err.vv:2:15: error: unexpected string `bye`, expecting `,` + 1 | fn main() { + 2 | assert false 'bye' + | ~~~~~ + 3 | } diff --git a/vlib/v/parser/tests/assert_missing_comma_message_err.vv b/vlib/v/parser/tests/assert_missing_comma_message_err.vv new file mode 100644 index 000000000..e925ad52b --- /dev/null +++ b/vlib/v/parser/tests/assert_missing_comma_message_err.vv @@ -0,0 +1,3 @@ +fn main() { + assert false 'bye' +} -- 2.39.5