]> git.proxmox.com Git - rustc.git/blob - src/librustc_lint/unused.rs
New upstream version 1.25.0+dfsg1
[rustc.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 pub(super) 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 // either function/method call, or something this lint doesn't care about
306 ref call_or_other => {
307 let args_to_check;
308 let call_kind;
309 match *call_or_other {
310 Call(_, ref args) => {
311 call_kind = "function";
312 args_to_check = &args[..];
313 },
314 MethodCall(_, ref args) => {
315 call_kind = "method";
316 // first "argument" is self (which sometimes needs parens)
317 args_to_check = &args[1..];
318 }
319 // actual catch-all arm
320 _ => { return; }
321 }
322 // Don't lint if this is a nested macro expansion: otherwise, the lint could
323 // trigger in situations that macro authors shouldn't have to care about, e.g.,
324 // when a parenthesized token tree matched in one macro expansion is matched as
325 // an expression in another and used as a fn/method argument (Issue #47775)
326 if e.span.ctxt().outer().expn_info()
327 .map_or(false, |info| info.call_site.ctxt().outer()
328 .expn_info().is_some()) {
329 return;
330 }
331 let msg = format!("{} argument", call_kind);
332 for arg in args_to_check {
333 self.check_unused_parens_core(cx, arg, &msg, false);
334 }
335 return;
336 }
337 };
338 self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
339 }
340
341 fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
342 let (value, msg) = match s.node {
343 ast::StmtKind::Local(ref local) => {
344 match local.init {
345 Some(ref value) => (value, "assigned value"),
346 None => return,
347 }
348 }
349 _ => return,
350 };
351 self.check_unused_parens_core(cx, &value, msg, false);
352 }
353 }
354
355 declare_lint! {
356 UNUSED_IMPORT_BRACES,
357 Allow,
358 "unnecessary braces around an imported item"
359 }
360
361 #[derive(Copy, Clone)]
362 pub struct UnusedImportBraces;
363
364 impl UnusedImportBraces {
365 fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) {
366 if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
367 // Recursively check nested UseTrees
368 for &(ref tree, _) in items {
369 self.check_use_tree(cx, tree, item);
370 }
371
372 // Trigger the lint only if there is one nested item
373 if items.len() != 1 {
374 return;
375 }
376
377 // Trigger the lint if the nested item is a non-self single item
378 let node_ident;
379 match items[0].0.kind {
380 ast::UseTreeKind::Simple(ident) => {
381 if ident.name == keywords::SelfValue.name() {
382 return;
383 } else {
384 node_ident = ident;
385 }
386 }
387 ast::UseTreeKind::Glob => {
388 node_ident = ast::Ident::from_str("*");
389 }
390 ast::UseTreeKind::Nested(_) => {
391 return;
392 }
393 }
394
395 let msg = format!("braces around {} is unnecessary", node_ident.name);
396 cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
397 }
398 }
399 }
400
401 impl LintPass for UnusedImportBraces {
402 fn get_lints(&self) -> LintArray {
403 lint_array!(UNUSED_IMPORT_BRACES)
404 }
405 }
406
407 impl EarlyLintPass for UnusedImportBraces {
408 fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
409 if let ast::ItemKind::Use(ref use_tree) = item.node {
410 self.check_use_tree(cx, use_tree, item);
411 }
412 }
413 }
414
415 declare_lint! {
416 pub(super) UNUSED_ALLOCATION,
417 Warn,
418 "detects unnecessary allocations that can be eliminated"
419 }
420
421 #[derive(Copy, Clone)]
422 pub struct UnusedAllocation;
423
424 impl LintPass for UnusedAllocation {
425 fn get_lints(&self) -> LintArray {
426 lint_array!(UNUSED_ALLOCATION)
427 }
428 }
429
430 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
431 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
432 match e.node {
433 hir::ExprBox(_) => {}
434 _ => return,
435 }
436
437 for adj in cx.tables.expr_adjustments(e) {
438 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
439 let msg = match m {
440 adjustment::AutoBorrowMutability::Immutable =>
441 "unnecessary allocation, use & instead",
442 adjustment::AutoBorrowMutability::Mutable { .. }=>
443 "unnecessary allocation, use &mut instead"
444 };
445 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
446 }
447 }
448 }
449 }