Skip to content

Commit 61a2912

Browse files
authored
Unrolled build for #147382
Rollup merge of #147382 - joshtriplett:unused-must-use-ignore-result-unit-uninhabited, r=fmease unused_must_use: Don't warn on `Result<(), Uninhabited>` or `ControlFlow<Uninhabited, ()>` This suppresses warnings on things like `Result<(), !>`, which helps simplify code using the common pattern of having an `Error` associated type: code will only have to check the error if there is a possibility of error. This will, for instance, help with future refactorings of `write!` in the standard library. As this is a user-visible change to lint behavior, it'll require a lang FCP. --- My proposal, here, is that we handle this simple case to make common patterns more usable. This does not rule out the possibility of adding more cases in the future, including general trait-based cases. However, I don't think we should make this common case wait on the more general cases. In particular, this solution does not close any doors on replacing this special case with a general case. This would unblock some planned work in the standard library to make `write!` more usable for infallible cases (e.g. writing into a `Vec` or `String`).
2 parents c0c37ca + 7aa7ecc commit 61a2912

File tree

4 files changed

+206
-7
lines changed

4 files changed

+206
-7
lines changed

compiler/rustc_lint/src/unused.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
273273
expr: &hir::Expr<'_>,
274274
span: Span,
275275
) -> Option<MustUsePath> {
276-
if ty.is_unit()
277-
|| !ty.is_inhabited_from(
278-
cx.tcx,
279-
cx.tcx.parent_module(expr.hir_id).to_def_id(),
280-
cx.typing_env(),
281-
)
282-
{
276+
if ty.is_unit() {
277+
return Some(MustUsePath::Suppressed);
278+
}
279+
let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id();
280+
let is_uninhabited =
281+
|t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
282+
if is_uninhabited(ty) {
283283
return Some(MustUsePath::Suppressed);
284284
}
285285

@@ -293,6 +293,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
293293
is_ty_must_use(cx, pinned_ty, expr, span)
294294
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
295295
}
296+
// Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
297+
ty::Adt(def, args)
298+
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
299+
&& args.type_at(0).is_unit()
300+
&& is_uninhabited(args.type_at(1)) =>
301+
{
302+
Some(MustUsePath::Suppressed)
303+
}
304+
// Suppress warnings on `ControlFlow<Uninhabited, ()>` (e.g. `ControlFlow<!, ()>`).
305+
ty::Adt(def, args)
306+
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
307+
&& args.type_at(1).is_unit()
308+
&& is_uninhabited(args.type_at(0)) =>
309+
{
310+
Some(MustUsePath::Suppressed)
311+
}
296312
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
297313
ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
298314
elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub enum MyUninhabited {}
2+
3+
#[non_exhaustive]
4+
pub enum MyUninhabitedNonexhaustive {}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//@ edition: 2024
2+
//@ aux-crate:dep=must_use_result_unit_uninhabited_extern_crate.rs
3+
4+
#![deny(unused_must_use)]
5+
#![feature(never_type)]
6+
7+
use core::ops::{ControlFlow, ControlFlow::Continue};
8+
use dep::{MyUninhabited, MyUninhabitedNonexhaustive};
9+
10+
fn result_unit_unit() -> Result<(), ()> {
11+
Ok(())
12+
}
13+
14+
fn result_unit_infallible() -> Result<(), core::convert::Infallible> {
15+
Ok(())
16+
}
17+
18+
fn result_unit_never() -> Result<(), !> {
19+
Ok(())
20+
}
21+
22+
fn result_unit_myuninhabited() -> Result<(), MyUninhabited> {
23+
Ok(())
24+
}
25+
26+
fn result_unit_myuninhabited_nonexhaustive() -> Result<(), MyUninhabitedNonexhaustive> {
27+
Ok(())
28+
}
29+
30+
trait AssocType {
31+
type Error;
32+
}
33+
34+
struct S1;
35+
impl AssocType for S1 {
36+
type Error = !;
37+
}
38+
39+
struct S2;
40+
impl AssocType for S2 {
41+
type Error = ();
42+
}
43+
44+
fn result_unit_assoctype<AT: AssocType>(_: AT) -> Result<(), AT::Error> {
45+
Ok(())
46+
}
47+
48+
trait UsesAssocType {
49+
type Error;
50+
fn method_use_assoc_type(&self) -> Result<(), Self::Error>;
51+
}
52+
53+
impl UsesAssocType for S1 {
54+
type Error = !;
55+
fn method_use_assoc_type(&self) -> Result<(), Self::Error> {
56+
Ok(())
57+
}
58+
}
59+
60+
impl UsesAssocType for S2 {
61+
type Error = ();
62+
fn method_use_assoc_type(&self) -> Result<(), Self::Error> {
63+
Err(())
64+
}
65+
}
66+
67+
fn controlflow_unit() -> ControlFlow<()> {
68+
Continue(())
69+
}
70+
71+
fn controlflow_infallible_unit() -> ControlFlow<core::convert::Infallible, ()> {
72+
Continue(())
73+
}
74+
75+
fn controlflow_never() -> ControlFlow<!> {
76+
Continue(())
77+
}
78+
79+
fn main() {
80+
result_unit_unit(); //~ ERROR: unused `Result` that must be used
81+
result_unit_infallible();
82+
result_unit_never();
83+
result_unit_myuninhabited();
84+
result_unit_myuninhabited_nonexhaustive(); //~ ERROR: unused `Result` that must be used
85+
result_unit_assoctype(S1);
86+
result_unit_assoctype(S2); //~ ERROR: unused `Result` that must be used
87+
S1.method_use_assoc_type();
88+
S2.method_use_assoc_type(); //~ ERROR: unused `Result` that must be used
89+
90+
controlflow_unit(); //~ ERROR: unused `ControlFlow` that must be used
91+
controlflow_infallible_unit();
92+
controlflow_never();
93+
}
94+
95+
trait AssocTypeBeforeMonomorphisation {
96+
type Error;
97+
fn generate(&self) -> Result<(), Self::Error>;
98+
fn process(&self) {
99+
self.generate(); //~ ERROR: unused `Result` that must be used
100+
}
101+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
error: unused `Result` that must be used
2+
--> $DIR/must_use-result-unit-uninhabited.rs:80:5
3+
|
4+
LL | result_unit_unit();
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this `Result` may be an `Err` variant, which should be handled
8+
note: the lint level is defined here
9+
--> $DIR/must_use-result-unit-uninhabited.rs:4:9
10+
|
11+
LL | #![deny(unused_must_use)]
12+
| ^^^^^^^^^^^^^^^
13+
help: use `let _ = ...` to ignore the resulting value
14+
|
15+
LL | let _ = result_unit_unit();
16+
| +++++++
17+
18+
error: unused `Result` that must be used
19+
--> $DIR/must_use-result-unit-uninhabited.rs:84:5
20+
|
21+
LL | result_unit_myuninhabited_nonexhaustive();
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= note: this `Result` may be an `Err` variant, which should be handled
25+
help: use `let _ = ...` to ignore the resulting value
26+
|
27+
LL | let _ = result_unit_myuninhabited_nonexhaustive();
28+
| +++++++
29+
30+
error: unused `Result` that must be used
31+
--> $DIR/must_use-result-unit-uninhabited.rs:86:5
32+
|
33+
LL | result_unit_assoctype(S2);
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
35+
|
36+
= note: this `Result` may be an `Err` variant, which should be handled
37+
help: use `let _ = ...` to ignore the resulting value
38+
|
39+
LL | let _ = result_unit_assoctype(S2);
40+
| +++++++
41+
42+
error: unused `Result` that must be used
43+
--> $DIR/must_use-result-unit-uninhabited.rs:88:5
44+
|
45+
LL | S2.method_use_assoc_type();
46+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
47+
|
48+
= note: this `Result` may be an `Err` variant, which should be handled
49+
help: use `let _ = ...` to ignore the resulting value
50+
|
51+
LL | let _ = S2.method_use_assoc_type();
52+
| +++++++
53+
54+
error: unused `ControlFlow` that must be used
55+
--> $DIR/must_use-result-unit-uninhabited.rs:90:5
56+
|
57+
LL | controlflow_unit();
58+
| ^^^^^^^^^^^^^^^^^^
59+
|
60+
help: use `let _ = ...` to ignore the resulting value
61+
|
62+
LL | let _ = controlflow_unit();
63+
| +++++++
64+
65+
error: unused `Result` that must be used
66+
--> $DIR/must_use-result-unit-uninhabited.rs:99:9
67+
|
68+
LL | self.generate();
69+
| ^^^^^^^^^^^^^^^
70+
|
71+
= note: this `Result` may be an `Err` variant, which should be handled
72+
help: use `let _ = ...` to ignore the resulting value
73+
|
74+
LL | let _ = self.generate();
75+
| +++++++
76+
77+
error: aborting due to 6 previous errors
78+

0 commit comments

Comments
 (0)