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:
parent
faee8e1756
commit
46a38dc183
4 changed files with 31 additions and 7 deletions
|
@ -741,7 +741,28 @@ impl<'hir> Map<'hir> {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
|
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 {
|
match node {
|
||||||
Node::Item(_) |
|
Node::Item(_) |
|
||||||
Node::ForeignItem(_) |
|
Node::ForeignItem(_) |
|
||||||
|
@ -750,10 +771,12 @@ impl<'hir> Map<'hir> {
|
||||||
Node::ImplItem(_) => return Some(hir_id),
|
Node::ImplItem(_) => return Some(hir_id),
|
||||||
Node::Expr(ref expr) => {
|
Node::Expr(ref expr) => {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
|
// Ignore `return`s on the first iteration
|
||||||
ExprKind::Loop(..) | ExprKind::Ret(..) => return None,
|
ExprKind::Loop(..) | ExprKind::Ret(..) => return None,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Node::Local(_) => return None,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,8 +620,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expr: &'tcx hir::Expr
|
expr: &'tcx hir::Expr
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
if self.ret_coercion.is_none() {
|
if self.ret_coercion.is_none() {
|
||||||
struct_span_err!(self.tcx.sess, expr.span, E0572,
|
struct_span_err!(
|
||||||
"return statement outside of function body").emit();
|
self.tcx.sess,
|
||||||
|
expr.span,
|
||||||
|
E0572,
|
||||||
|
"return statement outside of function body",
|
||||||
|
).emit();
|
||||||
} else if let Some(ref e) = expr_opt {
|
} else if let Some(ref e) = expr_opt {
|
||||||
if self.ret_coercion_span.borrow().is_none() {
|
if self.ret_coercion_span.borrow().is_none() {
|
||||||
*self.ret_coercion_span.borrow_mut() = Some(e.span);
|
*self.ret_coercion_span.borrow_mut() = Some(e.span);
|
||||||
|
|
|
@ -49,9 +49,6 @@ LL | if x == E::V { field } {}
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/struct-literal-variant-in-if.rs:10:20
|
--> $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 } {}
|
LL | if x == E::V { field } {}
|
||||||
| ^^^^^ expected (), found bool
|
| ^^^^^ expected (), found bool
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue