// 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 {
})
})
}
- _ => true,
+ _ => false,
}
} else {
true
// 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| {
(
{
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));
/// 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>;
}
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;
}
}
}