11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use clippy_utils:: is_lang_ctor;
32use clippy_utils:: source:: snippet;
43use clippy_utils:: ty:: is_type_diagnostic_item;
4+ use clippy_utils:: { is_lang_ctor, single_segment_path} ;
55use rustc_errors:: Applicability ;
66use rustc_hir as hir;
77use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
@@ -11,6 +11,26 @@ use rustc_span::symbol::sym;
1111use super :: OPTION_MAP_OR_NONE ;
1212use super :: RESULT_MAP_OR_INTO_OPTION ;
1313
14+ fn reduce_unit_expression < ' a > (
15+ cx : & LateContext < ' _ > ,
16+ expr : & ' a hir:: Expr < ' _ > ,
17+ ) -> Option < ( & ' a hir:: Expr < ' a > , & ' a [ hir:: Expr < ' a > ] ) > {
18+ match expr. kind {
19+ hir:: ExprKind :: Call ( func, arg_char) => Some ( ( func, arg_char) ) ,
20+ hir:: ExprKind :: Block ( block, _) => {
21+ match block. expr {
22+ Some ( inner_expr) => {
23+ // If block only contains an expression,
24+ // reduce `{ X }` to `X`
25+ reduce_unit_expression ( cx, inner_expr)
26+ } ,
27+ _ => None ,
28+ }
29+ } ,
30+ _ => None ,
31+ }
32+ }
33+
1434/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
1535pub ( super ) fn check < ' tcx > (
1636 cx : & LateContext < ' tcx > ,
@@ -31,58 +51,78 @@ pub(super) fn check<'tcx>(
3151 return ;
3252 }
3353
34- let ( lint_name, msg, instead, hint) = {
35- let default_arg_is_none = if let hir:: ExprKind :: Path ( ref qpath) = def_arg. kind {
36- is_lang_ctor ( cx, qpath, OptionNone )
37- } else {
38- return ;
39- } ;
54+ // let (lint_name, msg, instead, hint) = {
55+ let default_arg_is_none = if let hir:: ExprKind :: Path ( ref qpath) = def_arg. kind {
56+ is_lang_ctor ( cx, qpath, OptionNone )
57+ } else {
58+ return ;
59+ } ;
4060
41- if !default_arg_is_none {
42- // nothing to lint!
43- return ;
44- }
61+ if !default_arg_is_none {
62+ // nothing to lint!
63+ return ;
64+ }
4565
46- let f_arg_is_some = if let hir:: ExprKind :: Path ( ref qpath) = map_arg. kind {
47- is_lang_ctor ( cx, qpath, OptionSome )
48- } else {
49- false
50- } ;
66+ let f_arg_is_some = if let hir:: ExprKind :: Path ( ref qpath) = map_arg. kind {
67+ is_lang_ctor ( cx, qpath, OptionSome )
68+ } else {
69+ false
70+ } ;
5171
52- if is_option {
53- let self_snippet = snippet ( cx, recv. span , ".." ) ;
54- let func_snippet = snippet ( cx, map_arg. span , ".." ) ;
55- let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
72+ if is_option {
73+ let self_snippet = snippet ( cx, recv. span , ".." ) ;
74+ if let hir:: ExprKind :: Closure ( _, _, id, _, _) = map_arg. kind {
75+ if_chain ! {
76+ let body = cx. tcx. hir( ) . body( id) ;
77+ if let Some ( ( func, arg_char) ) = reduce_unit_expression( cx, & body. value) ;
78+ if arg_char. len( ) == 1 ;
79+ if let hir:: ExprKind :: Path ( ref qpath) = func. kind;
80+ if let Some ( segment) = single_segment_path( qpath) ;
81+ if segment. ident. name == sym:: Some ;
82+ then {
83+ let func_snippet = snippet( cx, arg_char[ 0 ] . span, ".." ) ;
84+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
5685 `map(..)` instead";
57- (
58- OPTION_MAP_OR_NONE ,
59- msg,
60- "try using `map` instead" ,
61- format ! ( "{0}.map({1})" , self_snippet, func_snippet) ,
62- )
63- } else if f_arg_is_some {
64- let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
65- `ok()` instead";
66- let self_snippet = snippet ( cx, recv. span , ".." ) ;
67- (
68- RESULT_MAP_OR_INTO_OPTION ,
69- msg,
70- "try using `ok` instead" ,
71- format ! ( "{0}.ok()" , self_snippet) ,
72- )
73- } else {
74- // nothing to lint!
75- return ;
86+ return span_lint_and_sugg(
87+ cx,
88+ OPTION_MAP_OR_NONE ,
89+ expr. span,
90+ msg,
91+ "try using `map` instead" ,
92+ format!( "{0}.map({1})" , self_snippet, func_snippet) ,
93+ Applicability :: MachineApplicable ,
94+ ) ;
95+ }
96+ }
7697 }
77- } ;
7898
79- span_lint_and_sugg (
80- cx,
81- lint_name,
82- expr. span ,
83- msg,
84- instead,
85- hint,
86- Applicability :: MachineApplicable ,
87- ) ;
99+ let func_snippet = snippet ( cx, map_arg. span , ".." ) ;
100+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
101+ `and_then(..)` instead";
102+ span_lint_and_sugg (
103+ cx,
104+ OPTION_MAP_OR_NONE ,
105+ expr. span ,
106+ msg,
107+ "try using `and_then` instead" ,
108+ format ! ( "{0}.and_then({1})" , self_snippet, func_snippet) ,
109+ Applicability :: MachineApplicable ,
110+ )
111+ } else if f_arg_is_some {
112+ let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
113+ `ok()` instead";
114+ let self_snippet = snippet ( cx, recv. span , ".." ) ;
115+ span_lint_and_sugg (
116+ cx,
117+ RESULT_MAP_OR_INTO_OPTION ,
118+ expr. span ,
119+ msg,
120+ "try using `ok` instead" ,
121+ format ! ( "{0}.ok()" , self_snippet) ,
122+ Applicability :: MachineApplicable ,
123+ )
124+ } else {
125+ // nothing to lint!
126+ return ;
127+ }
88128}
0 commit comments