Skip to content

Commit 4bfa3c6

Browse files
committed
Add a lint mode for unused unsafe blocks/functions
1 parent 8c2e5cc commit 4bfa3c6

File tree

6 files changed

+224
-70
lines changed

6 files changed

+224
-70
lines changed

src/librustc/middle/borrowck/check_loans.rs

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,26 @@ use util::ppaux::ty_to_str;
3333

3434
use core::hashmap::HashSet;
3535
use core::uint;
36+
use core::util::with;
3637
use syntax::ast::m_mutbl;
3738
use syntax::ast;
3839
use syntax::ast_util;
3940
use syntax::codemap::span;
4041
use syntax::print::pprust;
4142
use syntax::visit;
4243

44+
struct PurityState {
45+
def: ast::node_id,
46+
purity: ast::purity
47+
}
48+
4349
struct CheckLoanCtxt {
4450
bccx: @BorrowckCtxt,
4551
req_maps: ReqMaps,
4652

4753
reported: HashSet<ast::node_id>,
4854

49-
declared_purity: @mut ast::purity,
55+
declared_purity: @mut PurityState,
5056
fn_args: @mut @~[ast::node_id]
5157
}
5258

@@ -62,14 +68,25 @@ enum purity_cause {
6268
pc_cmt(bckerr)
6369
}
6470

