]> git.lizzy.rs Git - rust.git/blob - src/librustc_resolve/check_unused.rs
Reintroduce special pretty-printing for `$crate` when it's necessary for proc macros
[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 actual 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;
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     base_id: ast::NodeId,
39     item_span: Span,
40 }
41
42 // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
43 impl<'a, 'b> Deref for UnusedImportCheckVisitor<'a, 'b> {
44     type Target = Resolver<'b>;
45
46     fn deref<'c>(&'c self) -> &'c Resolver<'b> {
47         &*self.resolver
48     }
49 }
50
51 impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
52     fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
53         &mut *self.resolver
54     }
55 }
56
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) {
61         let mut used = false;
62         self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
63         if !used {
64             if self.maybe_unused_trait_imports.contains(&id) {
65                 // Check later.
66                 return;
67             }
68             self.unused_imports.entry(item_id).or_default().insert(id, span);
69         } else {
70             // This trait import is definitely used, in a way other than
71             // method resolution.
72             self.maybe_unused_trait_imports.remove(&id);
73             if let Some(i) = self.unused_imports.get_mut(&item_id) {
74                 i.remove(&id);
75             }
76         }
77     }
78 }
79
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;
83
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() {
90                 return;
91             }
92         }
93
94         visit::walk_item(self, item);
95     }
96
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
100         if !nested {
101             self.base_id = id;
102         }
103
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 {
107                 use_tree.span
108             } else {
109                 self.item_span
110             };
111
112             if items.is_empty() {
113                 self.unused_imports
114                     .entry(self.base_id)
115                     .or_default()
116                     .insert(id, span);
117             }
118         } else {
119             let base_id = self.base_id;
120             self.check_import(base_id, id, use_tree.span);
121         }
122
123         visit::walk_use_tree(self, use_tree, id);
124     }
125 }
126
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 !directive.span.is_dummy() {
135                         resolver.session.buffer_lint(
136                             lint::builtin::MACRO_USE_EXTERN_CRATE,
137                             directive.id,
138                             directive.span,
139                             "deprecated `#[macro_use]` directive used to \
140                              import macros should be replaced at use sites \
141                              with a `use` statement to import the macro \
142                              instead",
143                         );
144                     }
145                 }
146             }
147             ImportDirectiveSubclass::ExternCrate { .. } => {
148                 resolver.maybe_unused_extern_crates.push((directive.id, directive.span));
149             }
150             ImportDirectiveSubclass::MacroUse => {
151                 let lint = lint::builtin::UNUSED_IMPORTS;
152                 let msg = "unused `#[macro_use]` import";
153                 resolver.session.buffer_lint(lint, directive.id, directive.span, msg);
154             }
155             _ => {}
156         }
157     }
158
159     for (id, span) in resolver.unused_labels.iter() {
160         resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
161     }
162
163     let mut visitor = UnusedImportCheckVisitor {
164         resolver,
165         unused_imports: Default::default(),
166         base_id: ast::DUMMY_NODE_ID,
167         item_span: DUMMY_SP,
168     };
169     visit::walk_crate(&mut visitor, krate);
170
171     for (id, spans) in &visitor.unused_imports {
172         let len = spans.len();
173         let mut spans = spans.values().cloned().collect::<Vec<Span>>();
174         spans.sort();
175         let ms = MultiSpan::from_spans(spans.clone());
176         let mut span_snippets = spans.iter()
177             .filter_map(|s| {
178                 match visitor.session.source_map().span_to_snippet(*s) {
179                     Ok(s) => Some(format!("`{}`", s)),
180                     _ => None,
181                 }
182             }).collect::<Vec<String>>();
183         span_snippets.sort();
184         let msg = format!("unused import{}{}",
185                           if len > 1 { "s" } else { "" },
186                           if !span_snippets.is_empty() {
187                               format!(": {}", span_snippets.join(", "))
188                           } else {
189                               String::new()
190                           });
191         visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);
192     }
193 }