Skip to content

Commit 4519f54

Browse files
committed
Warn for unused variables
Modify typestate to check for unused variables and emit warnings where relevant. This exposed a (previously harmless) bug in collect_locals where outer functions had bit-vector entries for init constraints for variables declared in their inner nested functions. Fixing that required changing collect_locals to use visit instead of walk -- probably a good thing anyway.
1 parent add9031 commit 4519f54

File tree

7 files changed

+122
-69
lines changed

7 files changed

+122
-69
lines changed

src/comp/middle/tstate/annotate.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ fn collect_ids_local(&@local l, @mutable vec[node_id] rs) {
5151
vec::push(*rs, l.node.id);
5252
}
5353

54-
fn node_ids_in_fn(&_fn f, &span sp, &fn_ident i, node_id id,
55-
@mutable vec[node_id] rs) {
54+
fn node_ids_in_fn(&_fn f, &vec[ty_param] tps, &span sp, &fn_ident i,
55+
node_id id, @mutable vec[node_id] rs) {
5656
auto collect_ids = walk::default_visitor();
5757
collect_ids =
5858
rec(visit_expr_pre=bind collect_ids_expr(_, rs),
5959
visit_block_pre=bind collect_ids_block(_, rs),
6060
visit_stmt_pre=bind collect_ids_stmt(_, rs),
6161
visit_local_pre=bind collect_ids_local(_, rs) with collect_ids);
62-
walk::walk_fn(collect_ids, f, sp, i, id);
62+
walk::walk_fn(collect_ids, f, tps, sp, i, id);
6363
}
6464

6565
fn init_vecs(&crate_ctxt ccx, &vec[node_id] node_ids, uint len) {
@@ -69,24 +69,24 @@ fn init_vecs(&crate_ctxt ccx, &vec[node_id] node_ids, uint len) {
6969
}
7070
}
7171

72-
fn visit_fn(&crate_ctxt ccx, uint num_constraints, &_fn f, &span sp,
73-
&fn_ident i, node_id id) {
72+
fn visit_fn(&crate_ctxt ccx, uint num_constraints, &_fn f, &vec[ty_param] tps,
73+
&span sp, &fn_ident i, node_id id) {
7474
let @mutable vec[node_id] node_ids = @mutable [];
75-
node_ids_in_fn(f, sp, i, id, node_ids);
75+
node_ids_in_fn(f, tps, sp, i, id, node_ids);
7676
auto node_id_vec = *node_ids;
7777
init_vecs(ccx, node_id_vec, num_constraints);
7878
}
7979

80-
fn annotate_in_fn(&crate_ctxt ccx, &_fn f, &span sp, &fn_ident i,
81-
node_id id) {
80+
fn annotate_in_fn(&crate_ctxt ccx, &_fn f, &vec[ty_param] tps,
81+
&span sp, &fn_ident i, node_id id) {
8282
auto f_info = get_fn_info(ccx, id);
83-
visit_fn(ccx, num_constraints(f_info), f, sp, i, id);
83+
visit_fn(ccx, num_constraints(f_info), f, tps, sp, i, id);
8484
}
8585

8686
fn annotate_crate(&crate_ctxt ccx, &crate crate) {
8787
auto do_ann = walk::default_visitor();
8888
do_ann =
89-
rec(visit_fn_pre=bind annotate_in_fn(ccx, _, _, _, _) with do_ann);
89+
rec(visit_fn_pre=bind annotate_in_fn(ccx, _, _, _, _, _) with do_ann);
9090
walk::walk_crate(do_ann, crate);
9191
}
9292
//

src/comp/middle/tstate/auxiliary.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,15 @@ type norm_constraint = rec(uint bit_num, constr c);
217217

218218
type constr_map = @std::map::hashmap[node_id, constraint];
219219

220-
type fn_info = rec(constr_map constrs, uint num_constraints, controlflow cf);
220+
type fn_info = rec(constr_map constrs,
221+
uint num_constraints,
222+
controlflow cf,
223+
/* list, accumulated during pre/postcondition
224+
computation, of all local variables that may be
225+
used*/
226+
// Doesn't seem to work without the @ --
227+
// bug?
228+
@mutable vec[node_id] used_vars);
221229

222230

223231
/* mapping from node ID to typestate annotation */
@@ -770,6 +778,16 @@ fn args_mention(&vec[@constr_arg_use] args, &def_id v) -> bool {
770778
ret util::common::any[@constr_arg_use](bind mentions(v,_), args);
771779
}
772780

781+
fn use_var(&fn_ctxt fcx, &node_id v) {
782+
vec::push(*fcx.enclosing.used_vars, v);
783+
}
784+
785+
fn vec_contains(&@mutable vec[node_id] v, &node_id i) -> bool {
786+
for (node_id d in *v) {
787+
if (d == i) { ret true; }
788+
}
789+
ret false;
790+
}
773791
//
774792
// Local Variables:
775793
// mode: rust

src/comp/middle/tstate/ck.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,31 @@ import std::option;
4242
import std::option::t;
4343
import std::option::some;
4444
import std::option::none;
45-
import aux::fn_ctxt;
46-
import aux::crate_ctxt;
47-
import aux::new_crate_ctxt;
48-
import aux::expr_precond;
49-
import aux::expr_prestate;
50-
import aux::expr_poststate;
51-
import aux::stmt_poststate;
52-
import aux::stmt_to_ann;
53-
import aux::num_constraints;
54-
import aux::tritv_to_str;
55-
import aux::first_difference_string;
45+
import aux::*;
5646
import pretty::pprust::ty_to_str;
5747
import util::common::log_stmt_err;
58-
import aux::log_tritv_err;
5948
import bitvectors::promises;
6049
import annotate::annotate_crate;
6150
import collect_locals::mk_f_to_fn_info;
6251
import pre_post_conditions::fn_pre_post;
6352
import states::find_pre_post_state_fn;
6453

54+
fn check_unused_vars(&fn_ctxt fcx) {
55+
// FIXME: could be more efficient
56+
for (norm_constraint c in constraints(fcx)) {
57+
alt (c.c.node.c) {
58+
case (ninit(?v)) {
59+
if (!vec_contains(fcx.enclosing.used_vars,
60+
c.c.node.id)) {
61+
fcx.ccx.tcx.sess.span_warn(c.c.span,
62+
"Unused variable " + v);
63+
}
64+
}
65+
case (_) { /* ignore pred constraints */ }
66+
}
67+
}
68+
}
69+
6570
fn check_states_expr(&fn_ctxt fcx, &@expr e) {
6671
let precond prec = expr_precond(fcx.ccx, e);
6772
let prestate pres = expr_prestate(fcx.ccx, e);
@@ -119,8 +124,8 @@ fn check_states_stmt(&fn_ctxt fcx, &@stmt s) {
119124
}
120125
}
121126

122-
fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, node_id id,
123-
&span sp, &fn_ident i) {
127+
fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, &vec[ast::ty_param] tps,
128+
node_id id, &span sp, &fn_ident i) {
124129
/* Postorder traversal instead of pre is important
125130
because we want the smallest possible erroneous statement
126131
or expression. */
@@ -142,9 +147,9 @@ fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, node_id id,
142147
keep_going=bind kg(keepgoing)
143148
with walk::default_visitor());
144149

145-
walk::walk_fn(v, f, sp, i, id);
150+
walk::walk_fn(v, f, tps, sp, i, id);
146151

147-
/* Finally, check that the return value is initialized */
152+
/* Check that the return value is initialized */
148153
auto post = aux::block_poststate(fcx.ccx, f.body);
149154
let aux::constr_ ret_c = rec(id=fcx.id, c=aux::ninit(fcx.name));
150155
if (f.proto == ast::proto_fn && !promises(fcx, post, ret_c) &&
@@ -170,9 +175,13 @@ fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, node_id id,
170175
return to the caller");
171176
}
172177
}
178+
179+
/* Finally, check for unused variables */
180+
check_unused_vars(fcx);
173181
}
174182

