]> git.lizzy.rs Git - rust.git/commitdiff
Merge branch 'master' into sugg
authormcarton <cartonmartin+git@gmail.com>
Sun, 3 Jul 2016 21:56:06 +0000 (23:56 +0200)
committermcarton <cartonmartin+git@gmail.com>
Sun, 3 Jul 2016 22:51:19 +0000 (00:51 +0200)
30 files changed:
README.md
clippy_lints/src/array_indexing.rs
clippy_lints/src/assign_ops.rs
clippy_lints/src/collapsible_if.rs
clippy_lints/src/entry.rs
clippy_lints/src/formatting.rs
clippy_lints/src/lib.rs
clippy_lints/src/loops.rs
clippy_lints/src/matches.rs
clippy_lints/src/methods.rs
clippy_lints/src/misc.rs
clippy_lints/src/misc_early.rs
clippy_lints/src/mut_mut.rs
clippy_lints/src/ranges.rs
clippy_lints/src/shadow.rs
clippy_lints/src/swap.rs
clippy_lints/src/transmute.rs
clippy_lints/src/types.rs
clippy_lints/src/utils/higher.rs [new file with mode: 0644]
clippy_lints/src/utils/mod.rs
clippy_lints/src/utils/sugg.rs [new file with mode: 0644]
clippy_lints/src/vec.rs
tests/compile-fail/assign_ops.rs
tests/compile-fail/collapsible_if.rs
tests/compile-fail/entry.rs
tests/compile-fail/float_cmp.rs
tests/compile-fail/for_loop.rs
tests/compile-fail/matches.rs
tests/compile-fail/toplevel_ref_arg.rs
tests/compile-fail/transmute.rs

index 3159a3e8979c8219320cb904b932bf1e904ad7e9..c413b6a0dae7ba5e257a8d8307423c9518b478d3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ name
 [almost_swapped](https://github.com/Manishearth/rust-clippy/wiki#almost_swapped)                                     | warn    | `foo = bar; bar = foo` sequence
 [approx_constant](https://github.com/Manishearth/rust-clippy/wiki#approx_constant)                                   | warn    | the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) is found; suggests to use the constant
 [assign_op_pattern](https://github.com/Manishearth/rust-clippy/wiki#assign_op_pattern)                               | warn    | assigning the result of an operation on a variable to that same variable
-[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops)                                             | allow   | Any assignment operation
+[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops)                                             | allow   | any assignment operation
 [bad_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#bad_bit_mask)                                         | warn    | expressions of the form `_ & mask == select` that will only ever return `true` or `false` (because in the example `select` containing bits that `mask` doesn't have)
 [blacklisted_name](https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name)                                 | warn    | usage of a blacklisted/placeholder name
 [block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr)             | warn    | braces can be eliminated in conditions that are expressions, e.g `if { true } ...`
index f3b7297b2969326938e7ecf64e00b20f26fec603..2c84c7cc13213fcf3bb00f76a3e8eb7386c4c5ef 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_const_math::ConstInt;
 use rustc::hir::*;
 use syntax::ast::RangeLimits;
-use utils;
+use utils::{self, higher};
 
 /// **What it does:** Check for out of bounds array indexing with a constant index.
 ///
@@ -77,7 +77,7 @@ fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
                 }
 
                 // Index is a constant range
-                if let Some(range) = utils::unsugar_range(index) {
+                if let Some(range) = higher::range(index) {
                     let start = range.start
                         .map(|start| eval_const_expr_partial(cx.tcx, start, ExprTypeChecked, None))
                         .map(|v| v.ok());
@@ -94,7 +94,7 @@ fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
                 }
             }
 
-            if let Some(range) = utils::unsugar_range(index) {
+            if let Some(range) = higher::range(index) {
                 // Full ranges are always valid
                 if range.start.is_none() && range.end.is_none() {
                     return;
index 1a5ca16b9c574561bdfd7ca8413c94267eab44c6..6ae76f9c3eff93607f0c04ca48f50bf3af3db9f6 100644 (file)
@@ -1,13 +1,14 @@
 use rustc::hir;
 use rustc::lint::*;
 use utils::{span_lint_and_then, span_lint, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
+use utils::{higher, sugg};
 
-/// **What it does:** This lint checks for `+=` operations and similar
+/// **What it does:** This lint checks for `+=` operations and similar.
 ///
-/// **Why is this bad?** Projects with many developers from languages without those operations
-///                      may find them unreadable and not worth their weight
+/// **Why is this bad?** Projects with many developers from languages without those operations may
+/// find them unreadable and not worth their weight.
 ///
-/// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`
+/// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`.
 ///
 /// **Example:**
 /// ```
 /// ```
 declare_restriction_lint! {
     pub ASSIGN_OPS,
-    "Any assignment operation"
+    "any assignment operation"
 }
 
-/// **What it does:** Check for `a = a op b` or `a = b commutative_op a` patterns
+/// **What it does:** Check for `a = a op b` or `a = b commutative_op a` patterns.
 ///
-/// **Why is this bad?** These can be written as the shorter `a op= b`
+/// **Why is this bad?** These can be written as the shorter `a op= b`.
 ///
-/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have implementations that differ from the regular `Op` impl
+/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have implementations that differ from the regular `Op` impl.
 ///
 /// **Example:**
 ///
@@ -50,24 +51,14 @@ impl LateLintPass for AssignOps {
     fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
         match expr.node {
             hir::ExprAssignOp(op, ref lhs, ref rhs) => {
-                if let (Some(l), Some(r)) = (snippet_opt(cx, lhs.span), snippet_opt(cx, rhs.span)) {
-                    span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
-                        match rhs.node {
-                            hir::ExprBinary(op2, _, _) if op2 != op => {
-                                db.span_suggestion(expr.span,
-                                                   "replace it with",
-                                                   format!("{} = {} {} ({})", l, l, op.node.as_str(), r));
-                            }
-                            _ => {
-                                db.span_suggestion(expr.span,
-                                                   "replace it with",
-                                                   format!("{} = {} {} {}", l, l, op.node.as_str(), r));
-                            }
-                        }
-                    });
-                } else {
-                    span_lint(cx, ASSIGN_OPS, expr.span, "assign operation detected");
-                }
+                span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
+                    let lhs = &sugg::Sugg::hir(cx, lhs, "..");
+                    let rhs = &sugg::Sugg::hir(cx, rhs, "..");
+
+                    db.span_suggestion(expr.span,
+                                       "replace it with",
+                                       format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)));
+                });
             }
             hir::ExprAssign(ref assignee, ref e) => {
                 if let hir::ExprBinary(op, ref l, ref r) = e.node {
index 5725203727099b132014c28dd882a1b4589bd8f8..ca5a097119db9413b49600081cbc3937e6a4d5ab 100644 (file)
 //! This lint is **warn** by default
 
 use rustc::lint::*;
-use std::borrow::Cow;
-use syntax::codemap::Spanned;
 use syntax::ast;
 
-use utils::{in_macro, snippet, snippet_block, span_lint_and_then};
+use utils::{in_macro, snippet_block, span_lint_and_then};
+use utils::sugg::Sugg;
 
 /// **What it does:** This lint checks for nested `if`-statements which can be collapsed by
 /// `&&`-combining their conditions and for `else { if .. }` expressions that can be collapsed to
@@ -103,31 +102,17 @@ fn check_collapsible_no_if_let(
             return;
         }
         span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
+            let lhs = Sugg::ast(cx, check, "..");
+            let rhs = Sugg::ast(cx, check_inner, "..");
             db.span_suggestion(expr.span,
                                "try",
-                               format!("if {} && {} {}",
-                                       check_to_string(cx, check),
-                                       check_to_string(cx, check_inner),
+                               format!("if {} {}",
+                                       lhs.and(rhs),
                                        snippet_block(cx, content.span, "..")));
         });
     }}
 }
 
-fn requires_brackets(e: &ast::Expr) -> bool {
-    match e.node {
-        ast::ExprKind::Binary(Spanned { node: n, .. }, _, _) if n == ast::BinOpKind::Eq => false,
-        _ => true,
-    }
-}
-
-fn check_to_string(cx: &EarlyContext, e: &ast::Expr) -> Cow<'static, str> {
-    if requires_brackets(e) {
-        format!("({})", snippet(cx, e.span, "..")).into()
-    } else {
-        snippet(cx, e.span, "..")
-    }
-}
-
 /// If the block contains only one expression, returns it.
 fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
     let mut it = block.stmts.iter();
index bc209fd4846e8b0e98725e60315fba65d7e9bfa2..c7afc2d5cd9eff6afbdc9b8ced8c6f5ebbc1b24c 100644 (file)
@@ -121,21 +121,21 @@ fn visit_expr(&mut self, expr: &'v Expr) {
             SpanlessEq::new(self.cx).eq_expr(self.key, &params[1])
         ], {
             span_lint_and_then(self.cx, MAP_ENTRY, self.span,
-                               &format!("usage of `contains_key` followed by `insert` on `{}`", self.ty), |db| {
+                               &format!("usage of `contains_key` followed by `insert` on `{}`", self.ty), |db| {
                 if self.sole_expr {
                     let help = format!("{}.entry({}).or_insert({})",
                                        snippet(self.cx, self.map.span, "map"),
                                        snippet(self.cx, params[1].span, ".."),
                                        snippet(self.cx, params[2].span, ".."));
 
-                    db.span_suggestion(self.span, "Consider using", help);
+                    db.span_suggestion(self.span, "consider using", help);
                 }
                 else {
-                    let help = format!("Consider using `{}.entry({})`",
+                    let help = format!("{}.entry({})",
                                        snippet(self.cx, self.map.span, "map"),
                                        snippet(self.cx, params[1].span, ".."));
 
-                    db.span_note(self.span, &help);
+                    db.span_suggestion(self.span, "consider using", help);
                 }
             });
         }}
index 930fd7ae9a66c1c2e6ac7515d189f22984d762b7..0bc8c2bfd961eef1f0f5e7919c02767c55bb8e67 100644 (file)
@@ -148,7 +148,7 @@ fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Exp
     }
 }
 
