mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 21:23:56 +00:00
Extracting is_expr_identity_function
into clippy_utils
for reusability
This commit is contained in:
parent
e4a1e8524c
commit
967d815a42
2 changed files with 56 additions and 52 deletions
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
|
||||
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
@ -74,53 +74,3 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an expression represents the identity function
|
||||
/// Only examines closures and `std::convert::identity`
|
||||
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a function's body represents the identity function
|
||||
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
|
||||
/// return x; }`
|
||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||
let params = func.params;
|
||||
let body = remove_blocks(&func.value);
|
||||
|
||||
// if there's less/more than one parameter, then it is not the identity function
|
||||
if params.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
match body.kind {
|
||||
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
|
||||
ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
|
||||
ExprKind::Block(block, _) => {
|
||||
if_chain! {
|
||||
if block.stmts.len() == 1;
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind;
|
||||
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
||||
then {
|
||||
match_expr_param(cx, ret_val, params[0].pat)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff an expression returns the same thing as a parameter's pattern
|
||||
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
||||
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
match_var(expr, ident.name) && !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1399,6 +1399,60 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
|
||||
}
|
||||
|
||||
/// Checks if an expression represents the identity function
|
||||
/// Only examines closures and `std::convert::identity`
|
||||
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Returns true if the expression is a binding to the given pattern
|
||||
fn is_expr_pat_binding(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
|
||||
if let PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
if match_var(expr, ident.name) {
|
||||
return !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr));
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||
/// * `|x| x`
|
||||
/// * `|x| return x`
|
||||
/// * `|x| { return x }`
|
||||
/// * `|x| { return x; }`
|
||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||
let body = remove_blocks(&func.value);
|
||||
|
||||
let value_pat = if let [value_param] = func.params {
|
||||
value_param.pat
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
match body.kind {
|
||||
ExprKind::Path(QPath::Resolved(None, _)) => is_expr_pat_binding(cx, body, value_pat),
|
||||
ExprKind::Ret(Some(ret_val)) => is_expr_pat_binding(cx, ret_val, value_pat),
|
||||
ExprKind::Block(block, _) => {
|
||||
if_chain! {
|
||||
if let &[block_stmt] = &block.stmts;
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block_stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
|
||||
then {
|
||||
is_expr_pat_binding(cx, ret_val, value_pat)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
||||
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
|
||||
let map = tcx.hir();
|
||||
|
|
Loading…
Reference in a new issue