diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:13:23 +0000 |
commit | 20431706a863f92cb37dc512fef6e48d192aaf2c (patch) | |
tree | 2867f13f5fd5437ba628c67d7f87309ccadcd286 /compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | |
parent | Releasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs) | 199 |
1 files changed, 155 insertions, 44 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ee0ad7b5d..4db9c56f9 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,9 +1,7 @@ use super::FnCtxt; -use crate::astconv::AstConv; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; -use hir::def_id::DefId; -use rustc_ast::util::parser::ExprPrecedence; +use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; @@ -11,13 +9,16 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -64,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// When encountering an fn-like type, try accessing the output of the type - /// // and suggesting calling it if it satisfies a predicate (i.e. if the + /// and suggesting calling it if it satisfies a predicate (i.e. if the /// output has a method or a field): /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } @@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty.is_suggestable(self.tcx, false) { format!("/* {ty} */") } else { - "".to_string() + "/* value */".to_string() } }) .collect::<Vec<_>>() @@ -101,10 +102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } + DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), kind => format!("call this {}", kind.descr(def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), @@ -138,7 +137,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg, applicability, ); - return true; } false @@ -327,9 +325,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { + ) -> bool { let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose)) = + if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = self.check_ref(expr, found, expected) { if verbose { @@ -337,12 +335,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion(sp, &msg, suggestion, applicability); } + if annotation { + let suggest_annotation = match expr.peel_drop_temps().kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + _ => return true, + }; + let mut tuple_indexes = Vec::new(); + let mut expr_id = expr.hir_id; + for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { + match node { + Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { + tuple_indexes.push( + subs.iter() + .enumerate() + .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) + .unwrap() + .0, + ); + expr_id = parent_id; + } + Node::Local(local) => { + if let Some(mut ty) = local.ty { + while let Some(index) = tuple_indexes.pop() { + match ty.kind { + TyKind::Tup(tys) => ty = &tys[index], + _ => return true, + } + } + let annotation_span = ty.span; + err.span_suggestion( + annotation_span.with_hi(annotation_span.lo()), + format!("alternatively, consider changing the type annotation"), + suggest_annotation, + Applicability::MaybeIncorrect, + ); + } + break; + } + _ => break, + } + } + } + return true; + } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + return true; } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = &found.kind() && let Some(sp) = self.tcx.hir().span_if_local(*def_id) { err.span_label(sp, format!("{found} defined here")); - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + return true; + } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + return true; + } else { let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { let mut suggestions = methods.iter() @@ -393,6 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestions, Applicability::MaybeIncorrect, ); + return true; } } else if let ty::Adt(found_adt, found_substs) = found.kind() && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) @@ -417,9 +464,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), Applicability::MaybeIncorrect, ); + return true; } } } + + false } /// When encountering the expected boxed value allocated in the stack, suggest allocating it @@ -430,13 +480,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, - ) { + ) -> bool { if self.tcx.hir().is_inside_const_context(expr.hir_id) { // Do not suggest `Box::new` in const context. - return; + return false; } if !expected.is_box() || found.is_box() { - return; + return false; } let boxed_found = self.tcx.mk_box(found); if self.can_coerce(boxed_found, expected) { @@ -454,6 +504,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { https://doc.rust-lang.org/rust-by-example/std/box.html, and \ https://doc.rust-lang.org/std/boxed/index.html", ); + true + } else { + false } } @@ -464,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut Diagnostic, expected: Ty<'tcx>, found: Ty<'tcx>, - ) { + ) -> bool { if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { // Report upto four upvars being captured to reduce the amount error messages @@ -488,8 +541,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { multi_span, "closures can only be coerced to `fn` types if they do not capture any variables" ); + return true; } } + false } /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. @@ -860,18 +915,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = self.normalize_associated_types_in(expr.span, ty); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self - .tcx - .infer_ctxt() - .enter(|infcx| { - infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { + hir::IsAsync::Async => { + let infcx = self.tcx.infer_ctxt().build(); + infcx + .get_impl_future_output_ty(ty) + .unwrap_or_else(|| { span_bug!( fn_decl.output.span(), "failed to get output type of async function" ) }) - }) - .skip_binder(), + .skip_binder() + } hir::IsAsync::NotAsync => ty, }; if self.can_coerce(found, ty) { @@ -891,11 +946,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - ) { + ) -> bool { let sp = self.tcx.sess.source_map().start_point(expr.span); if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + true + } else { + false } } @@ -908,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut expr: &hir::Expr<'_>, mut expr_ty: Ty<'tcx>, mut expected_ty: Ty<'tcx>, - ) { + ) -> bool { loop { match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { ( @@ -922,9 +980,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (hir::ExprKind::Block(blk, _), _, _) => { self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); - break; + break true; } - _ => break, + _ => break false, } } } @@ -935,11 +993,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, - ) { - let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; }; - let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; }; + ) -> bool { + let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; + let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; if adt_def != expected_adt_def { - return; + return false; } let mut suggest_copied_or_cloned = || { @@ -958,6 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ".copied()", Applicability::MachineApplicable, ); + return true; } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, @@ -975,8 +1034,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ".cloned()", Applicability::MachineApplicable, ); + return true; } } + false }; if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) @@ -984,12 +1045,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that the error types are equal && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() { - suggest_copied_or_cloned(); + return suggest_copied_or_cloned(); } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) && adt_def.did() == option_did { - suggest_copied_or_cloned(); + return suggest_copied_or_cloned(); } + + false + } + + pub(crate) fn suggest_into( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + let expr = expr.peel_blocks(); + + // We have better suggestions for scalar interconversions... + if expr_ty.is_scalar() && expected_ty.is_scalar() { + return false; + } + + // Don't suggest turning a block into another type (e.g. `{}.into()`) + if matches!(expr.kind, hir::ExprKind::Block(..)) { + return false; + } + + // We'll later suggest `.as_ref` when noting the type error, + // so skip if we will suggest that instead. + if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() { + return false; + } + + if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) + && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + self.misc(expr.span), + self.param_env, + ty::Binder::dummy(ty::TraitRef { + def_id: into_def_id, + substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), + }) + .to_poly_trait_predicate() + .to_predicate(self.tcx), + )) + { + let sugg = if expr.precedence().order() >= PREC_POSTFIX { + vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] + } else { + vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] + }; + diag.multipart_suggestion( + format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), + sugg, + Applicability::MaybeIncorrect + ); + return true; + } + + false } /// Suggest wrapping the block in square brackets instead of curly braces @@ -1110,7 +1226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, err: &mut Diagnostic, ) -> bool { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { if let StatementAsExpression::NeedsBoxing = boxed { err.span_suggestion_verbose( span_semi, @@ -1121,7 +1237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion_short( span_semi, - "remove this semicolon", + "remove this semicolon to return this value", "", Applicability::MachineApplicable, ); @@ -1132,8 +1248,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} |