-/// Match `if` or `else if` expressions and return the `then` and `else` block.
+/// Match `if` or `if let` expressions and return the `then` and `else` block.
 fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
     match expr.node {
         ast::ExprKind::If(_, ref then, ref else_) |
index 5ba52374135ef79b35ae2afdb5d525172acda9b8..411be22411847f38bb34510bfcfd0d3657e8c757 100644 (file)
@@ -3,6 +3,7 @@
 #![feature(box_syntax)]
 #![feature(collections)]
 #![feature(custom_attribute)]
+#![feature(dotdot_in_tuple_patterns)]
 #![feature(iter_arith)]
 #![feature(question_mark)]
 #![feature(rustc_private)]
@@ -37,6 +38,7 @@
 
 extern crate rustc_serialize;
 
+extern crate rustc_errors;
 extern crate rustc_plugin;
 extern crate rustc_const_eval;
 extern crate rustc_const_math;
index 1816d88bddb59d4cff30e7690a16830b1169ca24..a4e338053e27b003add995aafcd537020e36d6e1 100644 (file)
@@ -9,15 +9,14 @@
 use rustc::ty;
 use rustc_const_eval::EvalHint::ExprTypeChecked;
 use rustc_const_eval::eval_const_expr_partial;
-use std::borrow::Cow;
 use std::collections::HashMap;
 use syntax::ast;
+use utils::sugg;
 
-use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, in_external_macro,
-            span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, unsugar_range,
-            walk_ptrs_ty, recover_for_loop};
+use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro,
+            span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher,
+            walk_ptrs_ty};
 use utils::paths;
-use utils::UnsugaredRange;
 
 /// **What it does:** This lint checks for looping over the range of `0..len` of some collection just to get the values by index.
 ///
@@ -224,7 +223,7 @@ fn get_lints(&self) -> LintArray {
 
 impl LateLintPass for Pass {
     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
-        if let Some((pat, arg, body)) = recover_for_loop(expr) {
+        if let Some((pat, arg, body)) = higher::for_loop(expr) {
             check_for_loop(cx, pat, arg, body, expr);
         }
         // check for `loop { if let {} else break }` that could be `while let`
@@ -333,7 +332,7 @@ fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &E
 /// Check for looping over a range and then indexing a sequence with it.
 /// The iteratee must be a range literal.
 fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
-    if let Some(UnsugaredRange { start: Some(ref start), ref end, .. }) = unsugar_range(arg) {
+    if let Some(higher::Range { start: Some(ref start), ref end, limits }) = higher::range(arg) {
         // the var must be a single name
         if let PatKind::Binding(_, ref ident, _) = pat.node {
             let mut visitor = VarVisitor {
@@ -361,34 +360,41 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
 
                 let starts_at_zero = is_integer_literal(start, 0);
 
-                let skip: Cow<_> = if starts_at_zero {
-                    "".into()
+                let skip = if starts_at_zero {
+                    "".to_owned()
                 } else {
-                    format!(".skip({})", snippet(cx, start.span, "..")).into()
+                    format!(".skip({})", snippet(cx, start.span, ".."))
                 };
 
-                let take: Cow<_> = if let Some(ref end) = *end {
+                let take = if let Some(ref end) = *end {
                     if is_len_call(end, &indexed) {
-                        "".into()
+                        "".to_owned()
                     } else {
-                        format!(".take({})", snippet(cx, end.span, "..")).into()
+                        match limits {
+                            ast::RangeLimits::Closed => {
+                                let end = sugg::Sugg::hir(cx, end, "<count>");
+                                format!(".take({})", end + sugg::ONE)
+                            }
+                            ast::RangeLimits::HalfOpen => {
+                                format!(".take({})", snippet(cx, end.span, ".."))
+                            }
+                        }
                     }
                 } else {
-                    "".into()
+                    "".to_owned()
                 };
 
                 if visitor.nonindex {
-                    span_lint(cx,
-                              NEEDLESS_RANGE_LOOP,
-                              expr.span,
-                              &format!("the loop variable `{}` is used to index `{}`. Consider using `for ({}, \
-                                        item) in {}.iter().enumerate(){}{}` or similar iterators",
-                                       ident.node,
-                                       indexed,
-                                       ident.node,
-                                       indexed,
-                                       take,
-                                       skip));
+                    span_lint_and_then(cx,
+                                       NEEDLESS_RANGE_LOOP,
+                                       expr.span,
+                                       &format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
+                                       |db| {
+                        multispan_sugg(db, "consider using an iterator".to_string(), &[
+                            (pat.span, &format!("({}, <item>)", ident.node)),
+                            (arg.span, &format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
+                        ]);
+                    });
                 } else {
                     let repl = if starts_at_zero && take.is_empty() {
                         format!("&{}", indexed)
@@ -396,14 +402,16 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
                         format!("{}.iter(){}{}", indexed, take, skip)
                     };
 
-                    span_lint(cx,
-                              NEEDLESS_RANGE_LOOP,
-                              expr.span,
-                              &format!("the loop variable `{}` is only used to index `{}`. \
-                                        Consider using `for item in {}` or similar iterators",
-                                       ident.node,
-                                       indexed,
-                                       repl));
+                    span_lint_and_then(cx,
+                                       NEEDLESS_RANGE_LOOP,
+                                       expr.span,
+                                       &format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
+                                       |db| {
+                        multispan_sugg(db, "consider using an iterator".to_string(), &[
+                            (pat.span, "<item>"),
+                            (arg.span, &repl),
+                        ]);
+                    });
                 }
             }
         }
@@ -427,7 +435,7 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
 
 fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
     // if this for loop is iterating over a two-sided range...
-    if let Some(UnsugaredRange { start: Some(ref start), end: Some(ref end), limits }) = unsugar_range(arg) {
+    if let Some(higher::Range { start: Some(ref start), end: Some(ref end), limits }) = higher::range(arg) {
         // ...and both sides are compile-time constant integers...
         if let Ok(start_idx) = eval_const_expr_partial(cx.tcx, start, ExprTypeChecked, None) {
             if let Ok(end_idx) = eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None) {
@@ -588,18 +596,20 @@ fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, ex
 
 /// Check for the `FOR_KV_MAP` lint.
 fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
+    let pat_span = pat.span;
+
     if let PatKind::Tuple(ref pat, _) = pat.node {
         if pat.len() == 2 {
-            let (pat_span, kind) = match (&pat[0].node, &pat[1].node) {
-                (key, _) if pat_is_wild(key, body) => (&pat[1].span, "values"),
-                (_, value) if pat_is_wild(value, body) => (&pat[0].span, "keys"),
+            let (new_pat_span, kind) = match (&pat[0].node, &pat[1].node) {
+                (key, _) if pat_is_wild(key, body) => (pat[1].span, "value"),
+                (_, value) if pat_is_wild(value, body) => (pat[0].span, "key"),
                 _ => return,
             };
 
-            let arg_span = match arg.node {
-                ExprAddrOf(MutImmutable, ref expr) => expr.span,
+            let (arg_span, arg) = match arg.node {
+                ExprAddrOf(MutImmutable, ref expr) => (arg.span, &**expr),
                 ExprAddrOf(MutMutable, _) => return, // for _ in &mut _, there is no {values,keys}_mut method
-                _ => arg.span,
+                _ => (arg.span, arg),
             };
 
             let ty = walk_ptrs_ty(cx.tcx.expr_ty(arg));
@@ -607,14 +617,13 @@ fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Ex
                 span_lint_and_then(cx,
                                    FOR_KV_MAP,
                                    expr.span,
-                                   &format!("you seem to want to iterate on a map's {}", kind),
+                                   &format!("you seem to want to iterate on a map's {}s", kind),
                                    |db| {
-                    db.span_suggestion(expr.span,
-                                       "use the corresponding method",
-                                       format!("for {} in {}.{}() {{ .. }}",
-                                               snippet(cx, *pat_span, ".."),
-                                               snippet(cx, arg_span, ".."),
-                                               kind));
+                    let map = sugg::Sugg::hir(cx, arg, "map");
+                    multispan_sugg(db, "use the corresponding method".into(), &[
+                        (pat_span, &snippet(cx, new_pat_span, kind)),
+                        (arg_span, &format!("{}.{}s()", map.maybe_par(), kind)),
+                    ]);
                 });
             }
         }
index e578fbf6d68ca3fbbc6d747d273582d266194a36..de21cabc6fb422d3b83277073581d62881151fd1 100644 (file)
@@ -10,6 +10,7 @@
 use syntax::codemap::Span;
 use utils::paths;
 use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, in_external_macro, expr_block};
+use utils::sugg::Sugg;
 
 /// **What it does:** This lint checks for matches with a single arm where an `if let` will usually suffice.
 ///
@@ -234,56 +235,54 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr:
 fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
     // type of expression == bool
     if cx.tcx.expr_ty(ex).sty == ty::TyBool {
-        let sugg = if arms.len() == 2 && arms[0].pats.len() == 1 {
-            // no guards
-            let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
-                if let ExprLit(ref lit) = arm_bool.node {
-                    match lit.node {
-                        LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
-                        LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
-                        _ => None,
+        span_lint_and_then(cx,
+                           MATCH_BOOL,
+                           expr.span,
+                           "you seem to be trying to match on a boolean expression",
+                           move |db| {
+            if arms.len() == 2 && arms[0].pats.len() == 1 {
+                // no guards
+                let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
+                    if let ExprLit(ref lit) = arm_bool.node {
+                        match lit.node {
+                            LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
+                            LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
+                            _ => None,
+                        }
+                    } else {
+                        None
                     }
                 } else {
                     None
-                }
-            } else {
-                None
-            };
-
-            if let Some((ref true_expr, ref false_expr)) = exprs {
-                match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
-                    (false, false) => {
-                        Some(format!("if {} {} else {}",
-                                     snippet(cx, ex.span, "b"),
-                                     expr_block(cx, true_expr, None, ".."),
-                                     expr_block(cx, false_expr, None, "..")))
+                };
+
+                if let Some((ref true_expr, ref false_expr)) = exprs {
+                    let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
+                        (false, false) => {
+                            Some(format!("if {} {} else {}",
+                                         snippet(cx, ex.span, "b"),
+                                         expr_block(cx, true_expr, None, ".."),
+                                         expr_block(cx, false_expr, None, "..")))
+                        }
+                        (false, true) => {
+                            Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
+                        }
+                        (true, false) => {
+                            let test = Sugg::hir(cx, ex, "..");
+                            Some(format!("if {} {}",
+                                         !test,
+                                         expr_block(cx, false_expr, None, "..")))
+                        }
+                        (true, true) => None,
+                    };
+
+                    if let Some(sugg) = sugg {
+                        db.span_suggestion(expr.span, "consider using an if/else expression", sugg);
                     }
-                    (false, true) => {
-                        Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
-                    }
-                    (true, false) => {
-                        Some(format!("try\nif !{} {}",
-                                     snippet(cx, ex.span, "b"),
-                                     expr_block(cx, false_expr, None, "..")))
-                    }
-                    (true, true) => None,
                 }
-            } else {
-                None
             }
-        } else {
-            None
-        };
 
-        span_lint_and_then(cx,
-                           MATCH_BOOL,
-                           expr.span,
-                           "you seem to be trying to match on a boolean expression. Consider using an if..else block:",
-                           move |db| {
-                               if let Some(sugg) = sugg {
-                                   db.span_suggestion(expr.span, "try this", sugg);
-                               }
-                           });
+       });
     }
 }
 
@@ -307,26 +306,28 @@ fn check_overlapping_arms(cx: &LateContext, ex: &Expr, arms: &[Arm]) {
 fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: MatchSource, expr: &Expr) {
     if has_only_ref_pats(arms) {
         if let ExprAddrOf(Mutability::MutImmutable, ref inner) = ex.node {
-            let template = match_template(cx, expr.span, source, "", inner);
             span_lint_and_then(cx,
                                MATCH_REF_PATS,
                                expr.span,
                                "you don't need to add `&` to both the expression and the patterns",
                                |db| {
-                                   db.span_suggestion(expr.span, "try", template);
-                               });
+                let inner = Sugg::hir(cx, inner, "..");
+                let template = match_template(expr.span, source, inner);
+                db.span_suggestion(expr.span, "try", template);
+            });
         } else {
-            let template = match_template(cx, expr.span, source, "*", ex);
             span_lint_and_then(cx,
                                MATCH_REF_PATS,
                                expr.span,
                                "you don't need to add `&` to all patterns",
                                |db| {
-                                   db.span_suggestion(expr.span,
-                                                      "instead of prefixing all patterns with `&`, you can \
-                                                       dereference the expression",
-                                                      template);
-                               });
+                let ex = Sugg::hir(cx, ex, "..");
+                let template = match_template(expr.span, source, ex.deref());
+                db.span_suggestion(expr.span,
+                                   "instead of prefixing all patterns with `&`, you can \
+                                   dereference the expression",
+                                   template);
+            });
         }
     }
 }
@@ -409,12 +410,11 @@ fn has_only_ref_pats(arms: &[Arm]) -> bool {
     mapped.map_or(false, |v| v.iter().any(|el| *el))
 }
 
-fn match_template(cx: &LateContext, span: Span, source: MatchSource, op: &str, expr: &Expr) -> String {
-    let expr_snippet = snippet(cx, expr.span, "..");
+fn match_template(span: Span, source: MatchSource, expr: Sugg) -> String {
     match source {
-        MatchSource::Normal => format!("match {}{} {{ .. }}", op, expr_snippet),
-        MatchSource::IfLetDesugar { .. } => format!("if let .. = {}{} {{ .. }}", op, expr_snippet),
-        MatchSource::WhileLetDesugar => format!("while let .. = {}{} {{ .. }}", op, expr_snippet),
+        MatchSource::Normal => format!("match {} {{ .. }}", expr),
+        MatchSource::IfLetDesugar { .. } => format!("if let .. = {} {{ .. }}", expr),
+        MatchSource::WhileLetDesugar => format!("while let .. = {} {{ .. }}", expr),
         MatchSource::ForLoopDesugar => span_bug!(span, "for loop desugared to match with &-patterns!"),
         MatchSource::TryDesugar => span_bug!(span, "`?` operator desugared to match with &-patterns!"),
     }
index c59453333a4d7ee817707a7c9b30184cd79915e0..9d5c436c2cbc03e88b7ff34cdef617ff1fcec187 100644 (file)
@@ -672,20 +672,20 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
 #[allow(ptr_arg)]
 // Type of MethodArgs is potentially a Vec
 fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_mut: bool){
-    let caller_type;
     let mut_str = if is_mut { "_mut" } else {""};
-    if let Some(_) = derefs_to_slice(cx, &iter_args[0], &cx.tcx.expr_ty(&iter_args[0])) {
-        caller_type = "slice";
+    let caller_type = if let Some(_) = derefs_to_slice(cx, &iter_args[0], &cx.tcx.expr_ty(&iter_args[0])) {
+        "slice"
     }
     else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC) {
-        caller_type = "Vec";
+        "Vec"
     }
     else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
-        caller_type = "VecDeque";
+        "VecDeque"
     }
     else {
         return; // caller is not a type that we want to lint
-    }
+    };
+
     span_lint(
         cx,
         ITER_NTH,
index 495325764690c18b96e8d2e167cd47a539b2e1ca..b1627c31c8b80033517ac667d372ad5db3304b40 100644 (file)
@@ -13,6 +13,7 @@
     get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path,
     snippet, span_lint, span_lint_and_then, walk_ptrs_ty
 };
+use utils::sugg::Sugg;
 
 /// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
 ///
@@ -69,14 +70,15 @@ fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
             span_lint_and_then(cx,
                 TOPLEVEL_REF_ARG,
                 l.pat.span,
-                "`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
+                "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
                 |db| {
+                    let init = Sugg::hir(cx, init, "..");
                     db.span_suggestion(s.span,
                                        "try",
-                                       format!("let {}{} = &{};",
+                                       format!("let {}{} = {};",
                                                snippet(cx, i.span, "_"),
                                                tyopt,
-                                               snippet(cx, init.span, "_")));
+                                               init.addr()));
                 }
             );
         }}
@@ -164,15 +166,19 @@ fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
                         return;
                     }
                 }
-                span_lint(cx,
-                          FLOAT_CMP,
-                          expr.span,
-                          &format!("{}-comparison of f32 or f64 detected. Consider changing this to `({} - {}).abs() < \
-                                    epsilon` for some suitable value of epsilon. \
-                                    std::f32::EPSILON and std::f64::EPSILON are available.",
-                                   op.as_str(),
-                                   snippet(cx, left.span, ".."),
-                                   snippet(cx, right.span, "..")));
+                span_lint_and_then(cx,
+                                   FLOAT_CMP,
+                                   expr.span,
+                                   "strict comparison of f32 or f64",
+                                   |db| {
+                    let lhs = Sugg::hir(cx, left, "..");
+                    let rhs = Sugg::hir(cx, right, "..");
+
+                    db.span_suggestion(expr.span,
+                                       "consider comparing them within some error",
+                                       format!("({}).abs() < error", lhs - rhs));
+                    db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
+                });
             }
         }
     }
