mirror of
https://github.com/vlang/v.git
synced 2025-09-14 23:12:33 +03:00
compiler: more precise 'declared and not used' error positioning
* compiler: extract and cleanup error handling functionality into its own file compiler/compile_errors.v * compiler: implement p.error_with_token_index and p.warn_with_token_index and use them. Fix tests. * tools/performance_compare: add a 'Source lines in compiler/' line * MSVC does not have STDOUT_FILENO nor STDERR_FILENO ...
This commit is contained in:
parent
6d483c0a56
commit
e72fe25224
9 changed files with 303 additions and 241 deletions
|
@ -7,7 +7,6 @@ module main
|
|||
import (
|
||||
os
|
||||
strings
|
||||
term
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -81,72 +80,6 @@ fn new_scanner(text string) &Scanner {
|
|||
}
|
||||
|
||||
|
||||
// The goal of ScannerPos is to track the current scanning position,
|
||||
// so that if there is an error found later, v could show a more accurate
|
||||
// position about where the error initially was.
|
||||
// NB: The fields of ScannerPos *should be kept synchronized* with the
|
||||
// corresponding fields in Scanner.
|
||||
struct ScannerPos {
|
||||
mut:
|
||||
pos int
|
||||
line_nr int
|
||||
last_nl_pos int
|
||||
}
|
||||
fn (s ScannerPos) str() string {
|
||||
return 'ScannerPos{ ${s.pos:5d} , ${s.line_nr:5d} , ${s.last_nl_pos:5d} }'
|
||||
}
|
||||
fn (s &Scanner) get_scanner_pos() ScannerPos {
|
||||
return ScannerPos{ pos: s.pos line_nr: s.line_nr last_nl_pos: s.last_nl_pos }
|
||||
}
|
||||
fn (s mut Scanner) goto_scanner_position(scp ScannerPos) {
|
||||
s.pos = scp.pos
|
||||
s.line_nr = scp.line_nr
|
||||
s.last_nl_pos = scp.last_nl_pos
|
||||
}
|
||||
|
||||
// get_scanner_pos_of_token rescans *the whole source* till it reaches {t.line_nr, t.col} .
|
||||
fn (s mut Scanner) get_scanner_pos_of_token(t Tok) ScannerPos {
|
||||
// This rescanning is done just once on error, so it is fine for now.
|
||||
// Be careful for the performance implications, if you want to
|
||||
// do it more frequently. The alternative would be to store
|
||||
// the scanpos (12 bytes) for each token, and there are potentially many tokens.
|
||||
tline := t.line_nr
|
||||
tcol := if t.line_nr == 0 { t.col + 1 } else { t.col - 1 }
|
||||
// save the current scanner position, it will be restored later
|
||||
cpos := s.get_scanner_pos()
|
||||
mut sptoken := ScannerPos{}
|
||||
// Starting from the start, scan the source lines
|
||||
// till the desired tline is reached, then
|
||||
// s.pos + tcol would be the proper position
|
||||
// of the token. Continue scanning for some more lines of context too.
|
||||
s.goto_scanner_position(ScannerPos{})
|
||||
s.file_lines = []string
|
||||
mut prevlinepos := 0
|
||||
for {
|
||||
prevlinepos = s.pos
|
||||
if s.pos >= s.text.len { break }
|
||||
if s.line_nr > tline + 10 { break }
|
||||
////////////////////////////////////////
|
||||
if tline == s.line_nr {
|
||||
sptoken = s.get_scanner_pos()
|
||||
sptoken.pos += tcol
|
||||
}
|
||||
s.ignore_line() s.eat_single_newline()
|
||||
sline := s.text.substr( prevlinepos, s.pos ).trim_right('\r\n')
|
||||
s.file_lines << sline
|
||||
}
|
||||
//////////////////////////////////////////////////
|
||||
s.goto_scanner_position(cpos)
|
||||
return sptoken
|
||||
}
|
||||
fn (s mut Scanner) eat_single_newline(){
|
||||
if s.pos >= s.text.len { return }
|
||||
if s.expect('\r\n', s.pos) { s.pos += 2 return }
|
||||
if s.text[ s.pos ] == `\n` { s.pos ++ return }
|
||||
if s.text[ s.pos ] == `\r` { s.pos ++ return }
|
||||
}
|
||||
|
||||
|
||||
// TODO remove once multiple return values are implemented
|
||||
struct ScanRes {
|
||||
tok Token
|
||||
|
@ -652,52 +585,6 @@ fn (s &Scanner) current_column() int {
|
|||
return s.pos - s.last_nl_pos
|
||||
}
|
||||
|
||||
fn imax(a,b int) int { return if a > b { a } else { b } }
|
||||
fn imin(a,b int) int { return if a < b { a } else { b } }
|
||||
fn (s &Scanner) error(msg string) {
|
||||
s.error_with_col(msg, 0)
|
||||
}
|
||||
|
||||
fn (s &Scanner) error_with_col(msg string, col int) {
|
||||
fullpath := os.realpath( s.file_path )
|
||||
color_on := s.should_print_errors_in_color && term.can_show_color()
|
||||
final_message := if color_on { term.red( term.bold( msg ) ) } else { msg }
|
||||
// The filepath:line:col: format is the default C compiler
|
||||
// error output format. It allows editors and IDE's like
|
||||
// emacs to quickly find the errors in the output
|
||||
// and jump to their source with a keyboard shortcut.
|
||||
// Using only the filename leads to inability of IDE/editors
|
||||
// to find the source file, when it is in another folder.
|
||||
eprintln('${fullpath}:${s.line_nr + 1}:${col}: $final_message')
|
||||
|
||||
if s.should_print_line_on_error && s.file_lines.len > 0 {
|
||||
context_start_line := imax(0, (s.line_nr - error_context_before + 1 ))
|
||||
context_end_line := imin(s.file_lines.len, (s.line_nr + error_context_after + 1 ))
|
||||
for cline := context_start_line; cline < context_end_line; cline++ {
|
||||
line := '${(cline+1):5d}| ' + s.file_lines[ cline ]
|
||||
coloredline := if cline == s.line_nr && color_on { term.red(line) } else { line }
|
||||
println( coloredline )
|
||||
if cline != s.line_nr { continue }
|
||||
// The pointerline should have the same spaces/tabs as the offending
|
||||
// line, so that it prints the ^ character exactly on the *same spot*
|
||||
// where it is needed. That is the reason we can not just
|
||||
// use strings.repeat(` `, col) to form it.
|
||||
mut pointerline := []string
|
||||
for i , c in line {
|
||||
if i < col {
|
||||
x := if c.is_space() { c } else { ` ` }
|
||||
pointerline << x.str()
|
||||
continue
|
||||
}
|
||||
pointerline << if color_on { term.bold( term.blue('^') ) } else { '^' }
|
||||
break
|
||||
}
|
||||
println( ' ' + pointerline.join('') )
|
||||
}
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
|
||||
fn (s Scanner) count_symbol_before(p int, sym byte) int {
|
||||
mut count := 0
|
||||
for i:=p; i>=0; i-- {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue