]> git.lizzy.rs Git - rust.git/blob - src/librustc_resolve/check_unused.rs
Rollup merge of #57368 - petrhosek:cmake-compiler-launcher, r=alexcrichton
[rust.git] / src / librustc_resolve / check_unused.rs
1 //
2 // Unused import checking
3 //
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
6 // `use` directives.
7 //
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.
10
11 use std::ops::{Deref, DerefMut};
12
13 use Resolver;
14 use resolve_imports::ImportDirectiveSubclass;
15
16 use rustc::{lint, ty};
17 use rustc::util::nodemap::NodeMap;
18 use syntax::ast;
19 use syntax::visit::{self, Visitor};
20 use syntax_pos::{Span, MultiSpan, DUMMY_SP};
21
22
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>>,
27     base_id: ast::NodeId,
28     item_span: Span,
29 }
30
31 // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
32 impl<'a, 'b> Deref for UnusedImportCheckVisitor<'a, 'b> {
33     type Target = Resolver<'b>;
34
35     fn deref<'c>(&'c self) -> &'c Resolver<'b> {
36         &*self.resolver
37     }
38 }
39
40 impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
41     fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b> {
42         &mut *self.resolver
43     }
44 }
45
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) {
50         let mut used = false;
51         self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns)));
52         if !used {
53             if self.maybe_unused_trait_imports.contains(&id) {
54                 // Check later.
55                 return;
56             }
57             self.unused_imports.entry(item_id).or_default().insert(id, span);
58         } else {
59             // This trait import is definitely used, in a way other than
60             // method resolution.
61             self.maybe_unused_trait_imports.remove(&id);
62             if let Some(i) = self.unused_imports.get_mut(&item_id) {
63                 i.remove(&id);
64             }
65         }
66     }
67 }
68
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;
72
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() {
79                 return;
80             }
81         }
82
83         visit::walk_item(self, item);
84     }
85
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
89         if !nested {
90             self.base_id = id;
91         }
92
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 {
96                 use_tree.span
97             } else {
98                 self.item_span
99             };
100
101             if items.is_empty() {
102                 self.unused_imports
103                     .entry(self.base_id)
104                     .or_default()
105                     .insert(id, span);
106             }
107         } else {
108             let base_id = self.base_id;
109             self.check_import(base_id, id, use_tree.span);
110         }
111
112         visit::walk_use_tree(self, use_tree, id);
113     }
114 }
115
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,
126                             directive.id,
127                             directive.span,
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 \
131                              instead",
132                         );
133                     }
134                 }
135             }
136             ImportDirectiveSubclass::ExternCrate { .. } => {
137                 resolver.maybe_unused_extern_crates.push((directive.id, directive.span));
138             }
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);
143             }
144             _ => {}
145         }
146     }
147
148     for (id, span) in resolver.unused_labels.iter() {
149         resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
150     }
151
152     let mut visitor = UnusedImportCheckVisitor {
153         resolver,
154         unused_imports: Default::default(),
155         base_id: ast::DUMMY_NODE_ID,
156         item_span: DUMMY_SP,
157     };
158     visit::walk_crate(&mut visitor, krate);
159
160     for (id, spans) in &visitor.unused_imports {
161         let len = spans.len();
162         let mut spans = spans.values().cloned().collect::<Vec<Span>>();
163         spans.sort();
164         let ms = MultiSpan::from_spans(spans.clone());
165         let mut span_snippets = spans.iter()
166             .filter_map(|s| {
167                 match visitor.session.source_map().span_to_snippet(*s) {
168                     Ok(s) => Some(format!("`{}`", s)),
169                     _ => None,
170                 }
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(", "))
177                           } else {
178                               String::new()
179                           });
180         visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);
181     }
182 }