Account for tail expressions when pointing at return type

When there's a type mismatch we make an effort to check if it was
caused by a function's return type. This logic now makes sure to
only point at the return type if the error happens in a tail
expression.
This commit is contained in:
Esteban Küber 2019-09-25 23:01:01 -07:00
parent faee8e1756
commit 46a38dc183
4 changed files with 31 additions and 7 deletions

View File

@ -741,7 +741,28 @@ pub fn is_hir_id_module(&self, hir_id: HirId) -> bool {
/// }
/// ```
pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
for (hir_id, node) in ParentHirIterator::new(id, &self) {
let mut iter = ParentHirIterator::new(id, &self).peekable();
let mut ignore_tail = false;
if let Some(entry) = self.find_entry(id) {
if let Node::Expr(Expr { node: ExprKind::Ret(_), .. }) = entry.node {
// When dealing with `return` statements, we don't care about climbing only tail
// expressions.
ignore_tail = true;
}
}
while let Some((hir_id, node)) = iter.next() {
if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) {
match next_node {
Node::Block(Block { expr: None, .. }) => return None,
Node::Block(Block { expr: Some(expr), .. }) => {
if hir_id != expr.hir_id {
// The current node is not the tail expression of its parent.
return None;
}
}
_ => {}
}
}
match node {
Node::Item(_) |
Node::ForeignItem(_) |
@ -750,10 +771,12 @@ pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
Node::ImplItem(_) => return Some(hir_id),
Node::Expr(ref expr) => {
match expr.kind {
// Ignore `return`s on the first iteration
ExprKind::Loop(..) | ExprKind::Ret(..) => return None,
_ => {}
}
}
Node::Local(_) => return None,
_ => {}
}
}

View File

@ -1563,7 +1563,7 @@ pub enum ExprKind {
/// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
/// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`.
MethodCall(P<PathSegment>, Span, HirVec<Expr>),
/// A tuple (e.g., `(a, b, c ,d)`).
/// A tuple (e.g., `(a, b, c, d)`).
Tup(HirVec<Expr>),
/// A binary operation (e.g., `a + b`, `a * b`).
Binary(BinOp, P<Expr>, P<Expr>),

View File

@ -620,8 +620,12 @@ fn check_expr_return(
expr: &'tcx hir::Expr
) -> Ty<'tcx> {
if self.ret_coercion.is_none() {
struct_span_err!(self.tcx.sess, expr.span, E0572,
"return statement outside of function body").emit();
struct_span_err!(
self.tcx.sess,
expr.span,
E0572,
"return statement outside of function body",
).emit();
} else if let Some(ref e) = expr_opt {
if self.ret_coercion_span.borrow().is_none() {
*self.ret_coercion_span.borrow_mut() = Some(e.span);

View File

@ -49,9 +49,6 @@ LL | if x == E::V { field } {}
error[E0308]: mismatched types
--> $DIR/struct-literal-variant-in-if.rs:10:20
|
LL | fn test_E(x: E) {
| - help: try adding a return type: `-> bool`
LL | let field = true;
LL | if x == E::V { field } {}
| ^^^^^ expected (), found bool
|