1 // Copyright 2012-2014 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.
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.
13 // Unused import checking
15 // Although this is mostly a lint pass, it lives in here because it depends on
16 // resolve data structures and because it finalises the privacy information for
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.
22 use std::ops::{Deref, DerefMut};
25 use resolve_imports::ImportDirectiveSubclass;
27 use rustc::{lint, ty};
28 use rustc::util::nodemap::NodeMap;
30 use syntax::visit::{self, Visitor};
31 use syntax_pos::{Span, MultiSpan, DUMMY_SP};
34 struct UnusedImportCheckVisitor<'a, 'b: 'a> {
35 resolver: &'a mut Resolver<'b>,
36 /// All the (so far) unused imports, grouped path list
37 unused_imports: NodeMap<NodeMap<Span>>,
42 // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
43 impl<'a, 'b> Deref for UnusedImportCheckVisitor<'a, 'b> {
44 type Target = Resolver<'b>;
46 fn deref<'c>(&'c self) -> &'c Resolver<'b> {
51 impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
52 fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
57 impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
58 // We have information about whether `use` (import) directives are actually
59 // used now. If an import is not used at all, we signal a lint error.
60 fn check_import(&mut self, item_id: ast::NodeId, id: ast::NodeId, span: Span) {
62 self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
64 if self.maybe_unused_trait_imports.contains(&id) {
68 self.unused_imports.entry(item_id).or_insert_with(NodeMap).insert(id, span);
70 // This trait import is definitely used, in a way other than
72 self.maybe_unused_trait_imports.remove(&id);
73 if let Some(i) = self.unused_imports.get_mut(&item_id) {
80 impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
81 fn visit_item(&mut self, item: &'a ast::Item) {
82 self.item_span = item.span;
84 // Ignore is_public import statements because there's no way to be sure
85 // whether they're used or not. Also ignore imports with a dummy span
86 // because this means that they were generated in some fashion by the
87 // compiler and we don't need to consider them.
88 if let ast::ItemKind::Use(..) = item.node {
89 if item.vis.node.is_pub() || item.span.is_dummy() {
94 visit::walk_item(self, item);
97 fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) {
98 // Use the base UseTree's NodeId as the item id
99 // This allows the grouping of all the lints in the same item
104 if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
105 // If it's the parent group, cover the entire use item
106 let span = if nested {
112 if items.len() == 0 {
115 .or_insert_with(NodeMap)
119 let base_id = self.base_id;
120 self.check_import(base_id, id, use_tree.span);
123 visit::walk_use_tree(self, use_tree, id);
127 pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
128 for directive in resolver.potentially_unused_imports.iter() {
129 match directive.subclass {
130 _ if directive.used.get() ||
131 directive.vis.get() == ty::Visibility::Public ||
132 directive.span.is_dummy() => {
133 if let ImportDirectiveSubclass::MacroUse = directive.subclass {
134 if resolver.session.features_untracked().use_extern_macros &&
135 !directive.span.is_dummy() {
136 resolver.session.buffer_lint(
137 lint::builtin::MACRO_USE_EXTERN_CRATE,
140 "deprecated `#[macro_use]` directive used to \
141 import macros should be replaced at use sites \
142 with a `use` statement to import the macro \
148 ImportDirectiveSubclass::ExternCrate(_) => {
149 resolver.maybe_unused_extern_crates.push((directive.id, directive.span));
151 ImportDirectiveSubclass::MacroUse => {
152 let lint = lint::builtin::UNUSED_IMPORTS;
153 let msg = "unused `#[macro_use]` import";
154 resolver.session.buffer_lint(lint, directive.id, directive.span, msg);
160 for (id, span) in resolver.unused_labels.iter() {
161 resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
164 let mut visitor = UnusedImportCheckVisitor {
166 unused_imports: NodeMap(),
167 base_id: ast::DUMMY_NODE_ID,
170 visit::walk_crate(&mut visitor, krate);
172 for (id, spans) in &visitor.unused_imports {
173 let len = spans.len();
174 let mut spans = spans.values().map(|s| *s).collect::<Vec<Span>>();
176 let ms = MultiSpan::from_spans(spans.clone());
177 let mut span_snippets = spans.iter()
179 match visitor.session.codemap().span_to_snippet(*s) {
180 Ok(s) => Some(format!("`{}`", s)),
183 }).collect::<Vec<String>>();
184 span_snippets.sort();
185 let msg = format!("unused import{}{}",
186 if len > 1 { "s" } else { "" },
187 if span_snippets.len() > 0 {
188 format!(": {}", span_snippets.join(", "))
192 visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);