From 6310e40578909d2747d86b75adf70033065511aa Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 Jul 2024 16:11:08 -0400 Subject: [PATCH] Get rid of infer_ctxt_ext --- compiler/rustc_hir_typeck/src/closure.rs | 4 +- .../traits/fulfillment_errors.rs | 235 ++++++++++++++++- .../error_reporting/traits/infer_ctxt_ext.rs | 244 ------------------ .../src/error_reporting/traits/mod.rs | 2 - .../clippy/clippy_lints/src/eta_reduction.rs | 4 +- 5 files changed, 237 insertions(+), 252 deletions(-) delete mode 100644 compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 08de871f6fa94..79854976bdd57 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -18,7 +18,6 @@ use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::traits::ArgKind; -use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _; use rustc_trait_selection::traits; use rustc_type_ir::ClosureKind; use std::iter; @@ -734,13 +733,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|ty| ArgKind::from_expected_ty(*ty, None)) .collect(); let (closure_span, closure_arg_span, found_args) = - match self.get_fn_like_arguments(expr_map_node) { + match self.err_ctxt().get_fn_like_arguments(expr_map_node) { Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args), None => (None, None, Vec::new()), }; let expected_span = expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); let guar = self + .err_ctxt() .report_arg_count_mismatch( expected_span, closure_span, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 85b37ff326090..a7ea308a818b4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,7 +1,6 @@ use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use crate::error_reporting::infer::TyCategory; -use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; use crate::error_reporting::traits::report_object_safety_error; use crate::error_reporting::TypeErrCtxt; use crate::errors::{ @@ -2602,7 +2601,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .unwrap_or((found_span, None, found)); - self.infcx.report_arg_count_mismatch( + self.report_arg_count_mismatch( span, closure_span, expected, @@ -2614,6 +2613,238 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) } + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + pub fn get_fn_like_arguments( + &self, + node: Node<'_>, + ) -> Option<(Span, Option, Vec)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), + .. + }) => ( + fn_decl_span, + fn_arg_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + None, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::>(), + ), + Node::Ctor(variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {node:?}"), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + pub fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + closure_arg_span: Option, + ) -> Diag<'a> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(ArgKind::Tuple(_, fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_code_err!( + self.dcx(), + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {kind} that takes {expected_str}")); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {found_str}")); + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + closure_arg_span.unwrap_or(found_span), + format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{underscores}|"), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{sugg}|"), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + pub fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::PredicatePolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the generic parameters + // of the trait are. + let var = self.next_ty_var(DUMMY_SP); + // FIXME(effects) + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); + let obligation = Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), + ); + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Ok(( + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } + fn report_not_const_evaluatable_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs deleted file mode 100644 index e8d7e80ac562c..0000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs +++ /dev/null @@ -1,244 +0,0 @@ -// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`. - -use crate::infer::InferCtxt; -use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; -use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag}; -use rustc_hir as hir; -use rustc_hir::Node; -use rustc_macros::extension; -use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, DUMMY_SP}; - -use super::ArgKind; - -#[extension(pub trait InferCtxtExt<'tcx>)] -impl<'tcx> InferCtxt<'tcx> { - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option, Vec)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), - .. - }) => ( - fn_decl_span, - fn_arg_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - None, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::>(), - ), - Node::Ctor(variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {node:?}"), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option, - expected_args: Vec, - found_args: Vec, - is_closure: bool, - closure_arg_span: Option, - ) -> Diag<'_> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(ArgKind::Tuple(_, fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_code_err!( - self.dcx(), - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {kind} that takes {expected_str}")); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {found_str}")); - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - closure_arg_span.unwrap_or(found_span), - format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{underscores}|"), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{sugg}|"), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - - err - } - - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::PredicatePolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the generic parameters - // of the trait are. - let var = self.next_ty_var(DUMMY_SP); - // FIXME(effects) - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); - let obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { trait_ref, polarity }), - ); - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if ocx.select_all_or_error().is_empty() { - return Ok(( - self.tcx - .fn_trait_kind_from_def_id(trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } - } - - Err(()) - }) - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 10624786baeac..87fdc5ddff85c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,6 +1,5 @@ pub mod ambiguity; mod fulfillment_errors; -mod infer_ctxt_ext; pub mod on_unimplemented; mod overflow; pub mod suggestions; @@ -23,7 +22,6 @@ use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; use crate::error_reporting::TypeErrCtxt; use crate::traits::{FulfillmentError, FulfillmentErrorCode}; -pub use self::infer_ctxt_ext::*; pub use self::overflow::*; // When outputting impl candidates, prefer showing those that are more similar. diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index d2a34c7558321..0ed7859418bc5 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::{ use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; declare_clippy_lint! { /// ### What it does @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx.tcx.infer_ctxt().build().type_implements_fn_trait( + match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( cx.param_env, Binder::bind_with_vars(callee_ty_adjusted, List::empty()), ty::PredicatePolarity::Positive,