X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Ftraits%2Ferror_reporting%2Fsuggestions.rs;h=b2aec78c175adbd05d4cac88545ae523f230aa1d;hb=600e385c43904eb4a5337427f3f6fb169fe32234;hp=4559007ea426aa879d59bc4f37c49e0540c06cab;hpb=3fa8cc38777362f30ec560f6164239923403ea3e;p=rust.git diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index 4559007ea42..b2aec78c175 100644 --- a/src/librustc/traits/error_reporting/suggestions.rs +++ b/src/librustc/traits/error_reporting/suggestions.rs @@ -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` 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>, 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` 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)); @@ -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; } } }