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',
|
||||
'-show-timings',
|
||||
'-check-syntax',
|
||||
'-check',
|
||||
'-v',
|
||||
'-progress',
|
||||
'-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.
|
||||
These options allow you to override the default detection.
|
||||
|
||||
-check
|
||||
Scans, parses, and checks the files without compiling the program.
|
||||
|
||||
-check-syntax
|
||||
Only scan and parse the files, but then stop. Useful for very quick syntax checks.
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ module builder
|
|||
import os
|
||||
import v.token
|
||||
import v.pref
|
||||
import v.errors
|
||||
import v.util
|
||||
import v.ast
|
||||
import v.vmod
|
||||
|
@ -26,6 +27,9 @@ mut:
|
|||
out_name_js string
|
||||
stats_lines int // size of backend generated source code in lines
|
||||
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:
|
||||
module_search_paths []string
|
||||
parsed_files []&ast.File
|
||||
|
@ -86,6 +90,9 @@ pub fn (mut b Builder) middle_stages() ? {
|
|||
b.checker.check_files(b.parsed_files)
|
||||
util.timing_measure('CHECK')
|
||||
b.print_warnings_and_errors()
|
||||
if b.pref.check_only {
|
||||
return error('stop_after_checker')
|
||||
}
|
||||
util.timing_start('TRANSFORM')
|
||||
b.transformer.transform_files(b.parsed_files)
|
||||
util.timing_measure('TRANSFORM')
|
||||
|
@ -133,7 +140,8 @@ pub fn (mut b Builder) parse_imports() {
|
|||
for imp in ast_file.imports {
|
||||
mod := imp.mod
|
||||
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
|
||||
}
|
||||
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 {
|
||||
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_ast.get_import_tok_idx(mod))
|
||||
// break
|
||||
error_with_pos('cannot import module "$mod" (not found)', ast_file.path,
|
||||
imp.pos)
|
||||
b.parsed_files[i].errors << b.error_with_pos('cannot import module "$mod" (not found)',
|
||||
ast_file.path, imp.pos)
|
||||
break
|
||||
}
|
||||
v_files := b.v_files_from_dir(import_path)
|
||||
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))
|
||||
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)
|
||||
continue
|
||||
}
|
||||
// Add all imports referenced by these libs
|
||||
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 {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
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
|
||||
}
|
||||
if b.pref.is_stats {
|
||||
estring := util.bold(b.checker.errors.len.str())
|
||||
wstring := util.bold(b.checker.warnings.len.str())
|
||||
nstring := util.bold(b.checker.nr_notices.str())
|
||||
mut nr_errors := b.checker.errors.len
|
||||
mut nr_warnings := b.checker.warnings.len
|
||||
mut nr_notices := b.checker.notices.len
|
||||
|
||||
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 {
|
||||
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.checker.nr_errors > 0 {
|
||||
if b.nr_errors > 0 {
|
||||
exit(1)
|
||||
}
|
||||
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 {
|
||||
println('$b.checker.nr_warnings warnings')
|
||||
}
|
||||
|
@ -455,12 +541,21 @@ struct FunctionRedefinition {
|
|||
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 {
|
||||
if !b.pref.check_only {
|
||||
ferror := util.formatted_error('builder error:', s, fpath, pos)
|
||||
eprintln(ferror)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
return errors.Error{
|
||||
file_path: fpath
|
||||
pos: pos
|
||||
reporter: .builder
|
||||
message: s
|
||||
}
|
||||
}
|
||||
|
||||
[noreturn]
|
||||
fn verror(s string) {
|
||||
util.verror('builder error', s)
|
||||
|
|
|
@ -489,6 +489,12 @@ fn (mut v Builder) cc() {
|
|||
}
|
||||
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() {
|
||||
// output to stdout
|
||||
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 {
|
||||
return
|
||||
}
|
||||
if b.pref.only_check_syntax {
|
||||
if b.pref.only_check_syntax || b.pref.check_only {
|
||||
return
|
||||
}
|
||||
if b.pref.should_output_to_stdout() {
|
||||
|
|
|
@ -6,6 +6,7 @@ pub enum Reporter {
|
|||
scanner
|
||||
parser
|
||||
checker
|
||||
builder
|
||||
gen
|
||||
}
|
||||
|
||||
|
|
|
@ -273,6 +273,17 @@ pub fn (mut p Parser) parse() &ast.File {
|
|||
}
|
||||
}
|
||||
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{
|
||||
path: p.file_name
|
||||
path_base: p.file_base
|
||||
|
@ -286,8 +297,9 @@ pub fn (mut p Parser) parse() &ast.File {
|
|||
stmts: stmts
|
||||
scope: p.scope
|
||||
global_scope: p.table.global_scope
|
||||
errors: p.errors
|
||||
warnings: p.warnings
|
||||
errors: errors
|
||||
warnings: warnings
|
||||
notices: notices
|
||||
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)
|
||||
}
|
||||
mut kind := 'error:'
|
||||
if p.pref.output_mode == .stdout {
|
||||
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||
if p.pref.is_verbose {
|
||||
print_backtrace()
|
||||
kind = 'parser error:'
|
||||
|
@ -1628,6 +1640,12 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Position) ast.NodeError
|
|||
reporter: .parser
|
||||
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 {
|
||||
// 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)
|
||||
}
|
||||
mut kind := 'error:'
|
||||
if p.pref.output_mode == .stdout {
|
||||
if p.pref.output_mode == .stdout && !p.pref.check_only {
|
||||
if p.pref.is_verbose {
|
||||
print_backtrace()
|
||||
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 {
|
||||
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)
|
||||
eprintln(ferror)
|
||||
} else {
|
||||
|
@ -1700,7 +1718,7 @@ pub fn (mut p Parser) note_with_pos(s string, pos token.Position) {
|
|||
if p.pref.skip_warnings {
|
||||
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)
|
||||
eprintln(ferror)
|
||||
} else {
|
||||
|
|
|
@ -178,6 +178,7 @@ pub mut:
|
|||
is_parallel bool
|
||||
is_vweb bool // skip _ var warning in templates
|
||||
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
|
||||
skip_unused bool // skip generating C code for functions, that are not used
|
||||
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' {
|
||||
res.only_check_syntax = true
|
||||
}
|
||||
'-check' {
|
||||
res.check_only = true
|
||||
}
|
||||
'-h', '-help', '--help' {
|
||||
// NB: help is *very important*, just respond to all variations:
|
||||
res.is_help = true
|
||||
|
|
|
@ -1336,7 +1336,7 @@ pub fn (mut s Scanner) note(msg string) {
|
|||
line_nr: s.line_nr
|
||||
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))
|
||||
} else {
|
||||
s.notices << errors.Notice{
|
||||
|
@ -1358,7 +1358,7 @@ pub fn (mut s Scanner) warn(msg string) {
|
|||
pos: s.pos
|
||||
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))
|
||||
} else {
|
||||
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
|
||||
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))
|
||||
exit(1)
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue