]>
Commit | Line | Data |
---|---|---|
b039eaaf SL |
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 | ||
54a0048b SL |
11 | use rustc::hir::pat_util; |
12 | use rustc::ty; | |
13 | use rustc::ty::adjustment; | |
b039eaaf SL |
14 | use util::nodemap::FnvHashMap; |
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::{self, AttrMetaMethods}; | |
b039eaaf SL |
22 | use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType}; |
23 | use syntax::ptr::P; | |
3157f602 | 24 | use syntax_pos::Span; |
b039eaaf SL |
25 | |
26 | use rustc_back::slice; | |
54a0048b SL |
27 | use rustc::hir; |
28 | use rustc::hir::intravisit::FnKind; | |
b039eaaf SL |
29 | |
30 | declare_lint! { | |
31 | pub UNUSED_MUT, | |
32 | Warn, | |
33 | "detect mut variables which don't need to be mutable" | |
34 | } | |
35 | ||
36 | #[derive(Copy, Clone)] | |
37 | pub struct UnusedMut; | |
38 | ||
39 | impl UnusedMut { | |
40 | fn check_unused_mut_pat(&self, cx: &LateContext, pats: &[P<hir::Pat>]) { | |
41 | // collect all mutable pattern and group their NodeIDs by their Identifier to | |
42 | // avoid false warnings in match arms with multiple patterns | |
43 | ||
44 | let mut mutables = FnvHashMap(); | |
45 | for p in pats { | |
3157f602 | 46 | pat_util::pat_bindings(p, |mode, id, _, path1| { |
b039eaaf SL |
47 | let name = path1.node; |
48 | if let hir::BindByValue(hir::MutMutable) = mode { | |
49 | if !name.as_str().starts_with("_") { | |
50 | match mutables.entry(name.0 as usize) { | |
51 | Vacant(entry) => { entry.insert(vec![id]); }, | |
52 | Occupied(mut entry) => { entry.get_mut().push(id); }, | |
53 | } | |
54 | } | |
55 | } | |
56 | }); | |
57 | } | |
58 | ||
59 | let used_mutables = cx.tcx.used_mut_nodes.borrow(); | |
60 | for (_, v) in &mutables { | |
61 | if !v.iter().any(|e| used_mutables.contains(e)) { | |
62 | cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]), | |
63 | "variable does not need to be mutable"); | |
64 | } | |
65 | } | |
66 | } | |
67 | } | |
68 | ||
69 | impl LintPass for UnusedMut { | |
70 | fn get_lints(&self) -> LintArray { | |
71 | lint_array!(UNUSED_MUT) | |
72 | } | |
73 | } | |
74 | ||
75 | impl LateLintPass for UnusedMut { | |
76 | fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { | |
77 | if let hir::ExprMatch(_, ref arms, _) = e.node { | |
78 | for a in arms { | |
79 | self.check_unused_mut_pat(cx, &a.pats) | |
80 | } | |
81 | } | |
82 | } | |
83 | ||
84 | fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { | |
85 | if let hir::StmtDecl(ref d, _) = s.node { | |
86 | if let hir::DeclLocal(ref l) = d.node { | |
87 | self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat)); | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
92 | fn check_fn(&mut self, cx: &LateContext, | |
93 | _: FnKind, decl: &hir::FnDecl, | |
94 | _: &hir::Block, _: Span, _: ast::NodeId) { | |
95 | for a in &decl.inputs { | |
96 | self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat)); | |
97 | } | |
98 | } | |
99 | } | |
100 | ||
101 | declare_lint! { | |
102 | pub UNUSED_MUST_USE, | |
103 | Warn, | |
104 | "unused result of a type flagged as #[must_use]" | |
105 | } | |
106 | ||
107 | declare_lint! { | |
108 | pub UNUSED_RESULTS, | |
109 | Allow, | |
110 | "unused result of an expression in a statement" | |
111 | } | |
112 | ||
113 | #[derive(Copy, Clone)] | |
114 | pub struct UnusedResults; | |
115 | ||
116 | impl LintPass for UnusedResults { | |
117 | fn get_lints(&self) -> LintArray { | |
118 | lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS) | |
119 | } | |
120 | } | |
121 | ||
122 | impl LateLintPass for UnusedResults { | |
123 | fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { | |
124 | let expr = match s.node { | |
125 | hir::StmtSemi(ref expr, _) => &**expr, | |
126 | _ => return | |
127 | }; | |
128 | ||
129 | if let hir::ExprRet(..) = expr.node { | |
130 | return; | |
131 | } | |
132 | ||
133 | let t = cx.tcx.expr_ty(&expr); | |
134 | let warned = match t.sty { | |
135 | ty::TyTuple(ref tys) if tys.is_empty() => return, | |
5bcae85e | 136 | ty::TyNever => return, |
b039eaaf SL |
137 | ty::TyBool => return, |
138 | ty::TyStruct(def, _) | | |
139 | ty::TyEnum(def, _) => { | |
92a42be0 SL |
140 | let attrs = cx.tcx.get_attrs(def.did); |
141 | check_must_use(cx, &attrs[..], s.span) | |
b039eaaf SL |
142 | } |
143 | _ => false, | |
144 | }; | |
145 | if !warned { | |
146 | cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); | |
147 | } | |
148 | ||
149 | fn check_must_use(cx: &LateContext, attrs: &[ast::Attribute], sp: Span) -> bool { | |
150 | for attr in attrs { | |
151 | if attr.check_name("must_use") { | |
152 | let mut msg = "unused result which must be used".to_string(); | |
153 | // check for #[must_use="..."] | |
3157f602 XL |
154 | if let Some(s) = attr.value_str() { |
155 | msg.push_str(": "); | |
156 | msg.push_str(&s); | |
b039eaaf SL |
157 | } |
158 | cx.span_lint(UNUSED_MUST_USE, sp, &msg); | |
159 | return true; | |
160 | } | |
161 | } | |
162 | false | |
163 | } | |
164 | } | |
165 | } | |
166 | ||
167 | declare_lint! { | |
168 | pub UNUSED_UNSAFE, | |
169 | Warn, | |
170 | "unnecessary use of an `unsafe` block" | |
171 | } | |
172 | ||
173 | #[derive(Copy, Clone)] | |
174 | pub struct UnusedUnsafe; | |
175 | ||
176 | impl LintPass for UnusedUnsafe { | |
177 | fn get_lints(&self) -> LintArray { | |
178 | lint_array!(UNUSED_UNSAFE) | |
179 | } | |
180 | } | |
181 | ||
182 | impl LateLintPass for UnusedUnsafe { | |
183 | fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { | |
184 | if let hir::ExprBlock(ref blk) = e.node { | |
185 | // Don't warn about generated blocks, that'll just pollute the output. | |
186 | if blk.rules == hir::UnsafeBlock(hir::UserProvided) && | |
187 | !cx.tcx.used_unsafe.borrow().contains(&blk.id) { | |
188 | cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block"); | |
189 | } | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | declare_lint! { | |
195 | pub PATH_STATEMENTS, | |
196 | Warn, | |
197 | "path statements with no effect" | |
198 | } | |
199 | ||
200 | #[derive(Copy, Clone)] | |
201 | pub struct PathStatements; | |
202 | ||
203 | impl LintPass for PathStatements { | |
204 | fn get_lints(&self) -> LintArray { | |
205 | lint_array!(PATH_STATEMENTS) | |
206 | } | |
207 | } | |
208 | ||
209 | impl LateLintPass for PathStatements { | |
210 | fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { | |
92a42be0 SL |
211 | if let hir::StmtSemi(ref expr, _) = s.node { |
212 | if let hir::ExprPath(..) = expr.node { | |
213 | cx.span_lint(PATH_STATEMENTS, s.span, | |
214 | "path statement with no effect"); | |
b039eaaf | 215 | } |
b039eaaf SL |
216 | } |
217 | } | |
218 | } | |
219 | ||
220 | declare_lint! { | |
221 | pub UNUSED_ATTRIBUTES, | |
222 | Warn, | |
223 | "detects attributes that were not used by the compiler" | |
224 | } | |
225 | ||
226 | #[derive(Copy, Clone)] | |
227 | pub struct UnusedAttributes; | |
228 | ||
229 | impl LintPass for UnusedAttributes { | |
230 | fn get_lints(&self) -> LintArray { | |
231 | lint_array!(UNUSED_ATTRIBUTES) | |
232 | } | |
233 | } | |
234 | ||
235 | impl LateLintPass for UnusedAttributes { | |
236 | fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) { | |
237 | // Note that check_name() marks the attribute as used if it matches. | |
238 | for &(ref name, ty, _) in KNOWN_ATTRIBUTES { | |
239 | match ty { | |
240 | AttributeType::Whitelisted if attr.check_name(name) => { | |
241 | break; | |
242 | }, | |
243 | _ => () | |
244 | } | |
245 | } | |
246 | ||
247 | let plugin_attributes = cx.sess().plugin_attributes.borrow_mut(); | |
248 | for &(ref name, ty) in plugin_attributes.iter() { | |
7453a54e | 249 | if ty == AttributeType::Whitelisted && attr.check_name(&name) { |
b039eaaf SL |
250 | break; |
251 | } | |
252 | } | |
253 | ||
254 | if !attr::is_used(attr) { | |
255 | cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute"); | |
256 | // Is it a builtin attribute that must be used at the crate level? | |
257 | let known_crate = KNOWN_ATTRIBUTES.iter().find(|&&(name, ty, _)| { | |
258 | attr.name() == name && | |
259 | ty == AttributeType::CrateLevel | |
260 | }).is_some(); | |
261 | ||
262 | // Has a plugin registered this attribute as one which must be used at | |
263 | // the crate level? | |
264 | let plugin_crate = plugin_attributes.iter() | |
265 | .find(|&&(ref x, t)| { | |
7453a54e | 266 | &*attr.name() == x && |
b039eaaf SL |
267 | AttributeType::CrateLevel == t |
268 | }).is_some(); | |
269 | if known_crate || plugin_crate { | |
270 | let msg = match attr.node.style { | |
271 | ast::AttrStyle::Outer => "crate-level attribute should be an inner \ | |
272 | attribute: add an exclamation mark: #![foo]", | |
273 | ast::AttrStyle::Inner => "crate-level attribute should be in the \ | |
274 | root module", | |
275 | }; | |
276 | cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg); | |
277 | } | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | declare_lint! { | |
283 | UNUSED_PARENS, | |
284 | Warn, | |
285 | "`if`, `match`, `while` and `return` do not need parentheses" | |
286 | } | |
287 | ||
288 | #[derive(Copy, Clone)] | |
289 | pub struct UnusedParens; | |
290 | ||
291 | impl UnusedParens { | |
292 | fn check_unused_parens_core(&self, cx: &EarlyContext, value: &ast::Expr, msg: &str, | |
293 | struct_lit_needs_parens: bool) { | |
7453a54e SL |
294 | if let ast::ExprKind::Paren(ref inner) = value.node { |
295 | let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&inner); | |
b039eaaf SL |
296 | if !necessary { |
297 | cx.span_lint(UNUSED_PARENS, value.span, | |
298 | &format!("unnecessary parentheses around {}", msg)) | |
299 | } | |
300 | } | |
301 | ||
302 | /// Expressions that syntactically contain an "exterior" struct | |
303 | /// literal i.e. not surrounded by any parens or other | |
304 | /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo | |
305 | /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X { | |
306 | /// y: 1 }) == foo` does not. | |
307 | fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
308 | match value.node { | |
7453a54e | 309 | ast::ExprKind::Struct(..) => true, |
b039eaaf | 310 | |
7453a54e SL |
311 | ast::ExprKind::Assign(ref lhs, ref rhs) | |
312 | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) | | |
313 | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { | |
b039eaaf | 314 | // X { y: 1 } + X { y: 2 } |
7453a54e SL |
315 | contains_exterior_struct_lit(&lhs) || |
316 | contains_exterior_struct_lit(&rhs) | |
b039eaaf | 317 | } |
7453a54e SL |
318 | ast::ExprKind::Unary(_, ref x) | |
319 | ast::ExprKind::Cast(ref x, _) | | |
320 | ast::ExprKind::Type(ref x, _) | | |
321 | ast::ExprKind::Field(ref x, _) | | |
322 | ast::ExprKind::TupField(ref x, _) | | |
323 | ast::ExprKind::Index(ref x, _) => { | |
b039eaaf | 324 | // &X { y: 1 }, X { y: 1 }.y |
7453a54e | 325 | contains_exterior_struct_lit(&x) |
b039eaaf SL |
326 | } |
327 | ||
7453a54e | 328 | ast::ExprKind::MethodCall(_, _, ref exprs) => { |
b039eaaf | 329 | // X { y: 1 }.bar(...) |
7453a54e | 330 | contains_exterior_struct_lit(&exprs[0]) |
b039eaaf SL |
331 | } |
332 | ||
333 | _ => false | |
334 | } | |
335 | } | |
336 | } | |
337 | } | |
338 | ||
339 | impl LintPass for UnusedParens { | |
340 | fn get_lints(&self) -> LintArray { | |
341 | lint_array!(UNUSED_PARENS) | |
342 | } | |
343 | } | |
344 | ||
345 | impl EarlyLintPass for UnusedParens { | |
346 | fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) { | |
7453a54e | 347 | use syntax::ast::ExprKind::*; |
b039eaaf | 348 | let (value, msg, struct_lit_needs_parens) = match e.node { |
7453a54e SL |
349 | If(ref cond, _, _) => (cond, "`if` condition", true), |
350 | While(ref cond, _, _) => (cond, "`while` condition", true), | |
351 | IfLet(_, ref cond, _, _) => (cond, "`if let` head expression", true), | |
352 | WhileLet(_, ref cond, _, _) => (cond, "`while let` head expression", true), | |
353 | ForLoop(_, ref cond, _, _) => (cond, "`for` head expression", true), | |
354 | Match(ref head, _) => (head, "`match` head expression", true), | |
355 | Ret(Some(ref value)) => (value, "`return` value", false), | |
356 | Assign(_, ref value) => (value, "assigned value", false), | |
357 | AssignOp(_, _, ref value) => (value, "assigned value", false), | |
358 | InPlace(_, ref value) => (value, "emplacement value", false), | |
b039eaaf SL |
359 | _ => return |
360 | }; | |
7453a54e | 361 | self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens); |
b039eaaf SL |
362 | } |
363 | ||
364 | fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) { | |
365 | let (value, msg) = match s.node { | |
3157f602 XL |
366 | ast::StmtKind::Local(ref local) => match local.init { |
367 | Some(ref value) => (value, "assigned value"), | |
368 | None => return | |
b039eaaf SL |
369 | }, |
370 | _ => return | |
371 | }; | |
7453a54e | 372 | self.check_unused_parens_core(cx, &value, msg, false); |
b039eaaf SL |
373 | } |
374 | } | |
375 | ||
376 | declare_lint! { | |
377 | UNUSED_IMPORT_BRACES, | |
378 | Allow, | |
379 | "unnecessary braces around an imported item" | |
380 | } | |
381 | ||
382 | #[derive(Copy, Clone)] | |
383 | pub struct UnusedImportBraces; | |
384 | ||
385 | impl LintPass for UnusedImportBraces { | |
386 | fn get_lints(&self) -> LintArray { | |
387 | lint_array!(UNUSED_IMPORT_BRACES) | |
388 | } | |
389 | } | |
390 | ||
391 | impl LateLintPass for UnusedImportBraces { | |
392 | fn check_item(&mut self, cx: &LateContext, item: &hir::Item) { | |
393 | if let hir::ItemUse(ref view_path) = item.node { | |
394 | if let hir::ViewPathList(_, ref items) = view_path.node { | |
395 | if items.len() == 1 { | |
396 | if let hir::PathListIdent {ref name, ..} = items[0].node { | |
397 | let m = format!("braces around {} is unnecessary", | |
398 | name); | |
399 | cx.span_lint(UNUSED_IMPORT_BRACES, item.span, | |
400 | &m[..]); | |
401 | } | |
402 | } | |
403 | } | |
404 | } | |
405 | } | |
406 | } | |
407 | ||
408 | declare_lint! { | |
409 | UNUSED_ALLOCATION, | |
410 | Warn, | |
411 | "detects unnecessary allocations that can be eliminated" | |
412 | } | |
413 | ||
414 | #[derive(Copy, Clone)] | |
415 | pub struct UnusedAllocation; | |
416 | ||
417 | impl LintPass for UnusedAllocation { | |
418 | fn get_lints(&self) -> LintArray { | |
419 | lint_array!(UNUSED_ALLOCATION) | |
420 | } | |
421 | } | |
422 | ||
423 | impl LateLintPass for UnusedAllocation { | |
424 | fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { | |
425 | match e.node { | |
426 | hir::ExprBox(_) => {} | |
427 | _ => return | |
428 | } | |
429 | ||
430 | if let Some(adjustment) = cx.tcx.tables.borrow().adjustments.get(&e.id) { | |
431 | if let adjustment::AdjustDerefRef(adjustment::AutoDerefRef { | |
432 | ref autoref, .. | |
433 | }) = *adjustment { | |
434 | match autoref { | |
435 | &Some(adjustment::AutoPtr(_, hir::MutImmutable)) => { | |
436 | cx.span_lint(UNUSED_ALLOCATION, e.span, | |
437 | "unnecessary allocation, use & instead"); | |
438 | } | |
439 | &Some(adjustment::AutoPtr(_, hir::MutMutable)) => { | |
440 | cx.span_lint(UNUSED_ALLOCATION, e.span, | |
441 | "unnecessary allocation, use &mut instead"); | |
442 | } | |
443 | _ => () | |
444 | } | |
445 | } | |
446 | } | |
447 | } | |
448 | } |