v.builder: implement a `-check` mode, that runs only the parser + the checker, without codegen (#11414)
parent
823c9a3c84
commit
aedb6b8e84
|
@ -94,6 +94,7 @@ const (
|
||||||
'-apk',
|
'-apk',
|
||||||
'-show-timings',
|
'-show-timings',
|
||||||
'-check-syntax',
|
'-check-syntax',
|
||||||
|
'-check',
|
||||||
'-v',
|
'-v',
|
||||||
'-progress',
|
'-progress',
|
||||||
'-silent',
|
'-silent',
|
||||||
|
|
|
@ -125,6 +125,9 @@ NB: the build flags are shared with the run command too:
|
||||||
that it uses to detect whether or not to use ANSI colors may not work in all cases.
|
that it uses to detect whether or not to use ANSI colors may not work in all cases.
|
||||||
These options allow you to override the default detection.
|
These options allow you to override the default detection.
|
||||||
|
|
||||||
|
-check
|
||||||
|
Scans, parses, and checks the files without compiling the program.
|
||||||
|
|
||||||
-check-syntax
|
-check-syntax
|
||||||
Only scan and parse the files, but then stop. Useful for very quick syntax checks.
|
Only scan and parse the files, but then stop. Useful for very quick syntax checks.
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ module builder
|
||||||
import os
|
import os
|
||||||
import v.token
|
import v.token
|
||||||
import v.pref
|
import v.pref
|
||||||
|
import v.errors
|
||||||
import v.util
|
import v.util
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.vmod
|
import v.vmod
|
||||||
|
@ -26,6 +27,9 @@ mut:
|
||||||
out_name_js string
|
out_name_js string
|
||||||
stats_lines int // size of backend generated source code in lines
|
stats_lines int // size of backend generated source code in lines
|
||||||
stats_bytes int // size of backend generated source code in bytes
|
stats_bytes int // size of backend generated source code in bytes
|
||||||
|
nr_errors int // accumulated error count of scanner, parser, checker, and builder
|
||||||
|
nr_warnings int // accumulated warning count of scanner, parser, checker, and builder
|
||||||
|
nr_notices int // accumulated notice count of scanner, parser, checker, and builder
|
||||||
pub mut:
|
pub mut:
|
||||||
module_search_paths []string
|
module_search_paths []string
|
||||||
parsed_files []&ast.File
|
parsed_files []&ast.File
|
||||||
|
@ -86,6 +90,9 @@ pub fn (mut b Builder) middle_stages() ? {
|
||||||
b.checker.check_files(b.parsed_files)
|
b.checker.check_files(b.parsed_files)
|
||||||
util.timing_measure('CHECK')
|
util.timing_measure('CHECK')
|
||||||
b.print_warnings_and_errors()
|
b.print_warnings_and_errors()
|
||||||
|
if b.pref.check_only {
|
||||||
|
return error('stop_after_checker')
|
||||||
|
}
|
||||||
util.timing_start('TRANSFORM')
|
util.timing_start('TRANSFORM')
|
||||||
b.transformer.transform_files(b.parsed_files)
|
b.transformer.transform_files(b.parsed_files)
|
||||||
util.timing_measure('TRANSFORM')
|
util.timing_measure('TRANSFORM')
|
||||||
|
@ -133,7 +140,8 @@ pub fn (mut b Builder) parse_imports() {
|
||||||
for imp in ast_file.imports {
|
for imp in ast_file.imports {
|
||||||
mod := imp.mod
|
mod := imp.mod
|
||||||
if mod == 'builtin' {
|
if mod == 'builtin' {
|
||||||
error_with_pos('cannot import module "builtin"', ast_file.path, imp.pos)
|
b.parsed_files[i].errors << b.error_with_pos('cannot import module "builtin"',
|
||||||
|
ast_file.path, imp.pos)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if mod in done_imports {
|
if mod in done_imports {
|
||||||
|
@ -142,15 +150,16 @@ pub fn (mut b Builder) parse_imports() {
|
||||||
import_path := b.find_module_path(mod, ast_file.path) or {
|
import_path := b.find_module_path(mod, ast_file.path) or {
|
||||||
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_ast.get_import_tok_idx(mod))
|
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_ast.get_import_tok_idx(mod))
|
||||||
// break
|
// break
|
||||||
error_with_pos('cannot import module "$mod" (not found)', ast_file.path,
|
b.parsed_files[i].errors << b.error_with_pos('cannot import module "$mod" (not found)',
|
||||||
imp.pos)
|
ast_file.path, imp.pos)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v_files := b.v_files_from_dir(import_path)
|
v_files := b.v_files_from_dir(import_path)
|
||||||
if v_files.len == 0 {
|
if v_files.len == 0 {
|
||||||
// v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_ast.get_import_tok_idx(mod))
|
// v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_ast.get_import_tok_idx(mod))
|
||||||
error_with_pos('cannot import module "$mod" (no .v files in "$import_path")',
|
b.parsed_files[i].errors << b.error_with_pos('cannot import module "$mod" (no .v files in "$import_path")',
|
||||||
ast_file.path, imp.pos)
|
ast_file.path, imp.pos)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
// Add all imports referenced by these libs
|
// Add all imports referenced by these libs
|
||||||
parsed_files := parser.parse_files(v_files, b.table, b.pref)
|
parsed_files := parser.parse_files(v_files, b.table, b.pref)
|
||||||
|
@ -161,7 +170,7 @@ pub fn (mut b Builder) parse_imports() {
|
||||||
}
|
}
|
||||||
if name != mod {
|
if name != mod {
|
||||||
// v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1
|
// v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1
|
||||||
error_with_pos('bad module definition: $ast_file.path imports module "$mod" but $file.path is defined as module `$name`',
|
b.parsed_files[i].errors << b.error_with_pos('bad module definition: $ast_file.path imports module "$mod" but $file.path is defined as module `$name`',
|
||||||
ast_file.path, imp.pos)
|
ast_file.path, imp.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,27 +344,104 @@ pub fn (b &Builder) find_module_path(mod string, fpath string) ?string {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (b &Builder) show_total_warns_and_errors_stats() {
|
fn (b &Builder) show_total_warns_and_errors_stats() {
|
||||||
if b.checker.nr_errors == 0 && b.checker.nr_warnings == 0 && b.checker.nr_notices == 0 {
|
if b.nr_errors == 0 && b.nr_warnings == 0 && b.nr_notices == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.pref.is_stats {
|
if b.pref.is_stats {
|
||||||
estring := util.bold(b.checker.errors.len.str())
|
mut nr_errors := b.checker.errors.len
|
||||||
wstring := util.bold(b.checker.warnings.len.str())
|
mut nr_warnings := b.checker.warnings.len
|
||||||
nstring := util.bold(b.checker.nr_notices.str())
|
mut nr_notices := b.checker.notices.len
|
||||||
println('checker summary: $estring V errors, $wstring V warnings, $nstring V notices')
|
|
||||||
|
if b.pref.check_only {
|
||||||
|
nr_errors = b.nr_errors
|
||||||
|
nr_warnings = b.nr_warnings
|
||||||
|
nr_notices = b.nr_notices
|
||||||
|
}
|
||||||
|
|
||||||
|
estring := util.bold(nr_errors.str())
|
||||||
|
wstring := util.bold(nr_warnings.str())
|
||||||
|
nstring := util.bold(nr_notices.str())
|
||||||
|
|
||||||
|
if b.pref.check_only {
|
||||||
|
println('summary: $estring V errors, $wstring V warnings, $nstring V notices')
|
||||||
|
} else {
|
||||||
|
println('checker summary: $estring V errors, $wstring V warnings, $nstring V notices')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (b &Builder) print_warnings_and_errors() {
|
fn (mut b Builder) print_warnings_and_errors() {
|
||||||
defer {
|
defer {
|
||||||
b.show_total_warns_and_errors_stats()
|
b.show_total_warns_and_errors_stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for file in b.parsed_files {
|
||||||
|
b.nr_errors += file.errors.len
|
||||||
|
b.nr_warnings += file.warnings.len
|
||||||
|
b.nr_notices += file.notices.len
|
||||||
|
}
|
||||||
|
|
||||||
if b.pref.output_mode == .silent {
|
if b.pref.output_mode == .silent {
|
||||||
if b.checker.nr_errors > 0 {
|
if b.nr_errors > 0 {
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.pref.check_only {
|
||||||
|
for file in b.parsed_files {
|
||||||
|
if !b.pref.skip_warnings {
|
||||||
|
for err in file.notices {
|
||||||
|
kind := if b.pref.is_verbose {
|
||||||
|
'$err.reporter notice #$b.nr_notices:'
|
||||||
|
} else {
|
||||||
|
'notice:'
|
||||||
|
}
|
||||||
|
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
|
||||||
|
eprintln(ferror)
|
||||||
|
if err.details.len > 0 {
|
||||||
|
eprintln('Details: $err.details')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in b.parsed_files {
|
||||||
|
for err in file.errors {
|
||||||
|
kind := if b.pref.is_verbose {
|
||||||
|
'$err.reporter error #$b.nr_errors:'
|
||||||
|
} else {
|
||||||
|
'error:'
|
||||||
|
}
|
||||||
|
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
|
||||||
|
eprintln(ferror)
|
||||||
|
if err.details.len > 0 {
|
||||||
|
eprintln('Details: $err.details')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in b.parsed_files {
|
||||||
|
if !b.pref.skip_warnings {
|
||||||
|
for err in file.warnings {
|
||||||
|
kind := if b.pref.is_verbose {
|
||||||
|
'$err.reporter warning #$b.nr_warnings:'
|
||||||
|
} else {
|
||||||
|
'warning:'
|
||||||
|
}
|
||||||
|
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
|
||||||
|
eprintln(ferror)
|
||||||
|
if err.details.len > 0 {
|
||||||
|
eprintln('Details: $err.details')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.show_total_warns_and_errors_stats()
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
if b.pref.is_verbose && b.checker.nr_warnings > 1 {
|
if b.pref.is_verbose && b.checker.nr_warnings > 1 {
|
||||||
println('$b.checker.nr_warnings warnings')
|
println('$b.checker.nr_warnings warnings')
|
||||||
}
|
}
|
||||||
|
@ -455,10 +541,19 @@ struct FunctionRedefinition {
|
||||||
f ast.FnDecl
|
f ast.FnDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_with_pos(s string, fpath string, pos token.Position) {
|
fn (b &Builder) error_with_pos(s string, fpath string, pos token.Position) errors.Error {
|
||||||
ferror := util.formatted_error('builder error:', s, fpath, pos)
|
if !b.pref.check_only {
|
||||||
eprintln(ferror)
|
ferror := util.formatted_error('builder error:', s, fpath, pos)
|
||||||
exit(1)
|
eprintln(ferror)
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Error{
|
||||||
|
file_path: fpath
|
||||||
|
pos: pos
|
||||||
|
reporter: .builder
|
||||||
|
message: s
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[noreturn]
|
[noreturn]
|
||||||
|
|
|
@ -489,6 +489,12 @@ fn (mut v Builder) cc() {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if v.pref.check_only {
|
||||||
|
if v.pref.is_verbose {
|
||||||
|
println('builder.cc returning early, since pref.check_only is true')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if v.pref.should_output_to_stdout() {
|
if v.pref.should_output_to_stdout() {
|
||||||
// output to stdout
|
// output to stdout
|
||||||
content := os.read_file(v.out_name_c) or { panic(err) }
|
content := os.read_file(v.out_name_c) or { panic(err) }
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
||||||
if b.pref.skip_running {
|
if b.pref.skip_running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.pref.only_check_syntax {
|
if b.pref.only_check_syntax || b.pref.check_only {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.pref.should_output_to_stdout() {
|
if b.pref.should_output_to_stdout() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub enum Reporter {
|
||||||
scanner
|
scanner
|
||||||
parser
|
parser
|
||||||
checker
|
checker
|
||||||
|
builder
|
||||||
gen
|
gen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -273,6 +273,17 @@ pub fn (mut p Parser) parse() &ast.File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.scope.end_pos = p.tok.pos
|
p.scope.end_pos = p.tok.pos
|
||||||
|
|
||||||
|
mut errors := p.errors
|
||||||
|
mut warnings := p.warnings
|
||||||
|
mut notices := p.notices
|
||||||
|
|
||||||
|
if p.pref.check_only {
|
||||||
|
errors << p.scanner.errors
|
||||||
|
warnings << p.scanner.warnings
|
||||||
|
notices << p.scanner.notices
|
||||||
|
}
|
||||||
|
|
||||||
return &ast.File{
|
return &ast.File{
|
||||||
path: p.file_name
|
path: p.file_name
|
||||||
path_base: p.file_base
|
path_base: p.file_base
|
||||||
|
@ -286,8 +297,9 @@ pub fn (mut p Parser) parse() &ast.File {
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
global_scope: p.table.global_scope
|
global_scope: p.table.global_scope
|
||||||
errors: p.errors
|
errors: errors
|
||||||
warnings: p.warnings
|
warnings: warnings
|
||||||
|
notices: notices
|
||||||
global_labels: p.global_labels
|
global_labels: p.global_labels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1613,7 +1625,7 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Position) ast.NodeError
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
mut kind := 'error:'
|
mut kind := 'error:'
|
||||||
if p.pref.output_mode == .stdout {
|
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||||
if p.pref.is_verbose {
|
if p.pref.is_verbose {
|
||||||
print_backtrace()
|
print_backtrace()
|
||||||
kind = 'parser error:'
|
kind = 'parser error:'
|
||||||
|
@ -1628,6 +1640,12 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Position) ast.NodeError
|
||||||
reporter: .parser
|
reporter: .parser
|
||||||
message: s
|
message: s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To avoid getting stuck after an error, the parser
|
||||||
|
// will proceed to the next token.
|
||||||
|
if p.pref.check_only {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if p.pref.output_mode == .silent {
|
if p.pref.output_mode == .silent {
|
||||||
// Normally, parser errors mean that the parser exits immediately, so there can be only 1 parser error.
|
// Normally, parser errors mean that the parser exits immediately, so there can be only 1 parser error.
|
||||||
|
@ -1647,7 +1665,7 @@ pub fn (mut p Parser) error_with_error(error errors.Error) {
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
mut kind := 'error:'
|
mut kind := 'error:'
|
||||||
if p.pref.output_mode == .stdout {
|
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||||
if p.pref.is_verbose {
|
if p.pref.is_verbose {
|
||||||
print_backtrace()
|
print_backtrace()
|
||||||
kind = 'parser error:'
|
kind = 'parser error:'
|
||||||
|
@ -1679,7 +1697,7 @@ pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) {
|
||||||
if p.pref.skip_warnings {
|
if p.pref.skip_warnings {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.pref.output_mode == .stdout {
|
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||||
ferror := util.formatted_error('warning:', s, p.file_name, pos)
|
ferror := util.formatted_error('warning:', s, p.file_name, pos)
|
||||||
eprintln(ferror)
|
eprintln(ferror)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1700,7 +1718,7 @@ pub fn (mut p Parser) note_with_pos(s string, pos token.Position) {
|
||||||
if p.pref.skip_warnings {
|
if p.pref.skip_warnings {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.pref.output_mode == .stdout {
|
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||||
ferror := util.formatted_error('notice:', s, p.file_name, pos)
|
ferror := util.formatted_error('notice:', s, p.file_name, pos)
|
||||||
eprintln(ferror)
|
eprintln(ferror)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -178,6 +178,7 @@ pub mut:
|
||||||
is_parallel bool
|
is_parallel bool
|
||||||
is_vweb bool // skip _ var warning in templates
|
is_vweb bool // skip _ var warning in templates
|
||||||
only_check_syntax bool // when true, just parse the files, then stop, before running checker
|
only_check_syntax bool // when true, just parse the files, then stop, before running checker
|
||||||
|
check_only bool // same as only_check_syntax, but also runs the checker
|
||||||
experimental bool // enable experimental features
|
experimental bool // enable experimental features
|
||||||
skip_unused bool // skip generating C code for functions, that are not used
|
skip_unused bool // skip generating C code for functions, that are not used
|
||||||
show_timings bool // show how much time each compiler stage took
|
show_timings bool // show how much time each compiler stage took
|
||||||
|
@ -245,6 +246,9 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
||||||
'-check-syntax' {
|
'-check-syntax' {
|
||||||
res.only_check_syntax = true
|
res.only_check_syntax = true
|
||||||
}
|
}
|
||||||
|
'-check' {
|
||||||
|
res.check_only = true
|
||||||
|
}
|
||||||
'-h', '-help', '--help' {
|
'-h', '-help', '--help' {
|
||||||
// NB: help is *very important*, just respond to all variations:
|
// NB: help is *very important*, just respond to all variations:
|
||||||
res.is_help = true
|
res.is_help = true
|
||||||
|
|
|
@ -1336,7 +1336,7 @@ pub fn (mut s Scanner) note(msg string) {
|
||||||
line_nr: s.line_nr
|
line_nr: s.line_nr
|
||||||
pos: s.pos
|
pos: s.pos
|
||||||
}
|
}
|
||||||
if s.pref.output_mode == .stdout {
|
if s.pref.output_mode == .stdout && !s.pref.check_only {
|
||||||
eprintln(util.formatted_error('notice:', msg, s.file_path, pos))
|
eprintln(util.formatted_error('notice:', msg, s.file_path, pos))
|
||||||
} else {
|
} else {
|
||||||
s.notices << errors.Notice{
|
s.notices << errors.Notice{
|
||||||
|
@ -1358,7 +1358,7 @@ pub fn (mut s Scanner) warn(msg string) {
|
||||||
pos: s.pos
|
pos: s.pos
|
||||||
col: s.current_column() - 1
|
col: s.current_column() - 1
|
||||||
}
|
}
|
||||||
if s.pref.output_mode == .stdout {
|
if s.pref.output_mode == .stdout && !s.pref.check_only {
|
||||||
eprintln(util.formatted_error('warning:', msg, s.file_path, pos))
|
eprintln(util.formatted_error('warning:', msg, s.file_path, pos))
|
||||||
} else {
|
} else {
|
||||||
if s.pref.message_limit >= 0 && s.warnings.len >= s.pref.message_limit {
|
if s.pref.message_limit >= 0 && s.warnings.len >= s.pref.message_limit {
|
||||||
|
@ -1380,7 +1380,7 @@ pub fn (mut s Scanner) error(msg string) {
|
||||||
pos: s.pos
|
pos: s.pos
|
||||||
col: s.current_column() - 1
|
col: s.current_column() - 1
|
||||||
}
|
}
|
||||||
if s.pref.output_mode == .stdout {
|
if s.pref.output_mode == .stdout && !s.pref.check_only {
|
||||||
eprintln(util.formatted_error('error:', msg, s.file_path, pos))
|
eprintln(util.formatted_error('error:', msg, s.file_path, pos))
|
||||||
exit(1)
|
exit(1)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue