|
| 1 | +use rustc::lint::*; |
| 2 | +use rustc::hir::*; |
| 3 | +use syntax::ast::NodeId; |
| 4 | +use utils::{in_macro, match_def_path, match_trait_method, same_tys, snippet, span_lint_and_then}; |
| 5 | +use utils::{opt_def_id, paths, resolve_node}; |
| 6 | + |
| 7 | +/// **What it does:** Checks for always-identical `Into`/`From` conversions. |
| 8 | +/// |
| 9 | +/// **Why is this bad?** Redundant code. |
| 10 | +/// |
| 11 | +/// **Known problems:** None. |
| 12 | +/// |
| 13 | +/// **Example:** |
| 14 | +/// ```rust |
| 15 | +/// // format!() returns a `String` |
| 16 | +/// let s: String = format!("hello").into(); |
| 17 | +/// ``` |
| 18 | +declare_lint! { |
| 19 | + pub IDENTITY_CONVERSION, |
| 20 | + Warn, |
| 21 | + "using always-identical `Into`/`From` conversions" |
| 22 | +} |
| 23 | + |
| 24 | +#[derive(Default)] |
| 25 | +pub struct IdentityConversion { |
| 26 | + try_desugar_arm: Vec<NodeId>, |
| 27 | +} |
| 28 | + |
| 29 | +impl LintPass for IdentityConversion { |
| 30 | + fn get_lints(&self) -> LintArray { |
| 31 | + lint_array!(IDENTITY_CONVERSION) |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { |
| 36 | + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) { |
| 37 | + if in_macro(e.span) { |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + if Some(&e.id) == self.try_desugar_arm.last() { |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + match e.node { |
| 46 | + ExprMatch(_, ref arms, MatchSource::TryDesugar) => { |
| 47 | + let e = match arms[0].body.node { |
| 48 | + ExprRet(Some(ref e)) | ExprBreak(_, Some(ref e)) => e, |
| 49 | + _ => return, |
| 50 | + }; |
| 51 | + if let ExprCall(_, ref args) = e.node { |
| 52 | + self.try_desugar_arm.push(args[0].id); |
| 53 | + } else { |
| 54 | + return; |
| 55 | + } |
| 56 | + }, |
| 57 | + |
| 58 | + ExprMethodCall(ref name, .., ref args) => { |
| 59 | + if match_trait_method(cx, e, &paths::INTO[..]) && &*name.name.as_str() == "into" { |
| 60 | + let a = cx.tables.expr_ty(e); |
| 61 | + let b = cx.tables.expr_ty(&args[0]); |
| 62 | + if same_tys(cx, a, b) { |
| 63 | + let sugg = snippet(cx, args[0].span, "<expr>").into_owned(); |
| 64 | + span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| { |
| 65 | + db.span_suggestion(e.span, "consider removing `.into()`", sugg); |
| 66 | + }); |
| 67 | + } |
| 68 | + } |
| 69 | + }, |
| 70 | + |
| 71 | + ExprCall(ref path, ref args) => if let ExprPath(ref qpath) = path.node { |
| 72 | + if let Some(def_id) = opt_def_id(resolve_node(cx, qpath, path.hir_id)) { |
| 73 | + if match_def_path(cx.tcx, def_id, &paths::FROM_FROM[..]) { |
| 74 | + let a = cx.tables.expr_ty(e); |
| 75 | + let b = cx.tables.expr_ty(&args[0]); |
| 76 | + if same_tys(cx, a, b) { |
| 77 | + let sugg = snippet(cx, args[0].span, "<expr>").into_owned(); |
| 78 | + let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); |
| 79 | + span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| { |
| 80 | + db.span_suggestion(e.span, &sugg_msg, sugg); |
| 81 | + }); |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + }, |
| 86 | + |
| 87 | + _ => {}, |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr) { |
| 92 | + if Some(&e.id) == self.try_desugar_arm.last() { |
| 93 | + self.try_desugar_arm.pop(); |
| 94 | + } |
| 95 | + } |
| 96 | +} |
0 commit comments