@@ -2027,36 +2027,31 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2027
2027
did. map_or ( false , |did| cx. tcx . has_attr ( did, sym:: must_use) )
2028
2028
}
2029
2029
2030
- /// Checks if an expression represents the identity function
2031
- /// Only examines closures and `std::convert::identity`
2030
+ /// Checks if a function's body represents the identity function. Looks for bodies of the form:
2031
+ /// * `|x| x`
2032
+ /// * `|x| return x`
2033
+ /// * `|x| { return x }`
2034
+ /// * `|x| { return x; }`
2032
2035
///
2033
- /// Closure bindings with type annotations and `std::convert::identity` with generic args
2034
- /// are not considered identity functions because they can guide type inference,
2035
- /// and removing it may lead to compile errors.
2036
- pub fn is_expr_identity_function ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
2037
- /// Checks if a function's body represents the identity function. Looks for bodies of the form:
2038
- /// * `|x| x`
2039
- /// * `|x| return x`
2040
- /// * `|x| { return x }`
2041
- /// * `|x| { return x; }`
2042
- fn is_body_identity_function ( cx : & LateContext < ' _ > , func : & Body < ' _ > ) -> bool {
2043
- let id = if_chain ! {
2044
- if let [ param] = func. params;
2045
- if let PatKind :: Binding ( _, id, _, _) = param. pat. kind;
2046
- then {
2047
- id
2048
- } else {
2049
- return false ;
2050
- }
2051
- } ;
2036
+ /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
2037
+ fn is_body_identity_function ( cx : & LateContext < ' _ > , func : & Body < ' _ > ) -> bool {
2038
+ let id = if_chain ! {
2039
+ if let [ param] = func. params;
2040
+ if let PatKind :: Binding ( _, id, _, _) = param. pat. kind;
2041
+ then {
2042
+ id
2043
+ } else {
2044
+ return false ;
2045
+ }
2046
+ } ;
2052
2047
2053
- let mut expr = func. value ;
2054
- loop {
2055
- match expr. kind {
2056
- #[ rustfmt:: skip]
2048
+ let mut expr = func. value ;
2049
+ loop {
2050
+ match expr. kind {
2051
+ #[ rustfmt:: skip]
2057
2052
ExprKind :: Block ( & Block { stmts : [ ] , expr : Some ( e) , .. } , _, )
2058
2053
| ExprKind :: Ret ( Some ( e) ) => expr = e,
2059
- #[ rustfmt:: skip]
2054
+ #[ rustfmt:: skip]
2060
2055
ExprKind :: Block ( & Block { stmts : [ stmt] , expr : None , .. } , _) => {
2061
2056
if_chain ! {
2062
2057
if let StmtKind :: Semi ( e) | StmtKind :: Expr ( e) = stmt. kind;
@@ -2068,11 +2063,16 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
2068
2063
}
2069
2064
}
2070
2065
} ,
2071
- _ => return path_to_local_id ( expr, id) && cx. typeck_results ( ) . expr_adjustments ( expr) . is_empty ( ) ,
2072
- }
2066
+ _ => return path_to_local_id ( expr, id) && cx. typeck_results ( ) . expr_adjustments ( expr) . is_empty ( ) ,
2073
2067
}
2074
2068
}
2069
+ }
2075
2070
2071
+ /// This is the same as [`is_expr_identity_function`], but does not consider closures
2072
+ /// with type annotations for its bindings (or similar) as identity functions:
2073
+ /// * `|x: u8| x`
2074
+ /// * `std::convert::identity::<u8>`
2075
+ pub fn is_expr_untyped_identity_function ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
2076
2076
match expr. kind {
2077
2077
ExprKind :: Closure ( & Closure { body, fn_decl, .. } )
2078
2078
if fn_decl. inputs . iter ( ) . all ( |ty| matches ! ( ty. kind, TyKind :: Infer ) ) =>
@@ -2089,6 +2089,21 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
2089
2089
}
2090
2090
}
2091
2091
2092
+ /// Checks if an expression represents the identity function
2093
+ /// Only examines closures and `std::convert::identity`
2094
+ ///
2095
+ /// NOTE: If you want to use this function to find out if a closure is unnecessary, you likely want
2096
+ /// to call [`is_expr_untyped_identity_function`] instead, which makes sure that the closure doesn't
2097
+ /// have type annotations. This is important because removing a closure with bindings can
2098
+ /// remove type information that helped type inference before, which can then lead to compile
2099
+ /// errors.
2100
+ pub fn is_expr_identity_function ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
2101
+ match expr. kind {
2102
+ ExprKind :: Closure ( & Closure { body, .. } ) => is_body_identity_function ( cx, cx. tcx . hir ( ) . body ( body) ) ,
2103
+ _ => path_def_id ( cx, expr) . map_or ( false , |id| match_def_path ( cx, id, & paths:: CONVERT_IDENTITY ) ) ,
2104
+ }
2105
+ }
2106
+
2092
2107
/// Gets the node where an expression is either used, or it's type is unified with another branch.
2093
2108
/// Returns both the node and the `HirId` of the closest child node.
2094
2109
pub fn get_expr_use_or_unification_node < ' tcx > ( tcx : TyCtxt < ' tcx > , expr : & Expr < ' _ > ) -> Option < ( Node < ' tcx > , HirId ) > {
0 commit comments