fmt: unwrap long infix exprs inside parenthesis if necessary (#8609)

pull/8631/head
Lukas Neubert 2021-02-07 23:10:39 +01:00 committed by GitHub
parent ff1aa06455
commit 7f4c582f1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 93 additions and 52 deletions

View File

@ -310,7 +310,7 @@ fn (vd VDoc) gen_html(d doc.Doc) string {
} else {
symbols_toc_str
}).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi
&& vd.docs.len > 1&& d.head.name != 'README' {
&& vd.docs.len > 1 && d.head.name != 'README' {
'<div class="doc-toc"><ul>' + symbols_toc_str + '</ul></div>'
} else {
''
@ -401,7 +401,7 @@ fn html_highlight(code string, tb &table.Table) string {
if token.is_key(tok.lit) || token.is_decl(tok.kind) {
tok_typ = .keyword
} else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary()
|| tok.kind.is_relational()|| tok.kind.is_infix() {
|| tok.kind.is_relational() || tok.kind.is_infix() {
tok_typ = .operator
}
}

View File

@ -303,7 +303,7 @@ pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
}
if ftyp in [table.string_type, table.bool_type]
|| sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .none_]
|| ftyp.has_flag(.optional)|| sym.has_method('str') {
|| ftyp.has_flag(.optional) || sym.has_method('str') {
return `s`
} else {
return `_`

View File

@ -1298,11 +1298,11 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
ast.AnonFn {
if arg_expr.decl.params.len > 1 {
c.error('function needs exactly 1 argument', arg_expr.decl.pos)
} else if is_map
&& (arg_expr.decl.return_type == table.void_type || arg_expr.decl.params[0].typ != elem_typ) {
} else if is_map && (arg_expr.decl.return_type == table.void_type
|| arg_expr.decl.params[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', arg_expr.decl.pos)
} else if !is_map
&& (arg_expr.decl.return_type != table.bool_type || arg_expr.decl.params[0].typ != elem_typ) {
} else if !is_map && (arg_expr.decl.return_type != table.bool_type
|| arg_expr.decl.params[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
arg_expr.decl.pos)
}
@ -2389,8 +2389,8 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
}
for i, exp_type in expected_types {
got_typ := c.unwrap_generic(got_types[i])
if got_typ.has_flag(.optional)
&& (!exp_type.has_flag(.optional) || c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
if got_typ.has_flag(.optional) && (!exp_type.has_flag(.optional)
|| c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
pos := return_stmt.exprs[i].position()
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
pos)
@ -2413,7 +2413,7 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
pos)
}
if (exp_type.is_ptr() || exp_type.is_pointer())
&& (!got_typ.is_ptr() && !got_typ.is_pointer())&& got_typ != table.int_literal_type {
&& (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != table.int_literal_type {
pos := return_stmt.exprs[i].position()
c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
pos)
@ -3765,7 +3765,8 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
node.pos)
}
} else if node.typ == table.string_type
&& (from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool] || (from_type_sym.kind == .array && from_type_sym.name == 'array_byte')) {
&& (from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool]
|| (from_type_sym.kind == .array && from_type_sym.name == 'array_byte')) {
type_name := c.table.type_to_str(node.expr_type)
c.error('cannot cast type `$type_name` to string, use `x.str()` instead', node.pos)
} else if node.expr_type == table.string_type {
@ -5195,7 +5196,7 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) table.Type {
node.left_type = typ
typ_sym := c.table.get_final_type_symbol(typ)
if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr()
&& typ !in [table.byteptr_type, table.charptr_type]&& !typ.has_flag(.variadic) {
&& typ !in [table.byteptr_type, table.charptr_type] && !typ.has_flag(.variadic) {
c.error('type `$typ_sym.name` does not support indexing', node.pos)
}
if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter {
@ -5640,9 +5641,8 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type {
}
fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Position, table_name string) []table.Field {
fields := info.fields.filter(
(it.typ in [table.string_type, table.int_type, table.bool_type] || c.table.types[int(it.typ)].kind == .struct_)
&& !it.attrs.contains('skip'))
fields := info.fields.filter((it.typ in [table.string_type, table.int_type, table.bool_type]
|| c.table.types[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip'))
if fields.len == 0 {
c.error('V orm: select: empty fields in `$table_name`', pos)
return []table.Field{}
@ -5812,7 +5812,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
}
node.has_return = c.returns || has_top_return(node.stmts)
if node.language == .v && !node.no_body && node.return_type != table.void_type
&& !node.has_return&& node.name !in ['panic', 'exit'] {
&& !node.has_return && node.name !in ['panic', 'exit'] {
if c.inside_anon_fn {
c.error('missing return at the end of an anonymous function', node.pos)
} else {

View File

@ -144,7 +144,7 @@ fn (mut c Checker) mark_used(ast_files []ast.File) {
if walker.n_maps > 0 {
for k, mut mfn in all_fns {
if k == 'new_map_2' || k.starts_with('map_') || k.ends_with('set_1')
|| k.ends_with('exists_1')|| k.ends_with('get_1') {
|| k.ends_with('exists_1') || k.ends_with('get_1') {
walker.fn_decl(mut mfn)
}
}

View File

@ -1541,7 +1541,7 @@ pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) {
if node.op == .left_shift {
f.is_assign = true // To write ternary if on a single line
}
infix_start := f.out.len
start_pos := f.out.len
start_len := f.line_len
f.expr(node.left)
is_one_val_array_init := node.op in [.key_in, .not_in] && node.right is ast.ArrayInit
@ -1562,14 +1562,14 @@ pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) {
if !buffering_save && f.buffering {
f.buffering = false
if !f.single_line_if && f.line_len > fmt.max_len.last() {
f.wrap_infix(infix_start, start_len)
f.wrap_infix(start_pos, start_len, false)
}
}
f.is_assign = is_assign_save
f.or_expr(node.or_block)
}
pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int) {
pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int, ignore_paren bool) {
cut_span := f.out.len - start_pos
condstr := f.out.cut_last(cut_span)
is_cond_infix := condstr.contains_any_substr(['&&', '||'])
@ -1604,6 +1604,9 @@ pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int) {
index++
} else {
conditions[index] += '$cp '
if ignore_paren {
continue
}
if cp.starts_with('(') {
grouped_cond = true
} else if cp.ends_with(')') {
@ -1614,15 +1617,21 @@ pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int) {
for i, c in conditions {
cnd := c.trim_space()
if f.line_len + cnd.len < fmt.max_len[penalties[i]] {
if i > 0 && (!is_cond_infix || i < conditions.len - 1) {
if (i > 0 && i < conditions.len) || (ignore_paren && i == 0 && cnd[3] == `(`) {
f.write(' ')
}
f.write(cnd)
} else {
prev_len := f.line_len
prev_pos := f.out.len
f.writeln('')
f.indent++
f.write(cnd)
f.indent--
if f.line_len > fmt.max_len.last() && (cnd[0] == `(` || cnd[3] == `(`)
&& cnd.ends_with(')') {
f.wrap_infix(prev_pos, prev_len, true)
}
}
}
}
@ -2129,8 +2138,9 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
f.writeln('')
}
f.comments(field.next_comments, inline: false, has_nl: true, level: .keep)
if single_line_fields
&& (field.comments.len > 0 || field.next_comments.len > 0 || !expr_is_single_line(field.expr)
if single_line_fields && (field.comments.len > 0
|| field.next_comments.len > 0
|| !expr_is_single_line(field.expr)
|| f.line_len > fmt.max_len.last()) {
single_line_fields = false
f.out.go_back_to(fields_start)

View File

@ -53,7 +53,7 @@ fn main() {
println('this is quite a long string' +
' that is followd by an even longer part that should go to another line')
if (a == b && b > r) || (d > r) || (a < b) || (b < d && a + b > r)
|| (a + b + d >= 0 && r < 0)|| (a > b && d - r < b) {
|| (a + b + d >= 0 && r < 0) || (a > b && d - r < b) {
println('ok')
}
}

View File

@ -0,0 +1,16 @@
fn grouped_cond_single_line() {
// fmt tries to keep grouped conditions together...
_ := one_condition_before && another_condition
&& (inside_paren || is_kept_together || if_possible) && end_cond
}
fn unwrap_grouped_conds() {
// ...but sometimes they have to be splitted
_ := one_condition && before_condition && (conds_inside_paren
|| are_kept_together || if_possible || but_this_is_really_too_much
|| for_one_line)
_ := (also_inside_parens || just_as_above || but_this_is_also_more
|| than_a_single_line_could_fit) && end_cond
fields = fields.filter((it.typ in [string_type, int_type, bool_type]
|| c.table.types[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip'))
}

View File

@ -0,0 +1,11 @@
fn grouped_cond_single_line() {
// fmt tries to keep grouped conditions together...
_ := one_condition_before && another_condition && (inside_paren || is_kept_together || if_possible) && end_cond
}
fn unwrap_grouped_conds() {
// ...but sometimes they have to be splitted
_ := one_condition && before_condition && (conds_inside_paren || are_kept_together || if_possible || but_this_is_really_too_much || for_one_line)
_ := (also_inside_parens || just_as_above || but_this_is_also_more || than_a_single_line_could_fit) && end_cond
fields = fields.filter((it.typ in [string_type, int_type, bool_type] || c.table.types[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip'))
}

View File

@ -1074,7 +1074,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// We are using prebuilt modules, we do not need to generate
// their functions in main.c.
if node.mod != 'main' && node.mod != 'help' && !should_bundle_module
&& !g.pref.is_test&& node.generic_params.len == 0 {
&& !g.pref.is_test && node.generic_params.len == 0 {
skip = true
}
}
@ -1788,7 +1788,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// int pos = *(int*)_t190.data;
mut tmp_opt := ''
is_optional := g.is_autofree && (assign_stmt.op in [.decl_assign, .assign])
&& assign_stmt.left_types.len == 1&& assign_stmt.right[0] is ast.CallExpr
&& assign_stmt.left_types.len == 1 && assign_stmt.right[0] is ast.CallExpr
if is_optional {
// g.write('/* optional assignment */')
call_expr := assign_stmt.right[0] as ast.CallExpr
@ -2028,7 +2028,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} else {
g.out.go_back_to(pos)
is_var_mut := !is_decl && left is ast.Ident
&& (g.for_in_mut_val_name == (left as ast.Ident).name || (left as ast.Ident).name in g.fn_mut_arg_names)
&& (g.for_in_mut_val_name == (left as ast.Ident).name
|| (left as ast.Ident).name in g.fn_mut_arg_names)
addr := if is_var_mut { '' } else { '&' }
g.writeln('')
g.write('memcpy($addr')
@ -2100,7 +2101,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
if !is_fixed_array_copy || is_decl {
if !is_decl && var_type != table.string_type_idx && left is ast.Ident
&& (g.for_in_mut_val_name == (left as ast.Ident).name || (left as ast.Ident).name in g.fn_mut_arg_names) {
&& (g.for_in_mut_val_name == (left as ast.Ident).name
|| (left as ast.Ident).name in g.fn_mut_arg_names) {
g.write('*')
}
g.expr(left)
@ -3337,7 +3339,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.or_block(tmp_opt, node.or_block, table.void_type)
}
} else if unaliased_left.idx() in [table.u32_type_idx, table.u64_type_idx]
&& unaliased_right.is_signed()&& node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
&& unaliased_right.is_signed() && node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
bitsize := if unaliased_left.idx() == table.u32_type_idx
&& unaliased_right.idx() != table.i64_type_idx {
32
@ -3350,7 +3352,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.expr(node.right)
g.write(')')
} else if unaliased_right.idx() in [table.u32_type_idx, table.u64_type_idx]
&& unaliased_left.is_signed()&& node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
&& unaliased_left.is_signed() && node.op in [.eq, .ne, .gt, .lt, .ge, .le] {
bitsize := if unaliased_right.idx() == table.u32_type_idx
&& unaliased_left.idx() != table.i64_type_idx {
32
@ -3785,7 +3787,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
expr := branch.stmt.expr as ast.InfixExpr
channels << expr.left
if expr.right is ast.Ident || expr.right is ast.IndexExpr
|| expr.right is ast.SelectorExpr|| expr.right is ast.StructInit {
|| expr.right is ast.SelectorExpr || expr.right is ast.StructInit {
// addressable objects in the `C` output
objs << expr.right
tmp_objs << ''
@ -4006,8 +4008,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
// (as it used to be done).
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
first_branch := node.branches[0]
needs_tmp_var := node.is_expr
&& (g.is_autofree || (g.pref.experimental && (first_branch.stmts.len > 1 || (first_branch.stmts[0] is ast.ExprStmt && (first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
needs_tmp_var := node.is_expr && (g.is_autofree || (g.pref.experimental
&& (first_branch.stmts.len > 1 || (first_branch.stmts[0] is ast.ExprStmt
&& (first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
/*
needs_tmp_var := node.is_expr &&
(g.autofree || g.pref.experimental) &&
@ -4925,7 +4928,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
}
if !cloned {
if field.expected_type.is_ptr() && !(field.typ.is_ptr()
|| field.typ.is_pointer())&& !field.typ.is_number() {
|| field.typ.is_pointer()) && !field.typ.is_number() {
g.write('/* autoref */&')
}
g.expr_with_cast(field.expr, field.typ, field.expected_type)
@ -4991,7 +4994,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
}
if !cloned {
if sfield.expected_type.is_ptr() && !(sfield.typ.is_ptr()
|| sfield.typ.is_pointer())&& !sfield.typ.is_number() {
|| sfield.typ.is_pointer()) && !sfield.typ.is_number() {
g.write('/* autoref */&')
}
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)

View File

@ -107,7 +107,7 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
}
}
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__')
&& name != 'main__main'&& node.name != 'str' {
&& name != 'main__main' && node.name != 'str' {
mut key := node.name
if node.is_method {
sym := g.table.get_type_symbol(node.receiver.typ)
@ -179,7 +179,7 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
fargs, fargtypes := g.fn_args(node.params, node.is_variadic)
arg_str := g.out.after(arg_start_pos)
if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
&& !g.is_test)|| skip {
&& !g.is_test) || skip {
// Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');') // // NO BODY')
g.writeln(');')
@ -901,7 +901,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
break
}
use_tmp_var_autofree := g.is_autofree && arg.typ == table.string_type && arg.is_tmp_autofree
&& !g.inside_const&& !g.is_builtin_mod
&& !g.inside_const && !g.is_builtin_mod
// g.write('/* af=$arg.is_tmp_autofree */')
// some c fn definitions dont have args (cfns.v) or are not updated in checker
// when these are fixed we wont need this check

View File

@ -343,8 +343,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
} else if sym_has_str_method
|| sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
is_ptr := typ.is_ptr()
is_var_mut := expr is ast.Ident
&& ((expr as ast.Ident).name == g.for_in_mut_val_name || (expr as ast.Ident).name in g.fn_mut_arg_names)
is_var_mut := expr is ast.Ident && ((expr as ast.Ident).name == g.for_in_mut_val_name
|| (expr as ast.Ident).name in g.fn_mut_arg_names)
str_fn_name := g.gen_str_for_type(typ)
if is_ptr && !is_var_mut {
g.write('_STR("&%.*s\\000", 2, ')

View File

@ -582,7 +582,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
}
types_only := p.tok.kind in [.amp, .ellipsis, .key_fn]
|| (p.peek_tok.kind == .comma && p.table.known_type(argname))
|| p.peek_tok.kind == .dot|| p.peek_tok.kind == .rpar
|| p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar
// TODO copy pasta, merge 2 branches
if types_only {
// p.warn('types only')
@ -763,7 +763,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .sum_type]
&& !typ.is_ptr()&& !typ.is_pointer() {
&& !typ.is_ptr() && !typ.is_pointer() {
p.error_with_pos(
'mutable arguments are only allowed for arrays, interfaces, maps, pointers and structs\n' +
'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`',

View File

@ -180,8 +180,8 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
p.next()
} else if (p.tok.kind == .name && !(p.tok.lit == 'C' && p.peek_tok.kind == .dot)
&& (p.tok.lit in table.builtin_type_names || p.tok.lit[0].is_capital()
|| (p.peek_tok.kind == .dot && p.peek_tok2.lit.len > 0 && p.peek_tok2.lit[0].is_capital())))
|| p.tok.kind == .lsbr {
|| (p.peek_tok.kind == .dot && p.peek_tok2.lit.len > 0
&& p.peek_tok2.lit[0].is_capital()))) || p.tok.kind == .lsbr {
mut types := []table.Type{}
for {
// Sum type match

View File

@ -78,7 +78,7 @@ pub fn (mut p Parser) parse_map_type() table.Type {
return 0
}
if !(key_type in [table.string_type_idx, table.voidptr_type_idx]
|| ((key_type.is_int() || key_type.is_float())&& !key_type.is_ptr())) {
|| ((key_type.is_int() || key_type.is_float()) && !key_type.is_ptr())) {
s := p.table.type_to_str(key_type)
p.error_with_pos('maps only support string, integer, float, rune or voidptr keys for now (not `$s`)',
p.tok.position())

View File

@ -691,7 +691,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position())
return ast.Stmt{}
} else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr
&& p.peek_tok.kind in [.rcbr, .eof]&& !p.mark_var_as_used(p.tok.lit) {
&& p.peek_tok.kind in [.rcbr, .eof] && !p.mark_var_as_used(p.tok.lit) {
p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
return ast.Stmt{}
}
@ -2070,7 +2070,7 @@ const (
fn (mut p Parser) global_decl() ast.GlobalDecl {
if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v
&& p.mod != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !p.pref.enable_globals
&& !p.pref.is_fmt&& p.mod !in parser.global_enabled_mods {
&& !p.pref.is_fmt && p.mod !in parser.global_enabled_mods {
p.error('use `v --enable-globals ...` to enable globals')
return ast.GlobalDecl{}
}

View File

@ -178,7 +178,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
}
field_start_pos := p.tok.position()
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital())
|| p.peek_tok.kind == .dot)&& language == .v
|| p.peek_tok.kind == .dot) && language == .v
is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_mut || is_field_global)
mut field_name := ''
mut typ := table.Type(0)

View File

@ -1091,8 +1091,9 @@ fn (mut s Scanner) ident_string() string {
s.error(r'`\x` used with no following hex digits')
}
// Escape `\u`
if c == `u`
&& (s.text[s.pos + 1] == s.quote || s.text[s.pos + 2] == s.quote || s.text[s.pos + 3] == s.quote || s.text[s.pos + 4] == s.quote || !s.text[s.pos + 1].is_hex_digit()
if c == `u` && (s.text[s.pos + 1] == s.quote
|| s.text[s.pos + 2] == s.quote || s.text[s.pos + 3] == s.quote
|| s.text[s.pos + 4] == s.quote || !s.text[s.pos + 1].is_hex_digit()
|| !s.text[s.pos + 2].is_hex_digit()
|| !s.text[s.pos + 3].is_hex_digit()
|| !s.text[s.pos + 4].is_hex_digit()) {

View File

@ -534,8 +534,8 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
if route_words.len == 1 && route_words[0] in vweb.methods_without_first {
req_method << route_words[0]
}
if url_words.len == route_words.len
|| (url_words.len >= route_words.len - 1 && route_words.len > 0 && route_words.last().ends_with('...')) {
if url_words.len == route_words.len || (url_words.len >= route_words.len - 1
&& route_words.len > 0 && route_words.last().ends_with('...')) {
if req_method.len > 0 {
if req_method_str.to_lower()[1..] !in req_method {
continue