71+
// if we're not pure, why?
72+
#[deriving(Eq)]
73+
enum impurity_cause {
74+
// some surrounding block was marked as 'unsafe'
75+
pc_unsafe,
76+
77+
// nothing was unsafe, and nothing was pure
78+
pc_default,
79+
}
80+
6581
pub fn check_loans(bccx: @BorrowckCtxt,
6682
+req_maps: ReqMaps,
6783
crate: @ast::crate) {
6884
let clcx = @mut CheckLoanCtxt {
6985
bccx: bccx,
7086
req_maps: req_maps,
7187
reported: HashSet::new(),
72-
declared_purity: @mut ast::impure_fn,
88+
declared_purity: @mut PurityState { purity: ast::impure_fn,
89+
def: 0 },
7390
fn_args: @mut @~[]
7491
};
7592
let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
@@ -106,16 +123,18 @@ pub impl assignment_type {
106123
pub impl CheckLoanCtxt {
107124
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
108125
109-
fn purity(&mut self, scope_id: ast::node_id) -> Option<purity_cause> {
110-
let default_purity = match *self.declared_purity {
126+
fn purity(&mut self, scope_id: ast::node_id)
127+
-> Either<purity_cause, impurity_cause>
128+
{
129+
let default_purity = match self.declared_purity.purity {
111130
// an unsafe declaration overrides all
112-
ast::unsafe_fn => return None,
131+
ast::unsafe_fn => return Right(pc_unsafe),
113132
114133
// otherwise, remember what was declared as the
115134
// default, but we must scan for requirements
116135
// imposed by the borrow check
117-
ast::pure_fn => Some(pc_pure_fn),
118-
ast::extern_fn | ast::impure_fn => None
136+
ast::pure_fn => Left(pc_pure_fn),
137+
ast::extern_fn | ast::impure_fn => Right(pc_default)
119138
};
120139
121140
// scan to see if this scope or any enclosing scope requires
@@ -125,7 +144,7 @@ pub impl CheckLoanCtxt {
125144
loop {
126145
match self.req_maps.pure_map.find(&scope_id) {
127146
None => (),
128-
Some(e) => return Some(pc_cmt(*e))
147+
Some(e) => return Left(pc_cmt(*e))
129148
}
130149
131150
match self.tcx().region_maps.opt_encl_scope(scope_id) {
@@ -171,7 +190,7 @@ pub impl CheckLoanCtxt {
171190
// overloaded operators the callee has an id but no expr.
172191
// annoying.
173192
fn check_pure_callee_or_arg(&mut self,
174-
pc: purity_cause,
193+
pc: Either<purity_cause, impurity_cause>,
175194
opt_expr: Option<@ast::expr>,
176195
callee_id: ast::node_id,
177196
callee_span: span) {
@@ -196,7 +215,7 @@ pub impl CheckLoanCtxt {
196215
match opt_expr {
197216
Some(expr) => {
198217
match expr.node {
199-
ast::expr_path(_) if pc == pc_pure_fn => {
218+
ast::expr_path(_) if pc == Left(pc_pure_fn) => {
200219
let def = *self.tcx().def_map.get(&expr.id);
201220
let did = ast_util::def_id_of_def(def);
202221
let is_fn_arg =
@@ -361,10 +380,10 @@ pub impl CheckLoanCtxt {
361380
// if this is a pure function, only loan-able state can be
362381
// assigned, because it is uniquely tied to this function and
363382
// is not visible from the outside
364-
match self.purity(ex.id) {
365-
None => (),
366-
Some(pc_cmt(_)) => {
367-
let purity = self.purity(ex.id).get();
383+
let purity = self.purity(ex.id);
384+
match purity {
385+
Right(_) => (),
386+
Left(pc_cmt(_)) => {
368387
// Subtle: Issue #3162. If we are enforcing purity
369388
// because there is a reference to aliasable, mutable data
370389
// that we require to be immutable, we can't allow writes
@@ -376,10 +395,10 @@ pub impl CheckLoanCtxt {
376395
ex.span,
377396
at.ing_form(self.bccx.cmt_to_str(cmt)));
378397
}
379-
Some(pc_pure_fn) => {
398+
Left(pc_pure_fn) => {
380399
if cmt.lp.is_none() {
381400
self.report_purity_error(
382-
pc_pure_fn, ex.span,
401+
purity, ex.span,
383402
at.ing_form(self.bccx.cmt_to_str(cmt)));
384403
}
385404
}
@@ -462,14 +481,23 @@ pub impl CheckLoanCtxt {
462481
}
463482
}
464483

465-
fn report_purity_error(&mut self, pc: purity_cause, sp: span, msg: ~str) {
484+
fn report_purity_error(&mut self, pc: Either<purity_cause, impurity_cause>,
485+
sp: span, msg: ~str) {
466486
match pc {
467-
pc_pure_fn => {
487+
Right(pc_default) => { fail!(~"pc_default should be filtered sooner") }
488+
Right(pc_unsafe) => {
489+
// this error was prevented by being marked as unsafe, so flag the
490+
// definition as having contributed to the validity of the program
491+
let def = self.declared_purity.def;
492+
debug!("flagging %? as a used unsafe source", def);
493+
self.tcx().used_unsafe.insert(def);
494+
}
495+
Left(pc_pure_fn) => {
468496
self.tcx().sess.span_err(
469497
sp,
470498
fmt!("%s prohibited in pure context", msg));
471499
}
472-
pc_cmt(ref e) => {
500+
Left(pc_cmt(ref e)) => {
473501
if self.reported.insert((*e).cmt.id) {
474502
self.tcx().sess.span_err(
475503
(*e).cmt.span,
@@ -556,16 +584,32 @@ pub impl CheckLoanCtxt {
556584
callee_id: ast::node_id,
557585
callee_span: span,
558586
args: &[@ast::expr]) {
559-
match self.purity(expr.id) {
560-
None => {}
561-
Some(ref pc) => {
562-
self.check_pure_callee_or_arg(
563-
(*pc), callee, callee_id, callee_span);
564-
for args.each |arg| {
565-
self.check_pure_callee_or_arg(
566-
(*pc), Some(*arg), arg.id, arg.span);
587+
let pc = self.purity(expr.id);
588+
match pc {
589+
// no purity, no need to check for anything
590+
Right(pc_default) => return,
591+
592+
// some form of purity, definitely need to check
593+
Left(_) => (),
594+
595+
// Unsafe trumped. To see if the unsafe is necessary, see what the
596+
// purity would have been without a trump, and if it's some form
597+
// of purity then we need to go ahead with the check
598+
Right(pc_unsafe) => {
599+
match do with(&mut self.declared_purity.purity,
600+
ast::impure_fn) { self.purity(expr.id) } {
601+
Right(pc_unsafe) => fail!(~"unsafe can't trump twice"),
602+
Right(pc_default) => return,
603+
Left(_) => ()
604+
}
567605
}
568-
}
606+
607+
}
608+
self.check_pure_callee_or_arg(
609+
pc, callee, callee_id, callee_span);
610+
for args.each |arg| {
611+
self.check_pure_callee_or_arg(
612+
pc, Some(*arg), arg.id, arg.span);
569613
}
570614
}
571615
}
@@ -580,27 +624,32 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
580624
let is_stack_closure = self.is_stack_closure(id);
581625
let fty = ty::node_id_to_type(self.tcx(), id);
582626
583-
let declared_purity;
627+
let declared_purity, src;
584628
match *fk {
585629
visit::fk_item_fn(*) | visit::fk_method(*) |
586630
visit::fk_dtor(*) => {
587631
declared_purity = ty::ty_fn_purity(fty);
632+
src = id;
588633
}
589634
590635
visit::fk_anon(*) | visit::fk_fn_block(*) => {
591636
let fty_sigil = ty::ty_closure_sigil(fty);
592637
check_moves_from_captured_variables(self, id, fty_sigil);
593-
declared_purity = ty::determine_inherited_purity(
594-
*self.declared_purity,
595-
ty::ty_fn_purity(fty),
638+
let pair = ty::determine_inherited_purity(
639+
(self.declared_purity.purity, self.declared_purity.def),
640+
(ty::ty_fn_purity(fty), id),
596641
fty_sigil);
642+
declared_purity = pair.first();
643+
src = pair.second();
597644
}
598645
}
599646
600647
debug!("purity on entry=%?", copy self.declared_purity);
601648
do save_and_restore_managed(self.declared_purity) {
602649
do save_and_restore_managed(self.fn_args) {
603-
*self.declared_purity = declared_purity;
650+
self.declared_purity = @mut PurityState {
651+
purity: declared_purity, def: src
652+
};
604653
605654
match *fk {
606655
visit::fk_anon(*) |
@@ -754,7 +803,10 @@ fn check_loans_in_block(blk: &ast::blk,
754803
ast::default_blk => {
755804
}
756805
ast::unsafe_blk => {
757-
*self.declared_purity = ast::unsafe_fn;
806+
*self.declared_purity = PurityState {
807+
purity: ast::unsafe_fn,
808+
def: blk.node.id,
809+
};
758810
}
759811
}
760812

src/librustc/middle/lint.rs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub enum lint {
7575
default_methods,
7676
deprecated_mutable_fields,
7777
deprecated_drop,
78+
unused_unsafe,
7879
foreign_mode,
7980

8081
managed_heap_memory,
@@ -256,6 +257,13 @@ pub fn get_lint_dict() -> LintDict {
256257
default: deny
257258
}),
258259

260+
(~"unused_unsafe",
261+
LintSpec {
262+
lint: unused_unsafe,
263+
desc: "unnecessary use of an \"unsafe\" block or function",
264+
default: warn
265+
}),
266+
259267
(~"unused_variable",
260268
LintSpec {
261269
lint: unused_variable,
@@ -490,6 +498,7 @@ fn check_item(i: @ast::item, cx: ty::ctxt) {
490498
check_item_default_methods(cx, i);
491499
check_item_deprecated_mutable_fields(cx, i);
492500
check_item_deprecated_drop(cx, i);
501+
check_item_unused_unsafe(cx, i);
493502
}
494503

495504
// Take a visitor, and modify it so that it will not proceed past subitems.
@@ -923,19 +932,55 @@ fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
923932
}
924933
}
925934

935+
fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
936+
let visit_expr: @fn(@ast::expr) = |e| {
937+
match e.node {
938+
ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
939+
if !cx.used_unsafe.contains(&blk.node.id) {
940+
cx.sess.span_lint(unused_unsafe, blk.node.id, it.id,
941+
blk.span,
942+
~"unnecessary \"unsafe\" block");
943+
}
944+
}
945+
_ => ()
946+
}
947+
};
948+
949+
let visit = item_stopping_visitor(
950+
visit::mk_simple_visitor(@visit::SimpleVisitor {
951+
visit_expr: visit_expr,
952+
.. *visit::default_simple_visitor()
953+
}));
954+
visit::visit_item(it, (), visit);
955+
}
956+
926957
fn check_fn(tcx: ty::ctxt, fk: &visit::fn_kind, decl: &ast::fn_decl,
927958
_body: &ast::blk, span: span, id: ast::node_id) {
928959
debug!("lint check_fn fk=%? id=%?", fk, id);
929960

930-
// don't complain about blocks, since they tend to get their modes
931-
// specified from the outside
961+
// Check for an 'unsafe fn' which doesn't need to be unsafe
962+
match *fk {
963+
visit::fk_item_fn(_, _, ast::unsafe_fn, _) => {
964+
if !tcx.used_unsafe.contains(&id) {
965+
tcx.sess.span_lint(unused_unsafe, id, id, span,
966+
~"unnecessary \"unsafe\" function");
967+
}
968+
}
969+
_ => ()
970+
}
971+
972+
// Check for deprecated modes
932973
match *fk {
933-
visit::fk_fn_block(*) => { return; }
934-
_ => {}
974+
// don't complain about blocks, since they tend to get their modes
975+
// specified from the outside
976+
visit::fk_fn_block(*) => {}
977+
978+
_ => {
979+
let fn_ty = ty::node_id_to_type(tcx, id);
980+
check_fn_deprecated_modes(tcx, fn_ty, decl, span, id);
981+
}
935982
}
936983

937-
let fn_ty = ty::node_id_to_type(tcx, id);
938-
check_fn_deprecated_modes(tcx, fn_ty, decl, span, id);
939984
}
940985

941986
fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: &ast::fn_decl,

src/librustc/middle/resolve.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5212,17 +5212,11 @@ pub impl Resolver {
52125212
import_resolution.span != dummy_sp() &&
52135213
import_resolution.privacy != Public {
52145214
import_resolution.state.warned = true;
5215-
match self.unused_import_lint_level(module_) {
5216-
warn => {
5217-
self.session.span_warn(copy import_resolution.span,
5218-
~"unused import");
5219-
}
5220-
deny | forbid => {
5221-
self.session.span_err(copy import_resolution.span,
5222-
~"unused import");
5223-
}
5224-
allow => ()
5225-
}
5215+
let span = import_resolution.span;
5216+
self.session.span_lint_level(
5217+
self.unused_import_lint_level(module_),
5218+
span,
5219+
~"unused import");
52265220
}
52275221
}
52285222
}

0 commit comments

Comments
 (0)