From ea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 15:59:44 +0000 Subject: [PATCH] Suggest adding self type to method --- compiler/rustc_hir_typeck/src/errors.rs | 25 ++++++++++++++++--- compiler/rustc_hir_typeck/src/fallback.rs | 25 ++++++++++++++++--- .../never-type-fallback-breaking.e2021.stderr | 4 +++ ...ng-fallback-control-flow.nofallback.stderr | 8 ++++++ 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 8fd545939341d..2f09e5e163c15 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -210,9 +210,15 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { pub sugg: SuggestAnnotations, } +#[derive(Clone)] +pub(crate) enum SuggestAnnotation { + Unit(Span), + Path(Span), +} + #[derive(Clone)] pub(crate) struct SuggestAnnotations { - pub suggestion_spans: Vec, + pub suggestions: Vec, } impl Subdiagnostic for SuggestAnnotations { fn add_to_diag_with>( @@ -220,13 +226,26 @@ impl Subdiagnostic for SuggestAnnotations { diag: &mut Diag<'_, G>, _: &F, ) { - if self.suggestion_spans.is_empty() { + if self.suggestions.is_empty() { return; } + let mut suggestions = vec![]; + for suggestion in self.suggestions { + match suggestion { + SuggestAnnotation::Unit(span) => { + suggestions.push((span, "()".to_string())); + } + SuggestAnnotation::Path(span) => { + suggestions.push((span.shrink_to_lo(), "<() as ".to_string())); + suggestions.push((span.shrink_to_hi(), ">".to_string())); + } + } + } + diag.multipart_suggestion_verbose( "use `()` annotations to avoid fallback changes", - self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(), + suggestions, Applicability::MachineApplicable, ); } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 02efb12f83561..d6be237acf4d6 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -8,6 +8,7 @@ use rustc_data_structures::graph::{self}; use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; @@ -573,7 +574,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // For each diverging var, look through the HIR for a place to give it // a type annotation. We do this per var because we only really need one // per var. - let suggestion_spans = diverging_vids + let suggestions = diverging_vids .iter() .copied() .filter_map(|vid| { @@ -582,16 +583,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> { VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() }) .collect(); - errors::SuggestAnnotations { suggestion_spans } + errors::SuggestAnnotations { suggestions } } } +/// Try to collect a useful suggestion to preserve fallback to `()`. struct VidVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { - type Result = ControlFlow; + type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { if let hir::TyKind::Infer = hir_ty.kind @@ -599,10 +601,25 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) { - return ControlFlow::Break(hir_ty.span); + return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span)); } hir::intravisit::walk_ty(self, hir_ty) } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Def(DefKind::AssocFn, def_id) = path.res + && self.fcx.tcx.trait_of_item(def_id).is_some() + && let self_ty = self.fcx.typeck_results.borrow().node_args(expr.hir_id).type_at(0) + && let Some(vid) = self.fcx.root_vid(self_ty) + && self.reachable_vids.contains(&vid) + && let [.., trait_segment, _method_segment] = path.segments + { + let span = path.span.shrink_to_lo().to(trait_segment.ident.span); + return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); + } + hir::intravisit::walk_expr(self, expr) + } } #[derive(Debug, Copy, Clone)] diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 134fd098b7e4a..703f3d6266cf6 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Default` will fail LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | true => <() as Default>::default(), + | ++++++ + warning: this function depends on never type fallback being `()` --> $DIR/never-type-fallback-breaking.rs:27:1 diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr index 2a3c5edc21847..dee112e245a76 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | x = <() as UnitDefault>::default(); + | ++++++ + warning: this function depends on never type fallback being `()` --> $DIR/diverging-fallback-control-flow.rs:42:1 @@ -28,6 +32,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail | LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | x = <() as UnitDefault>::default(); + | ++++++ + warning: 2 warnings emitted