index 45964cf3ce705d617309063a52971dc375a88915..c0f1c611f714dfd49d81b710bf304872077deb02 100644 (file)
@@ -149,7 +149,7 @@ fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
                                            "Try not to call a closure in the expression where it is declared.",
                                            |db| {
                                                if decl.inputs.is_empty() {
-                                                   let hint = format!("{}", snippet(cx, block.span, ".."));
+                                                   let hint = snippet(cx, block.span, "..").into_owned();
                                                    db.span_suggestion(expr.span, "Try doing something like: ", hint);
                                                }
                                            });
index 8a5439fb11e316d05101dec89bc21c9e2857800f..d5d9fe1a0bc8ca3484ba4272ae36299f4816c6f4 100644 (file)
@@ -2,7 +2,7 @@
 use rustc::hir::intravisit;
 use rustc::lint::*;
 use rustc::ty::{TypeAndMut, TyRef};
-use utils::{in_external_macro, recover_for_loop, span_lint};
+use utils::{higher, in_external_macro, span_lint};
 
 /// **What it does:** This lint checks for instances of `mut mut` references.
 ///
@@ -49,7 +49,7 @@ fn visit_expr(&mut self, expr: &'v hir::Expr) {
             return;
         }
 
-        if let Some((_, arg, body)) = recover_for_loop(expr) {
+        if let Some((_, arg, body)) = higher::for_loop(expr) {
             // A `for` loop lowers to:
             // ```rust
             // match ::std::iter::Iterator::next(&mut iter) {
index 8eacbadf8df6c2da3aee162a6a966173938a7c66..a042c966d942d9e41f8c57b7ee39200d0d23bc72 100644 (file)
@@ -1,7 +1,8 @@
 use rustc::lint::*;
 use rustc::hir::*;
 use syntax::codemap::Spanned;
-use utils::{is_integer_literal, match_type, paths, snippet, span_lint, unsugar_range, UnsugaredRange};
+use utils::{is_integer_literal, match_type, paths, snippet, span_lint};
+use utils::higher;
 
 /// **What it does:** This lint checks for iterating over ranges with a `.step_by(0)`, which never terminates.
 ///
@@ -54,7 +55,7 @@ fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
                     let ExprMethodCall( Spanned { node: ref iter_name, .. }, _, ref iter_args ) = *iter,
                     iter_name.as_str() == "iter",
                     // range expression in .zip() call: 0..x.len()
-                    let Some(UnsugaredRange { start: Some(ref start), end: Some(ref end), .. }) = unsugar_range(zip_arg),
+                    let Some(higher::Range { start: Some(ref start), end: Some(ref end), .. }) = higher::range(zip_arg),
                     is_integer_literal(start, 0),
                     // .len() call
                     let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = end.node,
index b12e68185c2d13287947a941a6e04ac5520aeb98..7bda94ed639792c68b9dbd6385922a2590cba960 100644 (file)
@@ -5,7 +5,7 @@
 use rustc::hir::intravisit::{Visitor, FnKind};
 use std::ops::Deref;
 use syntax::codemap::Span;
-use utils::{is_from_for_desugar, in_external_macro, snippet, span_lint_and_then};
+use utils::{higher, in_external_macro, snippet, span_lint_and_then};
 
 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.
 ///
@@ -91,7 +91,7 @@ fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
     if in_external_macro(cx, decl.span) {
         return;
     }
-    if is_from_for_desugar(decl) {
+    if higher::is_from_for_desugar(decl) {
         return;
     }
     if let DeclLocal(ref local) = decl.node {
index 5a3adfee409d1f92382083d6a1753437656140d6..667c450e66979140967a0a0342ab0c8639e413f9 100644 (file)
@@ -2,7 +2,8 @@
 use rustc::lint::*;
 use rustc::ty;
 use syntax::codemap::mk_sp;
-use utils::{differing_macro_contexts, match_type, paths, snippet, snippet_opt, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
+use utils::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
+use utils::sugg::Sugg;
 
 /// **What it does:** This lints manual swapping.
 ///
@@ -100,17 +101,17 @@ fn check_for_slice<'a>(cx: &LateContext, lhs1: &'a Expr, lhs2: &'a Expr) -> Opti
             }
 
             let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
-                if let Some(slice) = snippet_opt(cx, slice.span) {
+                if let Some(slice) = Sugg::hir_opt(cx, slice) {
                     (false,
                      format!(" elements of `{}`", slice),
-                     format!("{}.swap({}, {})",slice,  snippet(cx, idx1.span, ".."), snippet(cx, idx2.span, "..")))
+                     format!("{}.swap({}, {})", slice.maybe_par(), snippet(cx, idx1.span, ".."), snippet(cx, idx2.span, "..")))
                 } else {
                     (false, "".to_owned(), "".to_owned())
                 }
             } else {
-                 if let (Some(first), Some(second)) = (snippet_opt(cx, lhs1.span), snippet_opt(cx, rhs1.span)) {
+                 if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
                     (true, format!(" `{}` and `{}`", first, second),
-                     format!("std::mem::swap(&mut {}, &mut {})", first, second))
+                     format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
                 } else {
                     (true, "".to_owned(), "".to_owned())
                 }
@@ -147,8 +148,8 @@ fn check_suspicious_swap(cx: &LateContext, block: &Block) {
             SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
             SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
         ], {
-            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (snippet_opt(cx, lhs0.span), snippet_opt(cx, rhs0.span)) {
-                (format!(" `{}` and `{}`", first, second), first, second)
+            let (what, lhs, rhs) = if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs0), Sugg::hir_opt(cx, rhs0)) {
+                (format!(" `{}` and `{}`", first, second), first.mut_addr().to_string(), second.mut_addr().to_string())
             } else {
                 ("".to_owned(), "".to_owned(), "".to_owned())
             };
@@ -162,7 +163,7 @@ fn check_suspicious_swap(cx: &LateContext, block: &Block) {
                                |db| {
                                    if !what.is_empty() {
                                        db.span_suggestion(span, "try",
-                                                          format!("std::mem::swap(&mut {}, &mut {})", lhs, rhs));
+                                                          format!("std::mem::swap({}, {})", lhs, rhs));
                                        db.note("or maybe you should use `std::mem::replace`?");
                                    }
                                });
index bf6af4411b892d9fce7879344ee65f6d2c9ea630..d95fad8fc0f0f0b0502c6217bb41bafec12a0fd2 100644 (file)
@@ -3,6 +3,7 @@
 use rustc::ty;
 use rustc::hir::*;
 use utils::{match_def_path, paths, snippet_opt, span_lint, span_lint_and_then};
+use utils::sugg;
 
 /// **What it does:** This lint checks for transmutes that can't ever be correct on any architecture
 ///
@@ -148,28 +149,21 @@ fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
                                     from_ty,
                                     to_ty),
                             |db| {
-                                if let Some(arg) = snippet_opt(cx, args[0].span) {
-                                    let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
-                                        ("&mut *", "*mut")
-                                    } else {
-                                        ("&*", "*const")
-                                    };
+                                let arg = sugg::Sugg::hir(cx, &args[0], "..");
+                                let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
+                                    ("&mut *", "*mut")
+                                } else {
+                                    ("&*", "*const")
+                                };
 
 
-                                    let sugg = if from_pty.ty == to_rty.ty {
-                                        // Put things in parentheses if they are more complex
-                                        match args[0].node {
-                                            ExprPath(..) | ExprCall(..) | ExprMethodCall(..) | ExprBlock(..) => {
-                                                format!("{}{}", deref, arg)
-                                            }
-                                            _ => format!("{}({})", deref, arg)
-                                        }
-                                    } else {
-                                        format!("{}({} as {} {})", deref, arg, cast, to_rty.ty)
-                                    };
+                                let sugg = if from_pty.ty == to_rty.ty {
+                                    sugg::make_unop(deref, arg).to_string()
+                                } else {
+                                    format!("{}({} as {} {})", deref, arg, cast, to_rty.ty)
+                                };
 
-                                    db.span_suggestion(e.span, "try", sugg);
-                                }
+                                db.span_suggestion(e.span, "try", sugg);
                             },
                         ),
                         _ => return,
