diff options
| author | Yuki Okushi <jtitor@2k36.org> | 2022-06-24 16:43:46 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-24 16:43:46 +0900 |
| commit | 964fc41b8986c3631e85decbadea3f9f40425811 (patch) | |
| tree | ecb256d5d7b511b0a1953e524f27588449c8bf21 /compiler | |
| parent | 33eb3c05c54b306afea341dd233671a9f039156f (diff) | |
| parent | d15fed79b84c621b42699442ccf99563a6bc6881 (diff) | |
| download | rust-964fc41b8986c3631e85decbadea3f9f40425811.tar.gz rust-964fc41b8986c3631e85decbadea3f9f40425811.zip | |
Rollup merge of #98280 - compiler-errors:better-call-closure-on-type-err, r=estebank
Improve suggestion for calling fn-like expr on type mismatch 1.) Suggest calling values of with RPIT types (and probably TAIT) when we expect `Ty` and have `impl Fn() -> Ty` 2.) Suggest calling closures even when they're not assigned to a local variable first 3.) Drive-by fix of a pretty-printing bug (`impl Fn()-> Ty` => `impl Fn() -> Ty`) r? ```@estebank```
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_middle/src/ty/print/pretty.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs | 189 |
2 files changed, 77 insertions, 114 deletions
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index bfb822fab26..200253d5755 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -856,7 +856,7 @@ pub trait PrettyPrinter<'tcx>: p!(")"); if let Term::Ty(ty) = return_ty.skip_binder() { if !ty.is_unit() { - p!("-> ", print(return_ty)); + p!(" -> ", print(return_ty)); } } p!(write("{}", if paren_needed { ")" } else { "" })); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 03d91435e77..7195da863db 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -8,15 +8,14 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind, - WherePredicate, + Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; -use rustc_span::symbol::{kw, sym}; +use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -78,124 +77,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let hir = self.tcx.hir(); - let (def_id, sig) = match *found.kind() { - ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), + let (def_id, output, inputs) = match *found.kind() { + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len()) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1) + } + ty::Opaque(def_id, substs) => { + let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + pred.kind().rebind(proj.term.ty().unwrap()), + args.len(), + )) + } else { + None + } + }); + if let Some((output, inputs)) = sig { + (def_id, output, inputs) + } else { + return false; + } + } _ => return false, }; - let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, sig); - let sig = self.normalize_associated_types_in(expr.span, sig); - 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 output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + let output = self.normalize_associated_types_in(expr.span, output); + if !output.is_ty_var() && self.can_coerce(output, expected) { + let (sugg_call, mut applicability) = match inputs { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "), + Applicability::MachineApplicable, + ), + _ => ("...".to_string(), Applicability::HasPlaceholders), }; - let mut msg = "call this function"; - match hir.get_if_local(def_id) { - Some( - Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - }), - ) => { - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::<Vec<_>>() - .join(", "); - } - Some(Node::Expr(hir::Expr { - kind: ExprKind::Closure { body: body_id, .. }, - span: full_closure_span, - .. - })) => { - if *full_closure_span == expr.span { - return false; - } - msg = "call this closure"; - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - 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 def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) { - Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { - msg = "instantiate this tuple variant"; - } - Some(DefKind::Ctor(CtorOf::Struct, _)) => { - msg = "instantiate this tuple struct"; - } - _ => {} - } + + let msg = match self.tcx.def_kind(def_id) { + DefKind::Fn => "call this function", + DefKind::Closure | DefKind::OpaqueTy => "call this closure", + DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct", + DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant", + _ => "call this function", + }; + + let sugg = match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] } - Some(Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, idents, _), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::<Vec<_>>() - .join(", ") + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] } - Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::<Vec<_>>() - .join(", ") + _ => { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] } - _ => {} - } - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - &format!("use parentheses to {}", msg), - format!("({})", sugg_call), + }; + + err.multipart_suggestion_verbose( + format!("use parentheses to {msg}"), + sugg, applicability, ); + return true; } false |
