Suggest annotations for never type fallback
This commit is contained in:
parent
588c7a934a
commit
41966e71bc
5 changed files with 126 additions and 13 deletions
|
|
@ -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<Span>,
|
||||
}
|
||||
impl Subdiagnostic for SuggestAnnotations {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
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)]
|
||||
|
|
|
|||
|
|
@ -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::<Vec<_>>();
|
||||
|
||||
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::UnionField => {
|
||||
errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField
|
||||
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 {
|
||||
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<ty::TyVid, true>,
|
||||
) {
|
||||
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<ty::TyVid> {
|
||||
Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
|
||||
}
|
||||
|
||||
fn try_to_suggest_annotations(
|
||||
&self,
|
||||
diverging_vids: &[ty::TyVid],
|
||||
coercions: &VecGraph<ty::TyVid, true>,
|
||||
) -> 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<ty::TyVid>,
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> {
|
||||
type Result = ControlFlow<Span>;
|
||||
|
||||
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)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -102,6 +102,10 @@ LL | msg_send!();
|
|||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/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
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,10 @@ LL | msg_send!();
|
|||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue