@@ -2027,36 +2027,31 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
20272027 did. map_or ( false , |did| cx. tcx . has_attr ( did, sym:: must_use) )
20282028}
20292029
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; }`
20322035///
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+ } ;
20522047
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]
20572052 ExprKind :: Block ( & Block { stmts : [ ] , expr : Some ( e) , .. } , _, )
20582053 | ExprKind :: Ret ( Some ( e) ) => expr = e,
2059- #[ rustfmt:: skip]
2054+ #[ rustfmt:: skip]
20602055 ExprKind :: Block ( & Block { stmts : [ stmt] , expr : None , .. } , _) => {
20612056 if_chain ! {
20622057 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
20682063 }
20692064 }
20702065 } ,
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 ( ) ,
20732067 }
20742068 }
2069+ }
20752070
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 {
20762076 match expr. kind {
20772077 ExprKind :: Closure ( & Closure { body, fn_decl, .. } )
20782078 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
20892089 }
20902090}
20912091
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+
20922107/// Gets the node where an expression is either used, or it's type is unified with another branch.
20932108/// Returns both the node and the `HirId` of the closest child node.
20942109pub fn get_expr_use_or_unification_node < ' tcx > ( tcx : TyCtxt < ' tcx > , expr : & Expr < ' _ > ) -> Option < ( Node < ' tcx > , HirId ) > {
0 commit comments