From 588c7a934a05fb8d4aaabf7a42c28f75b749db6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 04:11:38 +0000 Subject: [PATCH 1/6] nit: stop using TypeckRootCtxt --- compiler/rustc_hir_typeck/src/fallback.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 68776c525551a..963801e57931d 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -14,7 +14,7 @@ use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::debug; -use crate::{FnCtxt, TypeckRootCtxt, errors}; +use crate::{FnCtxt, errors}; #[derive(Copy, Clone)] pub(crate) enum DivergingFallbackBehavior { @@ -419,7 +419,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { root_vid: ty::TyVid, ) { let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { - let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_id); debug!(?unsafe_infer_vars); unsafe_infer_vars }); @@ -569,27 +569,26 @@ pub(crate) enum UnsafeUseReason { /// /// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }` fn compute_unsafe_infer_vars<'a, 'tcx>( - root_ctxt: &'a TypeckRootCtxt<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, body_id: LocalDefId, ) -> UnordMap { - let body = - root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); + let body = fcx.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); let mut res = UnordMap::default(); struct UnsafeInferVarsVisitor<'a, 'tcx> { - root_ctxt: &'a TypeckRootCtxt<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, res: &'a mut UnordMap, } impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> { fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { - let typeck_results = self.root_ctxt.typeck_results.borrow(); + let typeck_results = self.fcx.typeck_results.borrow(); match ex.kind { hir::ExprKind::MethodCall(..) => { if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) - && let method_ty = self.root_ctxt.tcx.type_of(def_id).instantiate_identity() - && let sig = method_ty.fn_sig(self.root_ctxt.tcx) + && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity() + && let sig = method_ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -609,7 +608,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( let func_ty = typeck_results.expr_ty(func); if func_ty.is_fn() - && let sig = func_ty.fn_sig(self.root_ctxt.tcx) + && let sig = func_ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -640,7 +639,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( // If this path refers to an unsafe function, collect inference variables which may affect it. // `is_fn` excludes closures, but those can't be unsafe. if ty.is_fn() - && let sig = ty.fn_sig(self.root_ctxt.tcx) + && let sig = ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -698,7 +697,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( } } - UnsafeInferVarsVisitor { root_ctxt, res: &mut res }.visit_expr(&body.value); + UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value); debug!(?res, "collected the following unsafe vars for {body_id:?}"); From 41966e71bc386d073124d73cbc34fb279df5f4c8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 04:33:21 +0000 Subject: [PATCH 2/6] Suggest annotations for never type fallback --- compiler/rustc_hir_typeck/src/errors.rs | 49 ++++++++++-- compiler/rustc_hir_typeck/src/fallback.rs | 76 +++++++++++++++++-- .../dependency-on-fallback-to-unit.stderr | 4 + ...-fallback-flowing-into-unsafe.e2015.stderr | 4 + ...-fallback-flowing-into-unsafe.e2024.stderr | 4 + 5 files changed, 125 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index cceaabaff65b6..8fd545939341d 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -169,19 +169,34 @@ pub(crate) struct MissingParenthesesInRange { pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_call)] - Call, + Call { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_method)] - Method, + Method { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_path)] - Path, + Path { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_union_field)] - UnionField, + UnionField { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_deref)] - Deref, + Deref { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, } #[derive(LintDiagnostic)] @@ -191,6 +206,30 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { #[note] pub obligation_span: Span, pub obligation: ty::Predicate<'tcx>, + #[subdiagnostic] + pub sugg: SuggestAnnotations, +} + +#[derive(Clone)] +pub(crate) struct SuggestAnnotations { + pub suggestion_spans: Vec, +} +impl Subdiagnostic for SuggestAnnotations { + fn add_to_diag_with>( + self, + diag: &mut Diag<'_, G>, + _: &F, + ) { + if self.suggestion_spans.is_empty() { + return; + } + + diag.multipart_suggestion_verbose( + "use `()` annotations to avoid fallback changes", + self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(), + Applicability::MachineApplicable, + ); + } } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 963801e57931d..02efb12f83561 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,5 +1,7 @@ use std::cell::OnceCell; +use std::ops::ControlFlow; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::iterate::DepthFirstSearch; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::graph::{self}; @@ -321,7 +323,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); let unsafe_infer_vars = OnceCell::new(); - self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids); + self.lint_obligations_broken_by_never_type_fallback_change( + behavior, + &diverging_vids, + &coercion_graph, + ); for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); @@ -429,19 +435,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .filter_map(|x| unsafe_infer_vars.get(&x).copied()) .collect::>(); + let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph); + for (hir_id, span, reason) in affected_unsafe_infer_vars { self.tcx.emit_node_span_lint( lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, hir_id, span, match reason { - UnsafeUseReason::Call => errors::NeverTypeFallbackFlowingIntoUnsafe::Call, - UnsafeUseReason::Method => errors::NeverTypeFallbackFlowingIntoUnsafe::Method, - UnsafeUseReason::Path => errors::NeverTypeFallbackFlowingIntoUnsafe::Path, + UnsafeUseReason::Call => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() } + } + UnsafeUseReason::Method => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() } + } + UnsafeUseReason::Path => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() } + } UnsafeUseReason::UnionField => { - errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField { + sugg: sugg.clone(), + } + } + UnsafeUseReason::Deref => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() } } - UnsafeUseReason::Deref => errors::NeverTypeFallbackFlowingIntoUnsafe::Deref, }, ); } @@ -451,6 +469,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { &self, behavior: DivergingFallbackBehavior, diverging_vids: &[ty::TyVid], + coercions: &VecGraph, ) { let DivergingFallbackBehavior::ToUnit = behavior else { return }; @@ -478,13 +497,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`, - // then this code will be broken by the never type fallback change.qba + // then this code will be broken by the never type fallback change. let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit); if unit_errors.is_empty() && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) && let [ref mut never_error, ..] = never_errors.as_mut_slice() { self.adjust_fulfillment_error_for_expr_obligation(never_error); + let sugg = self.try_to_suggest_annotations(diverging_vids, coercions); self.tcx.emit_node_span_lint( lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, self.tcx.local_def_id_to_hir_id(self.body_id), @@ -492,6 +512,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { errors::DependencyOnUnitNeverTypeFallback { obligation_span: never_error.obligation.cause.span, obligation: never_error.obligation.predicate, + sugg, }, ) } @@ -541,6 +562,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fn root_vid(&self, ty: Ty<'tcx>) -> Option { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } + + fn try_to_suggest_annotations( + &self, + diverging_vids: &[ty::TyVid], + coercions: &VecGraph, + ) -> errors::SuggestAnnotations { + let body = + self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner"); + // 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 + .iter() + .copied() + .filter_map(|vid| { + let reachable_vids = + graph::depth_first_search_as_undirected(coercions, vid).collect(); + VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() + }) + .collect(); + errors::SuggestAnnotations { suggestion_spans } + } +} + +struct VidVisitor<'a, 'tcx> { + reachable_vids: FxHashSet, + fcx: &'a FnCtxt<'a, 'tcx>, +} +impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { + type Result = ControlFlow; + + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + if let hir::TyKind::Infer = hir_ty.kind + && let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id) + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(hir_ty.span); + } + hir::intravisit::walk_ty(self, hir_ty) + } } #[derive(Debug, Copy, Clone)] diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index ec49137ba7953..065a6e559c15c 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Default` will fail LL | false => <_>::default(), | ^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | false => <()>::default(), + | ~~ warning: this function depends on never type fallback being `()` --> $DIR/dependency-on-fallback-to-unit.rs:19:1 diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index a75039b8237a0..b8de1026e377e 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -102,6 +102,10 @@ LL | msg_send!(); = note: for more information, see issue #123748 = help: specify the type explicitly = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `()` annotations to avoid fallback changes + | +LL | match send_message::<() /* ?0 */>() { + | ~~ warning: 10 warnings emitted diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index 4138e9f8c8622..de44279ff8ccc 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -102,6 +102,10 @@ LL | msg_send!(); = note: for more information, see issue #123748 = help: specify the type explicitly = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `()` annotations to avoid fallback changes + | +LL | match send_message::<() /* ?0 */>() { + | ~~ warning: the type `!` does not permit zero-initialization --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18 From ea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 15:59:44 +0000 Subject: [PATCH 3/6] 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 From c930bba2836c1daa4ffa72ce3ec4d3e570ce188e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 16:05:42 +0000 Subject: [PATCH 4/6] And locals too --- compiler/rustc_hir_typeck/src/errors.rs | 4 ++++ compiler/rustc_hir_typeck/src/fallback.rs | 13 +++++++++++++ .../never-type-fallback-breaking.e2021.stderr | 4 ++-- .../defaulted-never-note.nofallback.stderr | 4 ++++ ...iverging-fallback-control-flow.nofallback.stderr | 8 ++++---- ...-fallback-unconstrained-return.nofallback.stderr | 4 ++++ 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 2f09e5e163c15..fdeeed92565c3 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -214,6 +214,7 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { pub(crate) enum SuggestAnnotation { Unit(Span), Path(Span), + Local(Span), } #[derive(Clone)] @@ -240,6 +241,9 @@ impl Subdiagnostic for SuggestAnnotations { suggestions.push((span.shrink_to_lo(), "<() as ".to_string())); suggestions.push((span.shrink_to_hi(), ">".to_string())); } + SuggestAnnotation::Local(span) => { + suggestions.push((span, ": ()".to_string())); + } } } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index d6be237acf4d6..cefc28b400f6e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -620,6 +620,19 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { } hir::intravisit::walk_expr(self, expr) } + + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result { + if let None = local.ty + && let ty = self.fcx.typeck_results.borrow().node_type(local.hir_id) + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(errors::SuggestAnnotation::Local( + local.pat.span.shrink_to_hi(), + )); + } + hir::intravisit::walk_local(self, local) + } } #[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 703f3d6266cf6..1c6685261fb3e 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -15,8 +15,8 @@ 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(), - | ++++++ + +LL | let x: () = match true { + | ++++ warning: this function depends on never type fallback being `()` --> $DIR/never-type-fallback-breaking.rs:27:1 diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr index d88615186dd63..6bc4501b6a375 100644 --- a/tests/ui/never_type/defaulted-never-note.nofallback.stderr +++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: ImplementedForUnitButNotNever` will f LL | foo(_x); | ^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | let _x: () = return; + | ++++ warning: 1 warning emitted 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 dee112e245a76..d40d1da76f9ac 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -15,8 +15,8 @@ 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(); - | ++++++ + +LL | let x: (); + | ++++ warning: this function depends on never type fallback being `()` --> $DIR/diverging-fallback-control-flow.rs:42:1 @@ -34,8 +34,8 @@ LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ help: use `()` annotations to avoid fallback changes | -LL | x = <() as UnitDefault>::default(); - | ++++++ + +LL | let x: (); + | ++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr index b485c94df4d6f..30a5e60a75848 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: UnitReturn` will fail LL | let _ = if true { unconstrained_return() } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | let _: () = if true { unconstrained_return() } else { panic!() }; + | ++++ warning: 1 warning emitted From df6f5841e5fcceb2c9ff43035dcede4f35450f5a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 16:58:28 +0000 Subject: [PATCH 5/6] And also suggest for qpaths --- compiler/rustc_hir_typeck/src/errors.rs | 11 +++ compiler/rustc_hir_typeck/src/fallback.rs | 68 +++++++++++++++++++ .../never-type-fallback-breaking.e2021.stderr | 4 ++ .../dependency-on-fallback-to-unit.stderr | 4 ++ ...verging-fallback-no-leak.nofallback.stderr | 4 ++ .../fallback-closure-ret.nofallback.stderr | 4 ++ ...-fallback-flowing-into-unsafe.e2015.stderr | 28 ++++++++ ...-fallback-flowing-into-unsafe.e2024.stderr | 28 ++++++++ 8 files changed, 151 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index fdeeed92565c3..4f579b05d83b6 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -215,6 +215,7 @@ pub(crate) enum SuggestAnnotation { Unit(Span), Path(Span), Local(Span), + Turbo(Span, usize, usize), } #[derive(Clone)] @@ -244,6 +245,16 @@ impl Subdiagnostic for SuggestAnnotations { SuggestAnnotation::Local(span) => { suggestions.push((span, ": ()".to_string())); } + SuggestAnnotation::Turbo(span, n_args, idx) => suggestions.push(( + span, + format!( + "::<{}>", + (0..n_args) + .map(|i| if i == idx { "()" } else { "_" }) + .collect::>() + .join(", "), + ), + )), } } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index cefc28b400f6e..ada716addd02d 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -9,6 +9,7 @@ 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::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; @@ -592,10 +593,45 @@ struct VidVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } +impl<'tcx> VidVisitor<'_, 'tcx> { + fn suggest_for_segment( + &self, + arg_segment: &'tcx hir::PathSegment<'tcx>, + def_id: DefId, + id: HirId, + ) -> ControlFlow { + if arg_segment.args.is_none() + && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id) + && let generics = self.fcx.tcx.generics_of(def_id) + && let args = &all_args[generics.parent_count..] + // We can't turbofish consts :( + && args.iter().all(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Lifetime(_))) + { + let n_tys = args + .iter() + .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) + .count(); + for (idx, arg) in args.iter().enumerate() { + if let Some(ty) = arg.as_type() + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(errors::SuggestAnnotation::Turbo( + arg_segment.ident.span.shrink_to_hi(), + n_tys, + idx, + )); + } + } + } + ControlFlow::Continue(()) + } +} impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + // Try to replace `_` with `()`. if let hir::TyKind::Infer = hir_ty.kind && let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id) && let Some(vid) = self.fcx.root_vid(ty) @@ -606,7 +642,32 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { hir::intravisit::walk_ty(self, hir_ty) } + fn visit_qpath( + &mut self, + qpath: &'tcx rustc_hir::QPath<'tcx>, + id: HirId, + _span: Span, + ) -> Self::Result { + let arg_segment = match qpath { + hir::QPath::Resolved(_, path) => { + path.segments.last().expect("paths should have a segment") + } + hir::QPath::TypeRelative(_, segment) => segment, + hir::QPath::LangItem(..) => { + return hir::intravisit::walk_qpath(self, qpath, id); + } + }; + // Alternatively, try to turbofish `::<_, (), _>` (ignoring lifetimes, + // since we don't need to turbofish those; they'll be inferred). + // FIXME: Same logic could work for types... + if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() { + self.suggest_for_segment(arg_segment, def_id, id)?; + } + hir::intravisit::walk_qpath(self, qpath, id) + } + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + // Try to suggest adding an explicit qself `()` to a trait method path. 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() @@ -618,6 +679,13 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { let span = path.span.shrink_to_lo().to(trait_segment.ident.span); return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); } + // Or else turbofishing the method + if let hir::ExprKind::MethodCall(segment, ..) = expr.kind + && let Some(def_id) = + self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id) + { + self.suggest_for_segment(segment, def_id, expr.hir_id)?; + } hir::intravisit::walk_expr(self, expr) } diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 1c6685261fb3e..79eee2a3defd0 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -32,6 +32,10 @@ note: in edition 2024, the requirement `!: Default` will fail | LL | deserialize()?; | ^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | deserialize::<()>()?; + | ++++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index 065a6e559c15c..79f47bb5fbc11 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -32,6 +32,10 @@ note: in edition 2024, the requirement `!: Default` will fail | LL | deserialize()?; | ^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | deserialize::<()>()?; + | ++++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr index 11245cc7aabf9..d11c21d9573be 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Test` will fail LL | unconstrained_arg(return); | ^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unconstrained_arg::<()>(return); + | ++++++ warning: 1 warning emitted diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr index 3fb5536dee7e3..fb0166dd9e06e 100644 --- a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr +++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Bar` will fail LL | foo(|| panic!()); | ^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | foo::<(), _>(|| panic!()); + | +++++++++ warning: 1 warning emitted diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index b8de1026e377e..6a48a7b9b47af 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -8,6 +8,10 @@ LL | unsafe { mem::zeroed() } = note: for more information, see issue #123748 = help: specify the type explicitly = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { mem::zeroed::<()>() } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 @@ -18,6 +22,10 @@ LL | core::mem::transmute(Zst) = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | core::mem::transmute::<_, ()>(Zst) + | +++++++++ warning: never type fallback affects this union access --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 @@ -38,6 +46,10 @@ LL | unsafe { *ptr::from_ref(&()).cast() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { *ptr::from_ref(&()).cast::<()>() } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 @@ -48,6 +60,10 @@ LL | unsafe { internally_create(x) } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { internally_create::<()>(x) } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 @@ -58,6 +74,10 @@ LL | unsafe { zeroed() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ warning: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 @@ -68,6 +88,10 @@ LL | let zeroed = mem::zeroed; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ warning: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 @@ -78,6 +102,10 @@ LL | let f = internally_create; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let f = internally_create::<()>; + | ++++++ warning: never type fallback affects this call to an `unsafe` method --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index de44279ff8ccc..844cd62c267b5 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -8,6 +8,10 @@ LL | unsafe { mem::zeroed() } = note: for more information, see issue #123748 = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { mem::zeroed::<()>() } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 @@ -18,6 +22,10 @@ LL | core::mem::transmute(Zst) = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | core::mem::transmute::<_, ()>(Zst) + | +++++++++ error: never type fallback affects this union access --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 @@ -38,6 +46,10 @@ LL | unsafe { *ptr::from_ref(&()).cast() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { *ptr::from_ref(&()).cast::<()>() } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 @@ -48,6 +60,10 @@ LL | unsafe { internally_create(x) } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { internally_create::<()>(x) } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 @@ -58,6 +74,10 @@ LL | unsafe { zeroed() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ error: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 @@ -68,6 +88,10 @@ LL | let zeroed = mem::zeroed; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ error: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 @@ -78,6 +102,10 @@ LL | let f = internally_create; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let f = internally_create::<()>; + | ++++++ error: never type fallback affects this call to an `unsafe` method --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 From b4248aeec366d31efe595eb5f671f589c849d6a3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 1 Nov 2024 01:46:17 +0000 Subject: [PATCH 6/6] nits --- compiler/rustc_hir_typeck/src/fallback.rs | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ada716addd02d..8d8573c65c5b8 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -565,6 +565,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } + /// Given a set of diverging vids and coercions, walk the HIR to gather a + /// set of suggestions which can be applied to preserve fallback to unit. fn try_to_suggest_annotations( &self, diverging_vids: &[ty::TyVid], @@ -574,26 +576,34 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner"); // 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. + // suggestion to influence a var to be `()`. let suggestions = diverging_vids .iter() .copied() .filter_map(|vid| { let reachable_vids = graph::depth_first_search_as_undirected(coercions, vid).collect(); - VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() + AnnotateUnitFallbackVisitor { reachable_vids, fcx: self } + .visit_expr(body.value) + .break_value() }) .collect(); errors::SuggestAnnotations { suggestions } } } -/// Try to collect a useful suggestion to preserve fallback to `()`. -struct VidVisitor<'a, 'tcx> { +/// Try to walk the HIR to find a place to insert a useful suggestion +/// to preserve fallback to `()` in 2024. +struct AnnotateUnitFallbackVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } -impl<'tcx> VidVisitor<'_, 'tcx> { +impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { + // For a given path segment, if it's missing a turbofish, try to suggest adding + // one so we can constrain an argument to `()`. To keep the suggestion simple, + // we want to simply suggest `_` for all the other args. This (for now) only + // works when there are only type variables (and region variables, since we can + // elide them)... fn suggest_for_segment( &self, arg_segment: &'tcx hir::PathSegment<'tcx>, @@ -627,7 +637,7 @@ impl<'tcx> VidVisitor<'_, 'tcx> { ControlFlow::Continue(()) } } -impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { +impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { @@ -657,9 +667,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { return hir::intravisit::walk_qpath(self, qpath, id); } }; - // Alternatively, try to turbofish `::<_, (), _>` (ignoring lifetimes, - // since we don't need to turbofish those; they'll be inferred). - // FIXME: Same logic could work for types... + // Alternatively, try to turbofish `::<_, (), _>`. if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() { self.suggest_for_segment(arg_segment, def_id, id)?; } @@ -668,6 +676,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { // Try to suggest adding an explicit qself `()` to a trait method path. + // i.e. changing `Default::default()` to `<() as Default>::default()`. 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() @@ -679,7 +688,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { let span = path.span.shrink_to_lo().to(trait_segment.ident.span); return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); } - // Or else turbofishing the method + // Or else, try suggesting turbofishing the method args. if let hir::ExprKind::MethodCall(segment, ..) = expr.kind && let Some(def_id) = self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id) @@ -690,6 +699,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { } fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result { + // For a local, try suggest annotating the type if it's missing. if let None = local.ty && let ty = self.fcx.typeck_results.borrow().node_type(local.hir_id) && let Some(vid) = self.fcx.root_vid(ty)