Skip to content

Commit ccb4fde

Browse files
Suggest annotations for never type fallback
1 parent 4932a36 commit ccb4fde

5 files changed

+125
-12
lines changed

compiler/rustc_hir_typeck/src/errors.rs

+44-5
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,34 @@ pub(crate) struct MissingParenthesesInRange {
169169
pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe {
170170
#[help]
171171
#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_call)]
172-
Call,
172+
Call {
173+
#[subdiagnostic]
174+
sugg: SuggestAnnotations,
175+
},
173176
#[help]
174177
#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_method)]
175-
Method,
178+
Method {
179+
#[subdiagnostic]
180+
sugg: SuggestAnnotations,
181+
},
176182
#[help]
177183
#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_path)]
178-
Path,
184+
Path {
185+
#[subdiagnostic]
186+
sugg: SuggestAnnotations,
187+
},
179188
#[help]
180189
#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_union_field)]
181-
UnionField,
190+
UnionField {
191+
#[subdiagnostic]
192+
sugg: SuggestAnnotations,
193+
},
182194
#[help]
183195
#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_deref)]
184-
Deref,
196+
Deref {
197+
#[subdiagnostic]
198+
sugg: SuggestAnnotations,
199+
},
185200
}
186201

187202
#[derive(LintDiagnostic)]
@@ -191,6 +206,30 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> {
191206
#[note]
192207
pub obligation_span: Span,
193208
pub obligation: ty::Predicate<'tcx>,
209+
#[subdiagnostic]
210+
pub sugg: SuggestAnnotations,
211+
}
212+
213+
#[derive(Clone)]
214+
pub(crate) struct SuggestAnnotations {
215+
pub suggestion_spans: Vec<Span>,
216+
}
217+
impl Subdiagnostic for SuggestAnnotations {
218+
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
219+
self,
220+
diag: &mut Diag<'_, G>,
221+
_: &F,
222+
) {
223+
if self.suggestion_spans.is_empty() {
224+
return;
225+
}
226+
227+
diag.multipart_suggestion_verbose(
228+
"use `()` annotations to avoid fallback changes",
229+
self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(),
230+
Applicability::MachineApplicable,
231+
);
232+
}
194233
}
195234

196235
#[derive(Subdiagnostic)]

compiler/rustc_hir_typeck/src/fallback.rs

+69-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::cell::OnceCell;
2+
use std::ops::ControlFlow;
23

4+
use rustc_data_structures::fx::FxHashSet;
35
use rustc_data_structures::graph::iterate::DepthFirstSearch;
46
use rustc_data_structures::graph::vec_graph::VecGraph;
57
use rustc_data_structures::graph::{self};
@@ -321,7 +323,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
321323
let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len());
322324
let unsafe_infer_vars = OnceCell::new();
323325

324-
self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids);
326+
self.lint_obligations_broken_by_never_type_fallback_change(
327+
behavior,
328+
&diverging_vids,
329+
&coercion_graph,
330+
);
325331

326332
for &diverging_vid in &diverging_vids {
327333
let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
@@ -429,19 +435,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
429435
.filter_map(|x| unsafe_infer_vars.get(&x).copied())
430436
.collect::<Vec<_>>();
431437

438+
let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph);
439+
432440
for (hir_id, span, reason) in affected_unsafe_infer_vars {
433441
self.tcx.emit_node_span_lint(
434442
lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE,
435443
hir_id,
436444
span,
437445
match reason {
438-
UnsafeUseReason::Call => errors::NeverTypeFallbackFlowingIntoUnsafe::Call,
439-
UnsafeUseReason::Method => errors::NeverTypeFallbackFlowingIntoUnsafe::Method,
440-
UnsafeUseReason::Path => errors::NeverTypeFallbackFlowingIntoUnsafe::Path,
446+
UnsafeUseReason::Call => {
447+
errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() }
448+
}
449+
UnsafeUseReason::Method => {
450+
errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() }
451+
}
452+
UnsafeUseReason::Path => {
453+
errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() }
454+
}
441455
UnsafeUseReason::UnionField => {
442-
errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField
456+
errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField {
457+
sugg: sugg.clone(),
458+
}
459+
}
460+
UnsafeUseReason::Deref => {
461+
errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() }
443462
}
444-
UnsafeUseReason::Deref => errors::NeverTypeFallbackFlowingIntoUnsafe::Deref,
445463
},
446464
);
447465
}
@@ -451,6 +469,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
451469
&self,
452470
behavior: DivergingFallbackBehavior,
453471
diverging_vids: &[ty::TyVid],
472+
coercions: &VecGraph<ty::TyVid, true>,
454473
) {
455474
let DivergingFallbackBehavior::ToUnit = behavior else { return };
456475

@@ -478,20 +497,22 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
478497
};
479498