index 83e2c1b549c145ecaa3c0ce1ff5307bf6cfe93b6..629834a3c0f7a3b71c728db043fca7df81ae723e 100644 (file)
@@ -6,7 +6,7 @@
 use std::cmp::Ordering;
 use syntax::ast::{IntTy, UintTy, FloatTy};
 use syntax::codemap::Span;
-use utils::{comparisons, in_external_macro, in_macro, is_from_for_desugar, match_def_path, snippet,
+use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet,
             span_help_and_lint, span_lint};
 use utils::paths;
 
@@ -106,7 +106,7 @@ fn check_let_unit(cx: &LateContext, decl: &Decl) {
             if in_external_macro(cx, decl.span) || in_macro(cx, local.pat.span) {
                 return;
             }
-            if is_from_for_desugar(decl) {
+            if higher::is_from_for_desugar(decl) {
                 return;
             }
             span_lint(cx,
diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs
new file mode 100644 (file)
index 0000000..ab8b7d8
--- /dev/null
@@ -0,0 +1,190 @@
+//! This module contains functions for retrieve the original AST from lowered `hir`.
+
+use rustc::hir;
+use rustc::lint::LateContext;
+use syntax::ast;
+use syntax::ptr::P;
+use utils::{is_expn_of, match_path, paths};
+
+/// Convert a hir binary operator to the corresponding `ast` type.
+pub fn binop(op: hir::BinOp_) -> ast::BinOpKind {
+    match op {
+        hir::BiEq => ast::BinOpKind::Eq,
+        hir::BiGe => ast::BinOpKind::Ge,
+        hir::BiGt => ast::BinOpKind::Gt,
+        hir::BiLe => ast::BinOpKind::Le,
+        hir::BiLt => ast::BinOpKind::Lt,
+        hir::BiNe => ast::BinOpKind::Ne,
+        hir::BiOr => ast::BinOpKind::Or,
+        hir::BiAdd => ast::BinOpKind::Add,
+        hir::BiAnd => ast::BinOpKind::And,
+        hir::BiBitAnd => ast::BinOpKind::BitAnd,
+        hir::BiBitOr => ast::BinOpKind::BitOr,
+        hir::BiBitXor => ast::BinOpKind::BitXor,
+        hir::BiDiv => ast::BinOpKind::Div,
+        hir::BiMul => ast::BinOpKind::Mul,
+        hir::BiRem => ast::BinOpKind::Rem,
+        hir::BiShl => ast::BinOpKind::Shl,
+        hir::BiShr => ast::BinOpKind::Shr,
+        hir::BiSub => ast::BinOpKind::Sub,
+    }
+}
+
+/// Represent a range akin to `ast::ExprKind::Range`.
+#[derive(Debug, Copy, Clone)]
+pub struct Range<'a> {
+    pub start: Option<&'a hir::Expr>,
+    pub end: Option<&'a hir::Expr>,
+    pub limits: ast::RangeLimits,
+}
+
+/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
+pub fn range(expr: &hir::Expr) -> Option<Range> {
+    // To be removed when ranges get stable.
+    fn unwrap_unstable(expr: &hir::Expr) -> &hir::Expr {
+        if let hir::ExprBlock(ref block) = expr.node {
+            if block.rules == hir::BlockCheckMode::PushUnstableBlock || block.rules == hir::BlockCheckMode::PopUnstableBlock {
+                if let Some(ref expr) = block.expr {
+                    return expr;
+                }
+            }
+        }
+
+        expr
+    }
+
+    fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {
+        let expr = &fields.iter()
+                          .find(|field| field.name.node.as_str() == name)
+                          .unwrap_or_else(|| panic!("missing {} field for range", name))
+                          .expr;
+
+        Some(unwrap_unstable(expr))
+    }
+
+    // The range syntax is expanded to literal paths starting with `core` or `std` depending on
+    // `#[no_std]`. Testing both instead of resolving the paths.
+
+    match unwrap_unstable(expr).node {
+        hir::ExprPath(None, ref path) => {
+            if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
+                Some(Range {
+                    start: None,
+                    end: None,
+                    limits: ast::RangeLimits::HalfOpen,
+                })
+            } else {
+                None
+            }
+        }
+        hir::ExprStruct(ref path, ref fields, None) => {
+            if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
+                Some(Range {
+                    start: get_field("start", fields),
+                    end: None,
+                    limits: ast::RangeLimits::HalfOpen,
+                })
+            } else if match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY_STD) ||
+               match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
+                Some(Range {
+                    start: get_field("start", fields),
+                    end: get_field("end", fields),
+                    limits: ast::RangeLimits::Closed,
+                })
+            } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
+                Some(Range {
+                    start: get_field("start", fields),
+                    end: get_field("end", fields),
+                    limits: ast::RangeLimits::HalfOpen,
+                })
+            } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
+                Some(Range {
+                    start: None,
+                    end: get_field("end", fields),
+                    limits: ast::RangeLimits::Closed,
+                })
+            } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
+                Some(Range {
+                    start: None,
+                    end: get_field("end", fields),
+                    limits: ast::RangeLimits::HalfOpen,
+                })
+            } else {
+                None
+            }
+        }
+        _ => None,
+    }
+}
+
+/// Checks if a `let` decl is from a `for` loop desugaring.
+pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
+    if_let_chain! {[
+        let hir::DeclLocal(ref loc) = decl.node,
+        let Some(ref expr) = loc.init,
+        let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node,
+    ], {
+        return true;
+    }}
+    false
+}
+
+/// Recover the essential nodes of a desugared for loop:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`.
+pub fn for_loop(expr: &hir::Expr) -> Option<(&hir::Pat, &hir::Expr, &hir::Expr)> {
+    if_let_chain! {[
+        let hir::ExprMatch(ref iterexpr, ref arms, _) = expr.node,
+        let hir::ExprCall(_, ref iterargs) = iterexpr.node,
+        iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
+        let hir::ExprLoop(ref block, _) = arms[0].body.node,
+        block.stmts.is_empty(),
+        let Some(ref loopexpr) = block.expr,
+        let hir::ExprMatch(_, ref innerarms, hir::MatchSource::ForLoopDesugar) = loopexpr.node,
+        innerarms.len() == 2 && innerarms[0].pats.len() == 1,
+        let hir::PatKind::TupleStruct(_, ref somepats, _) = innerarms[0].pats[0].node,
+        somepats.len() == 1
+    ], {
+        return Some((&somepats[0],
+                     &iterargs[0],
+                     &innerarms[0].body));
+    }}
+    None
+}
+
+/// Represent the pre-expansion arguments of a `vec!` invocation.
+pub enum VecArgs<'a> {
+    /// `vec![elem; len]`
+    Repeat(&'a P<hir::Expr>, &'a P<hir::Expr>),
+    /// `vec![a, b, c]`
+    Vec(&'a [P<hir::Expr>]),
+}
+
+/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
+pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
+    if_let_chain!{[
+        let hir::ExprCall(ref fun, ref args) = expr.node,
+        let hir::ExprPath(_, ref path) = fun.node,
+        is_expn_of(cx, fun.span, "vec").is_some()
+    ], {
+        return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
+            // `vec![elem; size]` case
+            Some(VecArgs::Repeat(&args[0], &args[1]))
+        }
+        else if match_path(path, &["into_vec"]) && args.len() == 1 {
+            // `vec![a, b, c]` case
+            if_let_chain!{[
+                let hir::ExprBox(ref boxed) = args[0].node,
+                let hir::ExprVec(ref args) = boxed.node
+            ], {
+                return Some(VecArgs::Vec(&*args));
+            }}
+
+            None
+        }
+        else {
+            None
+        };
+    }}
+
+    None
+}
index 30bb6dfeb7659408f899baac7c39ec6208e50438..189801c8c0b5615b778907b9553977fa900d6a75 100644 (file)
@@ -9,21 +9,23 @@
 use rustc::traits;
 use rustc::ty::subst::Subst;
 use rustc::ty;
