Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 11 additions & 38 deletions compiler/rustc_lint/src/unused/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
}
Expand Down Expand Up @@ -254,7 +226,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 {
Expand All @@ -276,8 +248,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))
Expand Down Expand Up @@ -341,7 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {

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);
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/lint/unused/must_use-result-unit-uninhabited.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand All @@ -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(())
}
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 13 additions & 7 deletions tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr
Original file line number Diff line number Diff line change
@@ -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();
| ^^^^^^^^^^^^^^^^^^
Expand All @@ -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();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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();
| ^^^^^^^^^^^^^^^^^^
Expand All @@ -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();
| ^^^^^^^^^^^^^^^
Expand All @@ -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

Loading