From 5db75e9e4ec69e9b4e3f69636a98db3497df7398 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 23:38:07 +0300 Subject: [PATCH] parser: fix `$tmpl` error message improvement (fixes #16127) --- vlib/v/checker/comptime.v | 16 ++++- .../checker/tests/template_call_position.out | 4 +- .../tests/template_keyword_ident_err.out | 7 +- vlib/v/parser/comptime.v | 69 ++++++++++++++++++- vlib/v/parser/parser.v | 9 ++- 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 26c330fd8..f16ee7234 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -226,6 +226,9 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { } mut c2 := new_checker(c.table, pref2) c2.comptime_call_pos = node.pos.pos + template_parser_errors := node.veb_tmpl.errors.clone() + template_parser_warnings := node.veb_tmpl.warnings.clone() + template_parser_notices := node.veb_tmpl.notices.clone() c2.check(mut node.veb_tmpl) // Cache template file content for error display using the relative path @@ -247,7 +250,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { c2.errors[i] = errors.Error{ message: err.message details: err.details - file_path: err.file_path + file_path: line_info.tmpl_path pos: token.Pos{ ...err.pos line_nr: line_info.tmpl_line @@ -263,7 +266,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { c2.warnings[i] = errors.Warning{ message: warn.message details: warn.details - file_path: warn.file_path + file_path: line_info.tmpl_path pos: token.Pos{ ...warn.pos line_nr: line_info.tmpl_line @@ -279,7 +282,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { c2.notices[i] = errors.Notice{ message: notice.message details: notice.details - file_path: notice.file_path + file_path: line_info.tmpl_path pos: token.Pos{ ...notice.pos line_nr: line_info.tmpl_line @@ -291,6 +294,13 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { } } + c.warnings << template_parser_warnings + c.errors << template_parser_errors + c.notices << template_parser_notices + c.nr_warnings += template_parser_warnings.len + c.nr_errors += template_parser_errors.len + c.nr_notices += template_parser_notices.len + c.warnings << c2.warnings c.errors << c2.errors c.notices << c2.notices diff --git a/vlib/v/checker/tests/template_call_position.out b/vlib/v/checker/tests/template_call_position.out index 26847438e..3e1698359 100644 --- a/vlib/v/checker/tests/template_call_position.out +++ b/vlib/v/checker/tests/template_call_position.out @@ -1,4 +1,4 @@ -template_call_position_test.txt:3:3: error: undefined ident: `unknown_var` (veb action: main__main) +vlib/v/checker/tests/template_call_position_test.txt:3:3: error: undefined ident: `unknown_var` (veb action: main__main) 1 | Hello World! 2 | This is a template file. 3 | @unknown_var @@ -9,7 +9,7 @@ called from vlib/v/checker/tests/template_call_position.vv:4:2 4 | $tmpl('template_call_position_test.txt') | ^ 5 | } -template_call_position_test.txt:3:3: error: expression does not return a value (veb action: main__main) +vlib/v/checker/tests/template_call_position_test.txt:3:3: error: expression does not return a value (veb action: main__main) 1 | Hello World! 2 | This is a template file. 3 | @unknown_var diff --git a/vlib/v/checker/tests/template_keyword_ident_err.out b/vlib/v/checker/tests/template_keyword_ident_err.out index 5f078c85c..9dab37cae 100644 --- a/vlib/v/checker/tests/template_keyword_ident_err.out +++ b/vlib/v/checker/tests/template_keyword_ident_err.out @@ -1,5 +1,4 @@ -templates/template_keyword_ident_err.html:9:30: error: invalid expression: unexpected keyword `import` -templates/template_keyword_ident_err.html:1:30: error: expression does not return a value (veb action: index) +vlib/v/checker/tests/templates/template_keyword_ident_err.html:1:30: error: invalid expression: unexpected keyword `import` 1 | @import 'example' | ^ called from vlib/v/checker/tests/template_keyword_ident_err.vv:12:9 @@ -9,9 +8,9 @@ called from vlib/v/checker/tests/template_keyword_ident_err.vv:12:9 | ^ 13 | } 14 | -templates/template_keyword_ident_err.html:1:1: error: missing return at end of function `veb_tmpl_index147` (veb action: index) +vlib/v/checker/tests/templates/template_keyword_ident_err.html:1:30: error: expression does not return a value (veb action: index) 1 | @import 'example' - | ~~~~~~~~~~~~~~~~~ + | ^ called from vlib/v/checker/tests/template_keyword_ident_err.vv:12:9 10 | 11 | pub fn (mut app Application) index() veb.Result { diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 76ef8f1b9..07924af27 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -566,16 +566,81 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { } mut file := parse_comptime(tmpl_path, v_code, mut p.table, p.pref, mut p.scope) file.path = tmpl_path - // Store call stack info for template errors - file.call_stack = [ + template_call_stack := [ errors.CallStackItem{ file_path: p.file_path pos: start_pos }, ] + // Store call stack info for template errors + file.call_stack = template_call_stack // Transfer template paths and line mapping from parser to file for error reporting file.template_paths = p.template_paths file.template_line_map = p.template_line_map + for i, err in file.errors { + mut file_path := err.file_path + mut line_nr := err.pos.line_nr + if err.pos.line_nr >= 0 && err.pos.line_nr < file.template_line_map.len { + line_info := file.template_line_map[err.pos.line_nr] + file_path = line_info.tmpl_path + line_nr = line_info.tmpl_line + } + file.errors[i] = errors.Error{ + message: err.message + details: err.details + file_path: file_path + pos: token.Pos{ + ...err.pos + line_nr: line_nr + } + reporter: err.reporter + call_stack: if err.call_stack.len > 0 { err.call_stack } else { template_call_stack } + } + } + for i, warn in file.warnings { + mut file_path := warn.file_path + mut line_nr := warn.pos.line_nr + if warn.pos.line_nr >= 0 && warn.pos.line_nr < file.template_line_map.len { + line_info := file.template_line_map[warn.pos.line_nr] + file_path = line_info.tmpl_path + line_nr = line_info.tmpl_line + } + file.warnings[i] = errors.Warning{ + message: warn.message + details: warn.details + file_path: file_path + pos: token.Pos{ + ...warn.pos + line_nr: line_nr + } + reporter: warn.reporter + call_stack: if warn.call_stack.len > 0 { warn.call_stack } else { template_call_stack } + } + } + for i, notice in file.notices { + mut file_path := notice.file_path + mut line_nr := notice.pos.line_nr + if notice.pos.line_nr >= 0 && notice.pos.line_nr < file.template_line_map.len { + line_info := file.template_line_map[notice.pos.line_nr] + file_path = line_info.tmpl_path + line_nr = line_info.tmpl_line + } + file.notices[i] = errors.Notice{ + message: notice.message + details: notice.details + file_path: file_path + pos: token.Pos{ + ...notice.pos + line_nr: line_nr + } + reporter: notice.reporter + call_stack: if notice.call_stack.len > 0 { + notice.call_stack + } else { + template_call_stack + } + } + } return ast.ComptimeCall{ scope: unsafe { nil } is_template: true diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index dd922677d..24ebbe069 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -175,12 +175,17 @@ pub fn parse_comptime(tmpl_path string, text string, mut table ast.Table, pref_ $if trace_parse_comptime ? { eprintln('> ${@MOD}.${@FN} text: ${text}') } + pref_copy := *pref_ + comptime_pref := &pref.Preferences{ + ...pref_copy + output_mode: .silent + } mut p := Parser{ content: .comptime file_path: tmpl_path - scanner: scanner.new_scanner(text, .skip_comments, pref_) + scanner: scanner.new_scanner(text, .skip_comments, comptime_pref) table: table - pref: pref_ + pref: comptime_pref scope: scope errors: []errors.Error{} warnings: []errors.Warning{} -- 2.39.5