480499
// If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
481-
// then this code will be broken by the never type fallback change.qba
500+
// then this code will be broken by the never type fallback change.
482501
let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
483502
if unit_errors.is_empty()
484503
&& let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
485504
&& let [ref mut never_error, ..] = never_errors.as_mut_slice()
486505
{
487506
self.adjust_fulfillment_error_for_expr_obligation(never_error);
507+
let sugg = self.try_to_suggest_annotations(diverging_vids, coercions);
488508
self.tcx.emit_node_span_lint(
489509
lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
490510
self.tcx.local_def_id_to_hir_id(self.body_id),
491511
self.tcx.def_span(self.body_id),
492512
errors::DependencyOnUnitNeverTypeFallback {
493513
obligation_span: never_error.obligation.cause.span,
494514
obligation: never_error.obligation.predicate,
515+
sugg,
495516
},
496517
)
497518
}
@@ -541,6 +562,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
541562
fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
542563
Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
543564
}
565+
566+
fn try_to_suggest_annotations(
567+
&self,
568+
diverging_vids: &[ty::TyVid],
569+
coercions: &VecGraph<ty::TyVid, true>,
570+
) -> errors::SuggestAnnotations {
571+
let body =
572+
self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner");
573+
// For each diverging var, look through the HIR for a place to give it
574+
// a type annotation. We do this per var because we only really need one
575+
// per var.
576+
let suggestion_spans = diverging_vids
577+
.iter()
578+
.copied()
579+
.filter_map(|vid| {
580+
let reachable_vids =
581+
graph::depth_first_search_as_undirected(coercions, vid).collect();
582+
VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value()
583+
})
584+
.collect();
585+
errors::SuggestAnnotations { suggestion_spans }
586+
}
587+
}
588+
589+
struct VidVisitor<'a, 'tcx> {
590+
reachable_vids: FxHashSet<ty::TyVid>,
591+
fcx: &'a FnCtxt<'a, 'tcx>,
592+
}
593+
impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> {
594+
type Result = ControlFlow<Span>;
595+
596+
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result {
597+
if let hir::TyKind::Infer = hir_ty.kind
598+
&& let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id)
599+
&& let Some(vid) = self.fcx.root_vid(ty)
600+
&& self.reachable_vids.contains(&vid)
601+
{
602+
return ControlFlow::Break(hir_ty.span);
603+
}
604+
hir::intravisit::walk_ty(self, hir_ty)
605+
}
544606
}
545607

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

tests/ui/never_type/dependency-on-fallback-to-unit.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 | false => <_>::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 | false => <()>::default(),
19+
| ~~
1620

1721
warning: this function depends on never type fallback being `()`
1822
--> $DIR/dependency-on-fallback-to-unit.rs:19:1

tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ LL | msg_send!();
102102
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
103103
= help: specify the type explicitly
104104
= note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info)
105+
help: use `()` annotations to avoid fallback changes
106+
|
107+
LL | match send_message::<() /* ?0 */>() {
108+
| ~~
105109

106110
warning: 10 warnings emitted
107111

tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ LL | msg_send!();
102102
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
103103
= help: specify the type explicitly
104104
= note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info)
105+
help: use `()` annotations to avoid fallback changes
106+
|
107+
LL | match send_message::<() /* ?0 */>() {
108+
| ~~
105109

106110
warning: the type `!` does not permit zero-initialization
107111
--> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18

0 commit comments

Comments
 (0)