parser: add unused variable warning

pull/4547/head
Kris Cherven 2020-04-21 19:52:56 -04:00 committed by GitHub
parent 08fac28c52
commit 155891a4e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 104 additions and 33 deletions

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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++
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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 {

View File

@ -6,4 +6,5 @@ fn main() {
2 { 'test' }
else { '' }
}
_ = res
}

View File

@ -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'

View File

@ -2,7 +2,7 @@ type A = int | string | f64
fn main() {
x := A('test')
res := match x {
_ = match x {
int {
'int'
}

View File

@ -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| }

View File

@ -4,4 +4,5 @@ struct Test {
fn main() {
t := Test{true, false}
_ = t
}

View File

@ -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

View File

@ -7,4 +7,5 @@ fn main() {
foo: true
bar: false
}
_ = t
}

View File

@ -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

View File

@ -4,4 +4,5 @@ fn main() {
mut a := 'aa'
a += x('a','b')
mut b := 'abcdef'
_ = b
}

View File

@ -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 = ')

View File

@ -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()

View File

@ -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)

View File

@ -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'

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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)