parser: simplify unused vars & add loop/if vars etc

pull/4624/head
joe-conigliaro 2020-04-27 23:16:31 +10:00
parent e67bf674e3
commit 9f76a7b250
No known key found for this signature in database
GPG Key ID: C12F7136C08206F1
15 changed files with 79 additions and 82 deletions

View File

@ -832,7 +832,7 @@ fn (ar []string) contains(val string) bool {
// TODO generic // TODO generic
fn (ar []int) contains(val int) bool { fn (ar []int) contains(val int) bool {
for i, s in ar { for s in ar {
if s == val { if s == val {
return true return true
} }
@ -1206,7 +1206,7 @@ pub fn (a []string) join(del string) string {
return '' return ''
} }
mut len := 0 mut len := 0
for i, val in a { for val in a {
len += val.len + del.len len += val.len + del.len
} }
len -= del.len len -= del.len

View File

@ -1061,7 +1061,7 @@ pub fn walk_ext(path, ext string) []string {
} }
mut res := []string{} mut res := []string{}
separator := if path.ends_with(os.path_separator) { '' } else { os.path_separator } separator := if path.ends_with(os.path_separator) { '' } else { os.path_separator }
for i, file in files { for file in files {
if file.starts_with('.') { if file.starts_with('.') {
continue continue
} }

View File

@ -276,6 +276,7 @@ pub:
mut: mut:
typ table.Type typ table.Type
pos token.Position pos token.Position
is_used bool
} }
pub struct GlobalDecl { pub struct GlobalDecl {

View File

@ -12,15 +12,9 @@ mut:
children []&Scope children []&Scope
start_pos int start_pos int
end_pos int end_pos int
unused_vars map[string]UnusedVar
objects map[string]ScopeObject objects map[string]ScopeObject
} }
pub struct UnusedVar {
name string
pos token.Position
}
pub fn new_scope(parent &Scope, start_pos int) &Scope { pub fn new_scope(parent &Scope, start_pos int) &Scope {
return &ast.Scope{ return &ast.Scope{
parent: parent parent: parent
@ -63,12 +57,12 @@ pub fn (s &Scope) is_known(name string) bool {
return false return false
} }
pub fn (s &Scope) find_var(name string) ?Var { pub fn (s &Scope) find_var(name string) ?&Var {
if obj := s.find(name) { if obj := s.find(name) {
v := ScopeObject(obj) v := ScopeObject(obj)
match v { match v {
Var { Var {
return *it return it
} }
else {} else {}
} }
@ -76,12 +70,12 @@ pub fn (s &Scope) find_var(name string) ?Var {
return none return none
} }
pub fn (s &Scope) find_const(name string) ?ConstField { pub fn (s &Scope) find_const(name string) ?&ConstField {
if obj := s.find(name) { if obj := s.find(name) {
cf := ScopeObject(obj) cf := ScopeObject(obj)
match cf { match cf {
ConstField { ConstField {
return *it return it
} }
else {} else {}
} }
@ -112,37 +106,13 @@ pub fn (s mut Scope) register(name string, obj ScopeObject) {
if name == '_' { if name == '_' {
return return
} }
if x := s.find(name) { if _ := s.find(name) {
// println('existing obect: $name') // println('existing obect: $name')
return return
} }
s.objects[name] = obj s.objects[name] = obj
} }
pub fn (s mut Scope) register_unused_var(name string, pos token.Position) {
s.unused_vars[name] = UnusedVar{name, pos}
}
pub fn (s mut Scope) remove_unused_var(name string) {
mut sc := s
for !isnil(sc) {
sc.unused_vars.delete(name)
sc = sc.parent
}
}
pub fn (s mut Scope) unused_vars() []UnusedVar {
ret := []UnusedVar{}
for _, v in s.unused_vars {
ret << v
}
return ret
}
pub fn (s mut Scope) clear_unused_vars() {
s.unused_vars = map[string]UnusedVar
}
pub fn (s &Scope) outermost() &Scope { pub fn (s &Scope) outermost() &Scope {
mut sc := s mut sc := s
for !isnil(sc.parent) { for !isnil(sc.parent) {

View File

@ -203,7 +203,7 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
} }
c.error('struct name must begin with capital letter', pos) c.error('struct name must begin with capital letter', pos)
} }
for fi, field in decl.fields { for field in decl.fields {
sym := c.table.get_type_symbol(field.typ) sym := c.table.get_type_symbol(field.typ)
if sym.kind == .placeholder && !decl.is_c && !sym.name.starts_with('C.') { if sym.kind == .placeholder && !decl.is_c && !sym.name.starts_with('C.') {
c.error('unknown type `$sym.name`', field.pos) c.error('unknown type `$sym.name`', field.pos)
@ -554,7 +554,7 @@ pub fn (mut c Checker) call_method(call_expr mut ast.CallExpr) table.Type {
mut scope := c.file.scope.innermost(call_expr.pos.pos) mut scope := c.file.scope.innermost(call_expr.pos.pos)
scope.update_var_type('it', array_info.elem_type) scope.update_var_type('it', array_info.elem_type)
} }
for i, arg in call_expr.args { for arg in call_expr.args {
c.expr(arg.expr) c.expr(arg.expr)
} }
// need to return `array_xxx` instead of `array` // need to return `array_xxx` instead of `array`

View File

@ -480,7 +480,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.typedefs.writeln('typedef enum {') g.typedefs.writeln('typedef enum {')
mut cur_enum_expr := '' mut cur_enum_expr := ''
mut cur_enum_offset := 0 mut cur_enum_offset := 0
for j, field in it.fields { for field in it.fields {
g.typedefs.write('\t${enum_name}_${field.name}') g.typedefs.write('\t${enum_name}_${field.name}')
if field.has_expr { if field.has_expr {
g.typedefs.write(' = ') g.typedefs.write(' = ')
@ -1918,7 +1918,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
} }
fn (mut g Gen) const_decl(node ast.ConstDecl) { fn (mut g Gen) const_decl(node ast.ConstDecl) {
for i, field in node.fields { for field in node.fields {
name := c_name(field.name) name := c_name(field.name)
// TODO hack. Cut the generated value and paste it into definitions. // TODO hack. Cut the generated value and paste it into definitions.
pos := g.out.len pos := g.out.len
@ -3061,7 +3061,7 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) {
g.definitions.writeln('string ${str_fn_name}($styp it); // auto') g.definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */') g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
g.auto_str_funcs.writeln('\tswitch(it) {') g.auto_str_funcs.writeln('\tswitch(it) {')
for i, val in info.vals { for val in info.vals {
g.auto_str_funcs.writeln('\t\tcase ${s}_$val: return tos3("$val");') g.auto_str_funcs.writeln('\t\tcase ${s}_$val: return tos3("$val");')
} }
g.auto_str_funcs.writeln('\t\tdefault: return tos3("unknown enum value");') g.auto_str_funcs.writeln('\t\tdefault: return tos3("unknown enum value");')
@ -3075,7 +3075,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
mut fnames2strfunc := { mut fnames2strfunc := {
'': '' '': ''
} // map[string]string // TODO vfmt bug } // map[string]string // TODO vfmt bug
for i, field in info.fields { for field in info.fields {
sym := g.table.get_type_symbol(field.typ) sym := g.table.get_type_symbol(field.typ)
if sym.kind in [.struct_, .array, .array_fixed, .map, .enum_] { if sym.kind in [.struct_, .array, .array_fixed, .map, .enum_] {
field_styp := g.typ(field.typ) field_styp := g.typ(field.typ)

View File

@ -426,7 +426,7 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic) is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic)
gen_vargs := is_variadic && !is_forwarding_varg gen_vargs := is_variadic && !is_forwarding_varg
mut arg_no := 0 mut arg_no := 0
for i, arg in args { for arg in args {
if gen_vargs && arg_no == expected_types.len - 1 { if gen_vargs && arg_no == expected_types.len - 1 {
break break
} }

View File

@ -600,7 +600,7 @@ fn (mut g Gen) allocate_var(name string, size, initial_val int) {
fn (mut g Gen) assign_stmt(node ast.AssignStmt) { fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
// `a := 1` | `a,b := 1,2` // `a := 1` | `a,b := 1,2`
for i, ident in node.left { for ident in node.left {
match node.right[0] { match node.right[0] {
ast.IntegerLiteral { ast.IntegerLiteral {
g.allocate_var(ident.name, 4, it.val.int()) g.allocate_var(ident.name, 4, it.val.int())

View File

@ -32,11 +32,13 @@ fn (mut p Parser) assign_stmt() ast.Stmt {
name: ident.name name: ident.name
expr: exprs[i] expr: exprs[i]
is_mut: ident.is_mut || p.inside_for is_mut: ident.is_mut || p.inside_for
pos: ident.pos
}) })
} 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 is_mut: ident.is_mut || p.inside_for
pos: ident.pos
}) })
} }
} }

View File

@ -37,10 +37,14 @@ pub fn (mut p Parser) call_expr(is_c, is_js bool, mod string) ast.CallExpr {
p.scope.register('err', ast.Var{ p.scope.register('err', ast.Var{
name: 'err' name: 'err'
typ: table.string_type typ: table.string_type
pos: p.tok.position()
is_used: true
}) })
p.scope.register('errcode', ast.Var{ p.scope.register('errcode', ast.Var{
name: 'errcode' name: 'errcode'
typ: table.int_type typ: table.int_type
pos: p.tok.position()
is_used: true
}) })
is_or_block_used = true is_or_block_used = true
or_stmts = p.parse_block_no_scope() or_stmts = p.parse_block_no_scope()
@ -164,6 +168,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
name: arg.name name: arg.name
typ: arg.typ typ: arg.typ
is_mut: arg.is_mut is_mut: arg.is_mut
pos: p.tok.position()
is_used: true
}) })
// Do not allow `mut` with simple types // Do not allow `mut` with simple types
// TODO move to checker? // TODO move to checker?
@ -264,6 +270,8 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
p.scope.register(arg.name, ast.Var{ p.scope.register(arg.name, ast.Var{
name: arg.name name: arg.name
typ: arg.typ typ: arg.typ
pos: p.tok.position()
is_used: true
}) })
} }
mut return_type := table.void_type mut return_type := table.void_type

View File

@ -67,12 +67,16 @@ fn (mut p Parser) for_stmt() ast.Stmt {
} }
} else if p.peek_tok.kind in [.key_in, .comma] { } else if p.peek_tok.kind in [.key_in, .comma] {
// `for i in vals`, `for i in start .. end` // `for i in vals`, `for i in start .. end`
mut val_var_pos := p.tok.position()
mut key_var_name := '' mut key_var_name := ''
mut val_var_name := p.check_name() mut val_var_name := p.check_name()
if p.tok.kind == .comma { if p.tok.kind == .comma {
p.check(.comma) p.check(.comma)
key_var_pos := val_var_pos
val_var_pos = p.tok.position()
key_var_name = val_var_name key_var_name = val_var_name
val_var_name = p.check_name() val_var_name = p.check_name()
if p.scope.known_var(key_var_name) { if p.scope.known_var(key_var_name) {
p.error('redefinition of key iteration variable `$key_var_name`') p.error('redefinition of key iteration variable `$key_var_name`')
} }
@ -82,6 +86,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
p.scope.register(key_var_name, ast.Var{ p.scope.register(key_var_name, ast.Var{
name: key_var_name name: key_var_name
typ: table.int_type typ: table.int_type
pos: key_var_pos
}) })
} else if p.scope.known_var(val_var_name) { } else if p.scope.known_var(val_var_name) {
p.error('redefinition of value iteration variable `$val_var_name`') p.error('redefinition of value iteration variable `$val_var_name`')
@ -104,11 +109,13 @@ fn (mut p Parser) for_stmt() ast.Stmt {
p.scope.register(val_var_name, ast.Var{ p.scope.register(val_var_name, ast.Var{
name: val_var_name name: val_var_name
typ: table.int_type typ: table.int_type
pos: val_var_pos
}) })
} else { } else {
// this type will be set in checker // this type will be set in checker
p.scope.register(val_var_name, ast.Var{ p.scope.register(val_var_name, ast.Var{
name: val_var_name name: val_var_name
pos: val_var_pos
}) })
} }
p.inside_for = false p.inside_for = false

View File

@ -43,12 +43,14 @@ fn (mut p Parser) if_expr() ast.IfExpr {
if p.peek_tok.kind == .decl_assign { if p.peek_tok.kind == .decl_assign {
is_or = true is_or = true
p.open_scope() p.open_scope()
var_pos := p.tok.position()
var_name := p.check_name() var_name := p.check_name()
p.check(.decl_assign) p.check(.decl_assign)
expr := p.expr(0) expr := p.expr(0)
p.scope.register(var_name, ast.Var{ p.scope.register(var_name, ast.Var{
name: var_name name: var_name
expr: expr expr: expr
pos: var_pos
}) })
cond = ast.IfGuardExpr{ cond = ast.IfGuardExpr{
var_name: var_name var_name: var_name
@ -89,6 +91,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
if is_mut { if is_mut {
p.next() p.next()
} }
cond_pos := p.tok.position()
cond := p.expr(0) cond := p.expr(0)
p.inside_match = false p.inside_match = false
p.check(.lcbr) p.check(.lcbr)
@ -119,6 +122,8 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
p.scope.register('it', ast.Var{ p.scope.register('it', ast.Var{
name: 'it' name: 'it'
typ: typ.to_ptr() typ: typ.to_ptr()
pos: cond_pos
is_used: true
}) })
// TODO // TODO
if p.tok.kind == .comma { if p.tok.kind == .comma {

View File

@ -195,16 +195,22 @@ pub fn (mut p Parser) open_scope() {
} }
pub fn (mut p Parser) close_scope() { pub fn (mut p Parser) close_scope() {
if !p.pref.is_repl && !p.pref.is_fmt { if !p.pref.is_repl && !p.scanner.is_fmt {
for v in p.scope.unused_vars() { for _, obj in p.scope.objects {
match obj {
ast.Var {
if !it.is_used && !it.name.starts_with('__') {
if p.pref.is_prod { if p.pref.is_prod {
p.error_with_pos('Unused variable: $v.name', v.pos) p.error_with_pos('Unused variable: $it.name', it.pos)
} else { } else {
p.warn_with_pos('Unused variable: $v.name', v.pos) p.warn_with_pos('Unused variable: $it.name', it.pos)
}
}
}
else {}
} }
} }
} }
p.scope.clear_unused_vars()
p.scope.end_pos = p.tok.pos p.scope.end_pos = p.tok.pos
p.scope.parent.children << p.scope p.scope.parent.children << p.scope
p.scope = p.scope.parent p.scope = p.scope.parent
@ -393,9 +399,6 @@ pub fn (mut p Parser) stmt() ast.Stmt {
} }
} }
.key_mut, .key_static, .key_var { .key_mut, .key_static, .key_var {
if p.peek_tok.kind == .name && p.peek_tok.lit != '_' && !p.peek_tok.lit.starts_with('__') {
p.scope.register_unused_var(p.peek_tok.lit, p.peek_tok.position())
}
return p.assign_stmt() return p.assign_stmt()
} }
.key_for { .key_for {
@ -457,14 +460,7 @@ pub fn (mut p Parser) stmt() ast.Stmt {
else { else {
// `x := ...` // `x := ...`
if p.tok.kind == .name && p.peek_tok.kind in [.decl_assign, .comma] { if p.tok.kind == .name && p.peek_tok.kind in [.decl_assign, .comma] {
register_unused := p.peek_tok.kind == .decl_assign return p.assign_stmt()
lit := p.tok.lit
pos := p.tok.position()
ret := p.assign_stmt()
if register_unused && lit != '_' && !lit.starts_with('__') {
p.scope.register_unused_var(lit, pos)
}
return ret
} else if p.tok.kind == .name && p.peek_tok.kind == .colon { } else if p.tok.kind == .name && p.peek_tok.kind == .colon {
// `label:` // `label:`
name := p.check_name() name := p.check_name()
@ -472,8 +468,6 @@ pub fn (mut p Parser) stmt() ast.Stmt {
return ast.GotoLabel{ return ast.GotoLabel{
name: name name: name
} }
} else if p.tok.kind == .name {
p.scope.remove_unused_var(p.tok.lit)
} }
epos := p.tok.position() epos := p.tok.position()
expr := p.expr(0) expr := p.expr(0)
@ -609,7 +603,16 @@ pub fn (mut p Parser) name_expr() ast.Expr {
if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && p.prev_tok.kind != .str_dollar { if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && p.prev_tok.kind != .str_dollar {
return p.string_expr() return p.string_expr()
} }
known_var := p.scope.known_var(p.tok.lit) mut known_var := false
if obj := p.scope.find(p.tok.lit) {
match mut obj {
ast.Var {
known_var = true
it.is_used = true
}
else {}
}
}
if p.peek_tok.kind == .dot && !known_var && (is_c || is_js || p.known_import(p.tok.lit) || if p.peek_tok.kind == .dot && !known_var && (is_c || is_js || p.known_import(p.tok.lit) ||
p.mod.all_after('.') == p.tok.lit) { p.mod.all_after('.') == p.tok.lit) {
if is_c { if is_c {
@ -753,6 +756,8 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr {
fn (mut p Parser) filter() { fn (mut p Parser) filter() {
p.scope.register('it', ast.Var{ p.scope.register('it', ast.Var{
name: 'it' name: 'it'
pos: p.tok.position()
is_used: true
}) })
} }
@ -783,10 +788,14 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
p.scope.register('errcode', ast.Var{ p.scope.register('errcode', ast.Var{
name: 'errcode' name: 'errcode'
typ: table.int_type typ: table.int_type
pos: p.tok.position()
is_used: true
}) })
p.scope.register('err', ast.Var{ p.scope.register('err', ast.Var{
name: 'err' name: 'err'
typ: table.string_type typ: table.string_type
pos: p.tok.position()
is_used: true
}) })
is_or_block_used = true is_or_block_used = true
or_stmts = p.parse_block_no_scope() or_stmts = p.parse_block_no_scope()
@ -1028,9 +1037,6 @@ fn (mut p Parser) return_stmt() ast.Return {
} }
} }
for { for {
if p.tok.kind == .name {
p.scope.remove_unused_var(p.tok.lit)
}
expr := p.expr(0) expr := p.expr(0)
exprs << expr exprs << expr
if p.tok.kind == .comma { if p.tok.kind == .comma {
@ -1220,10 +1226,11 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
fn (mut p Parser) assoc() ast.Assoc { fn (mut p Parser) assoc() ast.Assoc {
var_name := p.check_name() var_name := p.check_name()
pos := p.tok.position() pos := p.tok.position()
v := p.scope.find_var(var_name) or { mut v := p.scope.find_var(var_name) or {
p.error('unknown variable `$var_name`') p.error('unknown variable `$var_name`')
return ast.Assoc{} return ast.Assoc{}
} }
v.is_used = true
// println('assoc var $name typ=$var.typ') // println('assoc var $name typ=$var.typ')
mut fields := []string{} mut fields := []string{}
mut vals := []ast.Expr{} mut vals := []ast.Expr{}
@ -1245,7 +1252,7 @@ fn (mut p Parser) assoc() ast.Assoc {
fields: fields fields: fields
exprs: vals exprs: vals
pos: pos pos: pos
typ: v.typ // typ: v.typ
} }
} }

View File

@ -16,7 +16,6 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
// Prefix // Prefix
match p.tok.kind { match p.tok.kind {
.name { .name {
p.scope.remove_unused_var(p.tok.lit)
node = p.name_expr() node = p.name_expr()
p.is_stmt_ident = is_stmt_ident p.is_stmt_ident = is_stmt_ident
} }
@ -79,7 +78,6 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
type_name: p.check_name() type_name: p.check_name()
} }
} else { } else {
p.scope.remove_unused_var(p.tok.lit)
sizeof_type := p.parse_type() sizeof_type := p.parse_type()
node = ast.SizeOf{ node = ast.SizeOf{
typ: sizeof_type typ: sizeof_type
@ -104,7 +102,6 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
} else { } else {
// it should be a struct // it should be a struct
if p.peek_tok.kind == .pipe { if p.peek_tok.kind == .pipe {
p.scope.remove_unused_var(p.tok.lit)
node = p.assoc() node = p.assoc()
} else if p.peek_tok.kind == .colon || p.tok.kind == .rcbr { } else if p.peek_tok.kind == .colon || p.tok.kind == .rcbr {
node = p.struct_init(true) // short_syntax: true node = p.struct_init(true) // short_syntax: true

View File

@ -66,7 +66,7 @@ pub fn (t Type) is_ptr() bool {
[inline] [inline]
pub fn (t Type) set_nr_muls(nr_muls int) Type { pub fn (t Type) set_nr_muls(nr_muls int) Type {
if nr_muls < 0 || nr_muls > 255 { if nr_muls < 0 || nr_muls > 255 {
panic('typ_set_nr_muls: nr_muls must be between 0 & 255') panic('set_nr_muls: nr_muls must be between 0 & 255')
} }
return (((int(t) >> 24) & 0xff) << 24) | (nr_muls << 16) | (u16(t) & 0xffff) return (((int(t) >> 24) & 0xff) << 24) | (nr_muls << 16) | (u16(t) & 0xffff)
} }
@ -76,7 +76,7 @@ pub fn (t Type) set_nr_muls(nr_muls int) Type {
pub fn (t Type) to_ptr() Type { pub fn (t Type) to_ptr() Type {
nr_muls := (int(t) >> 16) & 0xff nr_muls := (int(t) >> 16) & 0xff
if nr_muls == 255 { if nr_muls == 255 {
panic('type_to_pre: nr_muls is already at max of 255') panic('to_ptr: nr_muls is already at max of 255')
} }
return (((int(t) >> 24) & 0xff) << 24) | ((nr_muls + 1) << 16) | (u16(t) & 0xffff) return (((int(t) >> 24) & 0xff) << 24) | ((nr_muls + 1) << 16) | (u16(t) & 0xffff)
} }