]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/demand.rs
only issue "variant of the expected type" suggestion for enums
[rust.git] / src / librustc_typeck / check / demand.rs
index 71c78e7f87c07613f09eb5537faa668ffb2074d2..7773e2d570844b892d2157b0941d05580c7e7e26 100644 (file)
@@ -111,34 +111,35 @@ pub fn demand_coerce_diag(&self,
         let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
         let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
 
-        // If the expected type is an enum with any variants whose sole
-        // field is of the found type, suggest such variants. See Issue
-        // #42764.
+        // If the expected type is an enum (Issue #55250) with any variants whose
+        // sole field is of the found type, suggest such variants. (Issue #42764)
         if let ty::Adt(expected_adt, substs) = expected.sty {
-            let compatible_variants = expected_adt.variants
-                                                  .iter()
-                                                  .filter(|variant| variant.fields.len() == 1)
-                                                  .filter_map(|variant| {
-                let sole_field = &variant.fields[0];
-                let sole_field_ty = sole_field.ty(self.tcx, substs);
-                if self.can_coerce(expr_ty, sole_field_ty) {
-                    let variant_path = self.tcx.item_path_str(variant.did);
-                    Some(variant_path.trim_left_matches("std::prelude::v1::").to_string())
-                } else {
-                    None
+            if expected_adt.is_enum() {
+                let mut compatible_variants = expected_adt.variants
+                    .iter()
+                    .filter(|variant| variant.fields.len() == 1)
+                    .filter_map(|variant| {
+                        let sole_field = &variant.fields[0];
+                        let sole_field_ty = sole_field.ty(self.tcx, substs);
+                        if self.can_coerce(expr_ty, sole_field_ty) {
+                            let variant_path = self.tcx.item_path_str(variant.did);
+                            Some(variant_path.trim_left_matches("std::prelude::v1::").to_string())
+                        } else {
+                            None
+                        }
+                    }).peekable();
+
+                if compatible_variants.peek().is_some() {
+                    let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
+                    let suggestions = compatible_variants
+                        .map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
+                    err.span_suggestions_with_applicability(
+                        expr.span,
+                        "try using a variant of the expected type",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
                 }
-            }).collect::<Vec<_>>();
-
-            if !compatible_variants.is_empty() {
-                let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
-                let suggestions = compatible_variants.iter()
-                    .map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
-                err.span_suggestions_with_applicability(
-                     expr.span,
-                     "try using a variant of the expected type",
-                     suggestions,
-                     Applicability::MaybeIncorrect,
-                );
             }
         }
 
@@ -309,11 +310,20 @@ pub fn check_ref(&self,
                 };
                 if self.can_coerce(ref_ty, expected) {
                     if let Ok(src) = cm.span_to_snippet(sp) {
-                        let sugg_expr = match expr.node { // parenthesize if needed (Issue #46756)
+                        let needs_parens = match expr.node {
+                            // parenthesize if needed (Issue #46756)
                             hir::ExprKind::Cast(_, _) |
-                            hir::ExprKind::Binary(_, _, _) => format!("({})", src),
-                            _ => src,
+                            hir::ExprKind::Binary(_, _, _) => true,
+                            // parenthesize borrows of range literals (Issue #54505)
+                            _ if self.is_range_literal(expr) => true,
+                            _ => false,
                         };
+                        let sugg_expr = if needs_parens {
+                            format!("({})", src)
+                        } else {
+                            src
+                        };
+
                         if let Some(sugg) = self.can_use_as_ref(expr) {
                             return Some(sugg);
                         }
@@ -374,6 +384,66 @@ pub fn check_ref(&self,
         None
     }
 
+    /// This function checks if the specified expression is a built-in range literal.
+    /// (See: `LoweringContext::lower_expr()` in `src/librustc/hir/lowering.rs`).
+    fn is_range_literal(&self, expr: &hir::Expr) -> bool {
+        use hir::{Path, QPath, ExprKind, TyKind};
+
+        // We support `::std::ops::Range` and `::core::ops::Range` prefixes
+        let is_range_path = |path: &Path| {
+            let mut segs = path.segments.iter()
+                .map(|seg| seg.ident.as_str());
+
+            if let (Some(root), Some(std_core), Some(ops), Some(range), None) =
+                (segs.next(), segs.next(), segs.next(), segs.next(), segs.next())
+            {
+                // "{{root}}" is the equivalent of `::` prefix in Path
+                root == "{{root}}" && (std_core == "std" || std_core == "core")
+                    && ops == "ops" && range.starts_with("Range")
+            } else {
+                false
+            }
+        };
+
+        let span_is_range_literal = |span: &Span| {
+            // Check whether a span corresponding to a range expression
+            // is a range literal, rather than an explicit struct or `new()` call.
+            let source_map = self.tcx.sess.source_map();
+            let end_point = source_map.end_point(*span);
+
+            if let Ok(end_string) = source_map.span_to_snippet(end_point) {
+                !(end_string.ends_with("}") || end_string.ends_with(")"))
+            } else {
+                false
+            }
+        };
+
+        match expr.node {
+            // All built-in range literals but `..=` and `..` desugar to Structs
+            ExprKind::Struct(QPath::Resolved(None, ref path), _, _) |
+            // `..` desugars to its struct path
+            ExprKind::Path(QPath::Resolved(None, ref path)) => {
+                return is_range_path(&path) && span_is_range_literal(&expr.span);
+            }
+
+            // `..=` desugars into `::std::ops::RangeInclusive::new(...)`
+            ExprKind::Call(ref func, _) => {
+                if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.node {
+                    if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.node {
+                        let call_to_new = segment.ident.as_str() == "new";
+
+                        return is_range_path(&path) && span_is_range_literal(&expr.span)
+                            && call_to_new;
+                    }
+                }
+            }
+
+            _ => {}
+        }
+
+        false
+    }
+
     pub fn check_for_cast(&self,
                       err: &mut DiagnosticBuilder<'tcx>,
                       expr: &hir::Expr,