]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/matches.rs
Auto merge of #4551 - mikerite:fix-ice-reporting, r=llogiq
[rust.git] / clippy_lints / src / matches.rs
index 101a31e308608cdbf7c601291bddebc6e8638482..2a65ea000db3de44b0f42f9863b652b31f96f477 100644 (file)
@@ -2,15 +2,15 @@
 use crate::utils::paths;
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    expr_block, in_macro, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
+    expr_block, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
     snippet_with_applicability, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty,
 };
 use if_chain::if_chain;
 use rustc::hir::def::CtorKind;
 use rustc::hir::*;
 use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
-use rustc::ty::{self, Ty, TyKind};
-use rustc::{declare_tool_lint, lint_array};
+use rustc::ty::{self, Ty};
+use rustc::{declare_lint_pass, declare_tool_lint};
 use rustc_errors::Applicability;
 use std::cmp::Ordering;
 use std::collections::Bound;
@@ -28,6 +28,8 @@
     ///
     /// **Example:**
     /// ```rust
+    /// # fn bar(stool: &str) {}
+    /// # let x = Some("abc");
     /// match x {
     ///     Some(ref foo) => bar(foo),
     ///     _ => (),
     /// ```
     pub SINGLE_MATCH,
     style,
-    "a match statement with a single nontrivial arm (i.e. where the other arm is `_ => {}`) instead of `if let`"
+    "a match statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
 }
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for matches with two arms where an `if let else` will
+    /// **What it does:** Checks for matches with two arms where an `if let else` will
     /// usually suffice.
     ///
     /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
     /// Using `match`:
     ///
     /// ```rust
+    /// # fn bar(foo: &usize) {}
+    /// # let other_ref: usize = 1;
+    /// # let x: Option<&usize> = Some(&1);
     /// match x {
     ///     Some(ref foo) => bar(foo),
-    ///     _ => bar(other_ref),
+    ///     _ => bar(&other_ref),
     /// }
     /// ```
     ///
     /// Using `if let` with `else`:
     ///
     /// ```rust
+    /// # fn bar(foo: &usize) {}
+    /// # let other_ref: usize = 1;
+    /// # let x: Option<&usize> = Some(&1);
     /// if let Some(ref foo) = x {
     ///     bar(foo);
     /// } else {
-    ///     bar(other_ref);
+    ///     bar(&other_ref);
     /// }
     /// ```
     pub SINGLE_MATCH_ELSE,
     pedantic,
-    "a match statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
+    "a match statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
 }
 
 declare_clippy_lint! {
@@ -82,7 +90,7 @@
     /// **Known problems:** None.
     ///
     /// **Example:**
-    /// ```rust
+    /// ```rust,ignore
     /// match x {
     ///     &A(ref y) => foo(y),
     ///     &B => bar(),
     ///
     /// **Example:**
     /// ```rust
+    /// # fn foo() {}
+    /// # fn bar() {}
     /// let condition: bool = true;
     /// match condition {
     ///     true => foo(),
     /// ```
     /// Use if/else instead:
     /// ```rust
+    /// # fn foo() {}
+    /// # fn bar() {}
     /// let condition: bool = true;
     /// if condition {
     ///     foo();
     ///
     /// **Example:**
     /// ```rust
-    /// let x: Result(i32, &str) = Ok(3);
+    /// let x: Result<i32, &str> = Ok(3);
     /// match x {
     ///     Ok(_) => println!("ok"),
     ///     Err(_) => panic!("err"),
     ///
     /// **Example:**
     /// ```rust
+    /// # enum Foo { A(usize), B(usize) }
+    /// # let x = Foo::B(1);
     /// match x {
     ///     A => {},
     ///     _ => {},
     "a wildcard enum match arm using `_`"
 }
 
-#[allow(missing_copy_implementations)]
-pub struct MatchPass;
-
-impl LintPass for MatchPass {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(
-            SINGLE_MATCH,
-            MATCH_REF_PATS,
-            MATCH_BOOL,
-            SINGLE_MATCH_ELSE,
-            MATCH_OVERLAPPING_ARM,
-            MATCH_WILD_ERR_ARM,
-            MATCH_AS_REF,
-            WILDCARD_ENUM_MATCH_ARM
-        )
-    }
-
-    fn name(&self) -> &'static str {
-        "Matches"
-    }
-}
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchPass {
+declare_lint_pass!(Matches => [
+    SINGLE_MATCH,
+    MATCH_REF_PATS,
+    MATCH_BOOL,
+    SINGLE_MATCH_ELSE,
+    MATCH_OVERLAPPING_ARM,
+    MATCH_WILD_ERR_ARM,
+    MATCH_AS_REF,
+    WILDCARD_ENUM_MATCH_ARM
+]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
         if in_external_macro(cx.sess(), expr.span) {
             return;
@@ -322,7 +325,7 @@ fn check_single_match_opt_like(
     ty: Ty<'_>,
     els: Option<&Expr>,
 ) {
-    // list of candidate Enums we know will never get any more members
+    // list of candidate `Enum`s we know will never get any more members
     let candidates = &[
         (&paths::COW, "Borrowed"),
         (&paths::COW, "Cow::Borrowed"),
@@ -335,7 +338,7 @@ fn check_single_match_opt_like(
 
     let path = match arms[1].pats[0].node {
         PatKind::TupleStruct(ref path, ref inner, _) => {
-            // contains any non wildcard patterns? e.g. Err(err)
+            // Contains any non wildcard patterns (e.g., `Err(err)`)?
             if !inner.iter().all(is_wild) {
                 return;
             }
@@ -354,7 +357,7 @@ fn check_single_match_opt_like(
 }
 
 fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
-    // type of expression == bool
+    // Type of expression is `bool`.
     if cx.tables.expr_ty(ex).sty == ty::Bool {
         span_lint_and_then(
             cx,
@@ -482,7 +485,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
         for pat in &arm.pats {
             if let PatKind::Wild = pat.node {
                 wildcard_span = Some(pat.span);
-            } else if let PatKind::Binding(_, _, _, ident, None) = pat.node {
+            } else if let PatKind::Binding(_, _, ident, None) = pat.node {
                 wildcard_span = Some(pat.span);
                 wildcard_ident = Some(ident);
             }
@@ -494,7 +497,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
         // already covered.
 
         let mut missing_variants = vec![];
-        if let TyKind::Adt(def, _) = ty.sty {
+        if let ty::Adt(def, _) = ty.sty {
             for variant in &def.variants {
                 missing_variants.push(variant);
             }
@@ -510,11 +513,11 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
             for pat in &arm.pats {
                 if let PatKind::Path(ref path) = pat.deref().node {
                     if let QPath::Resolved(_, p) = path {
-                        missing_variants.retain(|e| e.did != p.def.def_id());
+                        missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
                     }
                 } else if let PatKind::TupleStruct(ref path, ..) = pat.deref().node {
                     if let QPath::Resolved(_, p) = path {
-                        missing_variants.retain(|e| e.did != p.def.def_id());
+                        missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
                     }
                 }
             }
@@ -533,7 +536,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
                     String::new()
                 };
                 // This path assumes that the enum type is imported into scope.
-                format!("{}{}{}", ident_str, cx.tcx.item_path_str(v.did), suffix)
+                format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
             })
             .collect();
 
@@ -594,7 +597,7 @@ fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr:
         }));
 
         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
-            if !in_macro(expr.span) {
+            if !expr.span.from_expansion() {
                 multispan_sugg(db, msg.to_owned(), suggs);
             }
         });
@@ -621,6 +624,24 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &
             } else {
                 "as_mut"
             };
+
+            let output_ty = cx.tables.expr_ty(expr);
+            let input_ty = cx.tables.expr_ty(ex);
+
+            let cast = if_chain! {
+                if let ty::Adt(_, substs) = input_ty.sty;
+                let input_ty = substs.type_at(0);
+                if let ty::Adt(_, substs) = output_ty.sty;
+                let output_ty = substs.type_at(0);
+                if let ty::Ref(_, output_ty, _) = output_ty.sty;
+                if input_ty != output_ty;
+                then {
+                    ".map(|x| x as _)"
+                } else {
+                    ""
+                }
+            };
+
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
                 cx,
@@ -629,9 +650,10 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &
                 &format!("use {}() instead", suggestion),
                 "try this",
                 format!(
-                    "{}.{}()",
+                    "{}.{}(){}",
                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
-                    suggestion
+                    suggestion,
+                    cast,
                 ),
                 applicability,
             )
@@ -639,7 +661,7 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &
     }
 }
 
-/// Get all arms that are unbounded `PatRange`s.
+/// Gets all arms that are unbounded `PatRange`s.
 fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm]) -> Vec<SpannedRange<Constant>> {
     arms.iter()
         .flat_map(|arm| {
@@ -687,7 +709,7 @@ pub struct SpannedRange<T> {
 
 type TypedRanges = Vec<SpannedRange<u128>>;
 
-/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
+/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
 /// and other types than
 /// `Uint` and `Int` probably don't make sense.
 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
@@ -768,7 +790,7 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
     T: Copy + Ord,
 {
     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-    enum Kind<'a, T: 'a> {
+    enum Kind<'a, T> {
         Start(T, &'a SpannedRange<T>),
         End(Bound<T>, &'a SpannedRange<T>),
     }