Skip to content

Commit d0881e1

Browse files
committed
Warn unused trait imports
1 parent 4acdc52 commit d0881e1

File tree

9 files changed

+109
-6
lines changed

9 files changed

+109
-6
lines changed

src/librustc/middle/ty/context.rs

+8
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub struct ctxt<'tcx> {
271271

272272
pub map: ast_map::Map<'tcx>,
273273
pub freevars: RefCell<FreevarMap>,
274+
pub maybe_unused_trait_imports: NodeSet,
274275
pub tcache: RefCell<DefIdMap<ty::TypeScheme<'tcx>>>,
275276
pub rcache: RefCell<FnvHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
276277
pub tc_cache: RefCell<FnvHashMap<Ty<'tcx>, ty::contents::TypeContents>>,
@@ -306,6 +307,10 @@ pub struct ctxt<'tcx> {
306307
/// about.
307308
pub used_mut_nodes: RefCell<NodeSet>,
308309

310+
/// Set of trait imports actually used in method resolution.
311+
/// This is used for warning unused imports.
312+
pub used_trait_imports: RefCell<NodeSet>,
313+
309314
/// The set of external nominal types whose implementations have been read.
310315
/// This is used for lazy resolution of methods.
311316
pub populated_external_types: RefCell<DefIdSet>,
@@ -475,6 +480,7 @@ impl<'tcx> ctxt<'tcx> {
475480
named_region_map: resolve_lifetime::NamedRegionMap,
476481
map: ast_map::Map<'tcx>,
477482
freevars: FreevarMap,
483+
maybe_unused_trait_imports: NodeSet,
478484
region_maps: RegionMaps,
479485
lang_items: middle::lang_items::LanguageItems,
480486
stability: stability::Index<'tcx>,
@@ -508,6 +514,7 @@ impl<'tcx> ctxt<'tcx> {
508514
fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()),
509515
map: map,
510516
freevars: RefCell::new(freevars),
517+
maybe_unused_trait_imports: maybe_unused_trait_imports,
511518
tcache: RefCell::new(DefIdMap()),
512519
rcache: RefCell::new(FnvHashMap()),
513520
tc_cache: RefCell::new(FnvHashMap()),
@@ -522,6 +529,7 @@ impl<'tcx> ctxt<'tcx> {
522529
impl_items: RefCell::new(DefIdMap()),
523530
used_unsafe: RefCell::new(NodeSet()),
524531
used_mut_nodes: RefCell::new(NodeSet()),
532+
used_trait_imports: RefCell::new(NodeSet()),
525533
populated_external_types: RefCell::new(DefIdSet()),
526534
populated_external_primitive_impls: RefCell::new(DefIdSet()),
527535
extern_const_statics: RefCell::new(DefIdMap()),

src/librustc_driver/driver.rs

+2
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
703703
let resolve::CrateMap {
704704
def_map,
705705
freevars,
706+
maybe_unused_trait_imports,
706707
export_map,
707708
trait_map,
708709
external_exports,
@@ -741,6 +742,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
741742
named_region_map,
742743
ast_map,
743744
freevars,
745+
maybe_unused_trait_imports,
744746
region_map,
745747
lang_items,
746748
stability::Index::new(krate),

src/librustc_driver/test.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ fn test_env<F>(source_string: &str,
128128

129129
// run just enough stuff to build a tcx:
130130
let lang_items = lang_items::collect_language_items(&sess, &ast_map);
131-
let resolve::CrateMap { def_map, freevars, .. } =
131+
let resolve::CrateMap { def_map, freevars, maybe_unused_trait_imports, .. } =
132132
resolve::resolve_crate(&sess, &ast_map, resolve::MakeGlobMap::No);
133133
let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map.borrow());
134134
let region_map = region::resolve_crate(&sess, krate);
@@ -138,6 +138,7 @@ fn test_env<F>(source_string: &str,
138138
named_region_map,
139139
ast_map,
140140
freevars,
141+
maybe_unused_trait_imports,
141142
region_map,
142143
lang_items,
143144
stability::Index::new(krate),

src/librustc_resolve/check_unused.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// resolve data structures and because it finalises the privacy information for
1717
// `use` directives.
1818
//
19+
// Unused trait imports can't be checked until the method resolution. We save
20+
// candidates here, and do the actual check in librustc_typeck/check_unused.rs.
1921

2022
use std::ops::{Deref, DerefMut};
2123

@@ -104,13 +106,21 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
104106
};
105107
}
106108

107-
fn check_import(&self, id: ast::NodeId, span: Span) {
109+
fn check_import(&mut self, id: ast::NodeId, span: Span) {
108110
if !self.used_imports.contains(&(id, TypeNS)) &&
109111
!self.used_imports.contains(&(id, ValueNS)) {
112+
if self.maybe_unused_trait_imports.contains(&id) {
113+
// Check later.
114+
return;
115+
}
110116
self.session.add_lint(lint::builtin::UNUSED_IMPORTS,
111117
id,
112118
span,
113119
"unused import".to_string());
120+
} else {
121+
// This trait import is definitely used, in a way other than
122+
// method resolution.
123+
self.maybe_unused_trait_imports.remove(&id);
114124
}
115125
}
116126
}

src/librustc_resolve/lib.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ use rustc::middle::pat_util::pat_bindings;
6161
use rustc::middle::privacy::*;
6262
use rustc::middle::subst::{ParamSpace, FnSpace, TypeSpace};
6363
use rustc::middle::ty::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
64-
use rustc::util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
64+
use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap};
6565

6666
use syntax::ast;
6767
use syntax::ast::{CRATE_NODE_ID, Ident, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64};
@@ -1141,6 +1141,7 @@ pub struct Resolver<'a, 'tcx: 'a> {
11411141

11421142
used_imports: HashSet<(NodeId, Namespace)>,
11431143
used_crates: HashSet<CrateNum>,
1144+
maybe_unused_trait_imports: NodeSet,
11441145

11451146
// Callback function for intercepting walks
11461147
callback: Option<Box<Fn(hir_map::Node, &mut bool) -> bool>>,
@@ -1192,14 +1193,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
11921193
freevars_seen: NodeMap(),
11931194
export_map: NodeMap(),
11941195
trait_map: NodeMap(),
1195-
used_imports: HashSet::new(),
1196-
used_crates: HashSet::new(),
11971196
external_exports: DefIdSet(),
11981197

11991198
emit_errors: true,
12001199
make_glob_map: make_glob_map == MakeGlobMap::Yes,
12011200
glob_map: HashMap::new(),
12021201

1202+
used_imports: HashSet::new(),
1203+
used_crates: HashSet::new(),
1204+
maybe_unused_trait_imports: NodeSet(),
1205+
12031206
callback: None,
12041207
resolved: false,
12051208
}
@@ -3669,7 +3672,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
36693672
if self.trait_item_map.contains_key(&(name, did)) {
36703673
let id = import.type_ns.id;
36713674
add_trait_info(&mut found_traits, did, Some(id), name);
3672-
self.used_imports.insert((id, TypeNS));
3675+
self.maybe_unused_trait_imports.insert(id);
36733676
let trait_name = self.get_trait_name(did);
36743677
self.record_import_use(id, trait_name);
36753678
if let Some(DefId{krate: kid, ..}) = target.target_module.def_id() {
@@ -3820,6 +3823,7 @@ fn module_to_string(module: &Module) -> String {
38203823
pub struct CrateMap {
38213824
pub def_map: RefCell<DefMap>,
38223825
pub freevars: FreevarMap,
3826+
pub maybe_unused_trait_imports: NodeSet,
38233827
pub export_map: ExportMap,
38243828
pub trait_map: TraitMap,
38253829
pub external_exports: ExternalExports,
@@ -3848,6 +3852,7 @@ pub fn resolve_crate<'a, 'tcx>(session: &'a Session,
38483852
CrateMap {
38493853
def_map: resolver.def_map,
38503854
freevars: resolver.freevars,
3855+
maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
38513856
export_map: resolver.export_map,
38523857
trait_map: resolver.trait_map,
38533858
external_exports: resolver.external_exports,

src/librustc_typeck/check/method/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
125125
let mode = probe::Mode::MethodCall;
126126
let self_ty = fcx.infcx().resolve_type_vars_if_possible(&self_ty);
127127
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, call_expr.id));
128+
129+
if let Some(import_id) = pick.import_id {
130+
fcx.tcx().used_trait_imports.borrow_mut().insert(import_id);
131+
}
132+
128133
Ok(confirm::confirm(fcx, span, self_expr, call_expr, self_ty, pick, supplied_method_types))
129134
}
130135

@@ -337,6 +342,11 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
337342
{
338343
let mode = probe::Mode::Path;
339344
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id));
345+
346+
if let Some(import_id) = pick.import_id {
347+
fcx.tcx().used_trait_imports.borrow_mut().insert(import_id);
348+
}
349+
340350
let def_id = pick.item.def_id();
341351
let mut lp = LastMod(AllPublic);
342352
if let probe::InherentImplPick = pick.kind {

src/librustc_typeck/check_unused.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use lint;
12+
use middle::ty;
13+
14+
use syntax::ast;
15+
use syntax::codemap::{Span, DUMMY_SP};
16+
17+
use rustc_front::hir;
18+
use rustc_front::intravisit::Visitor;
19+
20+
struct UnusedTraitImportVisitor<'a, 'tcx: 'a> {
21+
tcx: &'a ty::ctxt<'tcx>,
22+
}
23+
24+
impl<'a, 'tcx> UnusedTraitImportVisitor<'a, 'tcx> {
25+
fn check_import(&self, id: ast::NodeId, span: Span) {
26+
if !self.tcx.maybe_unused_trait_imports.contains(&id) {
27+
return;
28+
}
29+
if self.tcx.used_trait_imports.borrow().contains(&id) {
30+
return;
31+
}
32+
self.tcx.sess.add_lint(lint::builtin::UNUSED_IMPORTS,
33+
id,
34+
span,
35+
"unused import".to_string());
36+
}
37+
}
38+
39+
impl<'a, 'tcx, 'v> Visitor<'v> for UnusedTraitImportVisitor<'a, 'tcx> {
40+
fn visit_item(&mut self, item: &hir::Item) {
41+
if item.vis == hir::Public || item.span == DUMMY_SP {
42+
return;
43+
}
44+
if let hir::ItemUse(ref path) = item.node {
45+
match path.node {
46+
hir::ViewPathSimple(..) | hir::ViewPathGlob(..) => {
47+
self.check_import(item.id, path.span);
48+
}
49+
hir::ViewPathList(_, ref path_list) => {
50+
for path_item in path_list {
51+
self.check_import(path_item.node.id(), path_item.span);
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
58+
59+
pub fn check_crate(tcx: &ty::ctxt) {
60+
let mut visitor = UnusedTraitImportVisitor { tcx: tcx };
61+
tcx.map.krate().visit_all_items(&mut visitor);
62+
}

src/librustc_typeck/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ use std::cell::RefCell;
120120
pub mod diagnostics;
121121

122122
pub mod check;
123+
pub mod check_unused;
123124
mod rscope;
124125
mod astconv;
125126
pub mod collect;
@@ -361,6 +362,7 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) {
361362
time(time_passes, "wf checking (new)", ||
362363
check::check_wf_new(&ccx));
363364

365+
check_unused::check_crate(tcx);
364366
check_for_entry_fn(&ccx);
365367
tcx.sess.abort_if_errors();
366368
}

src/test/compile-fail/lint-unused-imports.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use test::A; //~ ERROR unused import
2424
// Be sure that if we just bring some methods into scope that they're also
2525
// counted as being used.
2626
use test::B;
27+
// But only when actually used: do not get confused by the method with the same name.
28+
use test::B2; //~ ERROR unused import
2729

2830
// Make sure this import is warned about when at least one of its imported names
2931
// is unused
@@ -37,6 +39,7 @@ mod test2 {
3739
mod test {
3840
pub trait A { fn a(&self) {} }
3941
pub trait B { fn b(&self) {} }
42+
pub trait B2 { fn b(&self) {} }
4043
pub struct C;
4144
impl A for C {}
4245
impl B for C {}

0 commit comments

Comments
 (0)