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_id::DefId;
13 use rustc::ty::adjustment;
14 use lint::{LateContext, EarlyContext, LintContext, LintArray};
15 use lint::{LintPass, EarlyLintPass, LateLintPass};
19 use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
20 use syntax::print::pprust;
21 use syntax::symbol::keywords;
22 use syntax::util::parser;
30 "unused result of a type flagged as #[must_use]"
36 "unused result of an expression in a statement"
39 #[derive(Copy, Clone)]
40 pub struct UnusedResults;
42 impl LintPass for UnusedResults {
43 fn get_lints(&self) -> LintArray {
44 lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
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,
55 if let hir::ExprRet(..) = expr.node {
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() {
67 check_must_use(cx, def.did, s.span, "")
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, _) => {
79 hir::ExprPath(ref qpath) => {
80 Some(cx.tables.qpath_def(qpath, callee.hir_id))
85 hir::ExprMethodCall(..) => {
86 cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
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 ");
95 if let hir::ExprBinary(bin_op, ..) = expr.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);
111 if !(ty_warned || fn_warned || op_warned) {
112 cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
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() {
123 msg.push_str(&s.as_str());
125 cx.span_lint(UNUSED_MUST_USE, sp, &msg);
137 "path statements with no effect"
140 #[derive(Copy, Clone)]
141 pub struct PathStatements;
143 impl LintPass for PathStatements {
144 fn get_lints(&self) -> LintArray {
145 lint_array!(PATH_STATEMENTS)
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");
160 pub UNUSED_ATTRIBUTES,
162 "detects attributes that were not used by the compiler"
165 #[derive(Copy, Clone)]
166 pub struct UnusedAttributes;
168 impl LintPass for UnusedAttributes {
169 fn get_lints(&self) -> LintArray {
170 lint_array!(UNUSED_ATTRIBUTES)
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);
179 // Note that check_name() marks the attribute as used if it matches.
180 for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
182 AttributeType::Whitelisted if attr.check_name(name) => {
183 debug!("{:?} is Whitelisted", name);
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);
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)
206 // Has a plugin registered this attribute as one which must be used at
208 let plugin_crate = plugin_attributes.iter()
209 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
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 \
217 ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
219 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
222 debug!("Attr was used: {:?}", attr);
230 "`if`, `match`, `while` and `return` do not need parentheses"
233 #[derive(Copy, Clone)]
234 pub struct UnusedParens;
237 fn check_unused_parens_core(&self,
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);
246 let span_msg = format!("unnecessary parentheses around {}", msg);
247 let mut err = cx.struct_span_lint(UNUSED_PARENS,
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)
261 ate_left_paren = true;
269 ate_right_paren = true;
276 err.span_suggestion_short(value.span,
277 "remove these parentheses",
285 impl LintPass for UnusedParens {
286 fn get_lints(&self) -> LintArray {
287 lint_array!(UNUSED_PARENS)
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),
307 self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
310 fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
311 let (value, msg) = match s.node {
312 ast::StmtKind::Local(ref local) => {
314 Some(ref value) => (value, "assigned value"),
320 self.check_unused_parens_core(cx, &value, msg, false);
325 UNUSED_IMPORT_BRACES,
327 "unnecessary braces around an imported item"
330 #[derive(Copy, Clone)]
331 pub struct UnusedImportBraces;
333 impl LintPass for UnusedImportBraces {
334 fn get_lints(&self) -> LintArray {
335 lint_array!(UNUSED_IMPORT_BRACES)
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);
355 "detects unnecessary allocations that can be eliminated"
358 #[derive(Copy, Clone)]
359 pub struct UnusedAllocation;
361 impl LintPass for UnusedAllocation {
362 fn get_lints(&self) -> LintArray {
363 lint_array!(UNUSED_ALLOCATION)
367 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
368 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
370 hir::ExprBox(_) => {}
374 for adj in cx.tables.expr_adjustments(e) {
375 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
377 hir::MutImmutable => "unnecessary allocation, use & instead",
378 hir::MutMutable => "unnecessary allocation, use &mut instead"
380 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);