Skip to content

Commit 0d63e61

Browse files
Suggest adding self type to method
1 parent 3e11c0b commit 0d63e61

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

Diff for: compiler/rustc_hir_typeck/src/errors.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -210,23 +210,42 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> {
210210
pub sugg: SuggestAnnotations,
211211
}
212212

213+
#[derive(Clone)]
214+
pub(crate) enum SuggestAnnotation {
215+
Unit(Span),
216+
Path(Span),
217+
}
218+
213219
#[derive(Clone)]
214220
pub(crate) struct SuggestAnnotations {
215-
pub suggestion_spans: Vec<Span>,
221+
pub suggestions: Vec<SuggestAnnotation>,
216222
}
217223
impl Subdiagnostic for SuggestAnnotations {
218224
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
219225
self,
220226
diag: &mut Diag<'_, G>,
221227
_: &F,
222228
) {
223-
if self.suggestion_spans.is_empty() {
229+
if self.suggestions.is_empty() {
224230
return;
225231
}
226232

233+
let mut suggestions = vec![];
234+
for suggestion in self.suggestions {
235+
match suggestion {
236+
SuggestAnnotation::Unit(span) => {
237+
suggestions.push((span, "()".to_string()));
238+
}
239+
SuggestAnnotation::Path(span) => {
240+
suggestions.push((span.shrink_to_lo(), "<() as ".to_string()));
241+
suggestions.push((span.shrink_to_hi(), ">".to_string()));
242+
}
243+
}
244+
}
245+
227246
diag.multipart_suggestion_verbose(
228247
"use `()` annotations to avoid fallback changes",
229-
self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(),
248+
suggestions,
230249
Applicability::MachineApplicable,
231250
);
232251
}

Diff for: compiler/rustc_hir_typeck/src/fallback.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_data_structures::graph::{self};
88
use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet};
99
use rustc_hir as hir;
1010
use rustc_hir::HirId;
11+
use rustc_hir::def::{DefKind, Res};
1112
use rustc_hir::intravisit::Visitor;
1213
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
1314
use rustc_session::lint;
@@ -573,7 +574,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
573574
// For each diverging var, look through the HIR for a place to give it
574575
// a type annotation. We do this per var because we only really need one
575576
// per var.
576-
let suggestion_spans = diverging_vids
577+
let suggestions = diverging_vids
577578
.iter()
578579
.copied()
579580
.filter_map(|vid| {
@@ -582,27 +583,43 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
582583
VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value()
583584
})
584585
.collect();
585-
errors::SuggestAnnotations { suggestion_spans }
586+
errors::SuggestAnnotations { suggestions }
586587
}
587588
}
588589

590+
/// Try to collect a useful suggestion to preserve fallback to `()`.
589591
struct VidVisitor<'a, 'tcx> {
590592
reachable_vids: FxHashSet<ty::TyVid>,
591593
fcx: &'a FnCtxt<'a, 'tcx>,
592594
}
593595
impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> {
594-
type Result = ControlFlow<Span>;
596+
type Result = ControlFlow<errors::SuggestAnnotation>;
595597

596598
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result {
597599
if let hir::TyKind::Infer = hir_ty.kind
598600
&& let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id)
599601
&& let Some(vid) = self.fcx.root_vid(ty)
600602
&& self.reachable_vids.contains(&vid)
601603
{
602-
return ControlFlow::Break(hir_ty.span);
604+
return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span));
603605
}
604606
hir::intravisit::walk_ty(self, hir_ty)
605607
}
608+
609+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
610+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
611+
&& let Res::Def(DefKind::AssocFn, def_id) = path.res
612+
&& self.fcx.tcx.trait_of_item(def_id).is_some()
613+
&& let self_ty = self.fcx.typeck_results.borrow().node_args(expr.hir_id).type_at(0)
614+
&& let Some(vid) = self.fcx.root_vid(self_ty)
615+
&& self.reachable_vids.contains(&vid)
616+
&& let [.., trait_segment, _method_segment] = path.segments
617+
{
618+
let span = path.span.shrink_to_lo().to(trait_segment.ident.span);
619+
return ControlFlow::Break(errors::SuggestAnnotation::Path(span));
620+
}
621+
hir::intravisit::walk_expr(self, expr)
622+
}
606623
}
607624

608625
#[derive(Debug, Copy, Clone)]

Diff for: tests/ui/editions/never-type-fallback-breaking.e2021.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Default` will fail
1313
LL | true => Default::default(),
1414
| ^^^^^^^^^^^^^^^^^^
1515
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
16+
help: use `()` annotations to avoid fallback changes
17+
|
18+
LL | true => <() as Default>::default(),
19+
| ++++++ +
1620

1721
warning: this function depends on never type fallback being `()`
1822
--> $DIR/never-type-fallback-breaking.rs:27:1

Diff for: tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail
1313
LL | x = UnitDefault::default();
1414
| ^^^^^^^^^^^^^^^^^^^^^^
1515
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
16+
help: use `()` annotations to avoid fallback changes
17+
|
18+
LL | x = <() as UnitDefault>::default();
19+
| ++++++ +
1620

1721
warning: this function depends on never type fallback being `()`
1822
--> $DIR/diverging-fallback-control-flow.rs:42:1
@@ -28,6 +32,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail
2832
|
2933
LL | x = UnitDefault::default();
3034
| ^^^^^^^^^^^^^^^^^^^^^^
35+
help: use `()` annotations to avoid fallback changes
36+
|
37+
LL | x = <() as UnitDefault>::default();
38+
| ++++++ +
3139

3240
warning: 2 warnings emitted
3341

0 commit comments

Comments
 (0)