From 652881c73b95efd963aaca9d49116388e132950a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 10 Aug 2025 06:45:59 +0300 Subject: [PATCH] all: vls mode fixes and improvements; v -json-errors flag --- vlib/os/os_nix.c.v | 15 --------------- vlib/os/util/util.v | 24 +++++++++++++++++++++++ vlib/v/builder/builder.v | 22 +++++++++++++++++++-- vlib/v/checker/checker.v | 4 ++++ vlib/v/parser/parser.v | 9 ++++++--- vlib/v/pref/pref.v | 11 +++++++++-- vlib/v/util/errors.v | 41 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 vlib/os/util/util.v diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index cb41cffa7..2fdf999f6 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -29,35 +29,20 @@ const stderr_value = 2 // (Must be realized in Syscall) (Must be specified) // ref: http://www.ccfit.nsu.ru/~deviv/courses/unix/unix/ng7c229.html pub const s_ifmt = 0xF000 // type of file - pub const s_ifdir = 0x4000 // directory - pub const s_ifreg = 0x8000 // regular file - pub const s_iflnk = 0xa000 // link - pub const s_isuid = 0o4000 // SUID - pub const s_isgid = 0o2000 // SGID - pub const s_isvtx = 0o1000 // Sticky - pub const s_irusr = 0o0400 // Read by owner - pub const s_iwusr = 0o0200 // Write by owner - pub const s_ixusr = 0o0100 // Execute by owner - pub const s_irgrp = 0o0040 // Read by group - pub const s_iwgrp = 0o0020 // Write by group - pub const s_ixgrp = 0o0010 // Execute by group - pub const s_iroth = 0o0004 // Read by others - pub const s_iwoth = 0o0002 // Write by others - pub const s_ixoth = 0o0001 fn C.utime(&char, &C.utimbuf) int diff --git a/vlib/os/util/util.v b/vlib/os/util/util.v new file mode 100644 index 000000000..5b09403fe --- /dev/null +++ b/vlib/os/util/util.v @@ -0,0 +1,24 @@ +module util + +import os + +// TODO `select` doesn't work with time.Duration for some reason +pub fn execute_with_timeout(cmd string, timeout i64) ?os.Result { + ch := chan os.Result{cap: 1} + spawn fn [cmd] (c chan os.Result) { + res := os.execute(cmd) + c <- res + }(ch) + select { + a := <-ch { + return a + } + // timeout { + // 1000 * time.millisecond { + // timeout * time.millisecond { + timeout * 1_000_000 { + return none + } + } + return os.Result{} +} diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index fe27ebf57..0ab091f85 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -14,6 +14,7 @@ import v.markused import v.depgraph import v.callgraph import v.dotgraph +// import x.json2 pub struct Builder { pub: @@ -475,7 +476,8 @@ pub fn (b &Builder) show_total_warns_and_errors_stats() { println('checker summary: ${estring} V errors, ${wstring} V warnings, ${nstring} V notices') } } - if b.checker.nr_errors > 0 && b.pref.path.ends_with('.v') && os.is_file(b.pref.path) { + if !b.pref.is_vls && b.checker.nr_errors > 0 && b.pref.path.ends_with('.v') + && os.is_file(b.pref.path) { if b.checker.errors.any(it.message.starts_with('unknown ')) { // Sometimes users try to `v main.v`, when they have several .v files in their project. // Then, they encounter puzzling errors about missing or unknown types. In this case, @@ -519,6 +521,7 @@ pub fn (mut b Builder) print_warnings_and_errors() { } } + mut json_errors := []util.JsonError{} for file in b.parsed_files { for err in file.errors { kind := if b.pref.is_verbose { @@ -526,9 +529,24 @@ pub fn (mut b Builder) print_warnings_and_errors() { } else { 'error:' } - util.show_compiler_message(kind, err.CompilerMessage) + + if b.pref.json_errors { + json_errors << util.JsonError{ + message: err.message + path: err.file_path + line_nr: err.pos.line_nr + 1 + col: err.pos.col + 1 + } + // util.print_json_error(kind, err.CompilerMessage) + } else { + util.show_compiler_message(kind, err.CompilerMessage) + } } } + if b.pref.json_errors { + util.print_json_errors(json_errors) + // eprintln(json2.encode_pretty(json_errors)) + } if !b.pref.skip_warnings { for file in b.parsed_files { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 380edbff3..6fd133e4f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1881,6 +1881,10 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } } else { if unknown_field_msg == '' { + if field_name == '' && c.pref.is_vls { + // VLS will often have `foo.`, skip the no field error + return ast.void_type + } unknown_field_msg = 'type `${sym.name}` has no field named `${field_name}`' } if sym.info is ast.Struct { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 8a2f8b9d2..d03388490 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -249,9 +249,12 @@ pub fn parse_file(path string, mut table ast.Table, comments_mode scanner.Commen eprintln('> ${@MOD}.${@FN} comments_mode: ${comments_mode:-20} | path: ${path}') } mut p := Parser{ - scanner: scanner.new_scanner_file(path, comments_mode, pref_) or { panic(err) } - table: table - pref: pref_ + scanner: scanner.new_scanner_file(path, comments_mode, pref_) or { panic(err) } + table: table + pref: pref_ + // Only set vls mode if it's the file the user requested via `v -vls-mode file.v` + // Otherwise we'd be parsing entire stdlib in vls mode + is_vls: pref_.is_vls && path == pref_.path scope: &ast.Scope{ start_pos: 0 parent: table.global_scope diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 3244cadfd..ad0a62764 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -257,8 +257,9 @@ pub mut: // forwards compatibility settings: relaxed_gcc14 bool = true // turn on the generated pragmas, that make gcc versions > 14 a lot less pedantic. The default is to have those pragmas in the generated C output, so that gcc-14 can be used on Arch etc. // - subsystem Subsystem // the type of the window app, that is going to be generated; has no effect on !windows - is_vls bool + subsystem Subsystem // the type of the window app, that is going to be generated; has no effect on !windows + is_vls bool + json_errors bool // -json-errors, for VLS and other tools } pub struct LineInfo { @@ -406,6 +407,9 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin '-check' { res.check_only = true } + '-vls-mode' { + res.is_vls = true + } '-?', '-h', '-help', '--help' { // Note: help is *very important*, just respond to all variations: res.is_help = true @@ -561,6 +565,9 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin '-repl' { res.is_repl = true } + '-json-errors' { + res.json_errors = true + } '-live' { res.is_livemain = true } diff --git a/vlib/v/util/errors.v b/vlib/v/util/errors.v index 7b7f2628d..04a2902c8 100644 --- a/vlib/v/util/errors.v +++ b/vlib/v/util/errors.v @@ -210,3 +210,44 @@ pub fn show_compiler_message(kind string, err errors.CompilerMessage) { eprintln(bold('Details: ') + color('details', err.details)) } } + +pub struct JsonError { +pub: + path string + message string + line_nr int + col int + len int +} + +pub fn print_json_errors(errs []JsonError) { + // Can't import x.json2 or json, so have to manually generate json + eprintln('[') + for i, e in errs { + msg := e.message.replace('"', '\\"').replace('\n', '\\n') + eprintln('{ +"path":"${e.path}", +"message":"${msg}", +"line_nr":${e.line_nr}, +"col":${e.col}, +"len":${e.len} +}') + if i < errs.len - 1 { + eprintln(',') + } + } + eprintln(']') +} + +/* +pub fn print_json_error(kind string, err errors.CompilerMessage) { + e := JsonError{ + message: err.message + path: err.file_path + line_nr: err.pos.line_nr + 1 + col: err.pos.col + 1 + len: err.pos.len + } + eprintln(json2.encode_pretty(e)) +} +*/ -- 2.39.5