]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
New upstream version 1.52.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / or_fun_call.rs
CommitLineData
f20569fa
XL
1use crate::utils::eager_or_lazy::is_lazyness_candidate;
2use 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};
6use if_chain::if_chain;
7use rustc_errors::Applicability;
8use rustc_hir as hir;
9use rustc_lint::LateContext;
10use rustc_middle::ty;
11use rustc_span::source_map::Span;
12use rustc_span::symbol::sym;
13use std::borrow::Cow;
14
15use super::OR_FUN_CALL;
16
17/// Checks for the `OR_FUN_CALL` lint.
18#[allow(clippy::too_many_lines)]
19pub(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}