parser: add unused variable warning
parent
08fac28c52
commit
155891a4e0
|
@ -11,7 +11,7 @@ import term
|
|||
// / since it is done in normal V code, instead of in embedded C ...
|
||||
// //////////////////////////////////////////////////////////////////
|
||||
fn cb_assertion_failed(filename string, line int, sourceline string, funcname string) {
|
||||
color_on := term.can_show_color_on_stderr()
|
||||
// color_on := term.can_show_color_on_stderr()
|
||||
use_relative_paths := match os.getenv('VERROR_PATHS') {
|
||||
'absolute'{
|
||||
false
|
||||
|
|
|
@ -125,6 +125,9 @@ fn parse_args(args []string) (&pref.Preferences, string) {
|
|||
'-live' {
|
||||
res.is_live = true
|
||||
}
|
||||
'-repl' {
|
||||
res.is_repl = true
|
||||
}
|
||||
'-sharedlive' {
|
||||
res.is_live = true
|
||||
res.is_shared = true
|
||||
|
|
|
@ -92,7 +92,8 @@ $if msvc {
|
|||
handle := C.GetCurrentProcess()
|
||||
defer { C.SymCleanup(handle) }
|
||||
|
||||
options := C.SymSetOptions(SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME)
|
||||
C.SymSetOptions(SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME)
|
||||
|
||||
syminitok := C.SymInitialize( handle, 0, 1)
|
||||
if syminitok != 1 {
|
||||
println('Failed getting process: Aborting backtrace.\n')
|
||||
|
|
|
@ -1223,7 +1223,6 @@ pub fn (a []string) join(del string) string {
|
|||
// Go thru every string and copy its every char one by one
|
||||
for i, val in a {
|
||||
for j in 0..val.len {
|
||||
c := val[j]
|
||||
res.str[idx] = val.str[j]
|
||||
idx++
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
module ast
|
||||
|
||||
import v.table
|
||||
import v.token
|
||||
|
||||
pub struct Scope {
|
||||
mut:
|
||||
|
@ -11,9 +12,15 @@ mut:
|
|||
children []&Scope
|
||||
start_pos int
|
||||
end_pos int
|
||||
unused_vars map[string]UnusedVar
|
||||
objects map[string]ScopeObject
|
||||
}
|
||||
|
||||
pub struct UnusedVar {
|
||||
name string
|
||||
pos token.Position
|
||||
}
|
||||
|
||||
pub fn new_scope(parent &Scope, start_pos int) &Scope {
|
||||
return &ast.Scope{
|
||||
parent: parent
|
||||
|
@ -107,6 +114,30 @@ pub fn (s mut Scope) register(name string, obj ScopeObject) {
|
|||
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 {
|
||||
mut sc := s
|
||||
for !isnil(sc.parent) {
|
||||
|
|
|
@ -174,7 +174,7 @@ fn (v mut Builder) cc() {
|
|||
is_cc_clang := v.pref.ccompiler.contains('clang') || guessed_compiler == 'clang'
|
||||
is_cc_tcc := v.pref.ccompiler.contains('tcc') || guessed_compiler == 'tcc'
|
||||
is_cc_gcc := v.pref.ccompiler.contains('gcc') || guessed_compiler == 'gcc'
|
||||
is_cc_msvc := v.pref.ccompiler.contains('msvc') || guessed_compiler == 'msvc'
|
||||
// is_cc_msvc := v.pref.ccompiler.contains('msvc') || guessed_compiler == 'msvc'
|
||||
//
|
||||
if is_cc_clang {
|
||||
if debug_mode {
|
||||
|
|
|
@ -1515,7 +1515,7 @@ pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
|
|||
|
||||
if !has_else {
|
||||
mut used_values_count := 0
|
||||
for bi, branch in node.branches {
|
||||
for _, branch in node.branches {
|
||||
used_values_count += branch.exprs.len
|
||||
for bi_ei, bexpr in branch.exprs {
|
||||
match bexpr {
|
||||
|
|
|
@ -6,4 +6,5 @@ fn main() {
|
|||
2 { 'test' }
|
||||
else { '' }
|
||||
}
|
||||
_ = res
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
vlib/v/checker/tests/inout/match_expr_else.v:5:9: error: match must be exhaustive (add match branches for: `f64` or an else{} branch)
|
||||
vlib/v/checker/tests/inout/match_expr_else.v:5:6: error: match must be exhaustive (add match branches for: `f64` or an else{} branch)
|
||||
3| fn main() {
|
||||
4| x := A('test')
|
||||
5| res := match x {
|
||||
~~~~~~~~~
|
||||
5| _ = match x {
|
||||
~~~~~~~~~
|
||||
6| int {
|
||||
7| 'int'
|
||||
|
|
|
@ -2,7 +2,7 @@ type A = int | string | f64
|
|||
|
||||
fn main() {
|
||||
x := A('test')
|
||||
res := match x {
|
||||
_ = match x {
|
||||
int {
|
||||
'int'
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ vlib/v/checker/tests/inout/short_struct_too_many.v:6:7: error: too many fields
|
|||
5| fn main() {
|
||||
6| t := Test{true, false}
|
||||
~~~~~~~~~~~~~~~~~
|
||||
7| }
|
||||
7| _ = t
|
||||
8| }
|
||||
|
|
|
@ -4,4 +4,5 @@ struct Test {
|
|||
|
||||
fn main() {
|
||||
t := Test{true, false}
|
||||
_ = t
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ vlib/v/checker/tests/inout/struct_unknown_field.v:8:9: error: unknown field `bar
|
|||
8| bar: false
|
||||
~~~~~~~~~~
|
||||
9| }
|
||||
10| }
|
||||
10| _ = t
|
||||
|
|
|
@ -7,4 +7,5 @@ fn main() {
|
|||
foo: true
|
||||
bar: false
|
||||
}
|
||||
_ = t
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ vlib/v/checker/tests/inout/void_fn_as_value.v:5:8: error: unknown function: x
|
|||
5| a += x('a','b')
|
||||
~~~~~~~~~~
|
||||
6| mut b := 'abcdef'
|
||||
7| }
|
||||
7| _ = b
|
||||
|
|
|
@ -4,4 +4,5 @@ fn main() {
|
|||
mut a := 'aa'
|
||||
a += x('a','b')
|
||||
mut b := 'abcdef'
|
||||
_ = b
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
|||
x := 10 // line
|
||||
// sep
|
||||
y := 20
|
||||
_ = x
|
||||
_ = y
|
||||
} else {
|
||||
}
|
||||
// println('start cgen2')
|
||||
|
@ -919,7 +921,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
ast.ArrayInit {
|
||||
type_sym := g.table.get_type_symbol(it.typ)
|
||||
if type_sym.kind != .array_fixed {
|
||||
elem_sym := g.table.get_type_symbol(it.elem_type)
|
||||
// elem_sym := g.table.get_type_symbol(it.elem_type)
|
||||
elem_type_str := g.typ(it.elem_type)
|
||||
if it.exprs.len == 0 {
|
||||
// use __new_array to fix conflicts when the name of the variable is new_array
|
||||
|
@ -1801,8 +1803,8 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
|||
// multiple returns
|
||||
if node.exprs.len > 1 {
|
||||
g.write(' ')
|
||||
typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
||||
mr_info := typ_sym.info as table.MultiReturn
|
||||
// typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
||||
// mr_info := typ_sym.info as table.MultiReturn
|
||||
mut styp := g.typ(g.fn_decl.return_type)
|
||||
if fn_return_is_optional { // && !table.type_is(node.types[0], .optional) && node.types[0] !=
|
||||
styp = styp[7..] // remove 'Option_'
|
||||
|
@ -1958,7 +1960,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
}
|
||||
*/
|
||||
// User set fields
|
||||
for i, field in struct_init.fields {
|
||||
for _, field in struct_init.fields {
|
||||
field_name := c_name(field.name)
|
||||
inited_fields << field.name
|
||||
g.write('\t.$field_name = ')
|
||||
|
|
|
@ -594,7 +594,7 @@ fn (g mut JsGen) gen_branch_stmt(it ast.BranchStmt) {
|
|||
}
|
||||
|
||||
fn (g mut JsGen) gen_const_decl(it ast.ConstDecl) {
|
||||
old_indent := g.indents[g.namespace]
|
||||
// old_indent := g.indents[g.namespace]
|
||||
for i, field in it.fields {
|
||||
// TODO hack. Cut the generated value and paste it into definitions.
|
||||
pos := g.out.len
|
||||
|
@ -689,7 +689,7 @@ fn (g mut JsGen) gen_method_decl(it ast.FnDecl) {
|
|||
name = util.replace_op(name)
|
||||
}
|
||||
|
||||
type_name := g.typ(it.return_type)
|
||||
// type_name := g.typ(it.return_type)
|
||||
|
||||
// generate jsdoc for the function
|
||||
g.writeln(g.doc.gen_fn(it))
|
||||
|
@ -767,7 +767,7 @@ fn (g mut JsGen) gen_for_in_stmt(it ast.ForInStmt) {
|
|||
} else if it.kind == .array || table.type_is(it.cond_type, .variadic) {
|
||||
// `for num in nums {`
|
||||
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
|
||||
styp := g.typ(it.val_type)
|
||||
// styp := g.typ(it.val_type)
|
||||
g.inside_loop = true
|
||||
g.write('for (let $i = 0; $i < ')
|
||||
g.expr(it.cond)
|
||||
|
@ -780,8 +780,8 @@ fn (g mut JsGen) gen_for_in_stmt(it ast.ForInStmt) {
|
|||
g.writeln('}')
|
||||
} else if it.kind == .map {
|
||||
// `for key, val in map[string]int {`
|
||||
key_styp := g.typ(it.key_type)
|
||||
val_styp := g.typ(it.val_type)
|
||||
// key_styp := g.typ(it.key_type)
|
||||
// val_styp := g.typ(it.val_type)
|
||||
key := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
|
||||
g.write('for (let [$key, $it.val_var] of ')
|
||||
g.expr(it.cond)
|
||||
|
@ -817,7 +817,7 @@ fn (g mut JsGen) gen_for_stmt(it ast.ForStmt) {
|
|||
}
|
||||
|
||||
fn (g mut JsGen) fn_args(args []table.Arg, is_variadic bool) {
|
||||
no_names := args.len > 0 && args[0].name == 'arg_1'
|
||||
// no_names := args.len > 0 && args[0].name == 'arg_1'
|
||||
for i, arg in args {
|
||||
is_varg := i == args.len - 1 && is_variadic
|
||||
if is_varg {
|
||||
|
@ -860,10 +860,10 @@ fn (g mut JsGen) gen_go_stmt(node ast.GoStmt) {
|
|||
}
|
||||
|
||||
fn (g mut JsGen) gen_map_init_expr(it ast.MapInit) {
|
||||
key_typ_sym := g.table.get_type_symbol(it.key_type)
|
||||
value_typ_sym := g.table.get_type_symbol(it.value_type)
|
||||
key_typ_str := key_typ_sym.name.replace('.', '__')
|
||||
value_typ_str := value_typ_sym.name.replace('.', '__')
|
||||
// key_typ_sym := g.table.get_type_symbol(it.key_type)
|
||||
// value_typ_sym := g.table.get_type_symbol(it.value_type)
|
||||
// key_typ_str := key_typ_sym.name.replace('.', '__')
|
||||
// value_typ_str := value_typ_sym.name.replace('.', '__')
|
||||
if it.vals.len > 0 {
|
||||
g.writeln('new Map([')
|
||||
g.inc_indent()
|
||||
|
|
|
@ -43,6 +43,7 @@ fn (var p Parser) array_init() ast.ArrayInit {
|
|||
// NB: do not remove the next line without testing
|
||||
// v selfcompilation with tcc first
|
||||
tcc_stack_bug := 12345
|
||||
_ = tcc_stack_bug
|
||||
}
|
||||
last_pos = p.tok.position()
|
||||
p.check(.rsbr)
|
||||
|
|
|
@ -11,7 +11,6 @@ import v.util
|
|||
|
||||
pub fn (mut p Parser) call_expr(is_c, is_js bool, mod string) ast.CallExpr {
|
||||
first_pos := p.tok.position()
|
||||
tok := p.tok
|
||||
name := p.check_name()
|
||||
fn_name := if is_c {
|
||||
'C.$name'
|
||||
|
|
|
@ -43,7 +43,6 @@ fn (var p Parser) for_stmt() ast.Stmt {
|
|||
// Allow `for i = 0; i < ...`
|
||||
p.check(.semicolon)
|
||||
if p.tok.kind != .semicolon {
|
||||
var typ := table.void_type
|
||||
cond = p.expr(0)
|
||||
has_cond = true
|
||||
}
|
||||
|
|
|
@ -192,6 +192,16 @@ pub fn (mut p Parser) open_scope() {
|
|||
}
|
||||
|
||||
pub fn (mut p Parser) close_scope() {
|
||||
if !p.pref.is_repl {
|
||||
for v in p.scope.unused_vars() {
|
||||
if p.pref.is_prod {
|
||||
p.error_with_pos('Unused variable: $v.name', v.pos)
|
||||
} else {
|
||||
p.warn_with_pos('Unused variable: $v.name', v.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.scope.clear_unused_vars()
|
||||
p.scope.end_pos = p.tok.pos
|
||||
p.scope.parent.children << p.scope
|
||||
p.scope = p.scope.parent
|
||||
|
@ -376,6 +386,9 @@ pub fn (mut p Parser) stmt() ast.Stmt {
|
|||
}
|
||||
}
|
||||
.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()
|
||||
}
|
||||
.key_for {
|
||||
|
@ -437,7 +450,14 @@ pub fn (mut p Parser) stmt() ast.Stmt {
|
|||
else {
|
||||
// `x := ...`
|
||||
if p.tok.kind == .name && p.peek_tok.kind in [.decl_assign, .comma] {
|
||||
return p.assign_stmt()
|
||||
register_unused := p.peek_tok.kind == .decl_assign
|
||||
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 {
|
||||
// `label:`
|
||||
name := p.check_name()
|
||||
|
@ -445,6 +465,8 @@ pub fn (mut p Parser) stmt() ast.Stmt {
|
|||
return ast.GotoLabel{
|
||||
name: name
|
||||
}
|
||||
} else if p.tok.kind == .name {
|
||||
p.scope.remove_unused_var(p.tok.lit)
|
||||
}
|
||||
epos := p.tok.position()
|
||||
expr := p.expr(0)
|
||||
|
@ -994,6 +1016,9 @@ fn (mut p Parser) return_stmt() ast.Return {
|
|||
}
|
||||
}
|
||||
for {
|
||||
if p.tok.kind == .name {
|
||||
p.scope.remove_unused_var(p.tok.lit)
|
||||
}
|
||||
expr := p.expr(0)
|
||||
exprs << expr
|
||||
if p.tok.kind == .comma {
|
||||
|
|
|
@ -16,6 +16,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
// Prefix
|
||||
match p.tok.kind {
|
||||
.name {
|
||||
p.scope.remove_unused_var(p.tok.lit)
|
||||
node = p.name_expr()
|
||||
p.is_stmt_ident = is_stmt_ident
|
||||
}
|
||||
|
@ -78,6 +79,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
type_name: p.check_name()
|
||||
}
|
||||
} else {
|
||||
p.scope.remove_unused_var(p.tok.lit)
|
||||
sizeof_type := p.parse_type()
|
||||
node = ast.SizeOf{
|
||||
typ: sizeof_type
|
||||
|
@ -102,6 +104,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
} else {
|
||||
// it should be a struct
|
||||
if p.peek_tok.kind == .pipe {
|
||||
p.scope.remove_unused_var(p.tok.lit)
|
||||
node = p.assoc()
|
||||
} else if p.peek_tok.kind == .colon || p.tok.kind == .rcbr {
|
||||
node = p.struct_init(true) // short_syntax: true
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
struct MyStruct {
|
||||
s string
|
||||
s string
|
||||
}
|
||||
|
||||
fn new_st() MyStruct {
|
||||
return MyStruct{}
|
||||
return MyStruct{}
|
||||
}
|
||||
|
||||
fn get_st() MyStruct {
|
||||
r := new_st()
|
||||
return {r|s:'6'}
|
||||
r := new_st()
|
||||
return {r|s:'6'}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s := get_st()
|
||||
println(s)
|
||||
|
|
Loading…
Reference in New Issue