From 73e3508bb84bec2911f56e5c2463eee3688cf8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 23 Aug 2019 11:14:11 -0700 Subject: [PATCH 1/2] Suggest calling closure with resolved return type when appropriate --- src/librustc/ty/sty.rs | 3 +- src/librustc_typeck/check/coercion.rs | 13 +- src/librustc_typeck/check/mod.rs | 136 ++++++++++-------- .../fn-or-tuple-struct-without-args.rs | 2 + .../fn-or-tuple-struct-without-args.stderr | 16 ++- 5 files changed, 106 insertions(+), 64 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index da66fdf5b1b..7b7e2b8bfbd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -385,7 +385,8 @@ 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), + ty::Infer(_) | ty::Error => ty::Binder::dummy(FnSig::fake()), // ignore errors + _ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.sty), } } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 4edb6ad8931..61b9c2a15ba 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -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); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9c7ac83e82e..d92ce29f284 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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::>().join(", "); - } - Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { - sugg_call = fields.iter().map(|_| "_").collect::>().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::>() - .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::>().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; + } + 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::>().join(", "); + } + Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { + sugg_call = fields.iter().map(|_| "_").collect::>().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::>() + .join(", "), + _ => {} + } if let Ok(code) = self.sess().source_map().span_to_snippet(expr.span) { err.span_suggestion( expr.span, diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.rs index 9b6b1074817..dd5af3e344c 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.rs +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.rs @@ -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 } diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 0686b56f97d..28b331bdbdc 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -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`. From 3890befa8ea9def7e1c9c57a321c7b8c9f759f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Aug 2019 14:54:35 -0700 Subject: [PATCH 2/2] review comment --- src/librustc/ty/sty.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 7b7e2b8bfbd..f41fffe507d 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -385,7 +385,6 @@ impl<'tcx> ClosureSubsts<'tcx> { let ty = self.closure_sig_ty(def_id, tcx); match ty.sty { ty::FnPtr(sig) => sig, - ty::Infer(_) | ty::Error => ty::Binder::dummy(FnSig::fake()), // ignore errors _ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.sty), } }