+use rustc_errors;
 use std::borrow::Cow;
 use std::env;
 use std::mem;
 use std::str::FromStr;
-use syntax::ast::{self, LitKind, RangeLimits};
-use syntax::codemap::{ExpnInfo, Span, ExpnFormat};
+use syntax::ast::{self, LitKind};
+use syntax::codemap::{ExpnFormat, ExpnInfo, MultiSpan, Span};
 use syntax::errors::DiagnosticBuilder;
 use syntax::ptr::P;
 
+pub mod cargo;
 pub mod comparisons;
 pub mod conf;
 mod hir;
 pub mod paths;
+pub mod sugg;
 pub use self::hir::{SpanlessEq, SpanlessHash};
-pub mod cargo;
 
 pub type MethodArgs = HirVec<P<Expr>>;
 
@@ -80,6 +82,8 @@ macro_rules! if_let_chain {
     };
 }
 
+pub mod higher;
+
 /// Returns true if the two spans come from differing expansions (i.e. one is from a macro and one
 /// isn't).
 pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
@@ -317,19 +321,6 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
     }
 }
 
-/// Checks if a `let` decl is from a `for` loop desugaring.
-pub fn is_from_for_desugar(decl: &Decl) -> bool {
-    if_let_chain! {[
-        let DeclLocal(ref loc) = decl.node,
-        let Some(ref expr) = loc.init,
-        let ExprMatch(_, _, MatchSource::ForLoopDesugar) = expr.node
-    ], {
-        return true;
-    }}
-    false
-}
-
-
 /// Convert a span to a code snippet if available, otherwise use default.
 ///
 /// # Example
@@ -500,6 +491,25 @@ pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint,
     }
 }
 
+/// Create a suggestion made from several `span â†’ replacement`.
+///
+/// Note: in the JSON format (used by `compiletest_rs`), the help message will appear once per
+/// replacement. In human-readable format though, it only appears once before the whole suggestion.
+pub fn multispan_sugg(db: &mut DiagnosticBuilder, help_msg: String, sugg: &[(Span, &str)]) {
+    let sugg = rustc_errors::RenderSpan::Suggestion(rustc_errors::CodeSuggestion {
+        msp: MultiSpan::from_spans(sugg.iter().map(|&(span, _)| span).collect()),
+        substitutes: sugg.iter().map(|&(_, subs)| subs.to_owned()).collect(),
+    });
+
+    let sub = rustc_errors::SubDiagnostic {
+        level: rustc_errors::Level::Help,
+        message: help_msg,
+        span: MultiSpan::new(),
+        render_span: Some(sugg),
+    };
+    db.children.push(sub);
+}
+
 /// Return the base type for references and raw pointers.
 pub fn walk_ptrs_ty(ty: ty::Ty) -> ty::Ty {
     match ty.sty {
@@ -681,93 +691,6 @@ pub fn camel_case_from(s: &str) -> usize {
     last_i
 }
 
-/// Represent a range akin to `ast::ExprKind::Range`.
-#[derive(Debug, Copy, Clone)]
-pub struct UnsugaredRange<'a> {
-    pub start: Option<&'a Expr>,
-    pub end: Option<&'a Expr>,
-    pub limits: RangeLimits,
-}
-
-/// Unsugar a `hir` range.
-pub fn unsugar_range(expr: &Expr) -> Option<UnsugaredRange> {
-    // To be removed when ranges get stable.
-    fn unwrap_unstable(expr: &Expr) -> &Expr {
-        if let ExprBlock(ref block) = expr.node {
-            if block.rules == BlockCheckMode::PushUnstableBlock || block.rules == BlockCheckMode::PopUnstableBlock {
-                if let Some(ref expr) = block.expr {
-                    return expr;
-                }
-            }
-        }
-
-        expr
-    }
-
-    fn get_field<'a>(name: &str, fields: &'a [Field]) -> Option<&'a Expr> {
-        let expr = &fields.iter()
-                          .find(|field| field.name.node.as_str() == name)
-                          .unwrap_or_else(|| panic!("missing {} field for range", name))
-                          .expr;
-
-        Some(unwrap_unstable(expr))
-    }
-
-    // The range syntax is expanded to literal paths starting with `core` or `std` depending on
-    // `#[no_std]`. Testing both instead of resolving the paths.
-
-    match unwrap_unstable(expr).node {
-        ExprPath(None, ref path) => {
-            if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
-                Some(UnsugaredRange {
-                    start: None,
-                    end: None,
-                    limits: RangeLimits::HalfOpen,
-                })
-            } else {
-                None
-            }
-        }
-        ExprStruct(ref path, ref fields, None) => {
-            if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
-                Some(UnsugaredRange {
-                    start: get_field("start", fields),
-                    end: None,
-                    limits: RangeLimits::HalfOpen,
-                })
-            } else if match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY_STD) ||
-               match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
-                Some(UnsugaredRange {
-                    start: get_field("start", fields),
-                    end: get_field("end", fields),
-                    limits: RangeLimits::Closed,
-                })
-            } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
-                Some(UnsugaredRange {
-                    start: get_field("start", fields),
-                    end: get_field("end", fields),
-                    limits: RangeLimits::HalfOpen,
-                })
-            } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
-                Some(UnsugaredRange {
-                    start: None,
-                    end: get_field("end", fields),
-                    limits: RangeLimits::Closed,
-                })
-            } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
-                Some(UnsugaredRange {
-                    start: None,
-                    end: get_field("end", fields),
-                    limits: RangeLimits::HalfOpen,
-                })
-            } else {
-                None
-            }
-        }
-        _ => None,
-    }
-}
-
 /// Convenience function to get the return type of a function or `None` if the function diverges.
 pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> Option<ty::Ty<'tcx>> {
     let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item);
@@ -792,28 +715,6 @@ pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: ty::Ty<'tcx>, b: ty::Ty
     })
 }
 
-/// Recover the essential nodes of a desugared for loop:
-/// `for pat in arg { body }` becomes `(pat, arg, body)`.
-pub fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
-    if_let_chain! {[
-        let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
-        let ExprCall(_, ref iterargs) = iterexpr.node,
-        iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
-        let ExprLoop(ref block, _) = arms[0].body.node,
-        block.stmts.is_empty(),
-        let Some(ref loopexpr) = block.expr,
-        let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
-        innerarms.len() == 2 && innerarms[0].pats.len() == 1,
-        let PatKind::TupleStruct(_, ref somepats, _) = innerarms[0].pats[0].node,
-        somepats.len() == 1
-    ], {
-        return Some((&somepats[0],
-                     &iterargs[0],
-                     &innerarms[0].body));
-    }}
-    None
-}
-
 /// Return whether the given type is an `unsafe` function.
 pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
     match ty.sty {
diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs
new file mode 100644 (file)
index 0000000..f857c82
--- /dev/null
@@ -0,0 +1,350 @@
+use rustc::hir;
+use rustc::lint::{EarlyContext, LateContext};
+use std::borrow::Cow;
+use std;
+use syntax::ast;
+use syntax::util::parser::AssocOp;
+use utils::{higher, snippet, snippet_opt};
+use syntax::print::pprust::binop_to_string;
+
+/// A helper type to build suggestion correctly handling parenthesis.
+pub enum Sugg<'a> {
+    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
+    NonParen(Cow<'a, str>),
+    /// An expression that does not fit in other variants.
+    MaybeParen(Cow<'a, str>),
+    /// A binary operator expression, including `as`-casts and explicit type coercion.
+    BinOp(AssocOp, Cow<'a, str>),
+}
+
+/// Literal constant `1`, for convenience.
+pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
+
+impl<'a> std::fmt::Display for Sugg<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        match *self {
+            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => {
+                s.fmt(f)
+            }
+        }
+    }
+}
+
+impl<'a> Sugg<'a> {
+    pub fn hir_opt(cx: &LateContext, expr: &hir::Expr) -> Option<Sugg<'a>> {
+        snippet_opt(cx, expr.span).map(|snippet| {
+            let snippet = Cow::Owned(snippet);
+            match expr.node {
+                hir::ExprAddrOf(..) |
+                hir::ExprBox(..) |
+                hir::ExprClosure(..) |
+                hir::ExprIf(..) |
+                hir::ExprUnary(..) |
+                hir::ExprMatch(..) => Sugg::MaybeParen(snippet),
+                hir::ExprAgain(..) |
+                hir::ExprBlock(..) |
+                hir::ExprBreak(..) |
+                hir::ExprCall(..) |
+                hir::ExprField(..) |
+                hir::ExprIndex(..) |
+                hir::ExprInlineAsm(..) |
+                hir::ExprLit(..) |
+                hir::ExprLoop(..) |
+                hir::ExprMethodCall(..) |
+                hir::ExprPath(..) |
+                hir::ExprRepeat(..) |
+                hir::ExprRet(..) |
+                hir::ExprStruct(..) |
+                hir::ExprTup(..) |
+                hir::ExprTupField(..) |
+                hir::ExprVec(..) |
+                hir::ExprWhile(..) => Sugg::NonParen(snippet),
+                hir::ExprAssign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+                hir::ExprAssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
+                hir::ExprBinary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
+                hir::ExprCast(..) => Sugg::BinOp(AssocOp::As, snippet),
+                hir::ExprType(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+            }
+        })
+    }
+
+    pub fn hir(cx: &LateContext, expr: &hir::Expr, default: &'a str) -> Sugg<'a> {
+        Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
+    }
+
+    pub fn ast(cx: &EarlyContext, expr: &ast::Expr, default: &'a str) -> Sugg<'a> {
+        use syntax::ast::RangeLimits;
+
+        let snippet = snippet(cx, expr.span, default);
+
+        match expr.node {
+            ast::ExprKind::AddrOf(..) |
+            ast::ExprKind::Box(..) |
+            ast::ExprKind::Closure(..) |
+            ast::ExprKind::If(..) |
+            ast::ExprKind::IfLet(..) |
+            ast::ExprKind::InPlace(..) |
+            ast::ExprKind::Unary(..) |
+            ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+            ast::ExprKind::Block(..) |
+            ast::ExprKind::Break(..) |
+            ast::ExprKind::Call(..) |
+            ast::ExprKind::Continue(..) |
+            ast::ExprKind::Field(..) |
+            ast::ExprKind::ForLoop(..) |
+            ast::ExprKind::Index(..) |
+            ast::ExprKind::InlineAsm(..) |
+            ast::ExprKind::Lit(..) |
+            ast::ExprKind::Loop(..) |
+            ast::ExprKind::Mac(..) |
+            ast::ExprKind::MethodCall(..) |
+            ast::ExprKind::Paren(..) |
+            ast::ExprKind::Path(..) |
+            ast::ExprKind::Repeat(..) |
+            ast::ExprKind::Ret(..) |
+            ast::ExprKind::Struct(..) |
+            ast::ExprKind::Try(..) |
+            ast::ExprKind::Tup(..) |
+            ast::ExprKind::TupField(..) |
+            ast::ExprKind::Vec(..) |
+            ast::ExprKind::While(..) |
+            ast::ExprKind::WhileLet(..) => Sugg::NonParen(snippet),
+            ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
+            ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotDot, snippet),
+            ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+            ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
+            ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
+            ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+            ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+        }
+    }
+
+    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
+    pub fn and(self, rhs: Self) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::And, &self, &rhs)
+    }
+
+    /// Convenience method to create the `&<expr>` suggestion.
+    pub fn addr(self) -> Sugg<'static> {
+        make_unop("&", self)
+    }
+
+    /// Convenience method to create the `&mut <expr>` suggestion.
+    pub fn mut_addr(self) -> Sugg<'static> {
+        make_unop("&mut ", self)
+    }
+
+    /// Convenience method to create the `*<expr>` suggestion.
+    pub fn deref(self) -> Sugg<'static> {
+        make_unop("*", self)
+    }
+
+    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>` suggestion.
+    pub fn range(self, end: Self, limit: ast::RangeLimits) -> Sugg<'static> {
+        match limit {
+            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, &end),
+            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotDot, &self, &end),
+        }
+    }
+
+    /// Add parenthesis to any expression that might need them. Suitable to the `self` argument of
+    /// a method call (eg. to build `bar.foo()` or `(1 + 2).foo()`).
+    pub fn maybe_par(self) -> Self {
+        match self {
+            Sugg::NonParen(..) => self,
+            Sugg::MaybeParen(sugg) | Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
+        }
+    }
+}
+
+impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
+    type Output = Sugg<'static>;
+    fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Add, &self, &rhs)
+    }
+}
+
+impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
+    type Output = Sugg<'static>;
+    fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Sub, &self, &rhs)
+    }
+}
+
+impl<'a> std::ops::Not for Sugg<'a> {
+    type Output = Sugg<'static>;
+    fn not(self) -> Sugg<'static> {
+        make_unop("!", self)
+    }
+}
+
+struct ParenHelper<T> {
+    paren: bool,
+    wrapped: T,
+}
+
+impl<T> ParenHelper<T> {
+    fn new(paren: bool, wrapped: T) -> Self {
+        ParenHelper {
+            paren: paren,
+            wrapped: wrapped,
+        }
+    }
+}
+
+impl<T: std::fmt::Display> std::fmt::Display for ParenHelper<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        if self.paren {
+            write!(f, "({})", self.wrapped)
+        } else {
+            self.wrapped.fmt(f)
+        }
+    }
+}
+
+/// Build the string for `<op><expr>` adding parenthesis when necessary.
+///
+/// For convenience, the operator is taken as a string because all unary operators have the same
+/// precedence.
+pub fn make_unop(op: &str, expr: Sugg) -> Sugg<'static> {
+    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
+}
+
+/// Build the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
+///
+/// Precedence of shift operator relative to other arithmetic operation is often confusing so
+/// parenthesis will always be added for a mix of these.
+pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
+    fn is_shift(op: &AssocOp) -> bool {
+        matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+    }
+
+    fn is_arith(op: &AssocOp) -> bool {
+        matches!(*op, AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus)
+    }
+
+    fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
+        other.precedence() < op.precedence() ||
+            (other.precedence() == op.precedence() &&
+                ((op != other && associativity(op) != dir) ||
+                 (op == other && associativity(op) != Associativity::Both))) ||
+             is_shift(op) && is_arith(other) ||
+             is_shift(other) && is_arith(op)
+    }
+
+    let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
+        needs_paren(&op, lop, Associativity::Left)
+    } else {
+        false
+    };
+
+    let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
+        needs_paren(&op, rop, Associativity::Right)
+    } else {
+        false
+    };
+
+    let lhs = ParenHelper::new(lhs_paren, lhs);
+    let rhs = ParenHelper::new(rhs_paren, rhs);
+    let sugg = match op {
+        AssocOp::Add |
+        AssocOp::BitAnd |
+        AssocOp::BitOr |
+        AssocOp::BitXor |
+        AssocOp::Divide |
+        AssocOp::Equal |
+        AssocOp::Greater |
+        AssocOp::GreaterEqual |
+        AssocOp::LAnd |
+        AssocOp::LOr |
+        AssocOp::Less |
+        AssocOp::LessEqual |
+        AssocOp::Modulus |
+        AssocOp::Multiply |
+        AssocOp::NotEqual |
+        AssocOp::ShiftLeft |
+        AssocOp::ShiftRight |
+        AssocOp::Subtract => format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs),
+        AssocOp::Inplace => format!("in ({}) {}", lhs, rhs),
+        AssocOp::Assign => format!("{} = {}", lhs, rhs),
+        AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, binop_to_string(op), rhs),
+        AssocOp::As => format!("{} as {}", lhs, rhs),
+        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
+        AssocOp::DotDotDot => format!("{}...{}", lhs, rhs),
+        AssocOp::Colon => format!("{}: {}", lhs, rhs),
+    };
+
+    Sugg::BinOp(op, sugg.into())
+}
+
+/// Convinience wrapper arround `make_assoc` and `AssocOp::from_ast_binop`.
+pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
+    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
+}
+
+#[derive(PartialEq, Eq)]
+enum Associativity {
+    Both,
+    Left,
+    None,
+    Right,
+}
+
+/// Return the associativity/fixity of an operator. The difference with `AssocOp::fixity` is that
+/// an operator can be both left and right associative (such as `+`:
+/// `a + b + c == (a + b) + c == a + (b + c)`.
+///
+/// Chained `as` and explicit `:` type coercion never need inner parenthesis so they are considered
+/// associative.
+fn associativity(op: &AssocOp) -> Associativity {
+    use syntax::util::parser::AssocOp::*;
+
+    match *op {
+        Inplace | Assign | AssignOp(_) => Associativity::Right,
+        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply |
+        As | Colon => Associativity::Both,
+    Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft |
+        ShiftRight | Subtract => Associativity::Left,
+        DotDot | DotDotDot => Associativity::None
+    }
+}
+
+/// Convert a `hir::BinOp` to the corresponding assigning binary operator.
+fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
+    use rustc::hir::BinOp_::*;
+    use syntax::parse::token::BinOpToken::*;
+
+    AssocOp::AssignOp(match op.node {
+        BiAdd => Plus,
+        BiBitAnd => And,
+        BiBitOr => Or,
+        BiBitXor => Caret,
+        BiDiv => Slash,
+        BiMul => Star,
+        BiRem => Percent,
+        BiShl => Shl,
+        BiShr => Shr,
+        BiSub => Minus,
+        BiAnd | BiEq | BiGe | BiGt | BiLe | BiLt | BiNe | BiOr => panic!("This operator does not exist"),
+    })
+}
+
+/// Convert an `ast::BinOp` to the corresponding assigning binary operator.
+fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
+    use syntax::ast::BinOpKind::*;
+    use syntax::parse::token::BinOpToken;
+
+    AssocOp::AssignOp(match op.node {
+        Add => BinOpToken::Plus,
+        BitAnd => BinOpToken::And,
+        BitOr => BinOpToken::Or,
+        BitXor => BinOpToken::Caret,
+        Div => BinOpToken::Slash,
+        Mul => BinOpToken::Star,
+        Rem => BinOpToken::Percent,
+        Shl => BinOpToken::Shl,
+        Shr => BinOpToken::Shr,
+        Sub => BinOpToken::Minus,
+        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
+    })
+}
index a6289837d4e93c3786b2661f05cbe6733ccad054..114817989d8d1f9df158b8588575ead80641da0d 100644 (file)
@@ -4,8 +4,7 @@
 use rustc_const_eval::EvalHint::ExprTypeChecked;
 use rustc_const_eval::eval_const_expr_partial;
 use syntax::codemap::Span;
-use syntax::ptr::P;
-use utils::{is_expn_of, match_path, paths, recover_for_loop, snippet, span_lint_and_then};
+use utils::{higher, snippet, span_lint_and_then};
 
 /// **What it does:** This lint warns about using `&vec![..]` when using `&[..]` would be possible.
 ///
@@ -44,7 +43,7 @@ fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
         }}
 
         // search for `for _ in vec![…]`
-        if let Some((_, arg, _)) = recover_for_loop(expr) {
+        if let Some((_, arg, _)) = higher::for_loop(expr) {
             // report the error around the `vec!` not inside `<std macros>:`
             let span = cx.sess().codemap().source_callsite(arg.span);
             check_vec_macro(cx, arg, span);
@@ -53,18 +52,16 @@ fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
 }
 
 fn check_vec_macro(cx: &LateContext, vec: &Expr, span: Span) {
-    if let Some(vec_args) = unexpand(cx, vec) {
-
+    if let Some(vec_args) = higher::vec_macro(cx, vec) {
         let snippet = match vec_args {
-            Args::Repeat(elem, len) => {
-                // Check that the length is a constant expression
+            higher::VecArgs::Repeat(elem, len) => {
                 if eval_const_expr_partial(cx.tcx, len, ExprTypeChecked, None).is_ok() {
                     format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
                 } else {
                     return;
                 }
             }
-            Args::Vec(args) => {
+            higher::VecArgs::Vec(args) => {
                 if let Some(last) = args.iter().last() {
                     let span = Span {
                         lo: args[0].span.lo,
@@ -85,40 +82,3 @@ fn check_vec_macro(cx: &LateContext, vec: &Expr, span: Span) {
     }
 }
 
-/// Represent the pre-expansion arguments of a `vec!` invocation.
-pub enum Args<'a> {
-    /// `vec![elem; len]`
-    Repeat(&'a P<Expr>, &'a P<Expr>),
-    /// `vec![a, b, c]`
-    Vec(&'a [P<Expr>]),
-}
-
-/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
-pub fn unexpand<'e>(cx: &LateContext, expr: &'e Expr) -> Option<Args<'e>> {
-    if_let_chain!{[
-        let ExprCall(ref fun, ref args) = expr.node,
-        let ExprPath(_, ref path) = fun.node,
-        is_expn_of(cx, fun.span, "vec").is_some()
-    ], {
-        return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
-            // `vec![elem; size]` case
-            Some(Args::Repeat(&args[0], &args[1]))
-        }
-        else if match_path(path, &["into_vec"]) && args.len() == 1 {
-            // `vec![a, b, c]` case
-            if_let_chain!{[
-                let ExprBox(ref boxed) = args[0].node,
-                let ExprVec(ref args) = boxed.node
-            ], {
-                return Some(Args::Vec(&*args));
-            }}
-
-            None
-        }
-        else {
-            None
-        };
-    }}
-
-    None
-}
index 84d868ecfcc4e6a749a537f3f753d0bbf69e1e2b..2b69e110f43d7749dc9e3d93d5c4053051428c81 100644 (file)
@@ -8,15 +8,31 @@ fn main() {
     i += 2; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i + 2
+    i += 2 + 17; //~ ERROR assign operation detected
+    //~^ HELP replace it with
+    //~| SUGGESTION i = i + 2 + 17
     i -= 6; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i - 6
+    i -= 2 - 1;
+    //~^ ERROR assign operation detected
+    //~| HELP replace it with
+    //~| SUGGESTION i = i - (2 - 1)
     i *= 5; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i * 5
+    i *= 1+5; //~ ERROR assign operation detected
+    //~^ HELP replace it with
+    //~| SUGGESTION i = i * (1+5)
     i /= 32; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i / 32
+    i /= 32 | 5; //~ ERROR assign operation detected
+    //~^ HELP replace it with
+    //~| SUGGESTION i = i / (32 | 5)
+    i /= 32 / 5; //~ ERROR assign operation detected
+    //~^ HELP replace it with
+    //~| SUGGESTION i = i / (32 / 5)
     i %= 42; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i % 42
@@ -26,6 +42,10 @@ fn main() {
     i <<= 9 + 6 - 7; //~ ERROR assign operation detected
     //~^ HELP replace it with
     //~| SUGGESTION i = i << (9 + 6 - 7)
+    i += 1 << 5;
+    //~^ ERROR assign operation detected
+    //~| HELP replace it with
+    //~| SUGGESTION i = i + (1 << 5)
 }
 
 #[allow(dead_code, unused_assignments)]
index e51e31c693790b04271b072be3b749b2abc8947c..b23d6a6e4c186c19c237d927700fdd94b27a7ba2 100644 (file)
@@ -23,6 +23,42 @@ fn main() {
         }
     }
 
+    if x == "hello" && x == "world" {
+    //~^ ERROR this if statement can be collapsed
+    //~| HELP try
+    //~| SUGGESTION if x == "hello" && x == "world" && (y == "world" || y == "hello") {
+        if y == "world" || y == "hello" {
+            println!("Hello world!");
+        }
+    }
+
+    if x == "hello" || x == "world" {
+    //~^ ERROR this if statement can be collapsed
+    //~| HELP try
+    //~| SUGGESTION if (x == "hello" || x == "world") && y == "world" && y == "hello" {
+        if y == "world" && y == "hello" {
+            println!("Hello world!");
+        }
+    }
+
+    if x == "hello" && x == "world" {
+    //~^ ERROR this if statement can be collapsed
+    //~| HELP try
+    //~| SUGGESTION if x == "hello" && x == "world" && y == "world" && y == "hello" {
+        if y == "world" && y == "hello" {
+            println!("Hello world!");
+        }
+    }
+
+    if 42 == 1337 {
+    //~^ ERROR this if statement can be collapsed
+    //~| HELP try
+    //~| SUGGESTION if 42 == 1337 && 'a' != 'A' {
+        if 'a' != 'A' {
+            println!("world!")
+        }
+    }
+
     // Collaspe `else { if .. }` to `else if ..`
     if x == "hello" {
         print!("Hello ");
index 7dc4054ec5bf94404ad9276a990183268576bbd2..ec3b75abb371c3a7db3605ffe2dc0f624337b25e 100755 (executable)
@@ -11,45 +11,51 @@ fn foo() {}
 
 fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if !m.contains_key(&k) { m.insert(k, v); }
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| HELP Consider
+    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
+    //~| HELP consider
     //~| SUGGESTION m.entry(k).or_insert(v)
 }
 
 fn insert_if_absent1<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if !m.contains_key(&k) { foo(); m.insert(k, v); }
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if !m.contains_key(&k) { m.insert(k, v) } else { None };
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if m.contains_key(&k) { None } else { m.insert(k, v) };
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None };
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
     if m.contains_key(&k) { None } else { foo(); m.insert(k, v) };
-    //~^ ERROR usage of `contains_key` followed by `insert` on `HashMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `HashMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
     if !m.contains_key(&k) { foo(); m.insert(k, v) } else { None };
-    //~^ ERROR usage of `contains_key` followed by `insert` on `BTreeMap`
-    //~| NOTE Consider using `m.entry(k)`
+    //~^ ERROR usage of `contains_key` followed by `insert` on a `BTreeMap`
+    //~| HELP consider
+    //~| SUGGESTION m.entry(k)
 }
 
 fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
index 9f611dd3fd917c54b0b2db7068dbe2f3ccdd9c28..314cc721425507dd3ebf3d01c578f93073c51d5c 100644 (file)
@@ -40,23 +40,47 @@ fn main() {
     ZERO == 0.0; //no error, comparison with zero is ok
     ZERO + ZERO != 1.0; //no error, comparison with zero is ok
 
-    ONE == 1f32; //~ERROR ==-comparison of f32 or f64
-    ONE == (1.0 + 0.0); //~ERROR ==-comparison of f32 or f64
-
-    ONE + ONE == (ZERO + ONE + ONE); //~ERROR ==-comparison of f32 or f64
-
-    ONE != 2.0; //~ERROR !=-comparison of f32 or f64
+    ONE == 1f32;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (ONE - 1f32).abs() < error
+    ONE == 1.0 + 0.0;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (ONE - (1.0 + 0.0)).abs() < error
+
+    ONE + ONE == ZERO + ONE + ONE;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (ONE + ONE - (ZERO + ONE + ONE)).abs() < error
+
+    ONE != 2.0;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (ONE - 2.0).abs() < error
     ONE != 0.0; // no error, comparison with zero is ok
-    twice(ONE) != ONE; //~ERROR !=-comparison of f32 or f64
-    ONE as f64 != 2.0; //~ERROR !=-comparison of f32 or f64
+    twice(ONE) != ONE;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (twice(ONE) - ONE).abs() < error
+    ONE as f64 != 2.0;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (ONE as f64 - 2.0).abs() < error
     ONE as f64 != 0.0; // no error, comparison with zero is ok
 
     let x : f64 = 1.0;
 
-    x == 1.0; //~ERROR ==-comparison of f32 or f64
+    x == 1.0;
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (x - 1.0).abs() < error
     x != 0f64; // no error, comparison with zero is ok
 
-    twice(x) != twice(ONE as f64); //~ERROR !=-comparison of f32 or f64
+    twice(x) != twice(ONE as f64);
+    //~^ ERROR strict comparison of f32 or f64
+    //~| HELP within some error
+    //~| SUGGESTION (twice(x) - twice(ONE as f64)).abs() < error
 
 
     x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
index 411a4b11c17a064a442461c96b694d81fd1f2987..bcb20be4ff462d715541b840c25b6cdd094ccc55 100644 (file)
@@ -2,6 +2,7 @@
 #![plugin(clippy)]
 
 use std::collections::*;
+use std::rc::Rc;
 
 static STATIC: [usize; 4] = [ 0,  1,  8, 16 ];
 const CONST: [usize; 4] = [ 0,  1,  8, 16 ];
@@ -96,23 +97,41 @@ fn main() {
     let mut vec = vec![1, 2, 3, 4];
     let vec2 = vec![1, 2, 3, 4];
     for i in 0..vec.len() {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in &vec`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in &vec {
         println!("{}", vec[i]);
     }
 
