]> git.lizzy.rs Git - rust.git/blob - src/librustc_resolve/check_unused.rs
6e7f212bd4d8c9e5bd568df85a92ae208a78dea9
[rust.git] / src / librustc_resolve / check_unused.rs
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.
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
12 //
13 // Unused import checking
14 //
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
17 // `use` directives.
18 //
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.
21
22 use std::ops::{Deref, DerefMut};
23
24 use Resolver;
25 use resolve_imports::ImportDirectiveSubclass;
26
27 use rustc::{lint, ty};
28 use rustc::util::nodemap::NodeMap;
29 use syntax::ast::{self, ViewPathGlob, ViewPathList, ViewPathSimple};
30 use syntax::visit::{self, Visitor};
31 use syntax_pos::{Span, MultiSpan, DUMMY_SP};
32
33
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>>,
38 }
39
40 // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
41 impl<'a, 'b> Deref for UnusedImportCheckVisitor<'a, 'b> {
42     type Target = Resolver<'b>;
43
44     fn deref<'c>(&'c self) -> &'c Resolver<'b> {
45         &*self.resolver
46     }
47 }
48
49 impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
50     fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
51         &mut *self.resolver
52     }
53 }
54
55 impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
56     // We have information about whether `use` (import) directives are actually
57     // used now. If an import is not used at all, we signal a lint error.
58     fn check_import(&mut self, item_id: ast::NodeId, id: ast::NodeId, span: Span) {
59         let mut used = false;
60         self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
61         if !used {
62             if self.maybe_unused_trait_imports.contains(&id) {
63                 // Check later.
64                 return;
65             }
66             self.unused_imports.entry(item_id).or_insert_with(NodeMap).insert(id, span);
67         } else {
68             // This trait import is definitely used, in a way other than
69             // method resolution.
70             self.maybe_unused_trait_imports.remove(&id);
71             if let Some(i) = self.unused_imports.get_mut(&item_id) {
72                 i.remove(&id);
73             }
74         }
75     }
76 }
77
78 impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
79     fn visit_item(&mut self, item: &'a ast::Item) {
80         visit::walk_item(self, item);
81         // Ignore is_public import statements because there's no way to be sure
82         // whether they're used or not. Also ignore imports with a dummy span
83         // because this means that they were generated in some fashion by the
84         // compiler and we don't need to consider them.
85         if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) {
86             return;
87         }
88
89         match item.node {
90             ast::ItemKind::Use(ref p) => {
91                 match p.node {
92                     ViewPathSimple(..) => {
93                         self.check_import(item.id, item.id, p.span)
94                     }
95
96                     ViewPathList(_, ref list) => {
97                         if list.len() == 0 {
98                             self.unused_imports
99                                 .entry(item.id)
100                                 .or_insert_with(NodeMap)
101                                 .insert(item.id, item.span);
102                         }
103                         for i in list {
104                             self.check_import(item.id, i.node.id, i.span);
105                         }
106                     }
107                     ViewPathGlob(_) => {
108                         self.check_import(item.id, item.id, p.span);
109                     }
110                 }
111             }
112             _ => {}
113         }
114     }
115 }
116
117 pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
118     for directive in resolver.potentially_unused_imports.iter() {
119         match directive.subclass {
120             _ if directive.used.get() ||
121                  directive.vis.get() == ty::Visibility::Public ||
122                  directive.span.source_equal(&DUMMY_SP) => {}
123             ImportDirectiveSubclass::ExternCrate { cnum } => {
124                 resolver.maybe_unused_extern_crates.push((directive.id, directive.span, cnum));
125             }
126             ImportDirectiveSubclass::MacroUse => {
127                 let lint = lint::builtin::UNUSED_IMPORTS;
128                 let msg = "unused `#[macro_use]` import";
129                 resolver.session.buffer_lint(lint, directive.id, directive.span, msg);
130             }
131             _ => {}
132         }
133     }
134
135     let mut visitor = UnusedImportCheckVisitor {
136         resolver,
137         unused_imports: NodeMap(),
138     };
139     visit::walk_crate(&mut visitor, krate);
140
141     for (id, spans) in &visitor.unused_imports {
142         let len = spans.len();
143         let mut spans = spans.values().map(|s| *s).collect::<Vec<Span>>();
144         spans.sort();
145         let ms = MultiSpan::from_spans(spans.clone());
146         let mut span_snippets = spans.iter()
147             .filter_map(|s| {
148                 match visitor.session.codemap().span_to_snippet(*s) {
149                     Ok(s) => Some(format!("`{}`", s)),
150                     _ => None,
151                 }
152             }).collect::<Vec<String>>();
153         span_snippets.sort();
154         let msg = format!("unused import{}{}",
155                           if len > 1 { "s" } else { "" },
156                           if span_snippets.len() > 0 {
157                               format!(": {}", span_snippets.join(", "))
158                           } else {
159                               String::new()
160                           });
161         visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);
162     }
163 }