|
1 | 1 | use crate::utils::{
|
2 |
| - is_adjusted, is_type_diagnostic_item, match_trait_method, match_var, paths, remove_blocks, span_lint_and_sugg, |
| 2 | + is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks, span_lint_and_sugg, |
3 | 3 | };
|
4 | 4 | use if_chain::if_chain;
|
5 | 5 | use rustc_errors::Applicability;
|
6 |
| -use rustc_hir::{def, Body, Expr, ExprKind, Pat, PatKind, Path, QPath, StmtKind}; |
| 6 | +use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; |
7 | 7 | use rustc_lint::{LateContext, LateLintPass};
|
8 | 8 | use rustc_session::{declare_lint_pass, declare_tool_lint};
|
9 | 9 |
|
@@ -40,8 +40,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapIdentity {
|
40 | 40 |
|
41 | 41 | if_chain! {
|
42 | 42 | if let Some([caller, func]) = get_map_argument(cx, expr);
|
43 |
| - if let Some(body) = get_body(cx, func); |
44 |
| - if is_identity_function(cx, body); |
| 43 | + if is_expr_identity_function(cx, func); |
45 | 44 | then {
|
46 | 45 | span_lint_and_sugg(
|
47 | 46 | cx,
|
@@ -75,8 +74,19 @@ fn get_map_argument<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<
|
75 | 74 | }
|
76 | 75 | }
|
77 | 76 |
|
78 |
| -/// Determines if a function is the identity function (in a naive way) |
79 |
| -fn is_identity_function(cx: &LateContext<'_, '_>, func: &Body<'_>) -> bool { |
| 77 | +/// Checks if an expression represents the identity function |
| 78 | +/// Only examines closures and std::convert::identity |
| 79 | +fn is_expr_identity_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { |
| 80 | + match expr.kind { |
| 81 | + ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), |
| 82 | + ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), |
| 83 | + _ => false, |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +/// Checks if a function's body represents the identity function |
| 88 | +/// Looks for bodies of the form |x| x, |x| return x, |x| return x;, |x| { return x } or |x| { return x; } |
| 89 | +fn is_body_identity_function(cx: &LateContext<'_, '_>, func: &Body<'_>) -> bool { |
80 | 90 | let params = func.params;
|
81 | 91 | let body = remove_blocks(&func.value);
|
82 | 92 |
|
@@ -104,29 +114,7 @@ fn is_identity_function(cx: &LateContext<'_, '_>, func: &Body<'_>) -> bool {
|
104 | 114 | }
|
105 | 115 | }
|
106 | 116 |
|
107 |
| -/// Returns an associated function/closure body from an expression |
108 |
| -fn get_body<'a>(cx: &LateContext<'a, '_>, expr: &'a Expr<'a>) -> Option<&'a Body<'a>> { |
109 |
| - match expr.kind { |
110 |
| - ExprKind::Closure(_, _, body_id, _, _) => Some(cx.tcx.hir().body(body_id)), |
111 |
| - ExprKind::Path(QPath::Resolved(_, ref path)) => path_to_body(cx, path), |
112 |
| - _ => None, |
113 |
| - } |
114 |
| -} |
115 |
| - |
116 |
| -/// Returns the function body associated with a path |
117 |
| -fn path_to_body<'a>(cx: &LateContext<'a, '_>, path: &'a Path<'a>) -> Option<&'a Body<'a>> { |
118 |
| - if let def::Res::Def(_, def_id) = path.res { |
119 |
| - def_id |
120 |
| - .as_local() |
121 |
| - .and_then(|local_id| cx.tcx.hir().opt_local_def_id_to_hir_id(local_id)) |
122 |
| - .and_then(|hir_id| cx.tcx.hir().maybe_body_owned_by(hir_id)) |
123 |
| - .map(|body_id| cx.tcx.hir().body(body_id)) |
124 |
| - } else { |
125 |
| - None |
126 |
| - } |
127 |
| -} |
128 |
| - |
129 |
| -/// Determines if an expression (syntactically) returns the same thing as a parameter's pattern |
| 117 | +/// Returns true iff an expression returns the same thing as a parameter's pattern |
130 | 118 | fn match_expr_param(cx: &LateContext<'_, '_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
131 | 119 | if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
132 | 120 | match_var(expr, ident.name) && !(cx.tables.hir_owner == Some(expr.hir_id.owner) && is_adjusted(cx, expr))
|
|
0 commit comments