compiler: print asserted source line on failure

pull/2589/head
Delyan Angelov 2019-10-30 11:15:33 +02:00 committed by Alexander Medvednikov
parent ee6ec3faf3
commit ba6cc5df2a
6 changed files with 100 additions and 18 deletions

View File

@ -96,8 +96,8 @@ fn (s &Scanner) error_with_col(msg string, col int) {
eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
if s.should_print_line_on_error && s.nlines > 0 {
context_start_line := imax(0, (s.line_nr - error_context_before + 1 ))
context_end_line := imin(s.nlines-1, (s.line_nr + error_context_after + 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 ))
for cline := context_start_line; cline < context_end_line; cline++ {
line := '${(cline+1):5d}| ' + s.line( cline )
coloredline := if cline == s.line_nr && color_on { term.red(line) } else { line }
@ -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 } }
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
if s.file_path.starts_with(workdir) {
return s.file_path.replace( workdir, '')
@ -246,11 +252,12 @@ fn (s mut Scanner) get_scanner_pos_of_token(t &Token) ScannerPos {
/////////////////// s.get_scanner_pos()
/////////////////// which just returns a struct, and that works
/////////////////// in gcc and clang, but causes the TCC problem.
maxline := imin( s.nlines, tline + 2 * error_context_after)
for {
prevlinepos = s.pos
if s.pos >= s.text.len { break }
if s.line_nr > tline { break }
if s.line_nr > maxline { break }
////////////////////////////////////////
if tline == s.line_nr {
sptoken = s.get_scanner_pos()

View File

@ -648,11 +648,14 @@ pub fn (v &V) get_user_files() []string {
// libs, but we dont know which libs need to be added yet
mut user_files := []string
if v.pref.is_test && v.pref.is_stats {
user_files << os.join(v.vroot, 'vlib', 'benchmark', 'tests',
'always_imported.v')
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 {
user_files << os.join(v.vroot,'vlib','compiler','preludes','tests_with_stats.v')
}
// v volt/slack_test.v: compile all .v files to get the environment
// I need to implement user packages! TODO
is_test_with_imports := dir.ends_with('_test.v') &&

View File

@ -3578,20 +3578,49 @@ fn (p mut Parser) assert_statement() {
tmp := p.get_tmp()
p.gen('bool $tmp = ')
p.check_types(p.bool_expression(), 'bool')
nline := p.scanner.line_nr
// TODO print "expected: got" for failed tests
filename := cescaped_path(p.file_path)
p.genln(';
\n
cfname:=p.cur_fn.name.replace('main__', '')
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) {
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) {
println(tos2((byte *)"\\x1B[31mFAILED: $p.cur_fn.name() in $filename:$p.scanner.line_nr\\x1B[0m"));
g_test_fails++;
main__cb_assertion_failed(
tos3("$filename"),
$p.scanner.line_nr,
tos3("$sourceline"),
tos3("$p.cur_fn.name()")
);
return;
// TODO
// Maybe print all vars in a test function if it fails?
} else {
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()")
);
}
')

View File

@ -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 ')
}

View File

@ -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 {
//if s.line_comment != '' {
//s.fgenln('// LC "$s.line_comment"')
@ -246,7 +252,7 @@ fn (s mut Scanner) scan() ScanRes {
}
s.started = true
if s.pos >= s.text.len {
return scan_res(.eof, '')
return s.end_of_file()
}
if !s.inside_string {
s.skip_whitespace()
@ -263,7 +269,7 @@ fn (s mut Scanner) scan() ScanRes {
s.skip_whitespace()
// end of file
if s.pos >= s.text.len {
return scan_res(.eof, '')
return s.end_of_file()
}
// handle each char
c := s.text[s.pos]
@ -618,7 +624,7 @@ fn (s mut Scanner) scan() ScanRes {
}
$if windows {
if c == `\0` {
return scan_res(.eof, '')
return s.end_of_file()
}
}
mut msg := 'invalid character `${c.str()}`'
@ -626,7 +632,7 @@ fn (s mut Scanner) scan() ScanRes {
msg += ', use \' to denote strings'
}
s.error(msg)
return scan_res(.eof, '')
return s.end_of_file()
}
fn (s &Scanner) current_column() int {
@ -804,7 +810,9 @@ fn (s mut Scanner) inc_line_number() {
s.last_nl_pos = s.pos
s.line_nr++
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 {