Skip to content

Commit 82ffb99

Browse files
committed
move or_fun_call to its own module
1 parent 777f573 commit 82ffb99

File tree

2 files changed

+178
-163
lines changed

2 files changed

+178
-163
lines changed

clippy_lints/src/methods/mod.rs

+5-163
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod ok_expect;
3333
mod option_as_ref_deref;
3434
mod option_map_or_none;
3535
mod option_map_unwrap_or;
36+
mod or_fun_call;
3637
mod search_is_some;
3738
mod single_char_insert_string;
3839
mod single_char_pattern;
@@ -66,12 +67,11 @@ use rustc_span::source_map::Span;
6667
use rustc_span::symbol::{sym, SymbolStr};
6768
use rustc_typeck::hir_ty_to_ty;
6869

69-
use crate::utils::eager_or_lazy::is_lazyness_candidate;
7070
use crate::utils::{
7171
contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of,
72-
is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, method_calls,
73-
method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability,
74-
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
72+
is_type_diagnostic_item, iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths,
73+
return_ty, single_segment_path, snippet, snippet_with_applicability, span_lint, span_lint_and_help,
74+
span_lint_and_sugg, SpanlessEq,
7575
};
7676

7777
declare_clippy_lint! {
@@ -1778,7 +1778,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
17781778
}
17791779
},
17801780
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
1781-
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
1781+
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
17821782
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
17831783

17841784
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
@@ -1973,164 +1973,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
19731973
extract_msrv_attr!(LateContext);
19741974
}
19751975