175-
fn check_fn_states(&fn_ctxt fcx, &_fn f, node_id id, &span sp, &fn_ident i) {
183+
fn check_fn_states(&fn_ctxt fcx, &_fn f, &vec[ast::ty_param] tps,
184+
node_id id, &span sp, &fn_ident i) {
176185
/* Compute the pre- and post-states for this function */
177186

178187
// Fixpoint iteration
@@ -181,17 +190,18 @@ fn check_fn_states(&fn_ctxt fcx, &_fn f, node_id id, &span sp, &fn_ident i) {
181190
/* Now compare each expr's pre-state to its precondition
182191
and post-state to its postcondition */
183192

184-
check_states_against_conditions(fcx, f, id, sp, i);
193+
check_states_against_conditions(fcx, f, tps, id, sp, i);
185194
}
186195

187-
fn fn_states(&crate_ctxt ccx, &_fn f, &span sp, &fn_ident i, node_id id) {
196+
fn fn_states(&crate_ctxt ccx, &_fn f, &vec[ast::ty_param] tps,
197+
&span sp, &fn_ident i, node_id id) {
188198
/* Look up the var-to-bit-num map for this function */
189199

190200
assert (ccx.fm.contains_key(id));
191201
auto f_info = ccx.fm.get(id);
192202
auto name = option::from_maybe("anon", i);
193203
auto fcx = rec(enclosing=f_info, id=id, name=name, ccx=ccx);
194-
check_fn_states(fcx, f, id, sp, i);
204+
check_fn_states(fcx, f, tps, id, sp, i);
195205
}
196206

197207
fn check_crate(ty::ctxt cx, @crate crate) {
@@ -206,15 +216,16 @@ fn check_crate(ty::ctxt cx, @crate crate) {
206216

207217
auto do_pre_post = walk::default_visitor();
208218
do_pre_post =
209-
rec(visit_fn_post=bind fn_pre_post(ccx, _, _, _, _)
219+
rec(visit_fn_post=bind fn_pre_post(ccx, _, _, _, _, _)
210220
with do_pre_post);
211221
walk::walk_crate(do_pre_post, *crate);
212222
/* Check the pre- and postcondition against the pre- and poststate
213223
for every expression */
214224

215225
auto do_states = walk::default_visitor();
216226
do_states =
217-
rec(visit_fn_post=bind fn_states(ccx, _, _, _, _) with do_states);
227+
rec(visit_fn_post=bind fn_states(ccx, _, _, _, _, _)
228+
with do_states);
218229
walk::walk_crate(do_states, *crate);
219230
}
220231
//

src/comp/middle/tstate/collect_locals.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ import util::common::respan;
2828

2929
type ctxt = rec(@mutable vec[aux::constr] cs, ty::ctxt tcx);
3030

31-
fn collect_local(&ctxt cx, &@local loc) {
31+
fn collect_local(&@local loc, &ctxt cx, &visit::vt[ctxt] v) {
3232
log "collect_local: pushing " + loc.node.ident;
3333
vec::push(*cx.cs,
3434
respan(loc.span, rec(id=loc.node.id, c=ninit(loc.node.ident))));
35+
visit::visit_local(loc, cx, v);
3536
}
3637

37-
fn collect_pred(&ctxt cx, &@expr e) {
38+
fn collect_pred(&@expr e, &ctxt cx, &visit::vt[ctxt] v) {
3839
alt (e.node) {
3940
case (expr_check(_, ?ch)) {
4041
vec::push(*cx.cs, expr_to_constr(cx.tcx, ch));
@@ -55,16 +56,25 @@ fn collect_pred(&ctxt cx, &@expr e) {
5556
}
5657
case (_) { }
5758
}
59+
// visit subexpressions
60+
visit::visit_expr(e, cx, v);
5861
}
5962

60-
fn find_locals(&ty::ctxt tcx, &_fn f, &span sp, &fn_ident i, node_id id)
63+
fn do_nothing(&@item i, &ctxt ignore1, &visit::vt[ctxt] ignore) {
64+
}
65+
66+
fn find_locals(&ty::ctxt tcx, &_fn f, &vec[ast::ty_param] tps,
67+
&span sp, &fn_ident i, node_id id)
6168
-> ctxt {
6269
let ctxt cx = rec(cs=@mutable vec::alloc(0u), tcx=tcx);
63-
auto visitor = walk::default_visitor();
70+
auto visitor = visit::default_visitor[ctxt]();
71+
6472
visitor =
65-
rec(visit_local_pre=bind collect_local(cx, _),
66-
visit_expr_pre=bind collect_pred(cx, _) with visitor);
67-
walk_fn(visitor, f, sp, i, id);
73+
@rec(visit_local=collect_local,
74+
visit_expr=collect_pred,
75+
visit_item=do_nothing
76+
with *visitor);
77+
visit::visit_fn(f, tps, sp, i, id, cx, visit::vtor(visitor));
6878
ret cx;
6979
}
7080

@@ -104,15 +114,16 @@ fn add_constraint(&ty::ctxt tcx, aux::constr c, uint next, constr_map tbl) ->
104114

105115
/* builds a table mapping each local var defined in f
106116
to a bit number in the precondition/postcondition vectors */
107-
fn mk_fn_info(&crate_ctxt ccx, &_fn f, &span f_sp, &fn_ident f_name,
117+
fn mk_fn_info(&crate_ctxt ccx, &_fn f, &vec[ast::ty_param] tp,
118+
&span f_sp, &fn_ident f_name,
108119
node_id id) {
109120
auto res_map = @new_int_hash[constraint]();
110121
let uint next = 0u;
111122
let vec[arg] f_args = f.decl.inputs;
112123
/* ignore args, which we know are initialized;
113124
just collect locally declared vars */
114125

115-
let ctxt cx = find_locals(ccx.tcx, f, f_sp, f_name, id);
126+
let ctxt cx = find_locals(ccx.tcx, f, tp, f_sp, f_name, id);
116127
/* now we have to add bit nums for both the constraints
117128
and the variables... */
118129

@@ -125,10 +136,12 @@ fn mk_fn_info(&crate_ctxt ccx, &_fn f, &span f_sp, &fn_ident f_name,
125136
auto name = fn_ident_to_string(id, f_name);
126137
add_constraint(cx.tcx, respan(f_sp, rec(id=id, c=ninit(name))), next,
127138
res_map);
139+
let @mutable vec[node_id] v = @mutable [];
128140
auto rslt =
129141
rec(constrs=res_map,
130142
num_constraints=vec::len(*cx.cs) + 1u,
131-
cf=f.decl.cf);
143+
cf=f.decl.cf,
144+
used_vars=v);
132145
ccx.fm.insert(id, rslt);
133146
log name + " has " + uistr(num_constraints(rslt)) + " constraints";
134147
}
@@ -140,7 +153,7 @@ fn mk_fn_info(&crate_ctxt ccx, &_fn f, &span f_sp, &fn_ident f_name,
140153
fn mk_f_to_fn_info(&crate_ctxt ccx, @crate c) {
141154
let ast_visitor vars_visitor = walk::default_visitor();
142155
vars_visitor =
143-
rec(visit_fn_pre=bind mk_fn_info(ccx, _, _, _, _)
156+
rec(visit_fn_pre=bind mk_fn_info(ccx, _, _, _, _, _)
144157
with vars_visitor);
145158
walk_crate(vars_visitor, *c);
146159
}

src/comp/middle/tstate/pre_post_conditions.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import aux::substitute_constr_args;
5555
import aux::ninit;
5656
import aux::npred;
5757
import aux::path_to_ident;
58+
import aux::use_var;
5859
import bitvectors::bit_num;
5960
import bitvectors::promises;
6061
import bitvectors::seq_preconds;
@@ -74,10 +75,12 @@ import util::common::elt_exprs;
7475
import util::common::field_exprs;
7576
import util::common::has_nonlocal_exits;
7677
import util::common::log_stmt;
78+
import util::common::log_stmt_err;
7779
import util::common::log_expr_err;
7880
import util::common::log_block_err;
7981
import util::common::log_block;
8082
import util::common::span;
83+
import util::common::istr;
8184
import pretty::ppaux::fn_ident_to_string;
8285

8386
fn find_pre_post_mod(&_mod m) -> _mod {
@@ -109,11 +112,12 @@ fn find_pre_post_item(&crate_ctxt ccx, &item i) {
109112
alt (i.node) {
110113
case (item_const(_, ?e)) {
111114
// make a fake fcx
112-
115+
let @mutable vec[node_id] v = @mutable [];
113116
auto fake_fcx =
114117
rec(enclosing=rec(constrs=@new_int_hash[constraint](),
115118
num_constraints=0u,
116-
cf=return),
119+
cf=return,
120+
used_vars=v),
117121
id=0,
118122
name="",
119123
ccx=ccx);
@@ -323,6 +327,7 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
323327
bit_num(fcx,
324328
rec(id=d_id._1,
325329
c=ninit(path_to_ident(fcx.ccx.tcx, p))));
330+
use_var(fcx, d_id._1);
326331
require_and_preserve(i, rslt);
327332
}
328333
case (_) {/* nothing to check */ }
@@ -686,6 +691,9 @@ fn find_pre_post_block(&fn_ctxt fcx, block b) {
686691
}
687692

688693
fn find_pre_post_fn(&fn_ctxt fcx, &_fn f) {
694+
// hack
695+
use_var(fcx, fcx.id);
696+
689697
find_pre_post_block(fcx, f.body);
690698

691699
// Treat the tail expression as a return statement
@@ -697,8 +705,8 @@ fn find_pre_post_fn(&fn_ctxt fcx, &_fn f) {
697705
}
698706
}
699707

700-
fn fn_pre_post(crate_ctxt ccx, &_fn f, &span sp, &fn_ident i,
701-
node_id id) {
708+
fn fn_pre_post(crate_ctxt ccx, &_fn f, &vec[ty_param] tps,
709+
&span sp, &fn_ident i, node_id id) {
702710
assert (ccx.fm.contains_key(id));
703711
auto fcx = rec(enclosing=ccx.fm.get(id), id=id,
704712
name=fn_ident_to_string(id, i), ccx=ccx);

src/comp/middle/typeck.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,21 +1025,21 @@ mod writeback {
10251025
fn visit_item_post(@mutable bool ignore, &@ast::item item) {
10261026
*ignore = false;
10271027
}
1028-
fn visit_fn_pre(@mutable bool ignore, &ast::_fn f, &span sp,
1029-
&ast::fn_ident i, ast::node_id d) {
1028+
fn visit_fn_pre(@mutable bool ignore, &ast::_fn f, &vec[ast::ty_param] tps,
1029+
&span sp, &ast::fn_ident i, ast::node_id d) {
10301030
*ignore = true;
10311031
}
1032-
fn visit_fn_post(@mutable bool ignore, &ast::_fn f, &span sp,
1033-
&ast::fn_ident i, ast::node_id d) {
1032+
fn visit_fn_post(@mutable bool ignore, &ast::_fn f, &vec[ast::ty_param] tps,
1033+
&span sp, &ast::fn_ident i, ast::node_id d) {
10341034
*ignore = false;
10351035
}
10361036
fn keep_going(@mutable bool ignore) -> bool { ret !*ignore; }
10371037
auto visit =
10381038
rec(keep_going=bind keep_going(ignore),
10391039
visit_item_pre=bind visit_item_pre(ignore, _),
10401040
visit_item_post=bind visit_item_post(ignore, _),
1041-
visit_fn_pre=bind visit_fn_pre(ignore, _, _, _, _),
1042-
visit_fn_post=bind visit_fn_post(ignore, _, _, _, _),
1041+
visit_fn_pre=bind visit_fn_pre(ignore, _, _, _, _, _),
1042+
visit_fn_post=bind visit_fn_post(ignore, _, _, _, _, _),
10431043
visit_stmt_pre=bind visit_stmt_pre(fcx, _),
10441044
visit_expr_pre=bind visit_expr_pre(fcx, _),
10451045
visit_block_pre=bind visit_block_pre(fcx, _),

0 commit comments

Comments
 (0)