parser: implement support for -Wimpure-v (#7195)
parent
90c1c639fe
commit
d5915bde7c
|
@ -126,8 +126,15 @@ The build flags are shared by the build and run commands:
|
||||||
Treat all warnings as errors, even in development builds.
|
Treat all warnings as errors, even in development builds.
|
||||||
|
|
||||||
-Wfatal-errors
|
-Wfatal-errors
|
||||||
Unconditionally exit with exit(1) after the first error. Useful for scripts/tooling that calls V.
|
Unconditionally exit with exit(1) after the first error.
|
||||||
|
Useful for scripts/tooling that calls V.
|
||||||
|
|
||||||
|
-Wimpure-v
|
||||||
|
Warn about using C. or JS. symbols in plain .v files.
|
||||||
|
These should be moved in .c.v and .js.v .
|
||||||
|
NB: in the future, this will be turned on by default,
|
||||||
|
and will become an error, after vlib is cleaned up.
|
||||||
|
|
||||||
For C-specific build flags, use `v help build-c`.
|
For C-specific build flags, use `v help build-c`.
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
|
|
@ -20,6 +20,9 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
} else {
|
} else {
|
||||||
p.check_name()
|
p.check_name()
|
||||||
}
|
}
|
||||||
|
if language != .v {
|
||||||
|
p.check_for_impure_v(language, first_pos)
|
||||||
|
}
|
||||||
mut or_kind := ast.OrKind.absent
|
mut or_kind := ast.OrKind.absent
|
||||||
if fn_name == 'json.decode' {
|
if fn_name == 'json.decode' {
|
||||||
p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)`
|
p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)`
|
||||||
|
@ -155,6 +158,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
if language != .v {
|
if language != .v {
|
||||||
p.next()
|
p.next()
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
|
p.check_for_impure_v(language, p.tok.position())
|
||||||
}
|
}
|
||||||
// Receiver?
|
// Receiver?
|
||||||
mut rec_name := ''
|
mut rec_name := ''
|
||||||
|
|
|
@ -15,11 +15,12 @@ import runtime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
|
pref &pref.Preferences
|
||||||
|
mut:
|
||||||
file_base string // "hello.v"
|
file_base string // "hello.v"
|
||||||
file_name string // "/home/user/hello.v"
|
file_name string // "/home/user/hello.v"
|
||||||
file_name_dir string // "/home/user"
|
file_name_dir string // "/home/user"
|
||||||
pref &pref.Preferences
|
file_backend_mode table.Language // .c for .c.v|.c.vv|.c.vsh files; .js for .js.v files, .v otherwise.
|
||||||
mut:
|
|
||||||
scanner &scanner.Scanner
|
scanner &scanner.Scanner
|
||||||
comments_mode scanner.CommentsMode = .skip_comments
|
comments_mode scanner.CommentsMode = .skip_comments
|
||||||
// see comment in parse_file
|
// see comment in parse_file
|
||||||
|
@ -101,9 +102,6 @@ pub fn parse_text(text string, path string, table &table.Table, comments_mode sc
|
||||||
mut p := Parser{
|
mut p := Parser{
|
||||||
scanner: s
|
scanner: s
|
||||||
comments_mode: comments_mode
|
comments_mode: comments_mode
|
||||||
file_name: path
|
|
||||||
file_base: os.base(path)
|
|
||||||
file_name_dir: os.dir(path)
|
|
||||||
table: table
|
table: table
|
||||||
pref: pref
|
pref: pref
|
||||||
scope: &ast.Scope{
|
scope: &ast.Scope{
|
||||||
|
@ -114,9 +112,23 @@ pub fn parse_text(text string, path string, table &table.Table, comments_mode sc
|
||||||
warnings: []errors.Warning{}
|
warnings: []errors.Warning{}
|
||||||
global_scope: global_scope
|
global_scope: global_scope
|
||||||
}
|
}
|
||||||
|
p.set_path(path)
|
||||||
return p.parse()
|
return p.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Parser) set_path(path string) {
|
||||||
|
p.file_name = path
|
||||||
|
p.file_base = os.base(path)
|
||||||
|
p.file_name_dir = os.dir(path)
|
||||||
|
p.file_backend_mode = .v
|
||||||
|
if path.ends_with('.c.v') || path.ends_with('.c.vv') || path.ends_with('.c.vsh') {
|
||||||
|
p.file_backend_mode = .c
|
||||||
|
}
|
||||||
|
if path.ends_with('.js.v') || path.ends_with('.js.vv') || path.ends_with('.js.vsh') {
|
||||||
|
p.file_backend_mode = .js
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_file(path string, table &table.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences, global_scope &ast.Scope) ast.File {
|
pub fn parse_file(path string, table &table.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences, global_scope &ast.Scope) ast.File {
|
||||||
// NB: when comments_mode == .toplevel_comments,
|
// NB: when comments_mode == .toplevel_comments,
|
||||||
// the parser gives feedback to the scanner about toplevel statements, so that the scanner can skip
|
// the parser gives feedback to the scanner about toplevel statements, so that the scanner can skip
|
||||||
|
@ -130,9 +142,6 @@ pub fn parse_file(path string, table &table.Table, comments_mode scanner.Comment
|
||||||
scanner: scanner.new_scanner_file(path, comments_mode, pref)
|
scanner: scanner.new_scanner_file(path, comments_mode, pref)
|
||||||
comments_mode: comments_mode
|
comments_mode: comments_mode
|
||||||
table: table
|
table: table
|
||||||
file_name: path
|
|
||||||
file_base: os.base(path)
|
|
||||||
file_name_dir: os.dir(path)
|
|
||||||
pref: pref
|
pref: pref
|
||||||
scope: &ast.Scope{
|
scope: &ast.Scope{
|
||||||
start_pos: 0
|
start_pos: 0
|
||||||
|
@ -142,6 +151,7 @@ pub fn parse_file(path string, table &table.Table, comments_mode scanner.Comment
|
||||||
warnings: []errors.Warning{}
|
warnings: []errors.Warning{}
|
||||||
global_scope: global_scope
|
global_scope: global_scope
|
||||||
}
|
}
|
||||||
|
p.set_path(path)
|
||||||
return p.parse()
|
return p.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,9 +163,6 @@ pub fn parse_vet_file(path string, table_ &table.Table, pref &pref.Preferences)
|
||||||
scanner: scanner.new_vet_scanner_file(path, .parse_comments, pref)
|
scanner: scanner.new_vet_scanner_file(path, .parse_comments, pref)
|
||||||
comments_mode: .parse_comments
|
comments_mode: .parse_comments
|
||||||
table: table_
|
table: table_
|
||||||
file_name: path
|
|
||||||
file_base: os.base(path)
|
|
||||||
file_name_dir: os.dir(path)
|
|
||||||
pref: pref
|
pref: pref
|
||||||
scope: &ast.Scope{
|
scope: &ast.Scope{
|
||||||
start_pos: 0
|
start_pos: 0
|
||||||
|
@ -165,6 +172,7 @@ pub fn parse_vet_file(path string, table_ &table.Table, pref &pref.Preferences)
|
||||||
warnings: []errors.Warning{}
|
warnings: []errors.Warning{}
|
||||||
global_scope: global_scope
|
global_scope: global_scope
|
||||||
}
|
}
|
||||||
|
p.set_path(path)
|
||||||
if p.scanner.text.contains('\n ') {
|
if p.scanner.text.contains('\n ') {
|
||||||
source_lines := os.read_lines(path) or { []string{} }
|
source_lines := os.read_lines(path) or { []string{} }
|
||||||
for lnumber, line in source_lines {
|
for lnumber, line in source_lines {
|
||||||
|
@ -845,6 +853,29 @@ fn (mut p Parser) parse_attr() table.Attr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Parser) check_for_impure_v(language table.Language, pos token.Position) {
|
||||||
|
if language == .v {
|
||||||
|
// pure V code is always allowed everywhere
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !p.pref.warn_impure_v {
|
||||||
|
// the stricter mode is not ON yet => allow everything for now
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.file_backend_mode != language {
|
||||||
|
upcase_language := language.str().to_upper()
|
||||||
|
if p.file_backend_mode == .v {
|
||||||
|
p.warn_with_pos('$upcase_language code will not be allowed in pure .v files, please move it to a .${language}.v file instead',
|
||||||
|
pos)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
p.warn_with_pos('$upcase_language code is not allowed in .${p.file_backend_mode}.v files, please move it to a .${language}.v file',
|
||||||
|
pos)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut p Parser) error(s string) {
|
pub fn (mut p Parser) error(s string) {
|
||||||
p.error_with_pos(s, p.tok.position())
|
p.error_with_pos(s, p.tok.position())
|
||||||
}
|
}
|
||||||
|
@ -1016,12 +1047,13 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||||
pos: type_pos
|
pos: type_pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
language := if p.tok.lit == 'C' {
|
mut language := table.Language.v
|
||||||
table.Language.c
|
if p.tok.lit == 'C' {
|
||||||
|
language = table.Language.c
|
||||||
|
p.check_for_impure_v(language, p.tok.position())
|
||||||
} else if p.tok.lit == 'JS' {
|
} else if p.tok.lit == 'JS' {
|
||||||
table.Language.js
|
language = table.Language.js
|
||||||
} else {
|
p.check_for_impure_v(language, p.tok.position())
|
||||||
table.Language.v
|
|
||||||
}
|
}
|
||||||
mut mod := ''
|
mut mod := ''
|
||||||
// p.warn('resetting')
|
// p.warn('resetting')
|
||||||
|
@ -2030,12 +2062,13 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||||
parent_type := first_type
|
parent_type := first_type
|
||||||
parent_name := p.table.get_type_symbol(parent_type).name
|
parent_name := p.table.get_type_symbol(parent_type).name
|
||||||
pid := parent_type.idx()
|
pid := parent_type.idx()
|
||||||
language := if parent_name.len > 2 && parent_name.starts_with('C.') {
|
mut language := table.Language.v
|
||||||
table.Language.c
|
if parent_name.len > 2 && parent_name.starts_with('C.') {
|
||||||
|
language = table.Language.c
|
||||||
|
p.check_for_impure_v(language, decl_pos)
|
||||||
} else if parent_name.len > 2 && parent_name.starts_with('JS.') {
|
} else if parent_name.len > 2 && parent_name.starts_with('JS.') {
|
||||||
table.Language.js
|
language = table.Language.js
|
||||||
} else {
|
p.check_for_impure_v(language, decl_pos)
|
||||||
table.Language.v
|
|
||||||
}
|
}
|
||||||
prepend_mod_name := p.prepend_mod(name)
|
prepend_mod_name := p.prepend_mod(name)
|
||||||
p.table.register_type_symbol(table.TypeSymbol{
|
p.table.register_type_symbol(table.TypeSymbol{
|
||||||
|
|
|
@ -36,6 +36,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
p.next() // .
|
p.next() // .
|
||||||
}
|
}
|
||||||
name_pos := p.tok.position()
|
name_pos := p.tok.position()
|
||||||
|
p.check_for_impure_v(language, name_pos)
|
||||||
mut name := p.check_name()
|
mut name := p.check_name()
|
||||||
// defer {
|
// defer {
|
||||||
// if name.contains('App') {
|
// if name.contains('App') {
|
||||||
|
|
|
@ -121,7 +121,8 @@ pub mut:
|
||||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||||
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
||||||
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
|
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
|
||||||
skip_warnings bool // like C's "-w"
|
skip_warnings bool // like C's "-w", forces warnings to be ignored.
|
||||||
|
warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default
|
||||||
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
|
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
|
||||||
fatal_errors bool // unconditionally exit after the first error with exit(1)
|
fatal_errors bool // unconditionally exit after the first error with exit(1)
|
||||||
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
|
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
|
||||||
|
@ -173,6 +174,9 @@ pub fn parse_args(args []string) (&Preferences, string) {
|
||||||
'-progress' {
|
'-progress' {
|
||||||
// processed by testing tools in cmd/tools/modules/testing/common.v
|
// processed by testing tools in cmd/tools/modules/testing/common.v
|
||||||
}
|
}
|
||||||
|
'-Wimpure-v' {
|
||||||
|
res.warn_impure_v = true
|
||||||
|
}
|
||||||
'-Wfatal-errors' {
|
'-Wfatal-errors' {
|
||||||
res.fatal_errors = true
|
res.fatal_errors = true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue