parser: fix infinite loop in Parser.sql stmt in -silent -print-watched-files mode (used by v watch) (#20873)

This commit is contained in:
Delyan Angelov 2024-02-19 18:17:15 +02:00 committed by GitHub
parent d198a89897
commit efa98d9234
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 147 additions and 35 deletions

View File

@ -133,6 +133,10 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
mut lines := []ast.SqlStmtLine{}
for p.tok.kind != .rcbr {
if p.tok.kind == .eof {
p.unexpected_with_pos(pos, got: 'eof, while parsing an SQL statement')
return ast.SqlStmt{}
}
lines << p.parse_sql_stmt_line()
}

View File

@ -0,0 +1,68 @@
module main
import db.sqlite
@[table: 'modules']
struct Module {
id int @[primary; sql: serial]
name string
nr_downloads int @[sql: u64]
creator User
}
struct User {
id int @[primary; sql: serial]
age u32 @[unique: 'user']
name string @[sql: 'username'; sql_type: 'VARCHAR(200)'; unique]
is_customer bool @[sql: 'abc'; unique: 'user']
skipped_string string @[skip]
}
struct Parent {
id int @[primary; sql: serial]
name string
children []Child @[fkey: 'parent_id']
}
struct Child {
id int @[primary; sql: serial]
parent_id int
name string
}
fn main() {
eprintln('------------ ${@METHOD} -----------------')
mut db := sqlite.connect(':memory:')!
defer {
sql db {
drop table Parent
drop table Child
} or {}
db.close() or {}
}
sql db {
create table Parent
}!
sql db {
create table Child
}!
par := Parent{
name: 'test'
children: [
Child{
name: 'abc'
},
Child{
name: 'def'
},
]
}
sql db {
insert par into Parent
}!
sql db {
select from Parent where id == 1 & name == "whatever"
}!
eprintln(parent)
}

View File

View File

@ -7,6 +7,9 @@ import v.checker
import v.pref
import term
import os
import benchmark
const vroot = os.dir(os.dir(os.dir(os.dir(@FILE))))
fn test_eval() {
/*
@ -60,42 +63,40 @@ fn test_eval() {
return
}
fn test_parse_file() {
if true {
return
}
s := '
fn test_parse_text() {
println(@LOCATION)
source_text := '
fn foo() int {
f := 23
return 10+4
return 10+4+f
}
fn ff(x int) {}
12 + 3
x := 10
5+7
8+4
fn main() {
ff(12 + 3)
x := 10
bar(5+7)
ff(8+x)
}
'
table := ast.new_table()
vpref := &pref.Preferences{}
mut prog := parse_file(s, table, .skip_comments, vpref)
mut prog := parse_text(source_text, '', table, .skip_comments, vpref)
mut checker_ := checker.new_checker(table, vpref)
checker_.check(mut prog)
res, _, _, _ := c.gen([prog], table, vpref)
println(res)
}
fn test_one() {
if true {
return
}
println('\n\ntest_one()')
println(@LOCATION)
input := ['a := 10', 'b := -a', 'c := 20']
expected := 'int a = 10;int b = -a;int c = 20;'
table := ast.new_table()
vpref := &pref.Preferences{}
scope := &ast.Scope{
start_pos: 0
parent: 0
}
mut e := []ast.Stmt{}
for line in input {
@ -113,6 +114,7 @@ fn test_one() {
println(res)
ok := expected == res
println(res)
dump(ok)
assert ok
if !ok {
}
@ -120,6 +122,7 @@ fn test_one() {
}
fn test_parse_expr() {
println(@LOCATION)
if true {
return
}
@ -138,7 +141,6 @@ fn test_parse_expr() {
mut chk := checker.new_checker(table, vpref)
scope := &ast.Scope{
start_pos: 0
parent: 0
}
for s in input {
println('\n\nst="${s}"')
@ -176,6 +178,7 @@ fn test_parse_expr() {
}
fn test_num_literals() {
println(@LOCATION)
inputs := [
'a := -1',
'b := -12.e17',
@ -185,7 +188,6 @@ fn test_num_literals() {
table := ast.new_table()
mut scope := &ast.Scope{
start_pos: 0
parent: 0
}
mut rhs_types := []string{}
for input in inputs {
@ -220,6 +222,7 @@ for s in text_expr {
*/
fn test_fn_is_html_open_tag() {
println(@LOCATION)
mut s := '<style>'
mut b := is_html_open_tag('style', s)
assert b == true
@ -230,44 +233,47 @@ fn test_fn_is_html_open_tag() {
s = '<style/>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = 'styl'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = 'style'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<style'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<<style>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<style>>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<stylex>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<html>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
s = '<script>'
b = is_html_open_tag('style', s)
assert b == false
assert !b
}
fn scan_v(mut files []string, path string) ! {
for i in os.ls(path)! {
p := os.join_path(path, i)
if !os.exists(p) {
continue
}
if os.is_file(p) {
if i.ends_with('.v') && !i.contains_any_substr(['_test.', 'test_', 'tests_']) {
files << p
@ -281,26 +287,30 @@ fn scan_v(mut files []string, path string) ! {
}
fn parse(output_mode pref.OutputMode) ! {
mut b := benchmark.start()
mut files := []string{}
// mode_files := os.walk_ext(os.join_path(vroot, 'vlib/v/parser/testdata/${output_mode}'), '.vv')
// files << mode_files
scan_v(mut files, os.join_path(parser.vroot, 'vlib'))!
scan_v(mut files, os.join_path(parser.vroot, 'cmd'))!
mut pref_ := pref.new_preferences()
pref_.output_mode = output_mode
mut files := []string{}
scan_v(mut files, pref_.vlib)!
scan_v(mut files, os.join_path(pref_.vroot, 'cmd'))!
for f in files {
for idx, f in files {
// eprintln('> parsing in mode: ${output_mode}, ${idx+1:5}/${files.len} $f ...')
table := ast.new_table()
p := parse_file(f, table, .parse_comments, pref_)
assert isnil(p) == false
assert !isnil(p), 'failed to parse `${f}` in mode: ${output_mode}'
assert p.errors.len == 0, 'file ${f} should have been parsed with 0 errors'
}
b.measure('parsing ${files.len} files in ${output_mode} mode')
}
fn test_parse_with_silent() {
println(@LOCATION)
parse(.silent)!
}
fn test_parse_with_stdout() {
println(@LOCATION)
parse(.stdout)!
}
// For issue #15516
fn test_anon_struct() {
}

View File

@ -0,0 +1,30 @@
import os
const vexe = @VEXE
const vroot = os.dir(vexe)
fn test_print_v_files_in_stdout_mode() {
check_parsing_files_in_folder('vlib/v/parser/testdata/stdout', '-print-v-files')
}
//
fn test_print_v_files_in_silent_mode() {
check_parsing_files_in_folder('vlib/v/parser/testdata/silent', '-silent -print-v-files')
}
fn test_print_watched_files_in_silent_mode__used_by_vwatch() {
check_parsing_files_in_folder('vlib/v/parser/testdata/silent', '-silent -print-watched-files')
}
fn check_parsing_files_in_folder(folder string, options string) {
println('> checking .vv files in folder: `${folder}`, with `${options}` ...')
files := os.walk_ext(os.join_path(vroot, folder), '.vv')
for f in files {
cmd := '${os.quoted_path(vexe)} ${options} ${os.quoted_path(f)}'
// eprintln('> cmd: $cmd')
res := os.execute(cmd)
assert res.exit_code == 0, 'failed cmd: ${cmd}, output:\n${res.output}'
assert res.output.split_into_lines().len > 10, 'there should be several files printed by cmd: ${cmd}'
}
}