11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use clippy_utils:: peel_blocks;
3- use clippy_utils:: source:: snippet;
2+ use clippy_utils:: source:: snippet_with_applicability;
43use clippy_utils:: ty:: is_type_diagnostic_item;
4+ use clippy_utils:: { expr_or_init, find_binding_init, peel_blocks} ;
55use rustc_errors:: Applicability ;
6- use rustc_hir:: { Closure , Expr , ExprKind , HirId , QPath } ;
6+ use rustc_hir:: def:: { DefKind , Res } ;
7+ use rustc_hir:: { Body , BodyId , Closure , Expr , ExprKind , HirId , QPath } ;
78use rustc_lint:: LateContext ;
89use rustc_span:: symbol:: sym;
910
@@ -12,8 +13,9 @@ use super::utils::get_last_chain_binding_hir_id;
1213
1314fn emit_lint ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , def_arg : & Expr < ' _ > ) {
1415 let msg = "unused \" map closure\" when calling `Option::map_or_else` value" ;
15- let self_snippet = snippet ( cx, recv. span , ".." ) ;
16- let err_snippet = snippet ( cx, def_arg. span , ".." ) ;
16+ let mut applicability = Applicability :: MachineApplicable ;
17+ let self_snippet = snippet_with_applicability ( cx, recv. span , "_" , & mut applicability) ;
18+ let err_snippet = snippet_with_applicability ( cx, def_arg. span , ".." , & mut applicability) ;
1719 span_lint_and_sugg (
1820 cx,
1921 UNNECESSARY_OPTION_MAP_OR_ELSE ,
@@ -34,23 +36,21 @@ fn handle_qpath(
3436 qpath : QPath < ' _ > ,
3537) {
3638 if let QPath :: Resolved ( _, path) = qpath
37- && let rustc_hir :: def :: Res :: Local ( hir_id) = path. res
39+ && let Res :: Local ( hir_id) = path. res
3840 && expected_hir_id == hir_id
3941 {
4042 emit_lint ( cx, expr, recv, def_arg) ;
4143 }
4244}
4345
44- /// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s.
45- pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , def_arg : & Expr < ' _ > , map_arg : & Expr < ' _ > ) {
46- // lint if the caller of `map_or_else()` is an `Option`
47- if is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( recv) , sym:: Option )
48- && let ExprKind :: Closure ( & Closure { body, .. } ) = map_arg. kind
49- && let body = cx. tcx . hir_body ( body)
50- && let Some ( first_param) = body. params . first ( )
51- {
52- let body_expr = peel_blocks ( body. value ) ;
46+ fn handle_closure ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , def_arg : & Expr < ' _ > , body_id : BodyId ) {
47+ let body = cx. tcx . hir_body ( body_id) ;
48+ handle_fn_body ( cx, expr, recv, def_arg, body) ;
49+ }
5350
51+ fn handle_fn_body ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , def_arg : & Expr < ' _ > , body : & Body < ' _ > ) {
52+ if let Some ( first_param) = body. params . first ( ) {
53+ let body_expr = peel_blocks ( body. value ) ;
5454 match body_expr. kind {
5555 ExprKind :: Path ( qpath) => {
5656 handle_qpath ( cx, expr, recv, def_arg, first_param. pat . hir_id , qpath) ;
@@ -71,3 +71,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_
7171 }
7272 }
7373}
74+
75+ /// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s.
76+ pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , def_arg : & Expr < ' _ > , map_arg : & Expr < ' _ > ) {
77+ // lint if the caller of `map_or_else()` is an `Option`
78+ if !is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( recv) , sym:: Option ) {
79+ return ;
80+ }
81+ match map_arg. kind {
82+ // If the second argument is a closure, we can check its body.
83+ ExprKind :: Closure ( & Closure { body, .. } ) => {
84+ handle_closure ( cx, expr, recv, def_arg, body) ;
85+ } ,
86+ ExprKind :: Path ( qpath) => {
87+ let res = cx. qpath_res ( & qpath, map_arg. hir_id ) ;
88+ match res {
89+ // Case 1: Local variable (could be a closure)
90+ Res :: Local ( hir_id) => {
91+ if let Some ( init_expr) = find_binding_init ( cx, hir_id) {
92+ let origin = expr_or_init ( cx, init_expr) ;
93+ if let ExprKind :: Closure ( & Closure { body, .. } ) = origin. kind {
94+ handle_closure ( cx, expr, recv, def_arg, body) ;
95+ }
96+ }
97+ } ,
98+ // Case 2: Function definition
99+ Res :: Def ( DefKind :: Fn , def_id) => {
100+ if let Some ( local_def_id) = def_id. as_local ( )
101+ && let Some ( body) = cx. tcx . hir_maybe_body_owned_by ( local_def_id)
102+ {
103+ handle_fn_body ( cx, expr, recv, def_arg, body) ;
104+ }
105+ } ,
106+ _ => ( ) ,
107+ }
108+ } ,
109+ _ => ( ) ,
110+ }
111+ }
0 commit comments