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.
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.
11 use rustc::hir::def::Def;
12 use rustc::hir::def_id::DefId;
14 use rustc::ty::adjustment;
15 use lint::{LateContext, EarlyContext, LintContext, LintArray};
16 use lint::{LintPass, EarlyLintPass, LateLintPass};
20 use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
21 use syntax::print::pprust;
22 use syntax::symbol::keywords;
23 use syntax::util::parser;
31 "unused result of a type flagged as #[must_use]"
37 "unused result of an expression in a statement"
40 #[derive(Copy, Clone)]
41 pub struct UnusedResults;
43 impl LintPass for UnusedResults {
44 fn get_lints(&self) -> LintArray {
45 lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
49 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
50 fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
51 let expr = match s.node {
52 hir::StmtKind::Semi(ref expr, _) => &**expr,
56 if let hir::ExprKind::Ret(..) = expr.node {
60 let t = cx.tables.expr_ty(&expr);
61 let ty_warned = match t.sty {
62 ty::TyTuple(ref tys) if tys.is_empty() => return,
63 ty::TyNever => return,
64 ty::TyAdt(def, _) => {
65 if def.variants.is_empty() {
68 check_must_use(cx, def.did, s.span, "")
74 let mut fn_warned = false;
75 let mut op_warned = false;
76 let maybe_def = match expr.node {
77 hir::ExprKind::Call(ref callee, _) => {
79 hir::ExprKind::Path(ref qpath) => {
80 let def = cx.tables.qpath_def(qpath, callee.hir_id);
81 if let Def::Fn(_) = def {
83 } else { // `Def::Local` if it was a closure, for which we
84 None // do not currently support must-use linting
90 hir::ExprKind::MethodCall(..) => {
91 cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
95 if let Some(def) = maybe_def {
96 let def_id = def.def_id();
97 fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
99 let must_use_op = match expr.node {
100 // Hardcoding operators here seemed more expedient than the
101 // refactoring that would be needed to look up the `#[must_use]`
102 // attribute which does exist on the comparison trait methods
103 hir::ExprKind::Binary(bin_op, ..) => {
110 hir::BinOpKind::Gt => {
113 hir::BinOpKind::Add |
114 hir::BinOpKind::Sub |
115 hir::BinOpKind::Div |
116 hir::BinOpKind::Mul |
117 hir::BinOpKind::Rem => {
118 Some("arithmetic operation")
120 hir::BinOpKind::And | hir::BinOpKind::Or => {
121 Some("logical operation")
123 hir::BinOpKind::BitXor |
124 hir::BinOpKind::BitAnd |
125 hir::BinOpKind::BitOr |
126 hir::BinOpKind::Shl |
127 hir::BinOpKind::Shr => {
128 Some("bitwise operation")
132 hir::ExprKind::Unary(..) => Some("unary operation"),
136 if let Some(must_use_op) = must_use_op {
137 cx.span_lint(UNUSED_MUST_USE, expr.span,
138 &format!("unused {} which must be used", must_use_op));
142 if !(ty_warned || fn_warned || op_warned) {
143 cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
146 fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool {
147 for attr in cx.tcx.get_attrs(def_id).iter() {
148 if attr.check_name("must_use") {
149 let msg = format!("unused {}`{}` which must be used",
150 describe_path, cx.tcx.item_path_str(def_id));
151 let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg);
152 // check for #[must_use = "..."]
153 if let Some(note) = attr.value_str() {
154 err.note(¬e.as_str());
168 "path statements with no effect"
171 #[derive(Copy, Clone)]
172 pub struct PathStatements;
174 impl LintPass for PathStatements {
175 fn get_lints(&self) -> LintArray {
176 lint_array!(PATH_STATEMENTS)
180 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
181 fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
182 if let hir::StmtKind::Semi(ref expr, _) = s.node {
183 if let hir::ExprKind::Path(_) = expr.node {
184 cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
191 pub UNUSED_ATTRIBUTES,
193 "detects attributes that were not used by the compiler"
196 #[derive(Copy, Clone)]
197 pub struct UnusedAttributes;
199 impl LintPass for UnusedAttributes {
200 fn get_lints(&self) -> LintArray {
201 lint_array!(UNUSED_ATTRIBUTES)
205 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
206 fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
207 debug!("checking attribute: {:?}", attr);
208 // Note that check_name() marks the attribute as used if it matches.
209 for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
211 AttributeType::Whitelisted if attr.check_name(name) => {
212 debug!("{:?} is Whitelisted", name);
219 let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
220 for &(ref name, ty) in plugin_attributes.iter() {
221 if ty == AttributeType::Whitelisted && attr.check_name(&name) {
222 debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
227 let name = attr.name();
228 if !attr::is_used(attr) {
229 debug!("Emitting warning for: {:?}", attr);
230 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
231 // Is it a builtin attribute that must be used at the crate level?
232 let known_crate = BUILTIN_ATTRIBUTES.iter()
233 .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel)
236 // Has a plugin registered this attribute as one which must be used at
238 let plugin_crate = plugin_attributes.iter()
239 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
241 if known_crate || plugin_crate {
242 let msg = match attr.style {
243 ast::AttrStyle::Outer => {
244 "crate-level attribute should be an inner attribute: add an exclamation \
247 ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
249 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
252 debug!("Attr was used: {:?}", attr);
258 pub(super) UNUSED_PARENS,
260 "`if`, `match`, `while` and `return` do not need parentheses"
263 #[derive(Copy, Clone)]
264 pub struct UnusedParens;
267 fn check_unused_parens_core(&self,
271 struct_lit_needs_parens: bool) {
272 if let ast::ExprKind::Paren(ref inner) = value.node {
273 let necessary = struct_lit_needs_parens &&
274 parser::contains_exterior_struct_lit(&inner);
276 let span_msg = format!("unnecessary parentheses around {}", msg);
277 let mut err = cx.struct_span_lint(UNUSED_PARENS,
280 // Remove exactly one pair of parentheses (rather than naïvely
281 // stripping all paren characters)
282 let mut ate_left_paren = false;
283 let mut ate_right_paren = false;
284 let parens_removed = pprust::expr_to_string(value)
291 ate_left_paren = true;
299 ate_right_paren = true;
306 err.span_suggestion_short(value.span,
307 "remove these parentheses",
315 impl LintPass for UnusedParens {
316 fn get_lints(&self) -> LintArray {
317 lint_array!(UNUSED_PARENS)
321 impl EarlyLintPass for UnusedParens {
322 fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
323 use syntax::ast::ExprKind::*;
324 let (value, msg, struct_lit_needs_parens) = match e.node {
325 If(ref cond, ..) => (cond, "`if` condition", true),
326 While(ref cond, ..) => (cond, "`while` condition", true),
327 IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
328 WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
329 ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
330 Match(ref head, _) => (head, "`match` head expression", true),
331 Ret(Some(ref value)) => (value, "`return` value", false),
332 Assign(_, ref value) => (value, "assigned value", false),
333 AssignOp(.., ref value) => (value, "assigned value", false),
334 // either function/method call, or something this lint doesn't care about
335 ref call_or_other => {
338 match *call_or_other {
339 Call(_, ref args) => {
340 call_kind = "function";
341 args_to_check = &args[..];
343 MethodCall(_, ref args) => {
344 call_kind = "method";
345 // first "argument" is self (which sometimes needs parens)
346 args_to_check = &args[1..];
348 // actual catch-all arm
351 // Don't lint if this is a nested macro expansion: otherwise, the lint could
352 // trigger in situations that macro authors shouldn't have to care about, e.g.,
353 // when a parenthesized token tree matched in one macro expansion is matched as
354 // an expression in another and used as a fn/method argument (Issue #47775)
355 if e.span.ctxt().outer().expn_info()
356 .map_or(false, |info| info.call_site.ctxt().outer()
357 .expn_info().is_some()) {
360 let msg = format!("{} argument", call_kind);
361 for arg in args_to_check {
362 self.check_unused_parens_core(cx, arg, &msg, false);
367 self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
370 fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
371 let (value, msg) = match s.node {
372 ast::StmtKind::Local(ref local) => {
374 Some(ref value) => (value, "assigned value"),
380 self.check_unused_parens_core(cx, &value, msg, false);
385 UNUSED_IMPORT_BRACES,
387 "unnecessary braces around an imported item"
390 #[derive(Copy, Clone)]
391 pub struct UnusedImportBraces;
393 impl UnusedImportBraces {
394 fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) {
395 if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
396 // Recursively check nested UseTrees
397 for &(ref tree, _) in items {
398 self.check_use_tree(cx, tree, item);
401 // Trigger the lint only if there is one nested item
402 if items.len() != 1 {
406 // Trigger the lint if the nested item is a non-self single item
408 match items[0].0.kind {
409 ast::UseTreeKind::Simple(rename, ..) => {
410 let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
411 if orig_ident.name == keywords::SelfValue.name() {
414 node_ident = rename.unwrap_or(orig_ident);
417 ast::UseTreeKind::Glob => {
418 node_ident = ast::Ident::from_str("*");
420 ast::UseTreeKind::Nested(_) => {
425 let msg = format!("braces around {} is unnecessary", node_ident.name);
426 cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
431 impl LintPass for UnusedImportBraces {
432 fn get_lints(&self) -> LintArray {
433 lint_array!(UNUSED_IMPORT_BRACES)
437 impl EarlyLintPass for UnusedImportBraces {
438 fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
439 if let ast::ItemKind::Use(ref use_tree) = item.node {
440 self.check_use_tree(cx, use_tree, item);
446 pub(super) UNUSED_ALLOCATION,
448 "detects unnecessary allocations that can be eliminated"
451 #[derive(Copy, Clone)]
452 pub struct UnusedAllocation;
454 impl LintPass for UnusedAllocation {
455 fn get_lints(&self) -> LintArray {
456 lint_array!(UNUSED_ALLOCATION)
460 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
461 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
463 hir::ExprKind::Box(_) => {}
467 for adj in cx.tables.expr_adjustments(e) {
468 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
470 adjustment::AutoBorrowMutability::Immutable =>
471 "unnecessary allocation, use & instead",
472 adjustment::AutoBorrowMutability::Mutable { .. }=>
473 "unnecessary allocation, use &mut instead"
475 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);