Skip to content

Commit 990b266

Browse files
authored
Add unused variable warning (#1547)
* Add unused variable warning Will ignore variables starting with an underscore, or any variable marked as generated in it's location information. It's possible there might still be issues with generated definitions, as we aren't particularly strict about making sure we tag all generated locations as such. * Update tests for new warning
1 parent 4b452f8 commit 990b266

File tree

12 files changed

+135
-51
lines changed

12 files changed

+135
-51
lines changed

lib/dec_bits.sail

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ val "valid_dec_bits" : forall 'n, 'n > 0. (int('n), string) -> bool
5858
val dec_bits : forall 'n, 'n > 0. bits('n) <-> (int('n), string)
5959

6060
function dec_bits_forwards(bv) = (length(bv), dec_str(unsigned(bv)))
61-
function dec_bits_forwards_matches(bv) = true
61+
function dec_bits_forwards_matches(_) = true
6262

6363
function dec_bits_backwards(n, str) = parse_dec_bits(n, str)
6464
function dec_bits_backwards_matches(n, str) = valid_dec_bits(n, str)

lib/float/arith_internal.sail

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ function float_add_internal (op_0, op_1) = {
278278

279279
val float_sub_internal : forall 'n, 'n in {16, 32, 64, 128}.
280280
(bits('n), bits('n)) -> (bits('n), fp_exception_flags)
281-
function float_sub_internal (op_0, op_1) = {
281+
function float_sub_internal (_op_0, _op_1) = {
282282
assert (false, "Not implemented yet.");
283283
(sail_zeros ('n), fp_eflag_none);
284284
}

lib/hex_bits.sail

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ val "valid_hex_bits" : forall 'n, 'n > 0. (int('n), string) -> bool
6060
val hex_bits : forall 'n, 'n > 0. bits('n) <-> (int('n), string)
6161

6262
function hex_bits_forwards(bv) = (length(bv), hex_str(unsigned(bv)))
63-
function hex_bits_forwards_matches(bv) = true
63+
function hex_bits_forwards_matches(_) = true
6464

6565
function hex_bits_backwards(n, str) = parse_hex_bits(n, str)
6666
function hex_bits_backwards_matches(n, str) = valid_hex_bits(n, str)

lib/hex_bits_signed.sail

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function hex_bits_signed_forwards(bv) = {
6464
(length(bv), s)
6565
}
6666

67-
function hex_bits_signed_forwards_matches(bv) = true
67+
function hex_bits_signed_forwards_matches(_) = true
6868

6969
function hex_bits_signed_backwards(n, str) = {
7070
if string_take(str, 1) == "-" then {

src/lib/frontend.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ let opt_reformat : string option ref = ref None
5858

5959
let finalize_ast asserts_termination ctx env ast =
6060
Lint.warn_unmodified_variables ast;
61+
Lint.warn_unused_variables ast;
6162
let ast = Scattered.descatter env ast in
6263
let side_effects = Effects.infer_side_effects asserts_termination ast in
6364
if !opt_ddump_side_effect then Effects.dump_effects side_effects;

src/lib/initial_check.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,6 +2512,7 @@ let generate_undefined_record id typq fields =
25122512
let p_tup = function [pat] -> pat | pats -> mk_pat (P_tuple pats) in
25132513
let pat =
25142514
p_tup (quant_items typq |> List.map quant_item_param |> List.concat |> List.map (fun id -> mk_pat (P_id id)))
2515+
|> locate_pat gen_loc
25152516
in
25162517
[
25172518
mk_val_spec (VS_val_spec (undefined_typschm id typq, prepend_id "undefined_" id, None));

src/lib/lint.ml

Lines changed: 109 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -48,67 +48,133 @@ open Ast
4848
open Ast_defs
4949
open Ast_util
5050

51-
let scan_exp_in_pexp f (Pat_aux (aux, _)) =
52-
match aux with
53-
| Pat_exp (_, exp) -> f exp
54-
| Pat_when (_, guard, exp) ->
55-
f guard;
56-
f exp
51+
module Scan (F : sig
52+
type t
53+
val do_exp : t exp -> unit
54+
val do_funcl_pexp : (t pat -> t exp option -> t exp -> unit) option
55+
end) : sig
56+
val in_def : (F.t, 'b) def -> unit
57+
end = struct
58+
let in_pexp (Pat_aux (aux, _)) =
59+
match aux with
60+
| Pat_exp (_, exp) -> F.do_exp exp
61+
| Pat_when (_, guard, exp) ->
62+
F.do_exp guard;
63+
F.do_exp exp
5764

58-
let scan_exp_in_funcl f (FCL_aux (FCL_funcl (_, pexp), _)) = scan_exp_in_pexp f pexp
65+
let in_funcl (FCL_aux (FCL_funcl (_, pexp), _)) =
66+
match F.do_funcl_pexp with
67+
| Some g -> (
68+
match pexp with
69+
| Pat_aux (Pat_exp (pat, exp), _) -> g pat None exp
70+
| Pat_aux (Pat_when (pat, guard, exp), _) -> g pat (Some guard) exp
71+
)
72+
| None -> in_pexp pexp
5973

60-
let scan_exp_in_mpexp f (MPat_aux (aux, _)) = match aux with MPat_when (_, exp) -> f exp | MPat_pat _ -> ()
74+
let in_mpexp (MPat_aux (aux, _)) = match aux with MPat_when (_, exp) -> F.do_exp exp | MPat_pat _ -> ()
6175

62-
let scan_exp_in_mapcl f (MCL_aux (aux, _)) =
63-
match aux with
64-
| MCL_forwards pexp | MCL_backwards pexp -> scan_exp_in_pexp f pexp
65-
| MCL_bidir (left, right) ->
66-
scan_exp_in_mpexp f left;
67-
scan_exp_in_mpexp f right
76+
let in_mapcl (MCL_aux (aux, _)) =
77+
match aux with
78+
| MCL_forwards pexp | MCL_backwards pexp -> in_pexp pexp
79+
| MCL_bidir (left, right) ->
80+
in_mpexp left;
81+
in_mpexp right
6882

69-
let scan_exp_in_scattered_def f (SD_aux (aux, _)) =
70-
match aux with
71-
| SD_function _ | SD_unioncl _ | SD_variant _ | SD_internal_unioncl_record _ | SD_enumcl _ | SD_enum _ | SD_mapping _
72-
| SD_end _ ->
73-
()
74-
| SD_funcl funcl -> scan_exp_in_funcl f funcl
75-
| SD_mapcl (_, mapcl) -> scan_exp_in_mapcl f mapcl
83+
let in_scattered_def (SD_aux (aux, _)) =
84+
match aux with
85+
| SD_function _ | SD_unioncl _ | SD_variant _ | SD_internal_unioncl_record _ | SD_enumcl _ | SD_enum _
86+
| SD_mapping _ | SD_end _ ->
87+
()
88+
| SD_funcl funcl -> in_funcl funcl
89+
| SD_mapcl (_, mapcl) -> in_mapcl mapcl
7690

77-
let scan_exp_in_fundef f (FD_aux (FD_function (_, _, funcls), _)) = List.iter (scan_exp_in_funcl f) funcls
91+
let in_fundef (FD_aux (FD_function (_, _, funcls), _)) = List.iter in_funcl funcls
7892

79-
let rec scan_exp_in_def f (DEF_aux (aux, _)) =
80-
match aux with
81-
| DEF_fundef fdef -> scan_exp_in_fundef f fdef
82-
| DEF_mapdef (MD_aux (MD_mapping (_, _, mapcls), _)) -> List.iter (scan_exp_in_mapcl f) mapcls
83-
| DEF_register (DEC_aux (DEC_reg (_, _, exp_opt), _)) -> Option.iter f exp_opt
84-
| DEF_outcome (_, defs) -> List.iter (scan_exp_in_def f) defs
85-
| DEF_impl funcl -> scan_exp_in_funcl f funcl
86-
| DEF_let (LB_aux (LB_val (_, exp), _)) -> f exp
87-
| DEF_scattered sdef -> scan_exp_in_scattered_def f sdef
88-
| DEF_internal_mutrec fdefs -> List.iter (scan_exp_in_fundef f) fdefs
89-
| DEF_loop_measures _ -> ()
90-
| DEF_measure (_, _, exp) -> f exp
91-
| DEF_type _ | DEF_constraint _ | DEF_val _ | DEF_fixity _ | DEF_overload _ | DEF_default _ | DEF_pragma _
92-
| DEF_instantiation _ ->
93-
()
93+
let rec in_def (DEF_aux (aux, _)) =
94+
match aux with
95+
| DEF_fundef fdef -> in_fundef fdef
96+
| DEF_mapdef (MD_aux (MD_mapping (_, _, mapcls), _)) -> List.iter in_mapcl mapcls
97+
| DEF_register (DEC_aux (DEC_reg (_, _, exp_opt), _)) -> Option.iter F.do_exp exp_opt
98+
| DEF_outcome (_, defs) -> List.iter in_def defs
99+
| DEF_impl funcl -> in_funcl funcl
100+
| DEF_let (LB_aux (LB_val (_, exp), _)) -> F.do_exp exp
101+
| DEF_scattered sdef -> in_scattered_def sdef
102+
| DEF_internal_mutrec fdefs -> List.iter in_fundef fdefs
103+
| DEF_loop_measures _ -> ()
104+
| DEF_measure (_, _, exp) -> F.do_exp exp
105+
| DEF_type _ | DEF_constraint _ | DEF_val _ | DEF_fixity _ | DEF_overload _ | DEF_default _ | DEF_pragma _
106+
| DEF_instantiation _ ->
107+
()
108+
end
94109

95-
let warn_unmodified_variables ast =
96-
let warn_unused (lexp, bind, exp) =
97-
let unused = IdSet.diff lexp exp in
110+
let warn_unmodified_variables (type a) (ast : (a, 'b) ast) : unit =
111+
let warn_unmodified (lexp, bind, exp) =
112+
let unmodified = IdSet.diff lexp exp in
98113
IdSet.iter
99114
(fun id ->
100115
Reporting.warn "Unnecessary mutability" (id_loc id)
101116
"This variable is mutable, but it is never modified. It could be declared as immutable using 'let'."
102117
)
103-
unused;
118+
unmodified;
104119
IdSet.union (IdSet.diff exp lexp) bind
105120
in
106121
let alg =
107122
{
108123
(Rewriter.pure_exp_alg IdSet.empty IdSet.union) with
109124
le_id = IdSet.singleton;
110125
le_typ = (fun (_, id) -> IdSet.singleton id);
111-
e_var = warn_unused;
126+
e_var = warn_unmodified;
127+
}
128+
in
129+
let module S = Scan (struct
130+
type t = a
131+
let do_exp exp = ignore (Rewriter.fold_exp alg exp)
132+
let do_funcl_pexp = None
133+
end) in
134+
List.iter S.in_def ast.defs
135+
136+
let warn_unused_variables (ast : Type_check.typed_ast) : unit =
137+
let ignore_variable id = (string_of_id id).[0] = '_' || is_gen_loc (id_loc id) in
138+
let pexp_unused pat guard_opt exp =
139+
let used = IdSet.union exp (Option.value ~default:IdSet.empty guard_opt) in
140+
let unused = IdSet.diff pat used in
141+
IdSet.iter
142+
(fun id ->
143+
if not (ignore_variable id) then
144+
Reporting.warn "Unused variable" (id_loc id) "This variable is defined but never used."
145+
)
146+
unused;
147+
IdSet.diff used pat
148+
in
149+
(* Gather all the variables defined by a pattern *)
150+
let pat_alg env =
151+
{
152+
(Rewriter.pure_pat_alg IdSet.empty IdSet.union) with
153+
p_id = (fun id -> if Type_check.is_enum_member id env then IdSet.empty else IdSet.singleton id);
154+
p_vector_subrange = (fun (id, _, _) -> IdSet.singleton id);
155+
p_as = (fun (_, id) -> IdSet.singleton id);
156+
}
157+
in
158+
let alg env =
159+
{
160+
(Rewriter.pure_exp_alg IdSet.empty IdSet.union) with
161+
e_id = IdSet.singleton;
162+
pat_exp = (fun (pat, exp) -> pexp_unused pat None exp);
163+
pat_when = (fun (pat, guard, exp) -> pexp_unused pat (Some guard) exp);
164+
pat_alg = pat_alg env;
112165
}
113166
in
114-
List.iter (scan_exp_in_def (fun exp -> ignore (Rewriter.fold_exp alg exp))) ast.defs
167+
let module S = Scan (struct
168+
type t = Type_check.tannot
169+
let do_exp exp = ignore (Rewriter.fold_exp (alg (Type_check.env_of exp)) exp)
170+
let do_funcl_pexp =
171+
Some
172+
(fun pat guard_opt exp ->
173+
let env = Type_check.env_of_pat pat in
174+
let pat = Rewriter.fold_pat (pat_alg env) pat in
175+
let guard_opt = Option.map (Rewriter.fold_exp (alg env)) guard_opt in
176+
let exp = Rewriter.fold_exp (alg env) exp in
177+
ignore (pexp_unused pat guard_opt exp)
178+
)
179+
end) in
180+
List.iter S.in_def ast.defs

src/lib/lint.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@
4747
open Ast_defs
4848

4949
val warn_unmodified_variables : ('a, 'b) ast -> unit
50+
51+
val warn_unused_variables : Type_check.typed_ast -> unit

test/lean/map_tactics.expected.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def hex_bits_forwards (bv : (BitVec k_n)) : (Nat × String) :=
144144
((Sail.BitVec.length bv), (Int.toHex (BitVec.toNatInt bv)))
145145

146146
/-- Type quantifiers: k_n : Nat, k_n > 0 -/
147-
def hex_bits_forwards_matches (bv : (BitVec k_n)) : Bool :=
147+
def hex_bits_forwards_matches (x_0 : (BitVec k_n)) : Bool :=
148148
true
149149

150150
/-- Type quantifiers: tuple_0.1 : Nat, tuple_0.1 > 0 -/

test/pattern_completeness/run_tests.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from sailtest import *
1313

14+
update_expected = args.update_expected
15+
1416
sail_dir = get_sail_dir()
1517
sail = get_sail()
1618

@@ -28,9 +30,15 @@ def test_patterns(name):
2830
if tests[filename] == 0:
2931
step('\'{}\' --just-check {} 2> {}.error'.format(sail, filename, basename))
3032
if filename.startswith('warn'):
31-
step('diff {}.error {}.expect'.format(basename, basename))
33+
status = step_with_status('diff {}.error {}.expect'.format(basename, basename))
3234
else:
33-
step('diff {}.error no_error'.format(basename))
35+
status = step_with_status('diff {}.error no_error'.format(basename))
36+
if status != 0:
37+
if update_expected and filename.startswith('warn'):
38+
print(f'Overriding file {basename}.expected')
39+
step(f'\'{sail}\' --just-check {filename} 2> {basename}.expect')
40+
else:
41+
sys.exit(1)
3442
step('rm {}.error'.format(basename))
3543
print_ok(filename)
3644
sys.exit()

0 commit comments

Comments
 (0)