about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-10-31 15:59:44 +0000
committerMichael Goulet <michael@errs.io>2024-11-01 01:46:23 +0000
commitea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d (patch)
treea0b055a8247757ec9635257cfd1e06782755e495
parent41966e71bc386d073124d73cbc34fb279df5f4c8 (diff)
downloadrust-ea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d.tar.gz
rust-ea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d.zip
Suggest adding self type to method
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs25
-rw-r--r--tests/ui/editions/never-type-fallback-breaking.e2021.stderr4
-rw-r--r--tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr8
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 8fd54593934..2f09e5e163c 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -211,8 +211,14 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> {
 }
 
 #[derive(Clone)]
+pub(crate) enum SuggestAnnotation {
+    Unit(Span),
+    Path(Span),
+}
+
+#[derive(Clone)]
 pub(crate) struct SuggestAnnotations {
-    pub suggestion_spans: Vec<Span>,
+    pub suggestions: Vec<SuggestAnnotation>,
 }
 impl Subdiagnostic for SuggestAnnotations {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
@@ -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 02efb12f835..d6be237acf4 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<ty::TyVid>,
     fcx: &'a FnCtxt<'a, 'tcx>,
 }
 impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> {
-    type Result = ControlFlow<Span>;
+    type Result = ControlFlow<errors::SuggestAnnotation>;
 
     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 134fd098b7e..703f3d6266c 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 2a3c5edc218..dee112e245a 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