1976-
/// Checks for the `OR_FUN_CALL` lint.
1977-
#[allow(clippy::too_many_lines)]
1978-
fn lint_or_fun_call<'tcx>(
1979-
cx: &LateContext<'tcx>,
1980-
expr: &hir::Expr<'_>,
1981-
method_span: Span,
1982-
name: &str,
1983-
args: &'tcx [hir::Expr<'_>],
1984-
) {
1985-
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
1986-
fn check_unwrap_or_default(
1987-
cx: &LateContext<'_>,
1988-
name: &str,
1989-
fun: &hir::Expr<'_>,
1990-
self_expr: &hir::Expr<'_>,
1991-
arg: &hir::Expr<'_>,
1992-
or_has_args: bool,
1993-
span: Span,
1994-
) -> bool {
1995-
if_chain! {
1996-
if !or_has_args;
1997-
if name == "unwrap_or";
1998-
if let hir::ExprKind::Path(ref qpath) = fun.kind;
1999-
let path = &*last_path_segment(qpath).ident.as_str();
2000-
if ["default", "new"].contains(&path);
2001-
let arg_ty = cx.typeck_results().expr_ty(arg);
2002-
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
2003-
if implements_trait(cx, arg_ty, default_trait_id, &[]);
2004-
2005-
then {
2006-
let mut applicability = Applicability::MachineApplicable;
2007-
span_lint_and_sugg(
2008-
cx,
2009-
OR_FUN_CALL,
2010-
span,
2011-
&format!("use of `{}` followed by a call to `{}`", name, path),
2012-
"try this",
2013-
format!(
2014-
"{}.unwrap_or_default()",
2015-
snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
2016-
),
2017-
applicability,
2018-
);
2019-
2020-
true
2021-
} else {
2022-
false
2023-
}
2024-
}
2025-
}
2026-
2027-
/// Checks for `*or(foo())`.
2028-
#[allow(clippy::too_many_arguments)]
2029-
fn check_general_case<'tcx>(
2030-
cx: &LateContext<'tcx>,
2031-
name: &str,
2032-
method_span: Span,
2033-
self_expr: &hir::Expr<'_>,
2034-
arg: &'tcx hir::Expr<'_>,
2035-
span: Span,
2036-
// None if lambda is required
2037-
fun_span: Option<Span>,
2038-
) {
2039-
// (path, fn_has_argument, methods, suffix)
2040-
static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
2041-
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
2042-
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
2043-
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
2044-
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
2045-
];
2046-
2047-
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
2048-
if path.ident.as_str() == "len" {
2049-
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
2050-
2051-
match ty.kind() {
2052-
ty::Slice(_) | ty::Array(_, _) => return,
2053-
_ => (),
2054-
}
2055-
2056-
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
2057-
return;
2058-
}
2059-
}
2060-
}
2061-
2062-
if_chain! {
2063-
if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
2064-
2065-
if is_lazyness_candidate(cx, arg);
2066-
if !contains_return(&arg);
2067-
2068-
let self_ty = cx.typeck_results().expr_ty(self_expr);
2069-
2070-
if let Some(&(_, fn_has_arguments, poss, suffix)) =
2071-
KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
2072-
2073-
if poss.contains(&name);
2074-
2075-
then {
2076-
let macro_expanded_snipped;
2077-
let sugg: Cow<'_, str> = {
2078-
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
2079-
(false, Some(fun_span)) => (fun_span, false),
2080-
_ => (arg.span, true),
2081-
};
2082-
let snippet = {
2083-
let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
2084-
if not_macro_argument_snippet == "vec![]" {
2085-
macro_expanded_snipped = snippet(cx, snippet_span, "..");
2086-
match macro_expanded_snipped.strip_prefix("$crate::vec::") {
2087-
Some(stripped) => Cow::from(stripped),
2088-
None => macro_expanded_snipped
2089-
}
2090-
}
2091-
else {
2092-
not_macro_argument_snippet
2093-
}
2094-
};
2095-
2096-
if use_lambda {
2097-
let l_arg = if fn_has_arguments { "_" } else { "" };
2098-
format!("|{}| {}", l_arg, snippet).into()
2099-
} else {
2100-
snippet
2101-
}
2102-
};
2103-
let span_replace_word = method_span.with_hi(span.hi());
2104-
span_lint_and_sugg(
2105-
cx,
2106-
OR_FUN_CALL,
2107-
span_replace_word,
2108-
&format!("use of `{}` followed by a function call", name),
2109-
"try this",
2110-
format!("{}_{}({})", name, suffix, sugg),
2111-
Applicability::HasPlaceholders,
2112-
);
2113-
}
2114-
}
2115-
}
2116-
2117-
if args.len() == 2 {
2118-
match args[1].kind {
2119-
hir::ExprKind::Call(ref fun, ref or_args) => {
2120-
let or_has_args = !or_args.is_empty();
2121-
if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
2122-
let fun_span = if or_has_args { None } else { Some(fun.span) };
2123-
check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
2124-
}
2125-
},
2126-
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
2127-
check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
2128-
},
2129-
_ => {},
2130-
}
2131-
}
2132-
}
2133-
21341976
/// Checks for the `EXPECT_FUN_CALL` lint.
21351977
#[allow(clippy::too_many_lines)]
21361978
fn lint_expect_fun_call(
+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use crate::utils::eager_or_lazy::is_lazyness_candidate;
2+
use crate::utils::{
3+
contains_return, get_trait_def_id, implements_trait, is_type_diagnostic_item, last_path_segment, match_type, paths,
4+
snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint_and_sugg,
5+
};
6+
use if_chain::if_chain;
7+
use rustc_errors::Applicability;
8+
use rustc_hir as hir;
9+
use rustc_lint::LateContext;
10+
use rustc_middle::ty;
11+
use rustc_span::source_map::Span;
12+
use rustc_span::symbol::sym;
13+
use std::borrow::Cow;
14+
15+
use super::OR_FUN_CALL;
16+
17+
/// Checks for the `OR_FUN_CALL` lint.
18+
#[allow(clippy::too_many_lines)]
19+
pub(super) fn check<'tcx>(
20+
cx: &LateContext<'tcx>,
21+
expr: &hir::Expr<'_>,
22+
method_span: Span,
23+
name: &str,
24+
args: &'tcx [hir::Expr<'_>],
25+
) {
26+
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
27+
fn check_unwrap_or_default(
28+
cx: &LateContext<'_>,
29+
name: &str,
30+
fun: &hir::Expr<'_>,
31+
self_expr: &hir::Expr<'_>,
32+
arg: &hir::Expr<'_>,
33+
or_has_args: bool,
34+
span: Span,
35+
) -> bool {
36+
if_chain! {
37+
if !or_has_args;
38+
if name == "unwrap_or";
39+
if let hir::ExprKind::Path(ref qpath) = fun.kind;
40+
let path = &*last_path_segment(qpath).ident.as_str();
41+
if ["default", "new"].contains(&path);
42+
let arg_ty = cx.typeck_results().expr_ty(arg);
43+
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
44+
if implements_trait(cx, arg_ty, default_trait_id, &[]);
45+
46+
then {
47+
let mut applicability = Applicability::MachineApplicable;
48+
span_lint_and_sugg(
49+
cx,
50+
OR_FUN_CALL,
51+
span,
52+
&format!("use of `{}` followed by a call to `{}`", name, path),
53+
"try this",
54+
format!(
55+
"{}.unwrap_or_default()",
56+
snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
57+
),
58+
applicability,
59+
);
60+
61+
true
62+
} else {
63+
false
64+
}
65+
}
66+
}
67+
68+
/// Checks for `*or(foo())`.
69+
#[allow(clippy::too_many_arguments)]
70+
fn check_general_case<'tcx>(
71+
cx: &LateContext<'tcx>,
72+
name: &str,
73+
method_span: Span,
74+
self_expr: &hir::Expr<'_>,
75+
arg: &'tcx hir::Expr<'_>,
76+
span: Span,
77+
// None if lambda is required
78+
fun_span: Option<Span>,
79+
) {
80+
// (path, fn_has_argument, methods, suffix)
81+
static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
82+
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
83+
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
84+
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
85+
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
86+
];
87+
88+
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
89+
if path.ident.as_str() == "len" {
90+
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
91+
92+
match ty.kind() {
93+
ty::Slice(_) | ty::Array(_, _) => return,
94+
_ => (),
95+
}
96+
97+
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
98+
return;
99+
}
100+
}
101+
}
102+
103+
if_chain! {
104+
if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
105+
106+
if is_lazyness_candidate(cx, arg);
107+
if !contains_return(&arg);
108+
109+
let self_ty = cx.typeck_results().expr_ty(self_expr);
110+
111+
if let Some(&(_, fn_has_arguments, poss, suffix)) =
112+
KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
113+
114+
if poss.contains(&name);
115+
116+
then {
117+
let macro_expanded_snipped;
118+
let sugg: Cow<'_, str> = {
119+
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
120+
(false, Some(fun_span)) => (fun_span, false),
121+
_ => (arg.span, true),
122+
};
123+
let snippet = {
124+
let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
125+
if not_macro_argument_snippet == "vec![]" {
126+
macro_expanded_snipped = snippet(cx, snippet_span, "..");
127+
match macro_expanded_snipped.strip_prefix("$crate::vec::") {
128+
Some(stripped) => Cow::from(stripped),
129+
None => macro_expanded_snipped
130+
}
131+
}
132+
else {
133+
not_macro_argument_snippet
134+
}
135+
};
136+
137+
if use_lambda {
138+
let l_arg = if fn_has_arguments { "_" } else { "" };
139+
format!("|{}| {}", l_arg, snippet).into()
140+
} else {
141+
snippet
142+
}
143+
};
144+
let span_replace_word = method_span.with_hi(span.hi());
145+
span_lint_and_sugg(
146+
cx,
147+
OR_FUN_CALL,
148+
span_replace_word,
149+
&format!("use of `{}` followed by a function call", name),
150+
"try this",
151+
format!("{}_{}({})", name, suffix, sugg),
152+
Applicability::HasPlaceholders,
153+
);
154+
}
155+
}
156+
}
157+
158+
if args.len() == 2 {
159+
match args[1].kind {
160+
hir::ExprKind::Call(ref fun, ref or_args) => {
161+
let or_has_args = !or_args.is_empty();
162+
if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
163+
let fun_span = if or_has_args { None } else { Some(fun.span) };
164+
check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
165+
}
166+
},
167+
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
168+
check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
169+
},
170+
_ => {},
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)