Skip to content

Commit 24d8613

Browse files
committed
Warn unused trait imports
1 parent 7ad1900 commit 24d8613

File tree

13 files changed

+169
-25
lines changed

13 files changed

+169
-25
lines changed

src/librustc/dep_graph/dep_node.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub enum DepNode<D: Clone + Debug> {
5959
TypeckItemBody(D),
6060
Dropck,
6161
DropckImpl(D),
62+
UnusedTraitCheck,
6263
CheckConst(D),
6364
Privacy,
6465
IntrinsicCheck(D),
@@ -163,6 +164,7 @@ impl<D: Clone + Debug> DepNode<D> {
163164
CheckEntryFn => Some(CheckEntryFn),
164165
Variance => Some(Variance),
165166
Dropck => Some(Dropck),
167+
UnusedTraitCheck => Some(UnusedTraitCheck),
166168
Privacy => Some(Privacy),
167169
Reachability => Some(Reachability),
168170
DeadCheck => Some(DeadCheck),

src/librustc/hir/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,8 +1639,13 @@ pub type FreevarMap = NodeMap<Vec<Freevar>>;
16391639

16401640
pub type CaptureModeMap = NodeMap<CaptureClause>;
16411641

1642+
pub struct TraitCandidate {
1643+
pub def_id: DefId,
1644+
pub import_id: Option<NodeId>,
1645+
}
1646+
16421647
// Trait method resolution
1643-
pub type TraitMap = NodeMap<Vec<DefId>>;
1648+
pub type TraitMap = NodeMap<Vec<TraitCandidate>>;
16441649

16451650
// Map from the NodeId of a glob import to a list of items which are actually
16461651
// imported.

src/librustc/ty/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ pub struct TyCtxt<'tcx> {
289289
// scratch every time.
290290
pub freevars: RefCell<FreevarMap>,
291291

292+
pub maybe_unused_trait_imports: NodeSet,
293+
292294
// Records the type of every item.
293295
pub tcache: RefCell<DepTrackingMap<maps::Tcache<'tcx>>>,
294296

@@ -338,6 +340,10 @@ pub struct TyCtxt<'tcx> {
338340
/// about.
339341
pub used_mut_nodes: RefCell<NodeSet>,
340342

343+
/// Set of trait imports actually used in the method resolution.
344+
/// This is used for warning unused imports.
345+
pub used_trait_imports: RefCell<NodeSet>,
346+
341347
/// The set of external nominal types whose implementations have been read.
342348
/// This is used for lazy resolution of methods.
343349
pub populated_external_types: RefCell<DefIdSet>,
@@ -543,6 +549,7 @@ impl<'tcx> TyCtxt<'tcx> {
543549
named_region_map: resolve_lifetime::NamedRegionMap,
544550
map: ast_map::Map<'tcx>,
545551
freevars: FreevarMap,
552+
maybe_unused_trait_imports: NodeSet,
546553
region_maps: RegionMaps,
547554
lang_items: middle::lang_items::LanguageItems,
548555
stability: stability::Index<'tcx>,
@@ -581,6 +588,7 @@ impl<'tcx> TyCtxt<'tcx> {
581588
fulfilled_predicates: RefCell::new(fulfilled_predicates),
582589
map: map,
583590
freevars: RefCell::new(freevars),
591+
maybe_unused_trait_imports: maybe_unused_trait_imports,
584592
tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
585593
rcache: RefCell::new(FnvHashMap()),
586594
tc_cache: RefCell::new(FnvHashMap()),
@@ -595,6 +603,7 @@ impl<'tcx> TyCtxt<'tcx> {
595603
impl_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
596604
used_unsafe: RefCell::new(NodeSet()),
597605
used_mut_nodes: RefCell::new(NodeSet()),
606+
used_trait_imports: RefCell::new(NodeSet()),
598607
populated_external_types: RefCell::new(DefIdSet()),
599608
populated_external_primitive_impls: RefCell::new(DefIdSet()),
600609
extern_const_statics: RefCell::new(DefIdMap()),

src/librustc_driver/driver.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
795795
let resolve::CrateMap {
796796
def_map,
797797
freevars,
798+
maybe_unused_trait_imports,
798799
export_map,
799800
trait_map,
800801
glob_map,
@@ -844,6 +845,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
844845
named_region_map,
845846
hir_map,
846847
freevars,
848+
maybe_unused_trait_imports,
847849
region_map,
848850
lang_items,
849851
index,

src/librustc_driver/test.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ fn test_env<F>(source_string: &str,
132132

133133
// run just enough stuff to build a tcx:
134134
let lang_items = lang_items::collect_language_items(&sess, &ast_map);
135-
let resolve::CrateMap { def_map, freevars, .. } =
135+
let resolve::CrateMap { def_map, freevars, maybe_unused_trait_imports, .. } =
136136
resolve::resolve_crate(&sess, &ast_map, resolve::MakeGlobMap::No);
137137
let named_region_map = resolve_lifetime::krate(&sess, &ast_map, &def_map.borrow());
138138
let region_map = region::resolve_crate(&sess, &ast_map);
@@ -143,6 +143,7 @@ fn test_env<F>(source_string: &str,
143143
named_region_map.unwrap(),
144144
ast_map,
145145
freevars,
146+
maybe_unused_trait_imports,
146147
region_map,
147148
lang_items,
148149
index,

src/librustc_resolve/check_unused.rs

Lines changed: 10 additions & 0 deletions
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 acutal check in librustc_typeck/check_unused.rs.
1921

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

@@ -55,10 +57,18 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
5557
fn check_import(&mut self, id: ast::NodeId, span: Span) {
5658
if !self.used_imports.contains(&(id, TypeNS)) &&
5759
!self.used_imports.contains(&(id, ValueNS)) {
60+
if self.maybe_unused_trait_imports.contains(&id) {
61+
// Check later.
62+
return;
63+
}
5864
self.session.add_lint(lint::builtin::UNUSED_IMPORTS,
5965
id,
6066
span,
6167
"unused import".to_string());
68+
} else {
69+
// This trait import is definitely used, in a way other than
70+
// method resolution.
71+
self.maybe_unused_trait_imports.remove(&id);
6272
}
6373
}
6474
}

src/librustc_resolve/lib.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ use rustc::hir::def_id::DefId;
5252
use rustc::hir::pat_util::pat_bindings;
5353
use rustc::ty;
5454
use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace};
55-
use rustc::hir::{Freevar, FreevarMap, TraitMap, GlobMap};
56-
use rustc::util::nodemap::{NodeMap, FnvHashMap, FnvHashSet};
55+
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
56+
use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet};
5757

5858
use syntax::ast::{self, FloatTy};
5959
use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy};
@@ -1042,6 +1042,7 @@ pub struct Resolver<'a, 'tcx: 'a> {
10421042

10431043
used_imports: HashSet<(NodeId, Namespace)>,
10441044
used_crates: HashSet<CrateNum>,
1045+
maybe_unused_trait_imports: NodeSet,
10451046

10461047
privacy_errors: Vec<PrivacyError<'a>>,
10471048

@@ -1137,13 +1138,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
11371138
export_map: NodeMap(),
11381139
trait_map: NodeMap(),
11391140
module_map: module_map,
1140-
used_imports: HashSet::new(),
1141-
used_crates: HashSet::new(),
11421141

11431142
emit_errors: true,
11441143
make_glob_map: make_glob_map == MakeGlobMap::Yes,
11451144
glob_map: NodeMap(),
11461145

1146+
used_imports: HashSet::new(),
1147+
used_crates: HashSet::new(),
1148+
maybe_unused_trait_imports: NodeSet(),
1149+
11471150
privacy_errors: Vec::new(),
11481151

11491152
arenas: arenas,
@@ -1177,7 +1180,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
11771180
}
11781181

11791182
#[inline]
1180-
fn record_use(&mut self, name: Name, ns: Namespace, binding: &'a NameBinding<'a>) {
1183+
fn record_use(&mut self, name: Name, binding: &'a NameBinding<'a>) {
11811184
// track extern crates for unused_extern_crate lint
11821185
if let Some(DefId { krate, .. }) = binding.module().and_then(ModuleS::def_id) {
11831186
self.used_crates.insert(krate);
@@ -1189,7 +1192,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
11891192
_ => return,
11901193
};
11911194

1192-
self.used_imports.insert((directive.id, ns));
11931195
if let Some(error) = privacy_error.as_ref() {
11941196
self.privacy_errors.push((**error).clone());
11951197
}
@@ -1492,7 +1494,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
14921494
false => module.resolve_name(name, namespace, false),
14931495
}.and_then(|binding| {
14941496
if record_used {
1495-
self.record_use(name, namespace, binding);
1497+
if let NameBindingKind::Import { directive, .. } = binding.kind {
1498+
self.used_imports.insert((directive.id, namespace));
1499+
}
1500+
self.record_use(name, binding);
14961501
}
14971502
Success(binding)
14981503
})
@@ -3094,21 +3099,27 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
30943099
}
30953100
}
30963101

3097-
fn get_traits_containing_item(&mut self, name: Name) -> Vec<DefId> {
3102+
fn get_traits_containing_item(&mut self, name: Name) -> Vec<TraitCandidate> {
30983103
debug!("(getting traits containing item) looking for '{}'", name);
30993104

3100-
fn add_trait_info(found_traits: &mut Vec<DefId>, trait_def_id: DefId, name: Name) {
3105+
fn add_trait_info(found_traits: &mut Vec<TraitCandidate>,
3106+
trait_def_id: DefId,
3107+
import_id: Option<NodeId>,
3108+
name: Name) {
31013109
debug!("(adding trait info) found trait {:?} for method '{}'",
31023110
trait_def_id,
31033111
name);
3104-
found_traits.push(trait_def_id);
3112+
found_traits.push(TraitCandidate {
3113+
def_id: trait_def_id,
3114+
import_id: import_id,
3115+
});
31053116
}
31063117

31073118
let mut found_traits = Vec::new();
31083119
// Look for the current trait.
31093120
if let Some((trait_def_id, _)) = self.current_trait_ref {
31103121
if self.trait_item_map.contains_key(&(name, trait_def_id)) {
3111-
add_trait_info(&mut found_traits, trait_def_id, name);
3122+
add_trait_info(&mut found_traits, trait_def_id, None, name);
31123123
}
31133124
}
31143125

@@ -3131,8 +3142,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
31313142
for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
31323143
let trait_def_id = binding.def().unwrap().def_id();
31333144
if self.trait_item_map.contains_key(&(name, trait_def_id)) {
3134-
add_trait_info(&mut found_traits, trait_def_id, name);
3135-
self.record_use(trait_name, TypeNS, binding);
3145+
let mut import_id = None;
3146+
if let NameBindingKind::Import { directive, .. } = binding.kind {
3147+
let id = directive.id;
3148+
self.maybe_unused_trait_imports.insert(id);
3149+
import_id = Some(id);
3150+
}
3151+
add_trait_info(&mut found_traits, trait_def_id, import_id, name);
3152+
self.record_use(trait_name, binding);
31363153
}
31373154
}
31383155
};
@@ -3506,6 +3523,7 @@ fn err_path_resolution() -> PathResolution {
35063523
pub struct CrateMap {
35073524
pub def_map: RefCell<DefMap>,
35083525
pub freevars: FreevarMap,
3526+
pub maybe_unused_trait_imports: NodeSet,
35093527
pub export_map: ExportMap,
35103528
pub trait_map: TraitMap,
35113529
pub glob_map: Option<GlobMap>,
@@ -3543,6 +3561,7 @@ pub fn resolve_crate<'a, 'tcx>(session: &'a Session,
35433561
CrateMap {
35443562
def_map: resolver.def_map,
35453563
freevars: resolver.freevars,
3564+
maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
35463565
export_map: resolver.export_map,
35473566
trait_map: resolver.trait_map,
35483567
glob_map: if resolver.make_glob_map {

src/librustc_typeck/check/method/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
128128
let mode = probe::Mode::MethodCall;
129129
let self_ty = fcx.infcx().resolve_type_vars_if_possible(&self_ty);
130130
let pick = probe::probe(fcx, span, mode, method_name, self_ty, call_expr.id)?;
131+
132+
if let Some(import_id) = pick.import_id {
133+
fcx.tcx().used_trait_imports.borrow_mut().insert(import_id);
134+
}
135+
131136
Ok(confirm::confirm(fcx, span, self_expr, call_expr, self_ty, pick, supplied_method_types))
132137
}
133138

@@ -339,8 +344,12 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
339344
{
340345
let mode = probe::Mode::Path;
341346
let pick = probe::probe(fcx, span, mode, method_name, self_ty, expr_id)?;
342-
let def = pick.item.def();
343347

348+
if let Some(import_id) = pick.import_id {
349+
fcx.tcx().used_trait_imports.borrow_mut().insert(import_id);
350+
}
351+
352+
let def = pick.item.def();
344353
if let probe::InherentImplPick = pick.kind {
345354
if !pick.item.vis().is_accessible_from(fcx.body_id, &fcx.tcx().map) {
346355
let msg = format!("{} `{}` is private", def.kind_name(), &method_name.as_str());

0 commit comments

Comments
 (0)