checker: extract long match branches in c.stmt(), into separate checker functions (#8666)
parent
f2ad6dd4d9
commit
4305ce1493
|
@ -3104,37 +3104,16 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
// c.expected_type = table.void_type
|
// c.expected_type = table.void_type
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
cur_exp_typ := c.expected_type
|
c.assert_stmt(node)
|
||||||
assert_type := c.expr(node.expr)
|
|
||||||
if assert_type != table.bool_type_idx {
|
|
||||||
atype_name := c.table.get_type_symbol(assert_type).name
|
|
||||||
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',
|
|
||||||
node.pos)
|
|
||||||
}
|
|
||||||
c.expected_type = cur_exp_typ
|
|
||||||
}
|
}
|
||||||
ast.AssignStmt {
|
ast.AssignStmt {
|
||||||
c.assign_stmt(mut node)
|
c.assign_stmt(mut node)
|
||||||
}
|
}
|
||||||
ast.Block {
|
ast.Block {
|
||||||
if node.is_unsafe {
|
c.block(node)
|
||||||
assert !c.inside_unsafe
|
|
||||||
c.inside_unsafe = true
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
c.inside_unsafe = false
|
|
||||||
} else {
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.BranchStmt {
|
ast.BranchStmt {
|
||||||
if c.in_for_count == 0 {
|
c.branch_stmt(node)
|
||||||
c.error('$node.kind.str() statement not within a loop', node.pos)
|
|
||||||
}
|
|
||||||
if node.label.len > 0 {
|
|
||||||
if node.label != c.loop_label {
|
|
||||||
c.error('invalid label name `$node.label`', node.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.CompFor {
|
ast.CompFor {
|
||||||
// node.typ = c.expr(node.expr)
|
// node.typ = c.expr(node.expr)
|
||||||
|
@ -3166,147 +3145,19 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
c.fn_decl(mut node)
|
c.fn_decl(mut node)
|
||||||
}
|
}
|
||||||
ast.ForCStmt {
|
ast.ForCStmt {
|
||||||
c.in_for_count++
|
c.for_c_stmt(node)
|
||||||
prev_loop_label := c.loop_label
|
|
||||||
c.stmt(node.init)
|
|
||||||
c.expr(node.cond)
|
|
||||||
c.stmt(node.inc)
|
|
||||||
c.check_loop_label(node.label, node.pos)
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
c.loop_label = prev_loop_label
|
|
||||||
c.in_for_count--
|
|
||||||
}
|
}
|
||||||
ast.ForInStmt {
|
ast.ForInStmt {
|
||||||
c.in_for_count++
|
c.for_in_stmt(mut node)
|
||||||
prev_loop_label := c.loop_label
|
|
||||||
typ := c.expr(node.cond)
|
|
||||||
typ_idx := typ.idx()
|
|
||||||
if node.key_var.len > 0 && node.key_var != '_' {
|
|
||||||
c.check_valid_snake_case(node.key_var, 'variable name', node.pos)
|
|
||||||
}
|
|
||||||
if node.val_var.len > 0 && node.val_var != '_' {
|
|
||||||
c.check_valid_snake_case(node.val_var, 'variable name', node.pos)
|
|
||||||
}
|
|
||||||
if node.is_range {
|
|
||||||
high_type := c.expr(node.high)
|
|
||||||
high_type_idx := high_type.idx()
|
|
||||||
if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs {
|
|
||||||
c.error('range types do not match', node.cond.position())
|
|
||||||
} else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs {
|
|
||||||
c.error('range type can not be float', node.cond.position())
|
|
||||||
} else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx {
|
|
||||||
c.error('range type can not be bool', node.cond.position())
|
|
||||||
} else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx {
|
|
||||||
c.error('range type can not be string', node.cond.position())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sym := c.table.get_type_symbol(typ)
|
|
||||||
if sym.kind == .struct_ {
|
|
||||||
// iterators
|
|
||||||
next_fn := sym.find_method('next') or {
|
|
||||||
c.error('a struct must have a `next()` method to be an iterator',
|
|
||||||
node.cond.position())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !next_fn.return_type.has_flag(.optional) {
|
|
||||||
c.error('iterator method `next()` must return an optional', node.cond.position())
|
|
||||||
}
|
|
||||||
// the receiver
|
|
||||||
if next_fn.params.len != 1 {
|
|
||||||
c.error('iterator method `next()` must have 0 parameters', node.cond.position())
|
|
||||||
}
|
|
||||||
val_type := next_fn.return_type.clear_flag(.optional)
|
|
||||||
node.cond_type = typ
|
|
||||||
node.kind = sym.kind
|
|
||||||
node.val_type = val_type
|
|
||||||
node.scope.update_var_type(node.val_var, val_type)
|
|
||||||
} else {
|
|
||||||
if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
|
|
||||||
c.error(
|
|
||||||
'declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
|
|
||||||
'use `_` if you do not need the variable', node.pos)
|
|
||||||
}
|
|
||||||
if node.key_var.len > 0 {
|
|
||||||
key_type := match sym.kind {
|
|
||||||
.map { sym.map_info().key_type }
|
|
||||||
else { table.int_type }
|
|
||||||
}
|
|
||||||
node.key_type = key_type
|
|
||||||
node.scope.update_var_type(node.key_var, key_type)
|
|
||||||
}
|
|
||||||
mut value_type := c.table.value_type(typ)
|
|
||||||
if value_type == table.void_type || typ.has_flag(.optional) {
|
|
||||||
if typ != table.void_type {
|
|
||||||
c.error('for in: cannot index `${c.table.type_to_str(typ)}`',
|
|
||||||
node.cond.position())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.val_is_mut {
|
|
||||||
value_type = value_type.to_ptr()
|
|
||||||
match node.cond {
|
|
||||||
ast.Ident {
|
|
||||||
if node.cond.obj is ast.Var {
|
|
||||||
obj := node.cond.obj as ast.Var
|
|
||||||
if !obj.is_mut {
|
|
||||||
c.error('`$obj.name` is immutable, it cannot be changed',
|
|
||||||
node.cond.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast.ArrayInit {
|
|
||||||
c.error('array literal is immutable, it cannot be changed',
|
|
||||||
node.cond.pos)
|
|
||||||
}
|
|
||||||
ast.MapInit {
|
|
||||||
c.error('map literal is immutable, it cannot be changed',
|
|
||||||
node.cond.pos)
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.cond_type = typ
|
|
||||||
node.kind = sym.kind
|
|
||||||
node.val_type = value_type
|
|
||||||
node.scope.update_var_type(node.val_var, value_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.check_loop_label(node.label, node.pos)
|
|
||||||
if node.val_is_mut {
|
|
||||||
c.for_in_mut_val_name = node.val_var
|
|
||||||
}
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
if node.val_is_mut {
|
|
||||||
c.for_in_mut_val_name = ''
|
|
||||||
}
|
|
||||||
c.loop_label = prev_loop_label
|
|
||||||
c.in_for_count--
|
|
||||||
}
|
}
|
||||||
ast.ForStmt {
|
ast.ForStmt {
|
||||||
c.for_stmt(mut node)
|
c.for_stmt(mut node)
|
||||||
}
|
}
|
||||||
ast.GlobalDecl {
|
ast.GlobalDecl {
|
||||||
for field in node.fields {
|
c.global_decl(node)
|
||||||
c.check_valid_snake_case(field.name, 'global name', field.pos)
|
|
||||||
if field.name in c.global_names {
|
|
||||||
c.error('duplicate global `$field.name`', field.pos)
|
|
||||||
}
|
|
||||||
c.global_names << field.name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.GoStmt {
|
ast.GoStmt {
|
||||||
c.call_expr(mut node.call_expr)
|
c.go_stmt(mut node)
|
||||||
// Make sure there are no mutable arguments
|
|
||||||
for arg in node.call_expr.args {
|
|
||||||
if arg.is_mut && !arg.typ.is_ptr() {
|
|
||||||
c.error('function in `go` statement cannot contain mutable non-reference arguments',
|
|
||||||
arg.expr.position())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
|
|
||||||
&& !node.call_expr.left_type.is_ptr() {
|
|
||||||
c.error('method in `go` statement cannot have non-reference mutable receiver',
|
|
||||||
node.call_expr.left.position())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.GotoLabel {}
|
ast.GotoLabel {}
|
||||||
ast.GotoStmt {
|
ast.GotoStmt {
|
||||||
|
@ -3346,6 +3197,217 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) assert_stmt(node ast.AssertStmt) {
|
||||||
|
cur_exp_typ := c.expected_type
|
||||||
|
assert_type := c.expr(node.expr)
|
||||||
|
if assert_type != table.bool_type_idx {
|
||||||
|
atype_name := c.table.get_type_symbol(assert_type).name
|
||||||
|
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',
|
||||||
|
node.pos)
|
||||||
|
}
|
||||||
|
c.expected_type = cur_exp_typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) block(node ast.Block) {
|
||||||
|
if node.is_unsafe {
|
||||||
|
assert !c.inside_unsafe
|
||||||
|
c.inside_unsafe = true
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
c.inside_unsafe = false
|
||||||
|
} else {
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
|
||||||
|
if c.in_for_count == 0 {
|
||||||
|
c.error('$node.kind.str() statement not within a loop', node.pos)
|
||||||
|
}
|
||||||
|
if node.label.len > 0 {
|
||||||
|
if node.label != c.loop_label {
|
||||||
|
c.error('invalid label name `$node.label`', node.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
|
||||||
|
c.in_for_count++
|
||||||
|
prev_loop_label := c.loop_label
|
||||||
|
c.stmt(node.init)
|
||||||
|
c.expr(node.cond)
|
||||||
|
c.stmt(node.inc)
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
c.loop_label = prev_loop_label
|
||||||
|
c.in_for_count--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
||||||
|
c.in_for_count++
|
||||||
|
prev_loop_label := c.loop_label
|
||||||
|
typ := c.expr(node.cond)
|
||||||
|
typ_idx := typ.idx()
|
||||||
|
if node.key_var.len > 0 && node.key_var != '_' {
|
||||||
|
c.check_valid_snake_case(node.key_var, 'variable name', node.pos)
|
||||||
|
}
|
||||||
|
if node.val_var.len > 0 && node.val_var != '_' {
|
||||||
|
c.check_valid_snake_case(node.val_var, 'variable name', node.pos)
|
||||||
|
}
|
||||||
|
if node.is_range {
|
||||||
|
high_type := c.expr(node.high)
|
||||||
|
high_type_idx := high_type.idx()
|
||||||
|
if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs {
|
||||||
|
c.error('range types do not match', node.cond.position())
|
||||||
|
} else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs {
|
||||||
|
c.error('range type can not be float', node.cond.position())
|
||||||
|
} else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx {
|
||||||
|
c.error('range type can not be bool', node.cond.position())
|
||||||
|
} else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx {
|
||||||
|
c.error('range type can not be string', node.cond.position())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sym := c.table.get_type_symbol(typ)
|
||||||
|
if sym.kind == .struct_ {
|
||||||
|
// iterators
|
||||||
|
next_fn := sym.find_method('next') or {
|
||||||
|
c.error('a struct must have a `next()` method to be an iterator', node.cond.position())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !next_fn.return_type.has_flag(.optional) {
|
||||||
|
c.error('iterator method `next()` must return an optional', node.cond.position())
|
||||||
|
}
|
||||||
|
// the receiver
|
||||||
|
if next_fn.params.len != 1 {
|
||||||
|
c.error('iterator method `next()` must have 0 parameters', node.cond.position())
|
||||||
|
}
|
||||||
|
val_type := next_fn.return_type.clear_flag(.optional)
|
||||||
|
node.cond_type = typ
|
||||||
|
node.kind = sym.kind
|
||||||
|
node.val_type = val_type
|
||||||
|
node.scope.update_var_type(node.val_var, val_type)
|
||||||
|
} else {
|
||||||
|
if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
|
||||||
|
c.error(
|
||||||
|
'declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
|
||||||
|
'use `_` if you do not need the variable', node.pos)
|
||||||
|
}
|
||||||
|
if node.key_var.len > 0 {
|
||||||
|
key_type := match sym.kind {
|
||||||
|
.map { sym.map_info().key_type }
|
||||||
|
else { table.int_type }
|
||||||
|
}
|
||||||
|
node.key_type = key_type
|
||||||
|
node.scope.update_var_type(node.key_var, key_type)
|
||||||
|
}
|
||||||
|
mut value_type := c.table.value_type(typ)
|
||||||
|
if value_type == table.void_type || typ.has_flag(.optional) {
|
||||||
|
if typ != table.void_type {
|
||||||
|
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.val_is_mut {
|
||||||
|
value_type = value_type.to_ptr()
|
||||||
|
match node.cond {
|
||||||
|
ast.Ident {
|
||||||
|
if node.cond.obj is ast.Var {
|
||||||
|
obj := node.cond.obj as ast.Var
|
||||||
|
if !obj.is_mut {
|
||||||
|
c.error('`$obj.name` is immutable, it cannot be changed',
|
||||||
|
node.cond.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.ArrayInit {
|
||||||
|
c.error('array literal is immutable, it cannot be changed', node.cond.pos)
|
||||||
|
}
|
||||||
|
ast.MapInit {
|
||||||
|
c.error('map literal is immutable, it cannot be changed', node.cond.pos)
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.cond_type = typ
|
||||||
|
node.kind = sym.kind
|
||||||
|
node.val_type = value_type
|
||||||
|
node.scope.update_var_type(node.val_var, value_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
|
if node.val_is_mut {
|
||||||
|
c.for_in_mut_val_name = node.val_var
|
||||||
|
}
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
if node.val_is_mut {
|
||||||
|
c.for_in_mut_val_name = ''
|
||||||
|
}
|
||||||
|
c.loop_label = prev_loop_label
|
||||||
|
c.in_for_count--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
||||||
|
c.in_for_count++
|
||||||
|
prev_loop_label := c.loop_label
|
||||||
|
c.expected_type = table.bool_type
|
||||||
|
typ := c.expr(node.cond)
|
||||||
|
if !node.is_inf && typ.idx() != table.bool_type_idx && !c.pref.translated {
|
||||||
|
c.error('non-bool used as for condition', node.pos)
|
||||||
|
}
|
||||||
|
if node.cond is ast.InfixExpr {
|
||||||
|
infix := node.cond
|
||||||
|
if infix.op == .key_is {
|
||||||
|
if (infix.left is ast.Ident || infix.left is ast.SelectorExpr)
|
||||||
|
&& infix.right is ast.Type {
|
||||||
|
right_expr := infix.right as ast.Type
|
||||||
|
is_variable := if mut infix.left is ast.Ident {
|
||||||
|
infix.left.kind == .variable
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
left_type := c.expr(infix.left)
|
||||||
|
left_sym := c.table.get_type_symbol(left_type)
|
||||||
|
if is_variable {
|
||||||
|
if left_sym.kind == .sum_type {
|
||||||
|
c.smartcast_sumtype(infix.left, infix.left_type, right_expr.typ, mut
|
||||||
|
node.scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: update loop var type
|
||||||
|
// how does this work currenly?
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
c.loop_label = prev_loop_label
|
||||||
|
c.in_for_count--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) global_decl(node ast.GlobalDecl) {
|
||||||
|
for field in node.fields {
|
||||||
|
c.check_valid_snake_case(field.name, 'global name', field.pos)
|
||||||
|
if field.name in c.global_names {
|
||||||
|
c.error('duplicate global `$field.name`', field.pos)
|
||||||
|
}
|
||||||
|
c.global_names << field.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) go_stmt(mut node ast.GoStmt) {
|
||||||
|
c.call_expr(mut node.call_expr)
|
||||||
|
// Make sure there are no mutable arguments
|
||||||
|
for arg in node.call_expr.args {
|
||||||
|
if arg.is_mut && !arg.typ.is_ptr() {
|
||||||
|
c.error('function in `go` statement cannot contain mutable non-reference arguments',
|
||||||
|
arg.expr.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
|
||||||
|
&& !node.call_expr.left_type.is_ptr() {
|
||||||
|
c.error('method in `go` statement cannot have non-reference mutable receiver',
|
||||||
|
node.call_expr.left.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||||
if c.skip_flags {
|
if c.skip_flags {
|
||||||
return
|
return
|
||||||
|
@ -4659,44 +4721,6 @@ pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) table.Type {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
|
||||||
c.in_for_count++
|
|
||||||
prev_loop_label := c.loop_label
|
|
||||||
c.expected_type = table.bool_type
|
|
||||||
typ := c.expr(node.cond)
|
|
||||||
if !node.is_inf && typ.idx() != table.bool_type_idx && !c.pref.translated {
|
|
||||||
c.error('non-bool used as for condition', node.pos)
|
|
||||||
}
|
|
||||||
if node.cond is ast.InfixExpr {
|
|
||||||
infix := node.cond
|
|
||||||
if infix.op == .key_is {
|
|
||||||
if (infix.left is ast.Ident || infix.left is ast.SelectorExpr)
|
|
||||||
&& infix.right is ast.Type {
|
|
||||||
right_expr := infix.right as ast.Type
|
|
||||||
is_variable := if mut infix.left is ast.Ident {
|
|
||||||
infix.left.kind == .variable
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
left_type := c.expr(infix.left)
|
|
||||||
left_sym := c.table.get_type_symbol(left_type)
|
|
||||||
if is_variable {
|
|
||||||
if left_sym.kind == .sum_type {
|
|
||||||
c.smartcast_sumtype(infix.left, infix.left_type, right_expr.typ, mut
|
|
||||||
node.scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: update loop var type
|
|
||||||
// how does this work currenly?
|
|
||||||
c.check_loop_label(node.label, node.pos)
|
|
||||||
c.stmts(node.stmts)
|
|
||||||
c.loop_label = prev_loop_label
|
|
||||||
c.in_for_count--
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
||||||
expr_required := c.expected_type != table.void_type
|
expr_required := c.expected_type != table.void_type
|
||||||
|
|
Loading…
Reference in New Issue