+    for i in 0..vec.len() { let _ = vec[i]; }
+    //~^ ERROR `i` is only used to index `vec`
+    //~| HELP consider
+    //~| HELP consider
+    //~| SUGGESTION for <item> in &vec { let _ = vec[i]; }
+
     // ICE #746
     for j in 0..4 {
         //~^ ERROR `j` is only used to index `STATIC`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in STATIC.iter().take(4) {
         println!("{:?}", STATIC[j]);
     }
 
     for j in 0..4 {
         //~^ ERROR `j` is only used to index `CONST`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in CONST.iter().take(4) {
         println!("{:?}", CONST[j]);
     }
 
     for i in 0..vec.len() {
-        //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate()`
+        //~^ ERROR `i` is used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for (i, <item>) in vec.iter().enumerate() {
         println!("{} {}", vec[i], i);
     }
     for i in 0..vec.len() {      // not an error, indexing more than one variable
@@ -120,42 +139,66 @@ fn main() {
     }
 
     for i in 0..vec.len() {
-        //~^ ERROR `i` is only used to index `vec2`. Consider using `for item in vec2.iter().take(vec.len())`
+        //~^ ERROR `i` is only used to index `vec2`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec2.iter().take(vec.len()) {
         println!("{}", vec2[i]);
     }
 
     for i in 5..vec.len() {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().skip(5)`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec.iter().skip(5) {
         println!("{}", vec[i]);
     }
 
     for i in 0..MAX_LEN {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec.iter().take(MAX_LEN) {
         println!("{}", vec[i]);
     }
 
     for i in 0...MAX_LEN {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec.iter().take(MAX_LEN + 1) {
         println!("{}", vec[i]);
     }
 
     for i in 5..10 {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec.iter().take(10).skip(5) {
         println!("{}", vec[i]);
     }
 
     for i in 5...10 {
-        //~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
+        //~^ ERROR `i` is only used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for <item> in vec.iter().take(10 + 1).skip(5) {
         println!("{}", vec[i]);
     }
 
     for i in 5..vec.len() {
-        //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate().skip(5)`
+        //~^ ERROR `i` is used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for (i, <item>) in vec.iter().enumerate().skip(5) {
         println!("{} {}", vec[i], i);
     }
 
     for i in 5..10 {
-        //~^ ERROR `i` is used to index `vec`. Consider using `for (i, item) in vec.iter().enumerate().take(10).skip(5)`
+        //~^ ERROR `i` is used to index `vec`
+        //~| HELP consider
+        //~| HELP consider
+        //~| SUGGESTION for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
         println!("{} {}", vec[i], i);
     }
 
@@ -346,10 +389,22 @@ fn main() {
     for (_, v) in &m {
         //~^ you seem to want to iterate on a map's values
         //~| HELP use the corresponding method
-        //~| SUGGESTION for v in m.values()
+        //~| HELP use the corresponding method
+        //~| SUGGESTION for v in m.values() {
         let _v = v;
     }
 
+    let m : Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
+    for (_, v) in &*m {
+        //~^ you seem to want to iterate on a map's values
+        //~| HELP use the corresponding method
+        //~| HELP use the corresponding method
+        //~| SUGGESTION for v in (*m).values() {
+        let _v = v;
+        // Here the `*` is not actually necesarry, but the test tests that we don't suggest
+        // `in *m.values()` as we used to
+    }
+
     let mut m : HashMap<u64, u64> = HashMap::new();
     for (_, v) in &mut m {
         // Ok, there is no values_mut method or equivalent
@@ -361,7 +416,8 @@ fn main() {
     for (k, _value) in rm {
         //~^ you seem to want to iterate on a map's keys
         //~| HELP use the corresponding method
-        //~| SUGGESTION for k in rm.keys()
+        //~| HELP use the corresponding method
+        //~| SUGGESTION for k in rm.keys() {
         let _k = k;
     }
 
index 989106a5a16efb8d410090c0499b779f90ef81a0..f64cceb5c34711f6c7f6ae0246ad76cd8c93d5a8 100644 (file)
@@ -112,7 +112,7 @@ fn match_bool() {
 
     match test {
     //~^ ERROR you seem to be trying to match on a boolean expression
-    //~| HELP try
+    //~| HELP consider
     //~| SUGGESTION if test { 0 } else { 42 };
         true => 0,
         false => 42,
@@ -121,7 +121,7 @@ fn match_bool() {
     let option = 1;
     match option == 1 {
     //~^ ERROR you seem to be trying to match on a boolean expression
-    //~| HELP try
+    //~| HELP consider
     //~| SUGGESTION if option == 1 { 1 } else { 0 };
         true => 1,
         false => 0,
@@ -129,23 +129,32 @@ fn match_bool() {
 
     match test {
     //~^ ERROR you seem to be trying to match on a boolean expression
-    //~| HELP try
-    //~^^ SUGGESTION if !test { println!("Noooo!"); };
+    //~| HELP consider
+    //~| SUGGESTION if !test { println!("Noooo!"); };
         true => (),
         false => { println!("Noooo!"); }
     };
 
     match test {
     //~^ ERROR you seem to be trying to match on a boolean expression
-    //~| HELP try
-    //~^^ SUGGESTION if !test { println!("Noooo!"); };
+    //~| HELP consider
+    //~| SUGGESTION if !test { println!("Noooo!"); };
+        false => { println!("Noooo!"); }
+        _ => (),
+    };
+
+    match test && test {
+    //~^ ERROR you seem to be trying to match on a boolean expression
+    //~| HELP consider
+    //~| SUGGESTION if !(test && test) { println!("Noooo!"); };
+    //~| ERROR equal expressions as operands
         false => { println!("Noooo!"); }
         _ => (),
     };
 
     match test {
     //~^ ERROR you seem to be trying to match on a boolean expression
-    //~| HELP try
+    //~| HELP consider
     //~| SUGGESTION if test { println!("Yes!"); } else { println!("Noooo!"); };
         false => { println!("Noooo!"); }
         true => { println!("Yes!"); }
index de1556ed0e399fca9b9805adfff9fca6792aa41f..b2240cffb1a6638058fec7b45ab7682d4cb21aed 100644 (file)
@@ -20,11 +20,16 @@ fn main() {
   //~| HELP try
   //~| SUGGESTION let x = &1;
 
-  let ref y : (&_, u8) = (&1, 2);
+  let ref y: (&_, u8) = (&1, 2);
   //~^ ERROR `ref` on an entire `let` pattern is discouraged
   //~| HELP try
   //~| SUGGESTION let y: (&_, u8) = &(&1, 2);
 
+  let ref z = 1 + 2;
+  //~^ ERROR `ref` on an entire `let` pattern is discouraged
+  //~| HELP try
+  //~| SUGGESTION let z = &(1 + 2);
+
   let (ref x, _) = (1,2); // okay, not top level
   println!("The answer is {}.", x);
 }
index 4a120a6eeddec868122efeae6c0c58d785a19521..0dbd58b13089345458fcb1747bff38c235777c17 100644 (file)
@@ -62,6 +62,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     //~^ ERROR transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
     //~| HELP try
     //~| SUGGESTION = &mut *(p as *mut T);
+    let _ = &mut *(p as *mut T);
 
     let _: &T = std::mem::transmute(o);
     //~^ ERROR transmute from a pointer type (`*const U`) to a reference type (`&T`)