2 // Unused import checking
4 // Although this is mostly a lint pass, it lives in here because it depends on
5 // resolve data structures and because it finalises the privacy information for
8 // Unused trait imports can't be checked until the method resolution. We save
9 // candidates here, and do the actual check in librustc_typeck/check_unused.rs.
11 use std::ops::{Deref, DerefMut};
14 use resolve_imports::ImportDirectiveSubclass;
16 use rustc::{lint, ty};
17 use rustc::util::nodemap::NodeMap;
19 use syntax::visit::{self, Visitor};
20 use syntax_pos::{Span, MultiSpan, DUMMY_SP};
23 struct UnusedImportCheckVisitor<'a, 'b: 'a> {
24 resolver: &'a mut Resolver<'b>,
25 /// All the (so far) unused imports, grouped path list
26 unused_imports: NodeMap<NodeMap<Span>>,
31 // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
32 impl<'a, 'b> Deref for UnusedImportCheckVisitor<'a, 'b> {
33 type Target = Resolver<'b>;
35 fn deref<'c>(&'c self) -> &'c Resolver<'b> {
40 impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
41 fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
46 impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
47 // We have information about whether `use` (import) directives are actually
48 // used now. If an import is not used at all, we signal a lint error.
49 fn check_import(&mut self, item_id: ast::NodeId, id: ast::NodeId, span: Span) {
51 self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
53 if self.maybe_unused_trait_imports.contains(&id) {
57 self.unused_imports.entry(item_id).or_default().insert(id, span);
59 // This trait import is definitely used, in a way other than
61 self.maybe_unused_trait_imports.remove(&id);
62 if let Some(i) = self.unused_imports.get_mut(&item_id) {
69 impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
70 fn visit_item(&mut self, item: &'a ast::Item) {
71 self.item_span = item.span;
73 // Ignore is_public import statements because there's no way to be sure
74 // whether they're used or not. Also ignore imports with a dummy span
75 // because this means that they were generated in some fashion by the
76 // compiler and we don't need to consider them.
77 if let ast::ItemKind::Use(..) = item.node {
78 if item.vis.node.is_pub() || item.span.is_dummy() {
83 visit::walk_item(self, item);
86 fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) {
87 // Use the base UseTree's NodeId as the item id
88 // This allows the grouping of all the lints in the same item
93 if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
94 // If it's the parent group, cover the entire use item
95 let span = if nested {
101 if items.is_empty() {
108 let base_id = self.base_id;
109 self.check_import(base_id, id, use_tree.span);
112 visit::walk_use_tree(self, use_tree, id);
116 pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
117 for directive in resolver.potentially_unused_imports.iter() {
118 match directive.subclass {
119 _ if directive.used.get() ||
120 directive.vis.get() == ty::Visibility::Public ||
121 directive.span.is_dummy() => {
122 if let ImportDirectiveSubclass::MacroUse = directive.subclass {
123 if !directive.span.is_dummy() {
124 resolver.session.buffer_lint(
125 lint::builtin::MACRO_USE_EXTERN_CRATE,
128 "deprecated `#[macro_use]` directive used to \
129 import macros should be replaced at use sites \
130 with a `use` statement to import the macro \
136 ImportDirectiveSubclass::ExternCrate { .. } => {
137 resolver.maybe_unused_extern_crates.push((directive.id, directive.span));
139 ImportDirectiveSubclass::MacroUse => {
140 let lint = lint::builtin::UNUSED_IMPORTS;
141 let msg = "unused `#[macro_use]` import";
142 resolver.session.buffer_lint(lint, directive.id, directive.span, msg);
148 for (id, span) in resolver.unused_labels.iter() {
149 resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
152 let mut visitor = UnusedImportCheckVisitor {
154 unused_imports: Default::default(),
155 base_id: ast::DUMMY_NODE_ID,
158 visit::walk_crate(&mut visitor, krate);
160 for (id, spans) in &visitor.unused_imports {
161 let len = spans.len();
162 let mut spans = spans.values().cloned().collect::<Vec<Span>>();
164 let ms = MultiSpan::from_spans(spans.clone());
165 let mut span_snippets = spans.iter()
167 match visitor.session.source_map().span_to_snippet(*s) {
168 Ok(s) => Some(format!("`{}`", s)),
171 }).collect::<Vec<String>>();
172 span_snippets.sort();
173 let msg = format!("unused import{}{}",
174 if len > 1 { "s" } else { "" },
175 if !span_snippets.is_empty() {
176 format!(": {}", span_snippets.join(", "))
180 visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);