Skip to content

Commit

Permalink
Rollup merge of #63833 - estebank:suggest-closure-call, r=petrochenkov
Browse files Browse the repository at this point in the history
Suggest calling closure with resolved return type when appropriate

Follow up to #63337. CC #63100.

```
error[E0308]: mismatched types
  --> $DIR/fn-or-tuple-struct-without-args.rs:46:20
   |
LL |     let closure = || 42;
   |                   -- closure defined here
LL |     let _: usize = closure;
   |                    ^^^^^^^
   |                    |
   |                    expected usize, found closure
   |                    help: use parentheses to call this closure: `closure()`
   |
   = note: expected type `usize`
              found type `[closure@$DIR/fn-or-tuple-struct-without-args.rs:45:19: 45:24]`
```
  • Loading branch information
Centril authored Aug 25, 2019
2 parents ed8e13c + 3890bef commit 3d4b113
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ impl<'tcx> ClosureSubsts<'tcx> {
let ty = self.closure_sig_ty(def_id, tcx);
match ty.sty {
ty::FnPtr(sig) => sig,
_ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty),
_ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.sty),
}
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,12 +799,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// adjusted type of the expression, if successful.
/// Adjustments are only recorded if the coercion succeeded.
/// The expressions *must not* have any pre-existing adjustments.
pub fn try_coerce(&self,
expr: &hir::Expr,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase)
-> RelateResult<'tcx, Ty<'tcx>> {
pub fn try_coerce(
&self,
expr: &hir::Expr,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase,
) -> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_type_vars_with_obligations(expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);

Expand Down
130 changes: 77 additions & 53 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3917,75 +3917,99 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> bool {
match found.sty {
ty::FnDef(..) | ty::FnPtr(_) => {}
_ => return false,
}
let hir = self.tcx.hir();
let (def_id, sig) = match found.sty {
ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
ty::Closure(def_id, substs) => {
// We don't use `closure_sig` to account for malformed closures like
// `|_: [_; continue]| {}` and instead we don't suggest anything.
let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
(def_id, match closure_sig_ty.sty {
ty::FnPtr(sig) => sig,
_ => return false,
})
}
_ => return false,
};

let sig = found.fn_sig(self.tcx);
let sig = self
.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig)
.0;
let sig = self.normalize_associated_types_in(expr.span, &sig);
if let Ok(_) = self.try_coerce(expr, sig.output(), expected, AllowTwoPhase::No) {
if self.can_coerce(sig.output(), expected) {
let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
(String::new(), Applicability::MachineApplicable)
} else {
("...".to_string(), Applicability::HasPlaceholders)
};
let mut msg = "call this function";
if let ty::FnDef(def_id, ..) = found.sty {
match hir.get_if_local(def_id) {
Some(Node::Item(hir::Item {
node: ItemKind::Fn(.., body_id),
..
})) |
Some(Node::ImplItem(hir::ImplItem {
node: hir::ImplItemKind::Method(_, body_id),
..
})) |
Some(Node::TraitItem(hir::TraitItem {
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Provided(body_id)),
..
})) => {
let body = hir.body(*body_id);
sugg_call = body.arguments.iter()
.map(|arg| match &arg.pat.node {
hir::PatKind::Binding(_, _, ident, None)
if ident.name != kw::SelfLower => ident.to_string(),
_ => "_".to_string(),
}).collect::<Vec<_>>().join(", ");
match hir.get_if_local(def_id) {
Some(Node::Item(hir::Item {
node: ItemKind::Fn(.., body_id),
..
})) |
Some(Node::ImplItem(hir::ImplItem {
node: hir::ImplItemKind::Method(_, body_id),
..
})) |
Some(Node::TraitItem(hir::TraitItem {
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Provided(body_id)),
..
})) => {
let body = hir.body(*body_id);
sugg_call = body.arguments.iter()
.map(|arg| match &arg.pat.node {
hir::PatKind::Binding(_, _, ident, None)
if ident.name != kw::SelfLower => ident.to_string(),
_ => "_".to_string(),
}).collect::<Vec<_>>().join(", ");
}
Some(Node::Expr(hir::Expr {
node: ExprKind::Closure(_, _, body_id, closure_span, _),
span: full_closure_span,
..
})) => {
if *full_closure_span == expr.span {
return false;
}
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
match hir.as_local_hir_id(def_id).and_then(|hir_id| hir.def_kind(hir_id)) {
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
msg = "instantiate this tuple variant";
}
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, _)) => {
msg = "instantiate this tuple struct";
}
_ => {}
err.span_label(*closure_span, "closure defined here");
msg = "call this closure";
let body = hir.body(*body_id);
sugg_call = body.arguments.iter()
.map(|arg| match &arg.pat.node {
hir::PatKind::Binding(_, _, ident, None)
if ident.name != kw::SelfLower => ident.to_string(),
_ => "_".to_string(),
}).collect::<Vec<_>>().join(", ");
}
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
match hir.as_local_hir_id(def_id).and_then(|hir_id| hir.def_kind(hir_id)) {
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
msg = "instantiate this tuple variant";
}
Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, _)) => {
msg = "instantiate this tuple struct";
}
_ => {}
}
Some(Node::ForeignItem(hir::ForeignItem {
node: hir::ForeignItemKind::Fn(_, idents, _),
..
})) |
Some(Node::TraitItem(hir::TraitItem {
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Required(idents)),
..
})) => sugg_call = idents.iter()
.map(|ident| if ident.name != kw::SelfLower {
ident.to_string()
} else {
"_".to_string()
}).collect::<Vec<_>>()
.join(", "),
_ => {}
}
};
Some(Node::ForeignItem(hir::ForeignItem {
node: hir::ForeignItemKind::Fn(_, idents, _),
..
})) |
Some(Node::TraitItem(hir::TraitItem {
node: hir::TraitItemKind::Method(.., hir::TraitMethod::Required(idents)),
..
})) => sugg_call = idents.iter()
.map(|ident| if ident.name != kw::SelfLower {
ident.to_string()
} else {
"_".to_string()
}).collect::<Vec<_>>()
.join(", "),
_ => {}
}
if let Ok(code) = self.sess().source_map().span_to_snippet(expr.span) {
err.span_suggestion(
expr.span,
Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/suggestions/fn-or-tuple-struct-without-args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ fn main() {
let _: usize = X::bal; //~ ERROR mismatched types
let _: usize = X.ban; //~ ERROR attempted to take value of method
let _: usize = X.bal; //~ ERROR attempted to take value of method
let closure = || 42;
let _: usize = closure; //~ ERROR mismatched types
}
16 changes: 15 additions & 1 deletion src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,21 @@ error[E0615]: attempted to take value of method `bal` on type `X`
LL | let _: usize = X.bal;
| ^^^ help: use parentheses to call the method: `bal()`

error: aborting due to 16 previous errors
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:46:20
|
LL | let closure = || 42;
| -- closure defined here
LL | let _: usize = closure;
| ^^^^^^^
| |
| expected usize, found closure
| help: use parentheses to call this closure: `closure()`
|
= note: expected type `usize`
found type `[closure@$DIR/fn-or-tuple-struct-without-args.rs:45:19: 45:24]`

error: aborting due to 17 previous errors

Some errors have detailed explanations: E0308, E0423, E0615.
For more information about an error, try `rustc --explain E0308`.

0 comments on commit 3d4b113

Please sign in to comment.