]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
a058f84e58806da255b7b2ade7159e7263301795
[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 lint::{LateContext, EarlyContext, LintContext, LintArray};
15 use lint::{LintPass, EarlyLintPass, LateLintPass};
16
17 use syntax::ast;
18 use syntax::attr;
19 use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
20 use syntax::print::pprust;
21 use syntax::symbol::keywords;
22 use syntax::util::parser;
23 use syntax_pos::Span;
24
25 use rustc::hir;
26
27 declare_lint! {
28     pub UNUSED_MUST_USE,
29     Warn,
30     "unused result of a type flagged as #[must_use]"
31 }
32
33 declare_lint! {
34     pub UNUSED_RESULTS,
35     Allow,
36     "unused result of an expression in a statement"
37 }
38
39 #[derive(Copy, Clone)]
40 pub struct UnusedResults;
41
42 impl LintPass for UnusedResults {
43     fn get_lints(&self) -> LintArray {
44         lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
45     }
46 }
47
48 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
49     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
50         let expr = match s.node {
51             hir::StmtSemi(ref expr, _) => &**expr,
52             _ => return,
53         };
54
55         if let hir::ExprRet(..) = expr.node {
56             return;
57         }
58
59         let t = cx.tables.expr_ty(&expr);
60         let ty_warned = match t.sty {
61             ty::TyTuple(ref tys, _) if tys.is_empty() => return,
62             ty::TyNever => return,
63             ty::TyAdt(def, _) => {
64                 if def.variants.is_empty() {
65                     return;
66                 } else {
67                     check_must_use(cx, def.did, s.span, "")
68                 }
69             },
70             _ => false,
71         };
72
73         let mut fn_warned = false;
74         let mut op_warned = false;
75         if cx.tcx.sess.features.borrow().fn_must_use {
76             let maybe_def = match expr.node {
77                 hir::ExprCall(ref callee, _) => {
78                     match callee.node {
79                         hir::ExprPath(ref qpath) => {
80                             Some(cx.tables.qpath_def(qpath, callee.hir_id))
81                         },
82                         _ => None
83                     }
84                 },
85                 hir::ExprMethodCall(..) => {
86                     cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
87                 },
88                 _ => None
89             };
90             if let Some(def) = maybe_def {
91                 let def_id = def.def_id();
92                 fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
93             }
94
95             if let hir::ExprBinary(bin_op, ..) = expr.node {
96                 match bin_op.node {
97                     // Hardcoding the comparison operators here seemed more
98                     // expedient than the refactoring that would be needed to
99                     // look up the `#[must_use]` attribute which does exist on
100                     // the comparison trait methods
101                     hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => {
102                         let msg = "unused comparison which must be used";
103                         cx.span_lint(UNUSED_MUST_USE, expr.span, msg);
104                         op_warned = true;
105                     },
106                     _ => {},
107                 }
108             }
109         }
110
111         if !(ty_warned || fn_warned || op_warned) {
112             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
113         }
114
115         fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool {
116             for attr in cx.tcx.get_attrs(def_id).iter() {
117                 if attr.check_name("must_use") {
118                     let mut msg = format!("unused {}`{}` which must be used",
119                                           describe_path, cx.tcx.item_path_str(def_id));
120                     // check for #[must_use="..."]
121                     if let Some(s) = attr.value_str() {
122                         msg.push_str(": ");
123                         msg.push_str(&s.as_str());
124                     }
125                     cx.span_lint(UNUSED_MUST_USE, sp, &msg);
126                     return true;
127                 }
128             }
129             false
130         }
131     }
132 }
133
134 declare_lint! {
135     pub PATH_STATEMENTS,
136     Warn,
137     "path statements with no effect"
138 }
139
140 #[derive(Copy, Clone)]
141 pub struct PathStatements;
142
143 impl LintPass for PathStatements {
144     fn get_lints(&self) -> LintArray {
145         lint_array!(PATH_STATEMENTS)
146     }
147 }
148
149 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
150     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
151         if let hir::StmtSemi(ref expr, _) = s.node {
152             if let hir::ExprPath(_) = expr.node {
153                 cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
154             }
155         }
156     }
157 }
158
159 declare_lint! {
160     pub UNUSED_ATTRIBUTES,
161     Warn,
162     "detects attributes that were not used by the compiler"
163 }
164
165 #[derive(Copy, Clone)]
166 pub struct UnusedAttributes;
167
168 impl LintPass for UnusedAttributes {
169     fn get_lints(&self) -> LintArray {
170         lint_array!(UNUSED_ATTRIBUTES)
171     }
172 }
173
174 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
175     fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
176         debug!("checking attribute: {:?}", attr);
177         let name = unwrap_or!(attr.name(), return);
178
179         // Note that check_name() marks the attribute as used if it matches.
180         for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
181             match ty {
182                 AttributeType::Whitelisted if attr.check_name(name) => {
183                     debug!("{:?} is Whitelisted", name);
184                     break;
185                 }
186                 _ => (),
187             }
188         }
189
190         let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
191         for &(ref name, ty) in plugin_attributes.iter() {
192             if ty == AttributeType::Whitelisted && attr.check_name(&name) {
193                 debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
194                 break;
195             }
196         }
197
198         if !attr::is_used(attr) {
199             debug!("Emitting warning for: {:?}", attr);
200             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
201             // Is it a builtin attribute that must be used at the crate level?
202             let known_crate = BUILTIN_ATTRIBUTES.iter()
203                 .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel)
204                 .is_some();
205
206             // Has a plugin registered this attribute as one which must be used at
207             // the crate level?
208             let plugin_crate = plugin_attributes.iter()
209                 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
210                 .is_some();
211             if known_crate || plugin_crate {
212                 let msg = match attr.style {
213                     ast::AttrStyle::Outer => {
214                         "crate-level attribute should be an inner attribute: add an exclamation \
215                          mark: #![foo]"
216                     }
217                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
218                 };
219                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
220             }
221         } else {
222             debug!("Attr was used: {:?}", attr);
223         }
224     }
225 }
226
227 declare_lint! {
228     UNUSED_PARENS,
229     Warn,
230     "`if`, `match`, `while` and `return` do not need parentheses"
231 }
232
233 #[derive(Copy, Clone)]
234 pub struct UnusedParens;
235
236 impl UnusedParens {
237     fn check_unused_parens_core(&self,
238                                 cx: &EarlyContext,
239                                 value: &ast::Expr,
240                                 msg: &str,
241                                 struct_lit_needs_parens: bool) {
242         if let ast::ExprKind::Paren(ref inner) = value.node {
243             let necessary = struct_lit_needs_parens &&
244                             parser::contains_exterior_struct_lit(&inner);
245             if !necessary {
246                 let span_msg = format!("unnecessary parentheses around {}", msg);
247                 let mut err = cx.struct_span_lint(UNUSED_PARENS,
248                                                   value.span,
249                                                   &span_msg);
250                 // Remove exactly one pair of parentheses (rather than naïvely
251                 // stripping all paren characters)
252                 let mut ate_left_paren = false;
253                 let mut ate_right_paren = false;
254                 let parens_removed = pprust::expr_to_string(value)
255                     .trim_matches(|c| {
256                         match c {
257                             '(' => {
258                                 if ate_left_paren {
259                                     false
260                                 } else {
261                                     ate_left_paren = true;
262                                     true
263                                 }
264                             },
265                             ')' => {
266                                 if ate_right_paren {
267                                     false
268                                 } else {
269                                     ate_right_paren = true;
270                                     true
271                                 }
272                             },
273                             _ => false,
274                         }
275                     }).to_owned();
276                 err.span_suggestion_short(value.span,
277                                           "remove these parentheses",
278                                           parens_removed);
279                 err.emit();
280             }
281         }
282     }
283 }
284
285 impl LintPass for UnusedParens {
286     fn get_lints(&self) -> LintArray {
287         lint_array!(UNUSED_PARENS)
288     }
289 }
290
291 impl EarlyLintPass for UnusedParens {
292     fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
293         use syntax::ast::ExprKind::*;
294         let (value, msg, struct_lit_needs_parens) = match e.node {
295             If(ref cond, ..) => (cond, "`if` condition", true),
296             While(ref cond, ..) => (cond, "`while` condition", true),
297             IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
298             WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
299             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
300             Match(ref head, _) => (head, "`match` head expression", true),
301             Ret(Some(ref value)) => (value, "`return` value", false),
302             Assign(_, ref value) => (value, "assigned value", false),
303             AssignOp(.., ref value) => (value, "assigned value", false),
304             InPlace(_, ref value) => (value, "emplacement value", false),
305             _ => return,
306         };
307         self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
308     }
309
310     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
311         let (value, msg) = match s.node {
312             ast::StmtKind::Local(ref local) => {
313                 match local.init {
314                     Some(ref value) => (value, "assigned value"),
315                     None => return,
316                 }
317             }
318             _ => return,
319         };
320         self.check_unused_parens_core(cx, &value, msg, false);
321     }
322 }
323
324 declare_lint! {
325     UNUSED_IMPORT_BRACES,
326     Allow,
327     "unnecessary braces around an imported item"
328 }
329
330 #[derive(Copy, Clone)]
331 pub struct UnusedImportBraces;
332
333 impl LintPass for UnusedImportBraces {
334     fn get_lints(&self) -> LintArray {
335         lint_array!(UNUSED_IMPORT_BRACES)
336     }
337 }
338
339 impl EarlyLintPass for UnusedImportBraces {
340     fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
341         if let ast::ItemKind::Use(ref view_path) = item.node {
342             if let ast::ViewPathList(_, ref items) = view_path.node {
343                 if items.len() == 1 && items[0].node.name.name != keywords::SelfValue.name() {
344                     let msg = format!("braces around {} is unnecessary", items[0].node.name);
345                     cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
346                 }
347             }
348         }
349     }
350 }
351
352 declare_lint! {
353     UNUSED_ALLOCATION,
354     Warn,
355     "detects unnecessary allocations that can be eliminated"
356 }
357
358 #[derive(Copy, Clone)]
359 pub struct UnusedAllocation;
360
361 impl LintPass for UnusedAllocation {
362     fn get_lints(&self) -> LintArray {
363         lint_array!(UNUSED_ALLOCATION)
364     }
365 }
366
367 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
368     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
369         match e.node {
370             hir::ExprBox(_) => {}
371             _ => return,
372         }
373
374         for adj in cx.tables.expr_adjustments(e) {
375             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
376                 let msg = match m {
377                     hir::MutImmutable => "unnecessary allocation, use & instead",
378                     hir::MutMutable => "unnecessary allocation, use &mut instead"
379                 };
380                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
381             }
382         }
383     }
384 }