]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
Rollup merge of #44562 - eddyb:ugh-rustdoc, r=nikomatsakis
[rust.git] / src / librustc_lint / unused.rs
1 // Copyright 2015 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 use rustc::hir::def_id::DefId;
12 use rustc::ty;
13 use rustc::ty::adjustment;
14 use util::nodemap::FxHashMap;
15 use lint::{LateContext, EarlyContext, LintContext, LintArray};
16 use lint::{LintPass, EarlyLintPass, LateLintPass};
17
18 use std::collections::hash_map::Entry::{Occupied, Vacant};
19
20 use syntax::ast;
21 use syntax::attr;
22 use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
23 use syntax::symbol::keywords;
24 use syntax::ptr::P;
25 use syntax::util::parser;
26 use syntax_pos::Span;
27
28 use rustc_back::slice;
29 use rustc::hir;
30 use rustc::hir::intravisit::FnKind;
31
32 declare_lint! {
33     pub UNUSED_MUT,
34     Warn,
35     "detect mut variables which don't need to be mutable"
36 }
37
38 #[derive(Copy, Clone)]
39 pub struct UnusedMut;
40
41 impl UnusedMut {
42     fn check_unused_mut_pat(&self, cx: &LateContext, pats: &[P<hir::Pat>]) {
43         // collect all mutable pattern and group their NodeIDs by their Identifier to
44         // avoid false warnings in match arms with multiple patterns
45
46         let mut mutables = FxHashMap();
47         for p in pats {
48             p.each_binding(|_, id, span, path1| {
49                 let hir_id = cx.tcx.hir.node_to_hir_id(id);
50                 let bm = match cx.tables.pat_binding_modes().get(hir_id) {
51                     Some(&bm) => bm,
52                     None => span_bug!(span, "missing binding mode"),
53                 };
54                 let name = path1.node;
55                 if let ty::BindByValue(hir::MutMutable) = bm {
56                     if !name.as_str().starts_with("_") {
57                         match mutables.entry(name) {
58                             Vacant(entry) => {
59                                 entry.insert(vec![id]);
60                             }
61                             Occupied(mut entry) => {
62                                 entry.get_mut().push(id);
63                             }
64                         }
65                     }
66                 }
67             });
68         }
69
70         let used_mutables = cx.tcx.used_mut_nodes.borrow();
71         for (_, v) in &mutables {
72             if !v.iter().any(|e| used_mutables.contains(e)) {
73                 cx.span_lint(UNUSED_MUT,
74                              cx.tcx.hir.span(v[0]),
75                              "variable does not need to be mutable");
76             }
77         }
78     }
79 }
80
81 impl LintPass for UnusedMut {
82     fn get_lints(&self) -> LintArray {
83         lint_array!(UNUSED_MUT)
84     }
85 }
86
87 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedMut {
88     fn check_arm(&mut self, cx: &LateContext, a: &hir::Arm) {
89         self.check_unused_mut_pat(cx, &a.pats)
90     }
91
92     fn check_local(&mut self, cx: &LateContext, l: &hir::Local) {
93         self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat));
94     }
95
96     fn check_fn(&mut self,
97                 cx: &LateContext,
98                 _: FnKind,
99                 _: &hir::FnDecl,
100                 body: &hir::Body,
101                 _: Span,
102                 _: ast::NodeId) {
103         for a in &body.arguments {
104             self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
105         }
106     }
107 }
108
109 declare_lint! {
110     pub UNUSED_MUST_USE,
111     Warn,
112     "unused result of a type flagged as #[must_use]"
113 }
114
115 declare_lint! {
116     pub UNUSED_RESULTS,
117     Allow,
118     "unused result of an expression in a statement"
119 }
120
121 #[derive(Copy, Clone)]
122 pub struct UnusedResults;
123
124 impl LintPass for UnusedResults {
125     fn get_lints(&self) -> LintArray {
126         lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
127     }
128 }
129
130 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
131     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
132         let expr = match s.node {
133             hir::StmtSemi(ref expr, _) => &**expr,
134             _ => return,
135         };
136
137         if let hir::ExprRet(..) = expr.node {
138             return;
139         }
140
141         let t = cx.tables.expr_ty(&expr);
142         let ty_warned = match t.sty {
143             ty::TyTuple(ref tys, _) if tys.is_empty() => return,
144             ty::TyNever => return,
145             ty::TyAdt(def, _) => {
146                 if def.variants.is_empty() {
147                     return;
148                 } else {
149                     check_must_use(cx, def.did, s.span, "")
150                 }
151             },
152             _ => false,
153         };
154
155         let mut fn_warned = false;
156         if cx.tcx.sess.features.borrow().fn_must_use {
157             let maybe_def = match expr.node {
158                 hir::ExprCall(ref callee, _) => {
159                     match callee.node {
160                         hir::ExprPath(ref qpath) => {
161                             Some(cx.tables.qpath_def(qpath, callee.hir_id))
162                         },
163                         _ => None
164                     }
165                 },
166                 hir::ExprMethodCall(..) => {
167                     cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
168                 },
169                 _ => None
170             };
171             if let Some(def) = maybe_def {
172                 let def_id = def.def_id();
173                 fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
174             }
175         }
176
177         if !(ty_warned || fn_warned) {
178             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
179         }
180
181         fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool {
182             for attr in cx.tcx.get_attrs(def_id).iter() {
183                 if attr.check_name("must_use") {
184                     let mut msg = format!("unused {}`{}` which must be used",
185                                           describe_path, cx.tcx.item_path_str(def_id));
186                     // check for #[must_use="..."]
187                     if let Some(s) = attr.value_str() {
188                         msg.push_str(": ");
189                         msg.push_str(&s.as_str());
190                     }
191                     cx.span_lint(UNUSED_MUST_USE, sp, &msg);
192                     return true;
193                 }
194             }
195             false
196         }
197     }
198 }
199
200 declare_lint! {
201     pub PATH_STATEMENTS,
202     Warn,
203     "path statements with no effect"
204 }
205
206 #[derive(Copy, Clone)]
207 pub struct PathStatements;
208
209 impl LintPass for PathStatements {
210     fn get_lints(&self) -> LintArray {
211         lint_array!(PATH_STATEMENTS)
212     }
213 }
214
215 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
216     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
217         if let hir::StmtSemi(ref expr, _) = s.node {
218             if let hir::ExprPath(_) = expr.node {
219                 cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
220             }
221         }
222     }
223 }
224
225 declare_lint! {
226     pub UNUSED_ATTRIBUTES,
227     Warn,
228     "detects attributes that were not used by the compiler"
229 }
230
231 #[derive(Copy, Clone)]
232 pub struct UnusedAttributes;
233
234 impl LintPass for UnusedAttributes {
235     fn get_lints(&self) -> LintArray {
236         lint_array!(UNUSED_ATTRIBUTES)
237     }
238 }
239
240 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
241     fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
242         debug!("checking attribute: {:?}", attr);
243         let name = unwrap_or!(attr.name(), return);
244
245         // Note that check_name() marks the attribute as used if it matches.
246         for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
247             match ty {
248                 AttributeType::Whitelisted if attr.check_name(name) => {
249                     debug!("{:?} is Whitelisted", name);
250                     break;
251                 }
252                 _ => (),
253             }
254         }
255
256         let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
257         for &(ref name, ty) in plugin_attributes.iter() {
258             if ty == AttributeType::Whitelisted && attr.check_name(&name) {
259                 debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
260                 break;
261             }
262         }
263
264         if !attr::is_used(attr) {
265             debug!("Emitting warning for: {:?}", attr);
266             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
267             // Is it a builtin attribute that must be used at the crate level?
268             let known_crate = BUILTIN_ATTRIBUTES.iter()
269                 .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel)
270                 .is_some();
271
272             // Has a plugin registered this attribute as one which must be used at
273             // the crate level?
274             let plugin_crate = plugin_attributes.iter()
275                 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
276                 .is_some();
277             if known_crate || plugin_crate {
278                 let msg = match attr.style {
279                     ast::AttrStyle::Outer => {
280                         "crate-level attribute should be an inner attribute: add an exclamation \
281                          mark: #![foo]"
282                     }
283                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
284                 };
285                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
286             }
287         } else {
288             debug!("Attr was used: {:?}", attr);
289         }
290     }
291 }
292
293 declare_lint! {
294     UNUSED_PARENS,
295     Warn,
296     "`if`, `match`, `while` and `return` do not need parentheses"
297 }
298
299 #[derive(Copy, Clone)]
300 pub struct UnusedParens;
301
302 impl UnusedParens {
303     fn check_unused_parens_core(&self,
304                                 cx: &EarlyContext,
305                                 value: &ast::Expr,
306                                 msg: &str,
307                                 struct_lit_needs_parens: bool) {
308         if let ast::ExprKind::Paren(ref inner) = value.node {
309             let necessary = struct_lit_needs_parens &&
310                             parser::contains_exterior_struct_lit(&inner);
311             if !necessary {
312                 cx.span_lint(UNUSED_PARENS,
313                              value.span,
314                              &format!("unnecessary parentheses around {}", msg))
315             }
316         }
317     }
318 }
319
320 impl LintPass for UnusedParens {
321     fn get_lints(&self) -> LintArray {
322         lint_array!(UNUSED_PARENS)
323     }
324 }
325
326 impl EarlyLintPass for UnusedParens {
327     fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
328         use syntax::ast::ExprKind::*;
329         let (value, msg, struct_lit_needs_parens) = match e.node {
330             If(ref cond, ..) => (cond, "`if` condition", true),
331             While(ref cond, ..) => (cond, "`while` condition", true),
332             IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
333             WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
334             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
335             Match(ref head, _) => (head, "`match` head expression", true),
336             Ret(Some(ref value)) => (value, "`return` value", false),
337             Assign(_, ref value) => (value, "assigned value", false),
338             AssignOp(.., ref value) => (value, "assigned value", false),
339             InPlace(_, ref value) => (value, "emplacement value", false),
340             _ => return,
341         };
342         self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
343     }
344
345     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
346         let (value, msg) = match s.node {
347             ast::StmtKind::Local(ref local) => {
348                 match local.init {
349                     Some(ref value) => (value, "assigned value"),
350                     None => return,
351                 }
352             }
353             _ => return,
354         };
355         self.check_unused_parens_core(cx, &value, msg, false);
356     }
357 }
358
359 declare_lint! {
360     UNUSED_IMPORT_BRACES,
361     Allow,
362     "unnecessary braces around an imported item"
363 }
364
365 #[derive(Copy, Clone)]
366 pub struct UnusedImportBraces;
367
368 impl LintPass for UnusedImportBraces {
369     fn get_lints(&self) -> LintArray {
370         lint_array!(UNUSED_IMPORT_BRACES)
371     }
372 }
373
374 impl EarlyLintPass for UnusedImportBraces {
375     fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
376         if let ast::ItemKind::Use(ref view_path) = item.node {
377             if let ast::ViewPathList(_, ref items) = view_path.node {
378                 if items.len() == 1 && items[0].node.name.name != keywords::SelfValue.name() {
379                     let msg = format!("braces around {} is unnecessary", items[0].node.name);
380                     cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
381                 }
382             }
383         }
384     }
385 }
386
387 declare_lint! {
388     UNUSED_ALLOCATION,
389     Warn,
390     "detects unnecessary allocations that can be eliminated"
391 }
392
393 #[derive(Copy, Clone)]
394 pub struct UnusedAllocation;
395
396 impl LintPass for UnusedAllocation {
397     fn get_lints(&self) -> LintArray {
398         lint_array!(UNUSED_ALLOCATION)
399     }
400 }
401
402 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
403     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
404         match e.node {
405             hir::ExprBox(_) => {}
406             _ => return,
407         }
408
409         for adj in cx.tables.expr_adjustments(e) {
410             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
411                 let msg = match m {
412                     hir::MutImmutable => "unnecessary allocation, use & instead",
413                     hir::MutMutable => "unnecessary allocation, use &mut instead"
414                 };
415                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
416             }
417         }
418     }
419 }