Auto merge of #8549 - J-ZhengLi:issue8542, r=llogiq
fix FP in lint `[needless_match]` fixes: #8542 fixes: #8551 fixes: #8595 fixes: #8599 --- changelog: check for more complex custom type, and ignore type coercion in [`needless_match`]
This commit is contained in:
commit
81e004aadb
5 changed files with 438 additions and 209 deletions
|
@ -667,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||||
overlapping_arms::check(cx, ex, arms);
|
overlapping_arms::check(cx, ex, arms);
|
||||||
match_wild_enum::check(cx, ex, arms);
|
match_wild_enum::check(cx, ex, arms);
|
||||||
match_as_ref::check(cx, ex, arms, expr);
|
match_as_ref::check(cx, ex, arms, expr);
|
||||||
needless_match::check_match(cx, ex, arms);
|
needless_match::check_match(cx, ex, arms, expr);
|
||||||
|
|
||||||
if self.infallible_destructuring_match_linted {
|
if self.infallible_destructuring_match_linted {
|
||||||
self.infallible_destructuring_match_linted = false;
|
self.infallible_destructuring_match_linted = false;
|
||||||
|
|
|
@ -1,37 +1,25 @@
|
||||||
use super::NEEDLESS_MATCH;
|
use super::NEEDLESS_MATCH;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
|
||||||
use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt};
|
use clippy_utils::{
|
||||||
|
eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
|
||||||
|
peel_blocks_with_stmt,
|
||||||
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp};
|
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
use rustc_typeck::hir_ty_to_ty;
|
||||||
|
|
||||||
pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||||
// This is for avoiding collision with `match_single_binding`.
|
if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
|
||||||
if arms.len() < 2 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for arm in arms {
|
|
||||||
if let PatKind::Wild = arm.pat.kind {
|
|
||||||
let ret_expr = strip_return(arm.body);
|
|
||||||
if !eq_expr_value(cx, ex, ret_expr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if !pat_same_as_expr(arm.pat, arm.body) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(match_expr) = get_parent_expr(cx, ex) {
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
NEEDLESS_MATCH,
|
NEEDLESS_MATCH,
|
||||||
match_expr.span,
|
expr.span,
|
||||||
"this match expression is unnecessary",
|
"this match expression is unnecessary",
|
||||||
"replace it with",
|
"replace it with",
|
||||||
snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
|
snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
|
||||||
|
@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
|
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
|
||||||
if_chain! {
|
if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
|
||||||
if let Some(ref if_let) = higher::IfLet::hir(cx, ex);
|
if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
|
||||||
if !is_else_clause(cx.tcx, ex);
|
|
||||||
if check_if_let(cx, if_let);
|
|
||||||
then {
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
|
||||||
|
for arm in arms {
|
||||||
|
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||||
|
if let PatKind::Wild = arm.pat.kind {
|
||||||
|
return eq_expr_value(cx, match_expr, strip_return(arm_expr));
|
||||||
|
} else if !pat_same_as_expr(arm.pat, arm_expr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||||
if let Some(if_else) = if_let.if_else {
|
if let Some(if_else) = if_let.if_else {
|
||||||
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
|
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
|
||||||
|
@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||||
|
|
||||||
if matches!(if_else.kind, ExprKind::Block(..)) {
|
if matches!(if_else.kind, ExprKind::Block(..)) {
|
||||||
let else_expr = peel_blocks_with_stmt(if_else);
|
let else_expr = peel_blocks_with_stmt(if_else);
|
||||||
|
if matches!(else_expr.kind, ExprKind::Block(..)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let ret = strip_return(else_expr);
|
let ret = strip_return(else_expr);
|
||||||
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
|
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
|
||||||
if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
|
if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
|
||||||
if let ExprKind::Path(ref qpath) = ret.kind {
|
if let ExprKind::Path(ref qpath) = ret.kind {
|
||||||
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
|
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
|
||||||
}
|
}
|
||||||
} else {
|
return true;
|
||||||
return eq_expr_value(cx, if_let.let_expr, ret);
|
|
||||||
}
|
}
|
||||||
return true;
|
return eq_expr_value(cx, if_let.let_expr, ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manually check for coercion casting by checking if the type of the match operand or let expr
|
||||||
|
/// differs with the assigned local variable or the funtion return type.
|
||||||
|
fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
|
||||||
|
if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
|
||||||
|
match p_node {
|
||||||
|
// Compare match_expr ty with local in `let local = match match_expr {..}`
|
||||||
|
Node::Local(local) => {
|
||||||
|
let results = cx.typeck_results();
|
||||||
|
return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
|
||||||
|
},
|
||||||
|
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
|
||||||
|
Node::Item(..) => {
|
||||||
|
if let Some(fn_decl) = p_node.fn_decl() {
|
||||||
|
if let FnRetTy::Return(ret_ty) = fn_decl.output {
|
||||||
|
return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// check the parent expr for this whole block `{ match match_expr {..} }`
|
||||||
|
Node::Block(block) => {
|
||||||
|
if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
|
||||||
|
return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// recursively call on `if xxx {..}` etc.
|
||||||
|
Node::Expr(p_expr) => {
|
||||||
|
return expr_ty_matches_p_ty(cx, expr, p_expr);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
|
fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
|
||||||
let expr = strip_return(expr);
|
let expr = strip_return(expr);
|
||||||
match (&pat.kind, &expr.kind) {
|
match (&pat.kind, &expr.kind) {
|
||||||
// Example: `Some(val) => Some(val)`
|
// Example: `Some(val) => Some(val)`
|
||||||
(
|
(PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => {
|
||||||
PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _),
|
|
||||||
ExprKind::Call(call_expr, [first_param, ..]),
|
|
||||||
) => {
|
|
||||||
if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
|
if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
|
||||||
if has_identical_segments(path.segments, call_path.segments)
|
return over(path.segments, call_path.segments, |pat_seg, call_seg| {
|
||||||
&& has_same_non_ref_symbol(first_pat, first_param)
|
pat_seg.ident.name == call_seg.ident.name
|
||||||
{
|
}) && same_non_ref_symbols(tuple_params, call_params);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Example: `val => val`, or `ref val => *val`
|
// Example: `val => val`
|
||||||
(PatKind::Binding(annot, _, pat_ident, _), _) => {
|
(
|
||||||
let new_expr = if let (
|
PatKind::Binding(annot, _, pat_ident, _),
|
||||||
BindingAnnotation::Ref | BindingAnnotation::RefMut,
|
ExprKind::Path(QPath::Resolved(
|
||||||
ExprKind::Unary(UnOp::Deref, operand_expr),
|
|
||||||
) = (annot, &expr.kind)
|
|
||||||
{
|
|
||||||
operand_expr
|
|
||||||
} else {
|
|
||||||
expr
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ExprKind::Path(QPath::Resolved(
|
|
||||||
_,
|
_,
|
||||||
Path {
|
Path {
|
||||||
segments: [first_seg, ..],
|
segments: [first_seg, ..],
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
)) = new_expr.kind
|
)),
|
||||||
{
|
) => {
|
||||||
return pat_ident.name == first_seg.ident.name;
|
return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut)
|
||||||
}
|
&& pat_ident.name == first_seg.ident.name;
|
||||||
},
|
},
|
||||||
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
|
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
|
||||||
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
|
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
|
||||||
return has_identical_segments(p_path.segments, e_path.segments);
|
return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
|
||||||
|
p_seg.ident.name == e_seg.ident.name
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// Example: `5 => 5`
|
// Example: `5 => 5`
|
||||||
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
|
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
|
||||||
|
@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool {
|
fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool {
|
||||||
if left_segs.len() != right_segs.len() {
|
if pats.len() != exprs.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for i in 0..left_segs.len() {
|
|
||||||
if left_segs[i].ident.name != right_segs[i].ident.name {
|
for i in 0..pats.len() {
|
||||||
|
if !pat_same_as_expr(&pats[i], &exprs[i]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
|
|
||||||
if_chain! {
|
|
||||||
if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind;
|
|
||||||
if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
|
|
||||||
if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind;
|
|
||||||
then {
|
|
||||||
return pat_ident.name == first_seg.ident.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,38 +4,35 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum Choice {
|
enum Simple {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
C,
|
C,
|
||||||
D,
|
D,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
fn useless_match() {
|
fn useless_match() {
|
||||||
let mut i = 10;
|
let i = 10;
|
||||||
let _: i32 = i;
|
let _: i32 = i;
|
||||||
let _: i32 = i;
|
|
||||||
let mut _i_mut = i;
|
|
||||||
|
|
||||||
let s = "test";
|
let s = "test";
|
||||||
let _: &str = s;
|
let _: &str = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom_type_match(se: Choice) {
|
fn custom_type_match() {
|
||||||
let _: Choice = se;
|
let se = Simple::A;
|
||||||
|
let _: Simple = se;
|
||||||
// Don't trigger
|
// Don't trigger
|
||||||
let _: Choice = match se {
|
let _: Simple = match se {
|
||||||
Choice::A => Choice::A,
|
Simple::A => Simple::A,
|
||||||
Choice::B => Choice::B,
|
Simple::B => Simple::B,
|
||||||
_ => Choice::C,
|
_ => Simple::C,
|
||||||
};
|
};
|
||||||
// Mingled, don't trigger
|
// Mingled, don't trigger
|
||||||
let _: Choice = match se {
|
let _: Simple = match se {
|
||||||
Choice::A => Choice::B,
|
Simple::A => Simple::B,
|
||||||
Choice::B => Choice::C,
|
Simple::B => Simple::C,
|
||||||
Choice::C => Choice::D,
|
Simple::C => Simple::D,
|
||||||
Choice::D => Choice::A,
|
Simple::D => Simple::A,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,29 +52,146 @@ fn func_ret_err<T>(err: T) -> Result<i32, T> {
|
||||||
fn result_match() {
|
fn result_match() {
|
||||||
let _: Result<i32, i32> = Ok(1);
|
let _: Result<i32, i32> = Ok(1);
|
||||||
let _: Result<i32, i32> = func_ret_err(0_i32);
|
let _: Result<i32, i32> = func_ret_err(0_i32);
|
||||||
|
// as ref, don't trigger
|
||||||
|
let res = &func_ret_err(0_i32);
|
||||||
|
let _: Result<&i32, &i32> = match *res {
|
||||||
|
Ok(ref x) => Ok(x),
|
||||||
|
Err(ref x) => Err(x),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn if_let_option() -> Option<i32> {
|
fn if_let_option() {
|
||||||
Some(1)
|
let _ = Some(1);
|
||||||
}
|
|
||||||
|
|
||||||
fn if_let_result(x: Result<(), i32>) {
|
fn do_something() {}
|
||||||
let _: Result<(), i32> = x;
|
|
||||||
let _: Result<(), i32> = x;
|
|
||||||
// Input type mismatch, don't trigger
|
|
||||||
let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn if_let_custom_enum(x: Choice) {
|
|
||||||
let _: Choice = x;
|
|
||||||
// Don't trigger
|
// Don't trigger
|
||||||
let _: Choice = if let Choice::A = x {
|
let _ = if let Some(a) = Some(1) {
|
||||||
Choice::A
|
Some(a)
|
||||||
|
} else {
|
||||||
|
do_something();
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _ = if let Some(a) = Some(1) {
|
||||||
|
do_something();
|
||||||
|
Some(a)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let_result() {
|
||||||
|
let x: Result<i32, i32> = Ok(1);
|
||||||
|
let _: Result<i32, i32> = x;
|
||||||
|
let _: Result<i32, i32> = x;
|
||||||
|
// Input type mismatch, don't trigger
|
||||||
|
let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let_custom_enum(x: Simple) {
|
||||||
|
let _: Simple = x;
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Simple = if let Simple::A = x {
|
||||||
|
Simple::A
|
||||||
} else if true {
|
} else if true {
|
||||||
Choice::B
|
Simple::B
|
||||||
} else {
|
} else {
|
||||||
x
|
x
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod issue8542 {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum E {
|
||||||
|
VariantA(u8, u8),
|
||||||
|
VariantB(u8, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Complex {
|
||||||
|
A(u8),
|
||||||
|
B(u8, bool),
|
||||||
|
C(u8, i32, f64),
|
||||||
|
D(E, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_test() {
|
||||||
|
let ce = Complex::B(8, false);
|
||||||
|
let aa = 0_u8;
|
||||||
|
let bb = false;
|
||||||
|
|
||||||
|
let _: Complex = ce;
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Complex = match ce {
|
||||||
|
Complex::A(_) => Complex::A(aa),
|
||||||
|
Complex::B(_, b) => Complex::B(aa, b),
|
||||||
|
Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
|
||||||
|
Complex::D(e, b) => Complex::D(e, b),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Complex = match ce {
|
||||||
|
Complex::A(a) => Complex::A(a),
|
||||||
|
Complex::B(a, _) => Complex::B(a, bb),
|
||||||
|
Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
|
||||||
|
_ => ce,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lint triggered when type coercions happen.
|
||||||
|
/// Do NOT trigger on any of these.
|
||||||
|
mod issue8551 {
|
||||||
|
trait Trait {}
|
||||||
|
struct Struct;
|
||||||
|
impl Trait for Struct {}
|
||||||
|
|
||||||
|
fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
|
||||||
|
match s {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lint_tests() {
|
||||||
|
let option: Option<&Struct> = None;
|
||||||
|
let _: Option<&dyn Trait> = match option {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _: Option<&dyn Trait> = if true {
|
||||||
|
match option {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let result: Result<&Struct, i32> = Err(0);
|
||||||
|
let _: Result<&dyn Trait, i32> = match result {
|
||||||
|
Ok(s) => Ok(s),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Tr {
|
||||||
|
fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
|
||||||
|
}
|
||||||
|
impl Tr for Result<i32, i32> {
|
||||||
|
fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
|
||||||
|
match self {
|
||||||
|
Ok(x) => Ok(x),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -4,33 +4,21 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum Choice {
|
enum Simple {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
C,
|
C,
|
||||||
D,
|
D,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
fn useless_match() {
|
fn useless_match() {
|
||||||
let mut i = 10;
|
let i = 10;
|
||||||
let _: i32 = match i {
|
let _: i32 = match i {
|
||||||
0 => 0,
|
0 => 0,
|
||||||
1 => 1,
|
1 => 1,
|
||||||
2 => 2,
|
2 => 2,
|
||||||
_ => i,
|
_ => i,
|
||||||
};
|
};
|
||||||
let _: i32 = match i {
|
|
||||||
0 => 0,
|
|
||||||
1 => 1,
|
|
||||||
ref i => *i,
|
|
||||||
};
|
|
||||||
let mut _i_mut = match i {
|
|
||||||
0 => 0,
|
|
||||||
1 => 1,
|
|
||||||
ref mut i => *i,
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = "test";
|
let s = "test";
|
||||||
let _: &str = match s {
|
let _: &str = match s {
|
||||||
"a" => "a",
|
"a" => "a",
|
||||||
|
@ -39,25 +27,26 @@ fn useless_match() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom_type_match(se: Choice) {
|
fn custom_type_match() {
|
||||||
let _: Choice = match se {
|
let se = Simple::A;
|
||||||
Choice::A => Choice::A,
|
let _: Simple = match se {
|
||||||
Choice::B => Choice::B,
|
Simple::A => Simple::A,
|
||||||
Choice::C => Choice::C,
|
Simple::B => Simple::B,
|
||||||
Choice::D => Choice::D,
|
Simple::C => Simple::C,
|
||||||
|
Simple::D => Simple::D,
|
||||||
};
|
};
|
||||||
// Don't trigger
|
// Don't trigger
|
||||||
let _: Choice = match se {
|
let _: Simple = match se {
|
||||||
Choice::A => Choice::A,
|
Simple::A => Simple::A,
|
||||||
Choice::B => Choice::B,
|
Simple::B => Simple::B,
|
||||||
_ => Choice::C,
|
_ => Simple::C,
|
||||||
};
|
};
|
||||||
// Mingled, don't trigger
|
// Mingled, don't trigger
|
||||||
let _: Choice = match se {
|
let _: Simple = match se {
|
||||||
Choice::A => Choice::B,
|
Simple::A => Simple::B,
|
||||||
Choice::B => Choice::C,
|
Simple::B => Simple::C,
|
||||||
Choice::C => Choice::D,
|
Simple::C => Simple::D,
|
||||||
Choice::D => Choice::A,
|
Simple::D => Simple::A,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,37 +75,160 @@ fn result_match() {
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
Ok(a) => Ok(a),
|
Ok(a) => Ok(a),
|
||||||
};
|
};
|
||||||
}
|
// as ref, don't trigger
|
||||||
|
let res = &func_ret_err(0_i32);
|
||||||
fn if_let_option() -> Option<i32> {
|
let _: Result<&i32, &i32> = match *res {
|
||||||
if let Some(a) = Some(1) { Some(a) } else { None }
|
Ok(ref x) => Ok(x),
|
||||||
}
|
Err(ref x) => Err(x),
|
||||||
|
|
||||||
fn if_let_result(x: Result<(), i32>) {
|
|
||||||
let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
|
|
||||||
let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
|
|
||||||
// Input type mismatch, don't trigger
|
|
||||||
let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn if_let_custom_enum(x: Choice) {
|
|
||||||
let _: Choice = if let Choice::A = x {
|
|
||||||
Choice::A
|
|
||||||
} else if let Choice::B = x {
|
|
||||||
Choice::B
|
|
||||||
} else if let Choice::C = x {
|
|
||||||
Choice::C
|
|
||||||
} else {
|
|
||||||
x
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let_option() {
|
||||||
|
let _ = if let Some(a) = Some(1) { Some(a) } else { None };
|
||||||
|
|
||||||
|
fn do_something() {}
|
||||||
|
|
||||||
// Don't trigger
|
// Don't trigger
|
||||||
let _: Choice = if let Choice::A = x {
|
let _ = if let Some(a) = Some(1) {
|
||||||
Choice::A
|
Some(a)
|
||||||
} else if true {
|
} else {
|
||||||
Choice::B
|
do_something();
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _ = if let Some(a) = Some(1) {
|
||||||
|
do_something();
|
||||||
|
Some(a)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let_result() {
|
||||||
|
let x: Result<i32, i32> = Ok(1);
|
||||||
|
let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
|
||||||
|
let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
|
||||||
|
// Input type mismatch, don't trigger
|
||||||
|
let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let_custom_enum(x: Simple) {
|
||||||
|
let _: Simple = if let Simple::A = x {
|
||||||
|
Simple::A
|
||||||
|
} else if let Simple::B = x {
|
||||||
|
Simple::B
|
||||||
|
} else if let Simple::C = x {
|
||||||
|
Simple::C
|
||||||
} else {
|
} else {
|
||||||
x
|
x
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Simple = if let Simple::A = x {
|
||||||
|
Simple::A
|
||||||
|
} else if true {
|
||||||
|
Simple::B
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mod issue8542 {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum E {
|
||||||
|
VariantA(u8, u8),
|
||||||
|
VariantB(u8, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Complex {
|
||||||
|
A(u8),
|
||||||
|
B(u8, bool),
|
||||||
|
C(u8, i32, f64),
|
||||||
|
D(E, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_test() {
|
||||||
|
let ce = Complex::B(8, false);
|
||||||
|
let aa = 0_u8;
|
||||||
|
let bb = false;
|
||||||
|
|
||||||
|
let _: Complex = match ce {
|
||||||
|
Complex::A(a) => Complex::A(a),
|
||||||
|
Complex::B(a, b) => Complex::B(a, b),
|
||||||
|
Complex::C(a, b, c) => Complex::C(a, b, c),
|
||||||
|
Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
|
||||||
|
Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Complex = match ce {
|
||||||
|
Complex::A(_) => Complex::A(aa),
|
||||||
|
Complex::B(_, b) => Complex::B(aa, b),
|
||||||
|
Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
|
||||||
|
Complex::D(e, b) => Complex::D(e, b),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't trigger
|
||||||
|
let _: Complex = match ce {
|
||||||
|
Complex::A(a) => Complex::A(a),
|
||||||
|
Complex::B(a, _) => Complex::B(a, bb),
|
||||||
|
Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
|
||||||
|
_ => ce,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lint triggered when type coercions happen.
|
||||||
|
/// Do NOT trigger on any of these.
|
||||||
|
mod issue8551 {
|
||||||
|
trait Trait {}
|
||||||
|
struct Struct;
|
||||||
|
impl Trait for Struct {}
|
||||||
|
|
||||||
|
fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
|
||||||
|
match s {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lint_tests() {
|
||||||
|
let option: Option<&Struct> = None;
|
||||||
|
let _: Option<&dyn Trait> = match option {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _: Option<&dyn Trait> = if true {
|
||||||
|
match option {
|
||||||
|
Some(s) => Some(s),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let result: Result<&Struct, i32> = Err(0);
|
||||||
|
let _: Result<&dyn Trait, i32> = match result {
|
||||||
|
Ok(s) => Ok(s),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Tr {
|
||||||
|
fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
|
||||||
|
}
|
||||||
|
impl Tr for Result<i32, i32> {
|
||||||
|
fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
|
||||||
|
match self {
|
||||||
|
Ok(x) => Ok(x),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:17:18
|
--> $DIR/needless_match.rs:16:18
|
||||||
|
|
|
|
||||||
LL | let _: i32 = match i {
|
LL | let _: i32 = match i {
|
||||||
| __________________^
|
| __________________^
|
||||||
|
@ -13,29 +13,7 @@ LL | | };
|
||||||
= note: `-D clippy::needless-match` implied by `-D warnings`
|
= note: `-D clippy::needless-match` implied by `-D warnings`
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:23:18
|
--> $DIR/needless_match.rs:23:19
|
||||||
|
|
|
||||||
LL | let _: i32 = match i {
|
|
||||||
| __________________^
|
|
||||||
LL | | 0 => 0,
|
|
||||||
LL | | 1 => 1,
|
|
||||||
LL | | ref i => *i,
|
|
||||||
LL | | };
|
|
||||||
| |_____^ help: replace it with: `i`
|
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
|
||||||
--> $DIR/needless_match.rs:28:22
|
|
||||||
|
|
|
||||||
LL | let mut _i_mut = match i {
|
|
||||||
| ______________________^
|
|
||||||
LL | | 0 => 0,
|
|
||||||
LL | | 1 => 1,
|
|
||||||
LL | | ref mut i => *i,
|
|
||||||
LL | | };
|
|
||||||
| |_____^ help: replace it with: `i`
|
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
|
||||||
--> $DIR/needless_match.rs:35:19
|
|
||||||
|
|
|
|
||||||
LL | let _: &str = match s {
|
LL | let _: &str = match s {
|
||||||
| ___________________^
|
| ___________________^
|
||||||
|
@ -46,19 +24,19 @@ LL | | };
|
||||||
| |_____^ help: replace it with: `s`
|
| |_____^ help: replace it with: `s`
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:43:21
|
--> $DIR/needless_match.rs:32:21
|
||||||
|
|
|
|
||||||
LL | let _: Choice = match se {
|
LL | let _: Simple = match se {
|
||||||
| _____________________^
|
| _____________________^
|
||||||
LL | | Choice::A => Choice::A,
|
LL | | Simple::A => Simple::A,
|
||||||
LL | | Choice::B => Choice::B,
|
LL | | Simple::B => Simple::B,
|
||||||
LL | | Choice::C => Choice::C,
|
LL | | Simple::C => Simple::C,
|
||||||
LL | | Choice::D => Choice::D,
|
LL | | Simple::D => Simple::D,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ help: replace it with: `se`
|
| |_____^ help: replace it with: `se`
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:65:26
|
--> $DIR/needless_match.rs:54:26
|
||||||
|
|
|
|
||||||
LL | let _: Option<i32> = match x {
|
LL | let _: Option<i32> = match x {
|
||||||
| __________________________^
|
| __________________________^
|
||||||
|
@ -68,7 +46,7 @@ LL | | };
|
||||||
| |_____^ help: replace it with: `x`
|
| |_____^ help: replace it with: `x`
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:81:31
|
--> $DIR/needless_match.rs:70:31
|
||||||
|
|
|
|
||||||
LL | let _: Result<i32, i32> = match Ok(1) {
|
LL | let _: Result<i32, i32> = match Ok(1) {
|
||||||
| _______________________________^
|
| _______________________________^
|
||||||
|
@ -78,7 +56,7 @@ LL | | };
|
||||||
| |_____^ help: replace it with: `Ok(1)`
|
| |_____^ help: replace it with: `Ok(1)`
|
||||||
|
|
||||||
error: this match expression is unnecessary
|
error: this match expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:85:31
|
--> $DIR/needless_match.rs:74:31
|
||||||
|
|
|
|
||||||
LL | let _: Result<i32, i32> = match func_ret_err(0_i32) {
|
LL | let _: Result<i32, i32> = match func_ret_err(0_i32) {
|
||||||
| _______________________________^
|
| _______________________________^
|
||||||
|
@ -88,35 +66,48 @@ LL | | };
|
||||||
| |_____^ help: replace it with: `func_ret_err(0_i32)`
|
| |_____^ help: replace it with: `func_ret_err(0_i32)`
|
||||||
|
|
||||||
error: this if-let expression is unnecessary
|
error: this if-let expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:92:5
|
--> $DIR/needless_match.rs:87:13
|
||||||
|
|
|
|
||||||
LL | if let Some(a) = Some(1) { Some(a) } else { None }
|
LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
|
||||||
|
|
||||||
error: this if-let expression is unnecessary
|
error: this if-let expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:96:30
|
--> $DIR/needless_match.rs:110:31
|
||||||
|
|
|
|
||||||
LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
|
LL | let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
|
||||||
|
|
||||||
error: this if-let expression is unnecessary
|
error: this if-let expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:97:30
|
--> $DIR/needless_match.rs:111:31
|
||||||
|
|
|
|
||||||
LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
|
LL | let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
|
||||||
|
|
||||||
error: this if-let expression is unnecessary
|
error: this if-let expression is unnecessary
|
||||||
--> $DIR/needless_match.rs:103:21
|
--> $DIR/needless_match.rs:117:21
|
||||||
|
|
|
|
||||||
LL | let _: Choice = if let Choice::A = x {
|
LL | let _: Simple = if let Simple::A = x {
|
||||||
| _____________________^
|
| _____________________^
|
||||||
LL | | Choice::A
|
LL | | Simple::A
|
||||||
LL | | } else if let Choice::B = x {
|
LL | | } else if let Simple::B = x {
|
||||||
LL | | Choice::B
|
LL | | Simple::B
|
||||||
... |
|
... |
|
||||||
LL | | x
|
LL | | x
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ help: replace it with: `x`
|
| |_____^ help: replace it with: `x`
|
||||||
|
|
||||||
error: aborting due to 12 previous errors
|
error: this match expression is unnecessary
|
||||||
|
--> $DIR/needless_match.rs:156:26
|
||||||
|
|
|
||||||
|
LL | let _: Complex = match ce {
|
||||||
|
| __________________________^
|
||||||
|
LL | | Complex::A(a) => Complex::A(a),
|
||||||
|
LL | | Complex::B(a, b) => Complex::B(a, b),
|
||||||
|
LL | | Complex::C(a, b, c) => Complex::C(a, b, c),
|
||||||
|
LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
|
||||||
|
LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
|
||||||
|
LL | | };
|
||||||
|
| |_________^ help: replace it with: `ce`
|
||||||
|
|
||||||
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue