checker: check variable mutability
parent
9422cd1009
commit
053de0b8e3
|
@ -105,6 +105,7 @@ pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
comment Comment
|
comment Comment
|
||||||
default_expr string // token literal //Expr
|
default_expr string // token literal //Expr
|
||||||
|
attr string
|
||||||
mut:
|
mut:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
}
|
}
|
||||||
|
@ -302,6 +303,7 @@ pub:
|
||||||
tok_kind token.Kind
|
tok_kind token.Kind
|
||||||
mod string
|
mod string
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
is_mut bool
|
||||||
mut:
|
mut:
|
||||||
name string
|
name string
|
||||||
kind IdentKind
|
kind IdentKind
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn (node &FnDecl) str(t &table.Table) string {
|
||||||
receiver = '($node.receiver.name $m$name) '
|
receiver = '($node.receiver.name $m$name) '
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
name := node.name.after('.')
|
mut name := node.name.after('.')
|
||||||
if node.is_c {
|
if node.is_c {
|
||||||
name = 'C.$name'
|
name = 'C.$name'
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub fn (c mut Checker) check_files(ast_files []ast.File) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln('function `main` is undeclared in the main module')
|
eprintln('function `main` is undeclared in the main module')
|
||||||
//eprintln(ast_files[0].mod.name)
|
// eprintln(ast_files[0].mod.name)
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,10 +228,22 @@ fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) {
|
||||||
if ast.expr_is_blank_ident(assign_expr.left) {
|
if ast.expr_is_blank_ident(assign_expr.left) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
match assign_expr.left {
|
||||||
|
ast.Ident {
|
||||||
|
scope := c.file.scope.innermost(assign_expr.pos.pos)
|
||||||
|
if v := scope.find_var(it.name) {
|
||||||
|
if !v.is_mut {
|
||||||
|
c.error('`$it.name` is immutable, declare it with `mut`', assign_expr.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
if !c.table.check(right_type, left_type) {
|
if !c.table.check(right_type, left_type) {
|
||||||
left_type_sym := c.table.get_type_symbol(left_type)
|
left_type_sym := c.table.get_type_symbol(left_type)
|
||||||
right_type_sym := c.table.get_type_symbol(right_type)
|
right_type_sym := c.table.get_type_symbol(right_type)
|
||||||
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name` ', expr_pos(assign_expr.val))
|
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name` ',
|
||||||
|
expr_pos(assign_expr.val))
|
||||||
}
|
}
|
||||||
c.check_expr_opt_call(assign_expr.val, right_type, true)
|
c.check_expr_opt_call(assign_expr.val, right_type, true)
|
||||||
}
|
}
|
||||||
|
@ -539,10 +551,8 @@ pub fn (c mut Checker) return_stmt(return_stmt mut ast.Return) {
|
||||||
c.error('too many arguments to return, current function does not return anything',
|
c.error('too many arguments to return, current function does not return anything',
|
||||||
return_stmt.pos)
|
return_stmt.pos)
|
||||||
return
|
return
|
||||||
}
|
} else if return_stmt.exprs.len == 0 && c.fn_return_type != table.void_type {
|
||||||
else if return_stmt.exprs.len == 0 && c.fn_return_type != table.void_type {
|
c.error('too few arguments to return', return_stmt.pos)
|
||||||
c.error('too few arguments to return',
|
|
||||||
return_stmt.pos)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if return_stmt.exprs.len == 0 {
|
if return_stmt.exprs.len == 0 {
|
||||||
|
@ -588,11 +598,11 @@ pub fn (c mut Checker) enum_decl(decl ast.EnumDecl) {
|
||||||
ast.IntegerLiteral {}
|
ast.IntegerLiteral {}
|
||||||
ast.PrefixExpr {}
|
ast.PrefixExpr {}
|
||||||
else {
|
else {
|
||||||
pos := expr_pos(field.expr)
|
mut pos := expr_pos(field.expr)
|
||||||
if pos.pos == 0 {
|
if pos.pos == 0 {
|
||||||
pos = field.pos
|
pos = field.pos
|
||||||
}
|
}
|
||||||
c.error("default value for enum has to be an integer", pos)
|
c.error('default value for enum has to be an integer', pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,12 +622,14 @@ pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) {
|
||||||
right_type := c.expr(assign_stmt.right[0])
|
right_type := c.expr(assign_stmt.right[0])
|
||||||
right_type_sym := c.table.get_type_symbol(right_type)
|
right_type_sym := c.table.get_type_symbol(right_type)
|
||||||
if right_type_sym.kind != .multi_return {
|
if right_type_sym.kind != .multi_return {
|
||||||
c.error('expression on the right does not return multiple values, while at least $assign_stmt.left.len are expected', assign_stmt.pos)
|
c.error('expression on the right does not return multiple values, while at least $assign_stmt.left.len are expected',
|
||||||
|
assign_stmt.pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mr_info := right_type_sym.mr_info()
|
mr_info := right_type_sym.mr_info()
|
||||||
if mr_info.types.len < assign_stmt.left.len {
|
if mr_info.types.len < assign_stmt.left.len {
|
||||||
c.error('right expression returns only $mr_info.types.len values, but left one expects $assign_stmt.left.len', assign_stmt.pos)
|
c.error('right expression returns only $mr_info.types.len values, but left one expects $assign_stmt.left.len',
|
||||||
|
assign_stmt.pos)
|
||||||
}
|
}
|
||||||
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
|
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
|
||||||
for i, _ in assign_stmt.left {
|
for i, _ in assign_stmt.left {
|
||||||
|
@ -730,23 +742,26 @@ pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type {
|
||||||
fixed_size = it.val.int()
|
fixed_size = it.val.int()
|
||||||
}
|
}
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
//if obj := c.file.global_scope.find_const(it.name) {
|
// if obj := c.file.global_scope.find_const(it.name) {
|
||||||
//if obj := scope.find(it.name) {
|
// if obj := scope.find(it.name) {
|
||||||
//scope := c.file.scope.innermost(array_init.pos.pos)
|
// scope := c.file.scope.innermost(array_init.pos.pos)
|
||||||
//eprintln('scope: ${scope.str()}')
|
// eprintln('scope: ${scope.str()}')
|
||||||
//scope.find(it.name) or {
|
// scope.find(it.name) or {
|
||||||
// c.error('undefined: `$it.name`', array_init.pos)
|
// c.error('undefined: `$it.name`', array_init.pos)
|
||||||
//}
|
// }
|
||||||
mut full_const_name := if it.mod == 'main' { it.name } else {it.mod + '.' + it.name }
|
mut full_const_name := if it.mod == 'main' { it.name } else { it.mod + '.' +
|
||||||
if obj := c.file.global_scope.find_const( full_const_name ) {
|
it.name }
|
||||||
|
if obj := c.file.global_scope.find_const(full_const_name) {
|
||||||
cf := ast.ConstField(obj)
|
cf := ast.ConstField(obj)
|
||||||
if cint := is_const_integer(cf) {
|
if cint := is_const_integer(cf) {
|
||||||
fixed_size = cint.val.int()
|
fixed_size = cint.val.int()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.error('non existant integer const $full_const_name while initializing the size of a static array', array_init.pos)
|
c.error('non existant integer const $full_const_name while initializing the size of a static array',
|
||||||
|
array_init.pos)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
c.error('expecting `int` for fixed size', array_init.pos)
|
c.error('expecting `int` for fixed size', array_init.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1085,20 +1100,36 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type {
|
||||||
fn expr_pos(node ast.Expr) token.Position {
|
fn expr_pos(node ast.Expr) token.Position {
|
||||||
// all uncommented have to be implemented
|
// all uncommented have to be implemented
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.ArrayInit { return it.pos }
|
ast.ArrayInit {
|
||||||
ast.AsCast { return it.pos }
|
return it.pos
|
||||||
ast.AssignExpr { return it.pos }
|
}
|
||||||
ast.Assoc { return it.pos }
|
ast.AsCast {
|
||||||
// ast.BoolLiteral { }
|
return it.pos
|
||||||
// ast.CastExpr { }
|
}
|
||||||
ast.CallExpr { return it.pos }
|
|
||||||
// ast.CharLiteral { }
|
|
||||||
ast.EnumVal { return it.pos }
|
|
||||||
// ast.FloatLiteral { }
|
|
||||||
// ast.Ident { }
|
// ast.Ident { }
|
||||||
ast.IfExpr { return it.pos }
|
ast.AssignExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
|
// ast.CastExpr { }
|
||||||
|
ast.Assoc {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
|
// ast.BoolLiteral { }
|
||||||
|
ast.CallExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
|
// ast.CharLiteral { }
|
||||||
|
ast.EnumVal {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
|
// ast.FloatLiteral { }
|
||||||
|
ast.IfExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.IfGuardExpr { }
|
// ast.IfGuardExpr { }
|
||||||
ast.IndexExpr { return it.pos }
|
ast.IndexExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
ast.InfixExpr {
|
ast.InfixExpr {
|
||||||
left_pos := expr_pos(it.left)
|
left_pos := expr_pos(it.left)
|
||||||
right_pos := expr_pos(it.right)
|
right_pos := expr_pos(it.right)
|
||||||
|
@ -1111,21 +1142,41 @@ fn expr_pos(node ast.Expr) token.Position {
|
||||||
len: right_pos.pos - left_pos.pos + right_pos.len
|
len: right_pos.pos - left_pos.pos + right_pos.len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.IntegerLiteral { return it.pos }
|
ast.IntegerLiteral {
|
||||||
ast.MapInit { return it.pos }
|
return it.pos
|
||||||
ast.MatchExpr { return it.pos }
|
}
|
||||||
ast.PostfixExpr { return it.pos }
|
ast.MapInit {
|
||||||
ast.PrefixExpr { return it.pos }
|
return it.pos
|
||||||
|
}
|
||||||
|
ast.MatchExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
|
ast.PostfixExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.None { }
|
// ast.None { }
|
||||||
|
ast.PrefixExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.ParExpr { }
|
// ast.ParExpr { }
|
||||||
ast.SelectorExpr { return it.pos }
|
ast.SelectorExpr {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.SizeOf { }
|
// ast.SizeOf { }
|
||||||
ast.StringLiteral { return it.pos }
|
ast.StringLiteral {
|
||||||
ast.StringInterLiteral { return it.pos }
|
return it.pos
|
||||||
ast.StructInit { return it.pos }
|
}
|
||||||
|
ast.StringInterLiteral {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.Type { }
|
// ast.Type { }
|
||||||
|
ast.StructInit {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.TypeOf { }
|
// ast.TypeOf { }
|
||||||
else { return token.Position{} }
|
else {
|
||||||
|
return token.Position{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -574,8 +574,15 @@ fn (f mut Fmt) expr(node ast.Expr) {
|
||||||
if branch.stmts.len == 0 {
|
if branch.stmts.len == 0 {
|
||||||
f.writeln(' {}')
|
f.writeln(' {}')
|
||||||
} else {
|
} else {
|
||||||
|
// TODO single line branches
|
||||||
|
// if branch.stmts.len < 2 {
|
||||||
|
// f.write(' { ')
|
||||||
|
// } else {
|
||||||
f.writeln(' {')
|
f.writeln(' {')
|
||||||
|
// f.single_line_if = true
|
||||||
|
// }
|
||||||
f.stmts(branch.stmts)
|
f.stmts(branch.stmts)
|
||||||
|
// f.single_line_if = false
|
||||||
f.writeln('}')
|
f.writeln('}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1307,7 +1307,7 @@ fn (p mut Parser) array_init() ast.ArrayInit {
|
||||||
mut array_type := table.void_type
|
mut array_type := table.void_type
|
||||||
mut elem_type := table.void_type
|
mut elem_type := table.void_type
|
||||||
mut exprs := []ast.Expr
|
mut exprs := []ast.Expr
|
||||||
is_fixed := false
|
mut is_fixed := false
|
||||||
if p.tok.kind == .rsbr {
|
if p.tok.kind == .rsbr {
|
||||||
// []typ => `[]` and `typ` must be on the same line
|
// []typ => `[]` and `typ` must be on the same line
|
||||||
line_nr := p.tok.line_nr
|
line_nr := p.tok.line_nr
|
||||||
|
@ -1685,6 +1685,7 @@ fn (p mut Parser) parse_assign_lhs() []ast.Ident {
|
||||||
p.check(.key_static)
|
p.check(.key_static)
|
||||||
}
|
}
|
||||||
mut ident := p.parse_ident(false)
|
mut ident := p.parse_ident(false)
|
||||||
|
ident.is_mut = is_mut
|
||||||
ident.info = ast.IdentVar{
|
ident.info = ast.IdentVar{
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
is_static: is_static
|
is_static: is_static
|
||||||
|
@ -1741,10 +1742,12 @@ fn (p mut Parser) assign_stmt() ast.Stmt {
|
||||||
p.scope.register(ident.name, ast.Var{
|
p.scope.register(ident.name, ast.Var{
|
||||||
name: ident.name
|
name: ident.name
|
||||||
expr: exprs[i]
|
expr: exprs[i]
|
||||||
|
is_mut: ident.is_mut || p.inside_for
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
p.scope.register(ident.name, ast.Var{
|
p.scope.register(ident.name, ast.Var{
|
||||||
name: ident.name
|
name: ident.name
|
||||||
|
is_mut: ident.is_mut || p.inside_for
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,7 +728,7 @@ pub fn (s mut Scanner) scan() token.Token {
|
||||||
start := s.pos + 1
|
start := s.pos + 1
|
||||||
s.ignore_line()
|
s.ignore_line()
|
||||||
s.line_comment = s.text[start + 1..s.pos]
|
s.line_comment = s.text[start + 1..s.pos]
|
||||||
comment := s.line_comment.trim_space()
|
mut comment := s.line_comment.trim_space()
|
||||||
s.pos--
|
s.pos--
|
||||||
// fix line_nr, \n was read, and the comment is marked
|
// fix line_nr, \n was read, and the comment is marked
|
||||||
// on the next line
|
// on the next line
|
||||||
|
|
Loading…
Reference in New Issue