vweb: check function and route parameter count (#6761)
parent
6da8454b3b
commit
2994e7150f
|
@ -8,7 +8,7 @@ module http
|
||||||
#include "vschannel.c"
|
#include "vschannel.c"
|
||||||
fn C.new_tls_context() C.TlsContext
|
fn C.new_tls_context() C.TlsContext
|
||||||
|
|
||||||
fn (req &Request) ssl_do(port int, method Method, host_name, path string) ?Response {
|
fn (req &Request) ssl_do(port int, method Method, host_name string, path string) ?Response {
|
||||||
mut ctx := C.new_tls_context()
|
mut ctx := C.new_tls_context()
|
||||||
C.vschannel_init(&ctx)
|
C.vschannel_init(&ctx)
|
||||||
mut buff := malloc(C.vsc_init_resp_buff_size)
|
mut buff := malloc(C.vsc_init_resp_buff_size)
|
||||||
|
|
|
@ -8,7 +8,7 @@ module http
|
||||||
|
|
||||||
#include <urlmon.h>
|
#include <urlmon.h>
|
||||||
|
|
||||||
fn download_file_with_progress(url, out string, cb, cb_finished voidptr) {
|
fn download_file_with_progress(url string, out string, cb voidptr, cb_finished voidptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -260,6 +260,7 @@ pub:
|
||||||
receiver Field
|
receiver Field
|
||||||
receiver_pos token.Position
|
receiver_pos token.Position
|
||||||
is_method bool
|
is_method bool
|
||||||
|
method_idx int
|
||||||
rec_mut bool // is receiver mutable
|
rec_mut bool // is receiver mutable
|
||||||
rec_share table.ShareType
|
rec_share table.ShareType
|
||||||
language table.Language
|
language table.Language
|
||||||
|
@ -275,6 +276,7 @@ pub mut:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
|
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
|
||||||
|
source_file &File = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// break, continue
|
// break, continue
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct Checker {
|
||||||
pref &pref.Preferences // Preferences shared from V struct
|
pref &pref.Preferences // Preferences shared from V struct
|
||||||
pub mut:
|
pub mut:
|
||||||
table &table.Table
|
table &table.Table
|
||||||
file ast.File
|
file &ast.File = 0
|
||||||
nr_errors int
|
nr_errors int
|
||||||
nr_warnings int
|
nr_warnings int
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
|
@ -61,6 +61,7 @@ mut:
|
||||||
error_details []string
|
error_details []string
|
||||||
generic_funcs []&ast.FnDecl
|
generic_funcs []&ast.FnDecl
|
||||||
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path*
|
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path*
|
||||||
|
vweb_gen_types []table.Type // vweb route checks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
||||||
|
@ -71,7 +72,7 @@ pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) check(ast_file ast.File) {
|
pub fn (mut c Checker) check(ast_file &ast.File) {
|
||||||
c.file = ast_file
|
c.file = ast_file
|
||||||
for i, ast_import in ast_file.imports {
|
for i, ast_import in ast_file.imports {
|
||||||
for j in 0 .. i {
|
for j in 0 .. i {
|
||||||
|
@ -112,7 +113,7 @@ pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// not used right now
|
// not used right now
|
||||||
pub fn (mut c Checker) check2(ast_file ast.File) []errors.Error {
|
pub fn (mut c Checker) check2(ast_file &ast.File) []errors.Error {
|
||||||
c.file = ast_file
|
c.file = ast_file
|
||||||
for stmt in ast_file.stmts {
|
for stmt in ast_file.stmts {
|
||||||
c.stmt(stmt)
|
c.stmt(stmt)
|
||||||
|
@ -147,6 +148,7 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
||||||
has_main_fn = true
|
has_main_fn = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.verify_all_vweb_routes()
|
||||||
// Make sure fn main is defined in non lib builds
|
// Make sure fn main is defined in non lib builds
|
||||||
if c.pref.build_mode == .build_module || c.pref.is_test {
|
if c.pref.build_mode == .build_module || c.pref.is_test {
|
||||||
return
|
return
|
||||||
|
@ -4207,17 +4209,17 @@ fn (mut c Checker) post_process_generic_fns() {
|
||||||
// Loop thru each generic function concrete type.
|
// Loop thru each generic function concrete type.
|
||||||
// Check each specific fn instantiation.
|
// Check each specific fn instantiation.
|
||||||
for i in 0 .. c.generic_funcs.len {
|
for i in 0 .. c.generic_funcs.len {
|
||||||
mut node := c.generic_funcs[i]
|
|
||||||
if c.table.fn_gen_types.len == 0 {
|
if c.table.fn_gen_types.len == 0 {
|
||||||
// no concrete types, so just skip:
|
// no concrete types, so just skip:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// eprintln('>> post_process_generic_fns $c.file.path | $node.name , c.table.fn_gen_types.len: $c.table.fn_gen_types.len')
|
mut node := c.generic_funcs[i]
|
||||||
for gen_type in c.table.fn_gen_types[node.name] {
|
for gen_type in c.table.fn_gen_types[node.name] {
|
||||||
c.cur_generic_type = gen_type
|
c.cur_generic_type = gen_type
|
||||||
// sym:=c.table.get_type_symbol(gen_type)
|
|
||||||
// println('\ncalling check for $node.name for type $sym.source_name')
|
|
||||||
c.fn_decl(mut node)
|
c.fn_decl(mut node)
|
||||||
|
if node.name in ['vweb.run_app', 'vweb.run'] {
|
||||||
|
c.vweb_gen_types << gen_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.cur_generic_type = 0
|
c.cur_generic_type = 0
|
||||||
c.generic_funcs[i] = 0
|
c.generic_funcs[i] = 0
|
||||||
|
@ -4266,6 +4268,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
c.error('cannot define new methods on non-local `$sym.source_name` (' +
|
c.error('cannot define new methods on non-local `$sym.source_name` (' +
|
||||||
'current module is `$c.mod`, `$sym.source_name` is from `$sym.mod`)', node.pos)
|
'current module is `$c.mod`, `$sym.source_name` is from `$sym.mod`)', node.pos)
|
||||||
}
|
}
|
||||||
|
// needed for proper error reporting during vweb route checking
|
||||||
|
sym.methods[node.method_idx].source_fn = voidptr(node)
|
||||||
}
|
}
|
||||||
if node.language == .v {
|
if node.language == .v {
|
||||||
// Make sure all types are valid
|
// Make sure all types are valid
|
||||||
|
@ -4322,6 +4326,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
c.error('missing return at end of function `$node.name`', node.pos)
|
c.error('missing return at end of function `$node.name`', node.pos)
|
||||||
}
|
}
|
||||||
c.returns = false
|
c.returns = false
|
||||||
|
node.source_file = c.file
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_top_return(stmts []ast.Stmt) bool {
|
fn has_top_return(stmts []ast.Stmt) bool {
|
||||||
|
@ -4342,3 +4347,45 @@ fn has_top_return(stmts []ast.Stmt) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) verify_vweb_params_for_method(m table.Fn) (bool, int, int) {
|
||||||
|
margs := m.params.len - 1 // first arg is the receiver/this
|
||||||
|
if m.attrs.len == 0 {
|
||||||
|
// allow non custom routed methods, with 1:1 mapping
|
||||||
|
return true, -1, margs
|
||||||
|
}
|
||||||
|
mut route_attributes := 0
|
||||||
|
for a in m.attrs {
|
||||||
|
if a.name.starts_with('/') {
|
||||||
|
route_attributes += a.name.count(':')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return route_attributes == margs, route_attributes, margs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) verify_all_vweb_routes() {
|
||||||
|
if c.vweb_gen_types.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typ_vweb_result := c.table.find_type_idx('vweb.Result')
|
||||||
|
for vgt in c.vweb_gen_types {
|
||||||
|
sym_app := c.table.get_type_symbol(vgt)
|
||||||
|
for m in sym_app.methods {
|
||||||
|
if m.return_type_source_name == 'vweb.Result' {
|
||||||
|
is_ok, nroute_attributes, nargs := c.verify_vweb_params_for_method(m)
|
||||||
|
if !is_ok {
|
||||||
|
f := &ast.FnDecl(m.source_fn)
|
||||||
|
if isnil(f) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if f.return_type == typ_vweb_result &&
|
||||||
|
f.receiver.typ == m.params[0].typ && f.name == m.name {
|
||||||
|
c.file = f.source_file // setup of file path for the warning
|
||||||
|
c.warn('mismatched parameters count between vweb method `${sym_app.name}.$m.name` ($nargs) and route attribute $m.attrs ($nroute_attributes)',
|
||||||
|
f.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
vlib/v/checker/tests/vweb_routing_checks.vv:22:1: error: mismatched parameters count between vweb method `App.bar` (1) and route attribute ['/bar'] (0)
|
||||||
|
20 | // segfault because path taks 0 vars and fcn takes 1 arg
|
||||||
|
21 | ['/bar']
|
||||||
|
22 | pub fn (mut app App) bar(a string) vweb.Result {
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
23 | app.vweb.html('works')
|
||||||
|
24 | return vweb.Result{}
|
||||||
|
vlib/v/checker/tests/vweb_routing_checks.vv:29:1: error: mismatched parameters count between vweb method `App.cow` (0) and route attribute ['/cow/:low'] (1)
|
||||||
|
27 | // no segfault, but it shouldnt compile
|
||||||
|
28 | ['/cow/:low']
|
||||||
|
29 | pub fn (mut app App) cow() vweb.Result {
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
30 | app.vweb.html('works')
|
||||||
|
31 | return vweb.Result{}
|
|
@ -0,0 +1,50 @@
|
||||||
|
import vweb
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
pub mut:
|
||||||
|
vweb vweb.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut app App) no_attributes(a string) vweb.Result {
|
||||||
|
return vweb.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// works fine, as long as fcn gets 1 arg and route takes 1 var
|
||||||
|
['/foo/:bar']
|
||||||
|
pub fn (mut app App) foo(a string) vweb.Result {
|
||||||
|
eprintln('foo')
|
||||||
|
app.vweb.html('works')
|
||||||
|
return vweb.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// segfault because path taks 0 vars and fcn takes 1 arg
|
||||||
|
['/bar']
|
||||||
|
pub fn (mut app App) bar(a string) vweb.Result {
|
||||||
|
app.vweb.html('works')
|
||||||
|
return vweb.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no segfault, but it shouldnt compile
|
||||||
|
['/cow/:low']
|
||||||
|
pub fn (mut app App) cow() vweb.Result {
|
||||||
|
app.vweb.html('works')
|
||||||
|
return vweb.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (app App) init_once() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (app App) init() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut app App) index() {
|
||||||
|
app.vweb.html('hello')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
port := 8181
|
||||||
|
mut app := App{}
|
||||||
|
vweb.run_app<App>(mut app, port)
|
||||||
|
}
|
|
@ -258,15 +258,15 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
mut return_type := table.void_type
|
mut return_type := table.void_type
|
||||||
if p.tok.kind.is_start_of_type() ||
|
if p.tok.kind.is_start_of_type() ||
|
||||||
(p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) {
|
(p.tok.kind == .key_fn && p.tok.line_nr == p.prev_tok.line_nr) {
|
||||||
end_pos = p.tok.position()
|
|
||||||
return_type = p.parse_type()
|
return_type = p.parse_type()
|
||||||
}
|
}
|
||||||
|
mut type_sym_method_idx := 0
|
||||||
// Register
|
// Register
|
||||||
if is_method {
|
if is_method {
|
||||||
mut type_sym := p.table.get_type_symbol(rec_type)
|
mut type_sym := p.table.get_type_symbol(rec_type)
|
||||||
ret_type_sym := p.table.get_type_symbol(return_type)
|
ret_type_sym := p.table.get_type_symbol(return_type)
|
||||||
// p.warn('reg method $type_sym.name . $name ()')
|
// p.warn('reg method $type_sym.name . $name ()')
|
||||||
type_sym.register_method(table.Fn{
|
type_sym_method_idx = type_sym.register_method(table.Fn{
|
||||||
name: name
|
name: name
|
||||||
params: params
|
params: params
|
||||||
return_type: return_type
|
return_type: return_type
|
||||||
|
@ -307,6 +307,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
language: language
|
language: language
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
end_pos = p.prev_tok.position()
|
||||||
// Body
|
// Body
|
||||||
p.cur_fn_name = name
|
p.cur_fn_name = name
|
||||||
mut stmts := []ast.Stmt{}
|
mut stmts := []ast.Stmt{}
|
||||||
|
@ -336,6 +337,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
}
|
}
|
||||||
receiver_pos: receiver_pos
|
receiver_pos: receiver_pos
|
||||||
is_method: is_method
|
is_method: is_method
|
||||||
|
method_idx: type_sym_method_idx
|
||||||
rec_mut: rec_mut
|
rec_mut: rec_mut
|
||||||
language: language
|
language: language
|
||||||
no_body: no_body
|
no_body: no_body
|
||||||
|
|
|
@ -38,6 +38,7 @@ pub:
|
||||||
attrs []Attr
|
attrs []Attr
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
|
source_fn voidptr // set in the checker, while processing fn declarations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (f &Fn) method_equals(o &Fn) bool {
|
fn (f &Fn) method_equals(o &Fn) bool {
|
||||||
|
@ -162,9 +163,12 @@ pub fn (mut t Table) register_fn(new_fn Fn) {
|
||||||
t.fns[new_fn.name] = new_fn
|
t.fns[new_fn.name] = new_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut t TypeSymbol) register_method(new_fn Fn) {
|
pub fn (mut t TypeSymbol) register_method(new_fn Fn) int {
|
||||||
|
// returns a method index, stored in the ast.FnDecl
|
||||||
|
// for faster lookup in the checker's fn_decl method
|
||||||
// println('reg me $new_fn.name nr_args=$new_fn.args.len')
|
// println('reg me $new_fn.name nr_args=$new_fn.args.len')
|
||||||
t.methods << new_fn
|
t.methods << new_fn
|
||||||
|
return t.methods.len - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (t &Table) register_aggregate_method(mut sym TypeSymbol, name string) ?Fn {
|
pub fn (t &Table) register_aggregate_method(mut sym TypeSymbol, name string) ?Fn {
|
||||||
|
|
|
@ -485,7 +485,11 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
|
||||||
// search again for method
|
// search again for method
|
||||||
if action == method.name && method.attrs.len > 0 {
|
if action == method.name && method.attrs.len > 0 {
|
||||||
// call action method
|
// call action method
|
||||||
app.$method(vars)
|
if method.args.len == vars.len {
|
||||||
|
app.$method(vars)
|
||||||
|
} else {
|
||||||
|
eprintln('warning: uneven parameters count (${method.args.len}) in `$method.name`, compared to the vweb route `$method.attrs` (${vars.len})')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue