]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/traits/error_reporting/suggestions.rs
review comments
[rust.git] / src / librustc / traits / error_reporting / suggestions.rs
index bf6891214ace1637dbb5539135984f15e3266e19..b2aec78c175adbd05d4cac88545ae523f230aa1d 100644 (file)
@@ -6,7 +6,7 @@
 use crate::infer::InferCtxt;
 use crate::traits::object_safety::object_safety_violations;
 use crate::ty::TypeckTables;
-use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
 
 use rustc_errors::{
     error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style,
@@ -20,8 +20,6 @@
 use rustc_span::{MultiSpan, Span, DUMMY_SP};
 use std::fmt;
 
-use rustc_error_codes::*;
-
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     crate fn suggest_restricting_param_bound(
         &self,
@@ -50,7 +48,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             } else {
                                 " where"
                             },
-                            trait_ref.to_predicate(),
+                            trait_ref.without_const().to_predicate(),
                         ),
                         Applicability::MachineApplicable,
                     );
@@ -88,8 +86,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     ..
                 })
                 | hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Impl(_, _, _, generics, ..),
-                    ..
+                    kind: hir::ItemKind::Impl { generics, .. }, ..
                 }) if projection.is_some() => {
                     // Missing associated type bound.
                     suggest_restriction(&generics, "the associated type", err);
@@ -115,7 +112,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     ..
                 })
                 | hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Impl(_, _, _, generics, ..),
+                    kind: hir::ItemKind::Impl { generics, .. },
                     span,
                     ..
                 })
@@ -341,8 +338,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
             let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
             let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
-            let new_obligation =
-                Obligation::new(ObligationCause::dummy(), param_env, new_trait_ref.to_predicate());
+            let new_obligation = Obligation::new(
+                ObligationCause::dummy(),
+                param_env,
+                new_trait_ref.without_const().to_predicate(),
+            );
             if self.predicate_must_hold_modulo_regions(&new_obligation) {
                 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
                     // We have a very specific type of error, where just borrowing this argument
@@ -600,17 +600,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         // Visit to make sure there's a single `return` type to suggest `impl Trait`,
         // otherwise suggest using `Box<dyn Trait>` or an enum.
-        let mut visitor = ReturnsVisitor(vec![]);
+        let mut visitor = ReturnsVisitor::default();
         visitor.visit_body(&body);
 
         let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
 
-        let mut ret_types = visitor.0.iter().filter_map(|expr| tables.node_type_opt(expr.hir_id));
-        let (last_ty, all_returns_have_same_type) =
-            ret_types.clone().fold((None, true), |(last_ty, mut same), returned_ty| {
-                same &= last_ty.map_or(true, |ty| ty == returned_ty);
-                (Some(returned_ty), same)
-            });
+        let mut ret_types = visitor
+            .returns
+            .iter()
+            .filter_map(|expr| tables.node_type_opt(expr.hir_id))
+            .map(|ty| self.resolve_vars_if_possible(&ty));
+        let (last_ty, all_returns_have_same_type) = ret_types.clone().fold(
+            (None, true),
+            |(last_ty, mut same): (std::option::Option<Ty<'_>>, bool), ty| {
+                let ty = self.resolve_vars_if_possible(&ty);
+                same &= last_ty.map_or(true, |last_ty| last_ty == ty) && ty.kind != ty::Error;
+                (Some(ty), same)
+            },
+        );
         let all_returns_conform_to_trait =
             if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) {
                 match ty_ret_ty.kind {
@@ -625,7 +632,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             })
                         })
                     }
-                    _ => true,
+                    _ => false,
                 }
             } else {
                 true
@@ -675,7 +682,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
                 // Get all the return values and collect their span and suggestion.
                 let mut suggestions = visitor
-                    .0
+                    .returns
                     .iter()
                     .map(|expr| {
                         (
@@ -735,10 +742,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         {
             let body = hir.body(*body_id);
             // Point at all the `return`s in the function as they have failed trait bounds.
-            let mut visitor = ReturnsVisitor(vec![]);
+            let mut visitor = ReturnsVisitor::default();
             visitor.visit_body(&body);
             let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
-            for expr in &visitor.0 {
+            for expr in &visitor.returns {
                 if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) {
                     let ty = self.resolve_vars_if_possible(&returned_ty);
                     err.span_label(expr.span, &format!("this returned value is of type `{}`", ty));
@@ -1123,7 +1130,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         // the type. The last generator has information about where the bound was introduced. At
         // least one generator should be present for this diagnostic to be modified.
         let (mut trait_ref, mut target_ty) = match obligation.predicate {
-            ty::Predicate::Trait(p) => {
+            ty::Predicate::Trait(p, _) => {
                 (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty()))
             }
             _ => (None, None),
@@ -1546,7 +1553,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 err.note(&format!("required because it appears within the type `{}`", ty));
                 obligated_types.push(ty);
 
-                let parent_predicate = parent_trait_ref.to_predicate();
+                let parent_predicate = parent_trait_ref.without_const().to_predicate();
                 if !self.is_recursive_obligation(obligated_types, &data.parent_code) {
                     self.note_obligation_cause_code(
                         err,
@@ -1563,7 +1570,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     parent_trait_ref.print_only_trait_path(),
                     parent_trait_ref.skip_binder().self_ty()
                 ));
-                let parent_predicate = parent_trait_ref.to_predicate();
+                let parent_predicate = parent_trait_ref.without_const().to_predicate();
                 self.note_obligation_cause_code(
                     err,
                     &parent_predicate,
@@ -1689,7 +1696,11 @@ pub fn suggest_constraining_type_param(
 
 /// Collect all the returned expressions within the input expression.
 /// Used to point at the return spans when we want to suggest some change to them.
-struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
+#[derive(Default)]
+struct ReturnsVisitor<'v> {
+    returns: Vec<&'v hir::Expr<'v>>,
+    in_block_tail: bool,
+}
 
 impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
     type Map = rustc::hir::map::Map<'v>;
@@ -1699,17 +1710,41 @@ fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Ma
     }
 
     fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
-        if let hir::ExprKind::Ret(Some(ex)) = ex.kind {
-            self.0.push(ex);
+        // Visit every expression to detect `return` paths, either through the function's tail
+        // expression or `return` statements. We walk all nodes to find `return` statements, but
+        // we only care about tail expressions when `in_block_tail` is `true`, which means that
+        // they're in the return path of the function body.
+        match ex.kind {
+            hir::ExprKind::Ret(Some(ex)) => {
+                self.returns.push(ex);
+            }
+            hir::ExprKind::Block(block, _) if self.in_block_tail => {
+                self.in_block_tail = false;
+                for stmt in block.stmts {
+                    hir::intravisit::walk_stmt(self, stmt);
+                }
+                self.in_block_tail = true;
+                if let Some(expr) = block.expr {
+                    self.visit_expr(expr);
+                }
+            }
+            hir::ExprKind::Match(_, arms, _) if self.in_block_tail => {
+                for arm in arms {
+                    self.visit_expr(arm.body);
+                }
+            }
+            // We need to walk to find `return`s in the entire body.
+            _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex),
+            _ => self.returns.push(ex),
         }
-        hir::intravisit::walk_expr(self, ex);
     }
 
     fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+        assert!(!self.in_block_tail);
         if body.generator_kind().is_none() {
             if let hir::ExprKind::Block(block, None) = body.value.kind {
-                if let Some(expr) = block.expr {
-                    self.0.push(expr);
+                if block.expr.is_some() {
+                    self.in_block_tail = true;
                 }
             }
         }