mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-16 13:05:18 +03:00
Auto merge of #148214 - WaffleLapkin:never-worn-never-type, r=fee1-dead
Consider `Result<T, Uninhabited>` and `ControlFlow<Uninhabited, T>` to be equivalent to `T` for must use lint This is an extension to rust-lang/rust#147382. With this PR `Result<T, Uninhabited>` and `ControlFlow<Uninhabited, T>` considered as must use iif `T` must be used. For such cases the lint will mention that `T` is wrapped in a `Result`/`ControlFlow` with an uninhabited error/break. The reasoning here is that `Result<T, Uninhabited>` is equivalent to `T` in which values can be represented and thus the must-used-ness should also be equivalent. Fixes https://github.com/rust-lang/rust/issues/65861
This commit is contained in:
@@ -133,18 +133,11 @@ pub enum MustUsePath {
|
||||
|
||||
/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
|
||||
/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
|
||||
///
|
||||
/// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
|
||||
/// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
|
||||
/// so clippy can use this).
|
||||
//
|
||||
// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
|
||||
#[instrument(skip(cx, expr), level = "debug", ret)]
|
||||
pub fn is_ty_must_use<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
simplify_uninhabited: bool,
|
||||
) -> IsTyMustUse {
|
||||
if ty.is_unit() {
|
||||
return IsTyMustUse::Trivial;
|
||||
@@ -157,50 +150,29 @@ pub fn is_ty_must_use<'tcx>(
|
||||
match *ty.kind() {
|
||||
_ if is_uninhabited(ty) => IsTyMustUse::Trivial,
|
||||
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
|
||||
is_ty_must_use(cx, boxed, expr, simplify_uninhabited)
|
||||
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
|
||||
is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
|
||||
}
|
||||
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
|
||||
let pinned_ty = args.type_at(0);
|
||||
is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited)
|
||||
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
|
||||
is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
|
||||
}
|
||||
// Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
|
||||
ty::Adt(def, args)
|
||||
if simplify_uninhabited
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, def.did())
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
|
||||
&& is_uninhabited(args.type_at(1)) =>
|
||||
{
|
||||
let ok_ty = args.type_at(0);
|
||||
is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited)
|
||||
.map(|path| MustUsePath::Result(Box::new(path)))
|
||||
is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path)))
|
||||
}
|
||||
// Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
|
||||
ty::Adt(def, args)
|
||||
if simplify_uninhabited
|
||||
&& cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
|
||||
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
|
||||
&& is_uninhabited(args.type_at(0)) =>
|
||||
{
|
||||
let continue_ty = args.type_at(1);
|
||||
is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited)
|
||||
is_ty_must_use(cx, continue_ty, expr)
|
||||
.map(|path| MustUsePath::ControlFlow(Box::new(path)))
|
||||
}
|
||||
// Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
|
||||
ty::Adt(def, args)
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
|
||||
&& args.type_at(0).is_unit()
|
||||
&& is_uninhabited(args.type_at(1)) =>
|
||||
{
|
||||
IsTyMustUse::Trivial
|
||||
}
|
||||
// Suppress warnings on `ControlFlow<Uninhabited, ()>` (e.g. `ControlFlow<!, ()>`).
|
||||
ty::Adt(def, args)
|
||||
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
|
||||
&& args.type_at(1).is_unit()
|
||||
&& is_uninhabited(args.type_at(0)) =>
|
||||
{
|
||||
IsTyMustUse::Trivial
|
||||
}
|
||||
ty::Adt(def, _) => {
|
||||
is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
|
||||
}
|
||||
@@ -258,7 +230,7 @@ pub fn is_ty_must_use<'tcx>(
|
||||
let mut nested_must_use = Vec::new();
|
||||
|
||||
tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
|
||||
let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited);
|
||||
let must_use = is_ty_must_use(cx, ty, expr);
|
||||
|
||||
all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
|
||||
if let IsTyMustUse::Yes(path) = must_use {
|
||||
@@ -280,8 +252,9 @@ pub fn is_ty_must_use<'tcx>(
|
||||
// If the array is empty we don't lint, to avoid false positives
|
||||
Some(0) | None => IsTyMustUse::No,
|
||||
// If the array is definitely non-empty, we can do `#[must_use]` checking.
|
||||
Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited)
|
||||
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
|
||||
Some(len) => {
|
||||
is_ty_must_use(cx, ty, expr).map(|inner| MustUsePath::Array(Box::new(inner), len))
|
||||
}
|
||||
},
|
||||
ty::Closure(..) | ty::CoroutineClosure(..) => {
|
||||
IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
|
||||
@@ -345,7 +318,7 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
let must_use_result = is_ty_must_use(cx, ty, expr, false);
|
||||
let must_use_result = is_ty_must_use(cx, ty, expr);
|
||||
let type_lint_emitted_or_trivial = match must_use_result {
|
||||
IsTyMustUse::Yes(path) => {
|
||||
emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
use core::ops::{ControlFlow, ControlFlow::Continue};
|
||||
use dep::{MyUninhabited, MyUninhabitedNonexhaustive};
|
||||
|
||||
#[must_use]
|
||||
struct MustUse;
|
||||
|
||||
struct Struct;
|
||||
|
||||
fn result_unit_unit() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -19,6 +24,14 @@ fn result_unit_never() -> Result<(), !> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn result_struct_never() -> Result<Struct, !> {
|
||||
Ok(Struct)
|
||||
}
|
||||
|
||||
fn result_must_use_never() -> Result<MustUse, !> {
|
||||
Ok(MustUse)
|
||||
}
|
||||
|
||||
fn result_unit_myuninhabited() -> Result<(), MyUninhabited> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -80,6 +93,8 @@ fn main() {
|
||||
result_unit_unit(); //~ ERROR: unused `Result` that must be used
|
||||
result_unit_infallible();
|
||||
result_unit_never();
|
||||
result_must_use_never(); //~ ERROR: unused `MustUse` in a `Result` with an uninhabited error that must be used
|
||||
result_struct_never();
|
||||
result_unit_myuninhabited();
|
||||
result_unit_myuninhabited_nonexhaustive(); //~ ERROR: unused `Result` that must be used
|
||||
result_unit_assoctype(S1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: unused `Result` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:80:5
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:93:5
|
||||
|
|
||||
LL | result_unit_unit();
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@@ -15,8 +15,14 @@ help: use `let _ = ...` to ignore the resulting value
|
||||
LL | let _ = result_unit_unit();
|
||||
| +++++++
|
||||
|
||||
error: unused `MustUse` in a `Result` with an uninhabited error that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:96:5
|
||||
|
|
||||
LL | result_must_use_never();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unused `Result` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:84:5
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:99:5
|
||||
|
|
||||
LL | result_unit_myuninhabited_nonexhaustive();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -28,7 +34,7 @@ LL | let _ = result_unit_myuninhabited_nonexhaustive();
|
||||
| +++++++
|
||||
|
||||
error: unused `Result` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:86:5
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:101:5
|
||||
|
|
||||
LL | result_unit_assoctype(S2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -40,7 +46,7 @@ LL | let _ = result_unit_assoctype(S2);
|
||||
| +++++++
|
||||
|
||||
error: unused `Result` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:88:5
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:103:5
|
||||
|
|
||||
LL | S2.method_use_assoc_type();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -52,7 +58,7 @@ LL | let _ = S2.method_use_assoc_type();
|
||||
| +++++++
|
||||
|
||||
error: unused `ControlFlow` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:90:5
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:105:5
|
||||
|
|
||||
LL | controlflow_unit();
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@@ -63,7 +69,7 @@ LL | let _ = controlflow_unit();
|
||||
| +++++++
|
||||
|
||||
error: unused `Result` that must be used
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:99:9
|
||||
--> $DIR/must_use-result-unit-uninhabited.rs:114:9
|
||||
|
|
||||
LL | self.generate();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@@ -74,5 +80,5 @@ help: use `let _ = ...` to ignore the resulting value
|
||||
LL | let _ = self.generate();
|
||||
| +++++++
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user