Skip to content

Commit 63986fe

Browse files
committed
add a flag to is_ty_must_use to simplify Result<T, Uninhabited> and ControlFlow<Uninhabited, T>
1 parent fcb681e commit 63986fe

File tree

1 file changed

+63
-5
lines changed

1 file changed

+63
-5
lines changed

compiler/rustc_lint/src/unused/must_use.rs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ pub enum MustUsePath {
127127
Opaque(Box<Self>),
128128
TraitObject(Box<Self>),
129129
TupleElement(Vec<(usize, Self)>),
130+
/// `Result<T, Uninhabited>`
131+
Result(Box<Self>),
132+
/// `ControlFlow<Uninhabited, T>`
133+
ControlFlow(Box<Self>),
130134
Array(Box<Self>, u64),
131135
/// The root of the unused_closures lint.
132136
Closure(Span),
@@ -136,12 +140,19 @@ pub enum MustUsePath {
136140

137141
/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
138142
/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
143+
///
144+
/// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
145+
/// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
146+
/// so clippy can use this).
147+
//
148+
// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
139149
#[instrument(skip(cx, expr), level = "debug", ret)]
140150
pub fn is_ty_must_use<'tcx>(
141151
cx: &LateContext<'tcx>,
142152
ty: Ty<'tcx>,
143153
expr: &hir::Expr<'_>,
144154
span: Span,
155+
simplify_uninhabited: bool,
145156
) -> IsTyMustUse {
146157
if ty.is_unit() {
147158
return IsTyMustUse::Trivial;
@@ -154,13 +165,34 @@ pub fn is_ty_must_use<'tcx>(
154165
match *ty.kind() {
155166
_ if is_uninhabited(ty) => IsTyMustUse::Trivial,
156167
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
157-
is_ty_must_use(cx, boxed, expr, span).map(|inner| MustUsePath::Boxed(Box::new(inner)))
168+
is_ty_must_use(cx, boxed, expr, span, simplify_uninhabited)
169+
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
158170
}
159171
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
160172
let pinned_ty = args.type_at(0);
161-
is_ty_must_use(cx, pinned_ty, expr, span)
173+
is_ty_must_use(cx, pinned_ty, expr, span, simplify_uninhabited)
162174
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
163175
}
176+
// Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
177+
ty::Adt(def, args)
178+
if simplify_uninhabited
179+
&& cx.tcx.is_diagnostic_item(sym::Result, def.did())
180+
&& is_uninhabited(args.type_at(1)) =>
181+
{
182+
let ok_ty = args.type_at(0);
183+
is_ty_must_use(cx, ok_ty, expr, span, simplify_uninhabited)
184+
.map(|path| MustUsePath::Result(Box::new(path)))
185+
}
186+
// Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
187+
ty::Adt(def, args)
188+
if simplify_uninhabited
189+
&& cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
190+
&& is_uninhabited(args.type_at(0)) =>
191+
{
192+
let continue_ty = args.type_at(1);
193+
is_ty_must_use(cx, continue_ty, expr, span, simplify_uninhabited)
194+
.map(|path| MustUsePath::ControlFlow(Box::new(path)))
195+
}
164196
// Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
165197
ty::Adt(def, args)
166198
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
@@ -228,7 +260,9 @@ pub fn is_ty_must_use<'tcx>(
228260
.zip(elem_exprs)
229261
.enumerate()
230262
.filter_map(|(i, (ty, expr))| {
231-
is_ty_must_use(cx, ty, expr, expr.span).yes().map(|path| (i, path))
263+
is_ty_must_use(cx, ty, expr, expr.span, simplify_uninhabited)
264+
.yes()
265+
.map(|path| (i, path))
232266
})
233267
.collect::<Vec<_>>();
234268

@@ -242,7 +276,7 @@ pub fn is_ty_must_use<'tcx>(
242276
// If the array is empty we don't lint, to avoid false positives
243277
Some(0) | None => IsTyMustUse::No,
244278
// If the array is definitely non-empty, we can do `#[must_use]` checking.
245-
Some(len) => is_ty_must_use(cx, ty, expr, span)
279+
Some(len) => is_ty_must_use(cx, ty, expr, span, simplify_uninhabited)
246280
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
247281
},
248282
ty::Closure(..) | ty::CoroutineClosure(..) => IsTyMustUse::Yes(MustUsePath::Closure(span)),
@@ -305,7 +339,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
305339

306340
let ty = cx.typeck_results().expr_ty(expr);
307341

308-
let must_use_result = is_ty_must_use(cx, ty, expr, expr.span);
342+
let must_use_result = is_ty_must_use(cx, ty, expr, expr.span, false);
309343
let type_lint_emitted_or_trivial = match must_use_result {
310344
IsTyMustUse::Yes(path) => {
311345
emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
@@ -528,6 +562,30 @@ fn emit_must_use_untranslated(
528562
);
529563
}
530564
}
565+
MustUsePath::Result(path) => {
566+
let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}");
567+
emit_must_use_untranslated(
568+
cx,
569+
path,
570+
descr_pre,
571+
descr_post,
572+
plural_len,
573+
true,
574+
expr_is_from_block,
575+
);
576+
}
577+
MustUsePath::ControlFlow(path) => {
578+
let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}");
579+
emit_must_use_untranslated(
580+
cx,
581+
path,
582+
descr_pre,
583+
descr_post,
584+
plural_len,
585+
true,
586+
expr_is_from_block,
587+
);
588+
}
531589
MustUsePath::Array(path, len) => {
532590
let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
533591
emit_must_use_untranslated(

0 commit comments

Comments
 (0)