Fix if_then_some_else_none FP when require type coercion (#15267)
Closes rust-lang/rust-clippy#15257 changelog: [`if_then_some_else_none`] fix FP when require type coercion
This commit is contained in:
commit
98205e60cc
7 changed files with 251 additions and 3 deletions
|
|
@ -5,7 +5,8 @@ use clippy_utils::msrvs::{self, Msrv};
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks,
|
||||
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
|
||||
path_res, peel_blocks,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
|
@ -92,6 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
expr.span,
|
||||
format!("this could be simplified with `bool::{method_name}`"),
|
||||
|diag| {
|
||||
if expr_adjustment_requires_coercion(cx, then_arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
|
||||
.maybe_paren()
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ use rustc_middle::hir::nested_filter;
|
|||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
|
||||
|
|
@ -3567,3 +3567,14 @@ pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>)
|
|||
// enclosing body.
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if the expression has adjustments that require coercion, for example: dereferencing with
|
||||
/// overloaded deref, coercing pointers and `dyn` objects.
|
||||
pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
|
||||
matches!(
|
||||
adj.kind,
|
||||
Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,3 +122,46 @@ const fn issue12103(x: u32) -> Option<u32> {
|
|||
// Should not issue an error in `const` context
|
||||
if x > 42 { Some(150) } else { None }
|
||||
}
|
||||
|
||||
mod issue15257 {
|
||||
struct Range {
|
||||
start: u8,
|
||||
end: u8,
|
||||
}
|
||||
|
||||
fn can_be_safely_rewrite(rs: &[&Range]) -> Option<Vec<u8>> {
|
||||
(rs.len() == 1 && rs[0].start == rs[0].end).then(|| vec![rs[0].start])
|
||||
}
|
||||
|
||||
fn reborrow_as_ptr(i: *mut i32) -> Option<*const i32> {
|
||||
let modulo = unsafe { *i % 2 };
|
||||
(modulo == 0).then_some(i)
|
||||
}
|
||||
|
||||
fn reborrow_as_fn_ptr(i: i32) {
|
||||
fn do_something(fn_: Option<fn(i32)>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn item_fn(i: i32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
do_something((i % 2 == 0).then_some(item_fn));
|
||||
}
|
||||
|
||||
fn reborrow_as_fn_unsafe(i: i32) {
|
||||
fn do_something(fn_: Option<unsafe fn(i32)>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn item_fn(i: i32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
do_something((i % 2 == 0).then_some(item_fn));
|
||||
|
||||
let closure_fn = |i: i32| {};
|
||||
do_something((i % 2 == 0).then_some(closure_fn));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,3 +143,71 @@ const fn issue12103(x: u32) -> Option<u32> {
|
|||
// Should not issue an error in `const` context
|
||||
if x > 42 { Some(150) } else { None }
|
||||
}
|
||||
|
||||
mod issue15257 {
|
||||
struct Range {
|
||||
start: u8,
|
||||
end: u8,
|
||||
}
|
||||
|
||||
fn can_be_safely_rewrite(rs: &[&Range]) -> Option<Vec<u8>> {
|
||||
if rs.len() == 1 && rs[0].start == rs[0].end {
|
||||
//~^ if_then_some_else_none
|
||||
Some(vec![rs[0].start])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow_as_ptr(i: *mut i32) -> Option<*const i32> {
|
||||
let modulo = unsafe { *i % 2 };
|
||||
if modulo == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow_as_fn_ptr(i: i32) {
|
||||
fn do_something(fn_: Option<fn(i32)>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn item_fn(i: i32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
do_something(if i % 2 == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(item_fn)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
|
||||
fn reborrow_as_fn_unsafe(i: i32) {
|
||||
fn do_something(fn_: Option<unsafe fn(i32)>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn item_fn(i: i32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
do_something(if i % 2 == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(item_fn)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
let closure_fn = |i: i32| {};
|
||||
do_something(if i % 2 == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(closure_fn)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,5 +58,63 @@ error: this could be simplified with `bool::then`
|
|||
LL | if s == "1" { Some(true) } else { None }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(s == "1").then(|| true)`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: this could be simplified with `bool::then`
|
||||
--> tests/ui/if_then_some_else_none.rs:154:9
|
||||
|
|
||||
LL | / if rs.len() == 1 && rs[0].start == rs[0].end {
|
||||
LL | |
|
||||
LL | | Some(vec![rs[0].start])
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | }
|
||||
| |_________^ help: try: `(rs.len() == 1 && rs[0].start == rs[0].end).then(|| vec![rs[0].start])`
|
||||
|
||||
error: this could be simplified with `bool::then_some`
|
||||
--> tests/ui/if_then_some_else_none.rs:164:9
|
||||
|
|
||||
LL | / if modulo == 0 {
|
||||
LL | |
|
||||
LL | | Some(i)
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | }
|
||||
| |_________^ help: try: `(modulo == 0).then_some(i)`
|
||||
|
||||
error: this could be simplified with `bool::then_some`
|
||||
--> tests/ui/if_then_some_else_none.rs:181:22
|
||||
|
|
||||
LL | do_something(if i % 2 == 0 {
|
||||
| ______________________^
|
||||
LL | |
|
||||
LL | | Some(item_fn)
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | });
|
||||
| |_________^ help: try: `(i % 2 == 0).then_some(item_fn)`
|
||||
|
||||
error: this could be simplified with `bool::then_some`
|
||||
--> tests/ui/if_then_some_else_none.rs:198:22
|
||||
|
|
||||
LL | do_something(if i % 2 == 0 {
|
||||
| ______________________^
|
||||
LL | |
|
||||
LL | | Some(item_fn)
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | });
|
||||
| |_________^ help: try: `(i % 2 == 0).then_some(item_fn)`
|
||||
|
||||
error: this could be simplified with `bool::then_some`
|
||||
--> tests/ui/if_then_some_else_none.rs:206:22
|
||||
|
|
||||
LL | do_something(if i % 2 == 0 {
|
||||
| ______________________^
|
||||
LL | |
|
||||
LL | | Some(closure_fn)
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | });
|
||||
| |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
|
|||
35
tests/ui/if_then_some_else_none_unfixable.rs
Normal file
35
tests/ui/if_then_some_else_none_unfixable.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#![warn(clippy::if_then_some_else_none)]
|
||||
#![allow(clippy::manual_is_multiple_of)]
|
||||
|
||||
mod issue15257 {
|
||||
use std::pin::Pin;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Foo {}
|
||||
pub trait Bar {}
|
||||
impl Bar for Foo {}
|
||||
|
||||
fn pointer_unsized_coercion(i: u32) -> Option<Box<dyn Bar>> {
|
||||
if i % 2 == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(Box::new(Foo::default()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow_as_pin(i: Pin<&mut i32>) {
|
||||
use std::ops::Rem;
|
||||
|
||||
fn do_something(i: Option<&i32>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
do_something(if i.rem(2) == 0 {
|
||||
//~^ if_then_some_else_none
|
||||
Some(&i)
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
28
tests/ui/if_then_some_else_none_unfixable.stderr
Normal file
28
tests/ui/if_then_some_else_none_unfixable.stderr
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
error: this could be simplified with `bool::then`
|
||||
--> tests/ui/if_then_some_else_none_unfixable.rs:13:9
|
||||
|
|
||||
LL | / if i % 2 == 0 {
|
||||
LL | |
|
||||
LL | | Some(Box::new(Foo::default()))
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::if_then_some_else_none)]`
|
||||
|
||||
error: this could be simplified with `bool::then`
|
||||
--> tests/ui/if_then_some_else_none_unfixable.rs:28:22
|
||||
|
|
||||
LL | do_something(if i.rem(2) == 0 {
|
||||
| ______________________^
|
||||
LL | |
|
||||
LL | | Some(&i)
|
||||
LL | | } else {
|
||||
LL | | None
|
||||
LL | | });
|
||||
| |_________^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue