compiler: print asserted source line on failure
parent
ee6ec3faf3
commit
ba6cc5df2a
|
@ -96,7 +96,7 @@ fn (s &Scanner) error_with_col(msg string, col int) {
|
||||||
eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
|
eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
|
||||||
|
|
||||||
if s.should_print_line_on_error && s.nlines > 0 {
|
if s.should_print_line_on_error && s.nlines > 0 {
|
||||||
context_start_line := imax(0, (s.line_nr - error_context_before + 1 ))
|
context_start_line := imax(0, (s.line_nr - error_context_before ))
|
||||||
context_end_line := imin(s.nlines-1, (s.line_nr + error_context_after + 1 ))
|
context_end_line := imin(s.nlines-1, (s.line_nr + error_context_after + 1 ))
|
||||||
for cline := context_start_line; cline < context_end_line; cline++ {
|
for cline := context_start_line; cline < context_end_line; cline++ {
|
||||||
line := '${(cline+1):5d}| ' + s.line( cline )
|
line := '${(cline+1):5d}| ' + s.line( cline )
|
||||||
|
@ -132,7 +132,13 @@ fn (s &Scanner) error_with_col(msg string, col int) {
|
||||||
[inline] fn imin(a,b int) int { return if a < b { a } else { b } }
|
[inline] fn imin(a,b int) int { return if a < b { a } else { b } }
|
||||||
|
|
||||||
fn (s &Scanner) get_error_filepath() string {
|
fn (s &Scanner) get_error_filepath() string {
|
||||||
if s.should_print_relative_paths_on_error {
|
verror_paths_override := os.getenv('VERROR_PATHS')
|
||||||
|
use_relative_paths := match verror_paths_override {
|
||||||
|
'relative' { true }
|
||||||
|
'absolute' { false }
|
||||||
|
else { s.should_print_relative_paths_on_error }
|
||||||
|
}
|
||||||
|
if use_relative_paths {
|
||||||
workdir := os.getwd() + os.path_separator
|
workdir := os.getwd() + os.path_separator
|
||||||
if s.file_path.starts_with(workdir) {
|
if s.file_path.starts_with(workdir) {
|
||||||
return s.file_path.replace( workdir, '')
|
return s.file_path.replace( workdir, '')
|
||||||
|
@ -247,10 +253,11 @@ fn (s mut Scanner) get_scanner_pos_of_token(t &Token) ScannerPos {
|
||||||
/////////////////// which just returns a struct, and that works
|
/////////////////// which just returns a struct, and that works
|
||||||
/////////////////// in gcc and clang, but causes the TCC problem.
|
/////////////////// in gcc and clang, but causes the TCC problem.
|
||||||
|
|
||||||
|
maxline := imin( s.nlines, tline + 2 * error_context_after)
|
||||||
for {
|
for {
|
||||||
prevlinepos = s.pos
|
prevlinepos = s.pos
|
||||||
if s.pos >= s.text.len { break }
|
if s.pos >= s.text.len { break }
|
||||||
if s.line_nr > tline { break }
|
if s.line_nr > maxline { break }
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
if tline == s.line_nr {
|
if tline == s.line_nr {
|
||||||
sptoken = s.get_scanner_pos()
|
sptoken = s.get_scanner_pos()
|
||||||
|
|
|
@ -648,9 +648,12 @@ pub fn (v &V) get_user_files() []string {
|
||||||
// libs, but we dont know which libs need to be added yet
|
// libs, but we dont know which libs need to be added yet
|
||||||
mut user_files := []string
|
mut user_files := []string
|
||||||
|
|
||||||
|
if v.pref.is_test {
|
||||||
|
user_files << os.join(v.vroot,'vlib','compiler','preludes','tests_assertions.v')
|
||||||
|
}
|
||||||
|
|
||||||
if v.pref.is_test && v.pref.is_stats {
|
if v.pref.is_test && v.pref.is_stats {
|
||||||
user_files << os.join(v.vroot, 'vlib', 'benchmark', 'tests',
|
user_files << os.join(v.vroot,'vlib','compiler','preludes','tests_with_stats.v')
|
||||||
'always_imported.v')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// v volt/slack_test.v: compile all .v files to get the environment
|
// v volt/slack_test.v: compile all .v files to get the environment
|
||||||
|
|
|
@ -3578,20 +3578,49 @@ fn (p mut Parser) assert_statement() {
|
||||||
tmp := p.get_tmp()
|
tmp := p.get_tmp()
|
||||||
p.gen('bool $tmp = ')
|
p.gen('bool $tmp = ')
|
||||||
p.check_types(p.bool_expression(), 'bool')
|
p.check_types(p.bool_expression(), 'bool')
|
||||||
|
nline := p.scanner.line_nr
|
||||||
// TODO print "expected: got" for failed tests
|
// TODO print "expected: got" for failed tests
|
||||||
filename := cescaped_path(p.file_path)
|
filename := cescaped_path(p.file_path)
|
||||||
p.genln(';
|
cfname:=p.cur_fn.name.replace('main__', '')
|
||||||
\n
|
sourceline := p.scanner.line( nline - 1 ).replace('"', '\'')
|
||||||
|
|
||||||
|
if !p.pref.is_test {
|
||||||
|
// an assert used in a normal v program. no fancy formatting
|
||||||
|
p.genln(';\n
|
||||||
|
/// sline: "$sourceline"
|
||||||
if (!$tmp) {
|
if (!$tmp) {
|
||||||
println(tos2((byte *)"\\x1B[31mFAILED: $p.cur_fn.name() in $filename:$p.scanner.line_nr\\x1B[0m"));
|
|
||||||
g_test_fails++;
|
g_test_fails++;
|
||||||
|
eprintln(tos3("${filename}:${p.scanner.line_nr}: FAILED: ${cfname}()"));
|
||||||
|
eprintln(tos3("Source: $sourceline"));
|
||||||
|
v_panic(tos3("An assertion failed."));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
g_test_oks++;
|
||||||
|
}
|
||||||
|
')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.genln(';\n
|
||||||
|
if (!$tmp) {
|
||||||
|
g_test_fails++;
|
||||||
|
main__cb_assertion_failed(
|
||||||
|
tos3("$filename"),
|
||||||
|
$p.scanner.line_nr,
|
||||||
|
tos3("$sourceline"),
|
||||||
|
tos3("$p.cur_fn.name()")
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
// TODO
|
// TODO
|
||||||
// Maybe print all vars in a test function if it fails?
|
// Maybe print all vars in a test function if it fails?
|
||||||
} else {
|
} else {
|
||||||
g_test_oks++;
|
g_test_oks++;
|
||||||
//println(tos2((byte *)"\\x1B[32mPASSED: $p.cur_fn.name()\\x1B[0m"));
|
main__cb_assertion_ok(
|
||||||
|
tos3("$filename"),
|
||||||
|
$p.scanner.line_nr,
|
||||||
|
tos3("$sourceline"),
|
||||||
|
tos3("$p.cur_fn.name()")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
')
|
')
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
import os
|
||||||
|
import term
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// This file will get compiled as part of the main program,
|
||||||
|
/// for a _test.v file.
|
||||||
|
/// The methods defined here are called back by the test program's
|
||||||
|
/// assert statements, on each success/fail. The goal is to make
|
||||||
|
/// customizing the look & feel of the assertions results easier,
|
||||||
|
/// since it is done in normal V code, instead of in embedded C ...
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
fn cb_assertion_failed(filename string, line int, sourceline string, funcname string){
|
||||||
|
color_on := term.can_show_color_on_stderr()
|
||||||
|
use_relative_paths := match os.getenv('VERROR_PATHS') {
|
||||||
|
'absolute' { false }
|
||||||
|
else { true }
|
||||||
|
}
|
||||||
|
final_filename := if use_relative_paths { filename } else { os.realpath( filename ) }
|
||||||
|
final_funcname := funcname.replace('main__','')
|
||||||
|
|
||||||
|
mut fail_message := 'FAILED assertion'
|
||||||
|
if color_on { fail_message = term.bold(term.red(fail_message)) }
|
||||||
|
|
||||||
|
eprintln('$final_filename:$line: $fail_message')
|
||||||
|
eprintln('Function: $final_funcname')
|
||||||
|
eprintln('Source : $sourceline')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cb_assertion_ok(filename string, line int, sourceline string, funcname string){
|
||||||
|
//do nothing for now on an OK assertion
|
||||||
|
//println('OK ${line:5d}|$sourceline ')
|
||||||
|
}
|
|
@ -236,6 +236,12 @@ fn (s mut Scanner) skip_whitespace() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) end_of_file() ScanRes {
|
||||||
|
s.pos = s.text.len
|
||||||
|
s.inc_line_number()
|
||||||
|
return scan_res(.eof, '')
|
||||||
|
}
|
||||||
|
|
||||||
fn (s mut Scanner) scan() ScanRes {
|
fn (s mut Scanner) scan() ScanRes {
|
||||||
//if s.line_comment != '' {
|
//if s.line_comment != '' {
|
||||||
//s.fgenln('// LC "$s.line_comment"')
|
//s.fgenln('// LC "$s.line_comment"')
|
||||||
|
@ -246,7 +252,7 @@ fn (s mut Scanner) scan() ScanRes {
|
||||||
}
|
}
|
||||||
s.started = true
|
s.started = true
|
||||||
if s.pos >= s.text.len {
|
if s.pos >= s.text.len {
|
||||||
return scan_res(.eof, '')
|
return s.end_of_file()
|
||||||
}
|
}
|
||||||
if !s.inside_string {
|
if !s.inside_string {
|
||||||
s.skip_whitespace()
|
s.skip_whitespace()
|
||||||
|
@ -263,7 +269,7 @@ fn (s mut Scanner) scan() ScanRes {
|
||||||
s.skip_whitespace()
|
s.skip_whitespace()
|
||||||
// end of file
|
// end of file
|
||||||
if s.pos >= s.text.len {
|
if s.pos >= s.text.len {
|
||||||
return scan_res(.eof, '')
|
return s.end_of_file()
|
||||||
}
|
}
|
||||||
// handle each char
|
// handle each char
|
||||||
c := s.text[s.pos]
|
c := s.text[s.pos]
|
||||||
|
@ -618,7 +624,7 @@ fn (s mut Scanner) scan() ScanRes {
|
||||||
}
|
}
|
||||||
$if windows {
|
$if windows {
|
||||||
if c == `\0` {
|
if c == `\0` {
|
||||||
return scan_res(.eof, '')
|
return s.end_of_file()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut msg := 'invalid character `${c.str()}`'
|
mut msg := 'invalid character `${c.str()}`'
|
||||||
|
@ -626,7 +632,7 @@ fn (s mut Scanner) scan() ScanRes {
|
||||||
msg += ', use \' to denote strings'
|
msg += ', use \' to denote strings'
|
||||||
}
|
}
|
||||||
s.error(msg)
|
s.error(msg)
|
||||||
return scan_res(.eof, '')
|
return s.end_of_file()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (s &Scanner) current_column() int {
|
fn (s &Scanner) current_column() int {
|
||||||
|
@ -804,7 +810,9 @@ fn (s mut Scanner) inc_line_number() {
|
||||||
s.last_nl_pos = s.pos
|
s.last_nl_pos = s.pos
|
||||||
s.line_nr++
|
s.line_nr++
|
||||||
s.line_ends << s.pos
|
s.line_ends << s.pos
|
||||||
s.nlines++
|
if s.line_nr > s.nlines {
|
||||||
|
s.nlines = s.line_nr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (s Scanner) line(n int) string {
|
fn (s Scanner) line(n int) string {
|
||||||
|
|
Loading…
Reference in New Issue