Skip to content

Commit 6836941

Browse files
committed
Split out match_as_ref
1 parent 6c00b02 commit 6836941

File tree

2 files changed

+89
-83
lines changed

2 files changed

+89
-83
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use clippy_utils::{is_lang_ctor, peel_blocks};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, LangItem, PatKind, QPath};
6+
use rustc_lint::LateContext;
7+
use rustc_middle::ty;
8+
9+
use super::MATCH_AS_REF;
10+
11+
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
12+
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
13+
let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
14+
is_ref_some_arm(cx, &arms[1])
15+
} else if is_none_arm(cx, &arms[1]) {
16+
is_ref_some_arm(cx, &arms[0])
17+
} else {
18+
None
19+
};
20+
if let Some(rb) = arm_ref {
21+
let suggestion = if rb == BindingAnnotation::Ref {
22+
"as_ref"
23+
} else {
24+
"as_mut"
25+
};
26+
27+
let output_ty = cx.typeck_results().expr_ty(expr);
28+
let input_ty = cx.typeck_results().expr_ty(ex);
29+
30+
let cast = if_chain! {
31+
if let ty::Adt(_, substs) = input_ty.kind();
32+
let input_ty = substs.type_at(0);
33+
if let ty::Adt(_, substs) = output_ty.kind();
34+
let output_ty = substs.type_at(0);
35+
if let ty::Ref(_, output_ty, _) = *output_ty.kind();
36+
if input_ty != output_ty;
37+
then {
38+
".map(|x| x as _)"
39+
} else {
40+
""
41+
}
42+
};
43+
44+
let mut applicability = Applicability::MachineApplicable;
45+
span_lint_and_sugg(
46+
cx,
47+
MATCH_AS_REF,
48+
expr.span,
49+
&format!("use `{}()` instead", suggestion),
50+
"try this",
51+
format!(
52+
"{}.{}(){}",
53+
snippet_with_applicability(cx, ex.span, "_", &mut applicability),
54+
suggestion,
55+
cast,
56+
),
57+
applicability,
58+
);
59+
}
60+
}
61+
}
62+
63+
// Checks if arm has the form `None => None`
64+
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
65+
matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone))
66+
}
67+
68+
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
69+
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
70+
if_chain! {
71+
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
72+
if is_lang_ctor(cx, qpath, LangItem::OptionSome);
73+
if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
74+
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
75+
if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
76+
if let ExprKind::Path(ref some_path) = e.kind;
77+
if is_lang_ctor(cx, some_path, LangItem::OptionSome) && args.len() == 1;
78+
if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
79+
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
80+
then {
81+
return Some(rb)
82+
}
83+
}
84+
None
85+
}

clippy_lints/src/matches/mod.rs

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_help, span_lint_an
22
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
33
use clippy_utils::sugg::Sugg;
44
use clippy_utils::{
5-
get_parent_expr, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs, path_to_local_id, peel_blocks,
6-
strip_pat_refs,
5+
get_parent_expr, is_refutable, is_wild, meets_msrv, msrvs, path_to_local_id, peel_blocks, strip_pat_refs,
76
};
87
use core::iter::once;
98
use if_chain::if_chain;
109
use rustc_errors::Applicability;
11-
use rustc_hir::LangItem::{OptionNone, OptionSome};
12-
use rustc_hir::{
13-
Arm, BindingAnnotation, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, QPath,
14-
};
10+
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, QPath};
1511
use rustc_lint::{LateContext, LateLintPass};
1612
use rustc_middle::ty;
1713
use rustc_semver::RustcVersion;
1814
use rustc_session::{declare_tool_lint, impl_lint_pass};
1915

16+
mod match_as_ref;
2017
mod match_bool;
2118
mod match_like_matches;
2219
mod match_same_arms;
@@ -627,7 +624,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
627624
overlapping_arms::check(cx, ex, arms);
628625
match_wild_err_arm::check(cx, ex, arms);
629626
match_wild_enum::check(cx, ex, arms);
630-
check_match_as_ref(cx, ex, arms, expr);
627+
match_as_ref::check(cx, ex, arms, expr);
631628
check_wild_in_or_pats(cx, arms);
632629

633630
if self.infallible_destructuring_match_linted {
@@ -738,58 +735,6 @@ where
738735
});
739736
}
740737

741-
fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
742-
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
743-
let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
744-
is_ref_some_arm(cx, &arms[1])
745-
} else if is_none_arm(cx, &arms[1]) {
746-
is_ref_some_arm(cx, &arms[0])
747-
} else {
748-
None
749-
};
750-
if let Some(rb) = arm_ref {
751-
let suggestion = if rb == BindingAnnotation::Ref {
752-
"as_ref"
753-
} else {
754-
"as_mut"
755-
};
756-
757-
let output_ty = cx.typeck_results().expr_ty(expr);
758-
let input_ty = cx.typeck_results().expr_ty(ex);
759-
760-
let cast = if_chain! {
761-
if let ty::Adt(_, substs) = input_ty.kind();
762-
let input_ty = substs.type_at(0);
763-
if let ty::Adt(_, substs) = output_ty.kind();
764-
let output_ty = substs.type_at(0);
765-
if let ty::Ref(_, output_ty, _) = *output_ty.kind();
766-
if input_ty != output_ty;
767-
then {
768-
".map(|x| x as _)"
769-
} else {
770-
""
771-
}
772-
};
773-
774-
let mut applicability = Applicability::MachineApplicable;
775-
span_lint_and_sugg(
776-
cx,
777-
MATCH_AS_REF,
778-
expr.span,
779-
&format!("use `{}()` instead", suggestion),
780-
"try this",
781-
format!(
782-
"{}.{}(){}",
783-
snippet_with_applicability(cx, ex.span, "_", &mut applicability),
784-
suggestion,
785-
cast,
786-
),
787-
applicability,
788-
);
789-
}
790-
}
791-
}
792-
793738
fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
794739
for arm in arms {
795740
if let PatKind::Or(fields) = arm.pat.kind {
@@ -965,30 +910,6 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
965910
None
966911
}
967912

968-
// Checks if arm has the form `None => None`
969-
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
970-
matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
971-
}
972-
973-
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
974-
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
975-
if_chain! {
976-
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
977-
if is_lang_ctor(cx, qpath, OptionSome);
978-
if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
979-
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
980-
if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
981-
if let ExprKind::Path(ref some_path) = e.kind;
982-
if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
983-
if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
984-
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
985-
then {
986-
return Some(rb)
987-
}
988-
}
989-
None
990-
}
991-
992913
fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
993914
where
994915
'b: 'a,

0 commit comments

Comments
 (0)