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,28 @@ use rustc_span::symbol::sym;
1111use super :: OPTION_MAP_OR_NONE ;
1212use super :: RESULT_MAP_OR_INTO_OPTION ;
1313
14+ // The expression inside a closure may or may not have surrounding braces
15+ // which causes problems when generating a suggestion.
16+ fn reduce_unit_expression < ' a > (
17+ cx : & LateContext < ' _ > ,
18+ expr : & ' a hir:: Expr < ' _ > ,
19+ ) -> Option < ( & ' a hir:: Expr < ' a > , & ' a [ hir:: Expr < ' a > ] ) > {
20+ match expr. kind {
21+ hir:: ExprKind :: Call ( func, arg_char) => Some ( ( func, arg_char) ) ,
22+ hir:: ExprKind :: Block ( block, _) => {
23+ match ( block. stmts , block. expr ) {
24+ ( & [ ] , Some ( inner_expr) ) => {
25+ // If block only contains an expression,
26+ // reduce `|x| { x + 1 }` to `|x| x + 1`
27+ reduce_unit_expression ( cx, inner_expr)
28+ } ,
29+ _ => None ,
30+ }
31+ } ,
32+ _ => None ,
33+ }
34+ }
35+
1436/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
1537pub ( super ) fn check < ' tcx > (
1638 cx : & LateContext < ' tcx > ,
@@ -31,58 +53,75 @@ pub(super) fn check<'tcx>(
3153 return ;
3254 }
3355
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- } ;
56+ let default_arg_is_none = if let hir:: ExprKind :: Path ( ref qpath) = def_arg. kind {
57+ is_lang_ctor ( cx, qpath, OptionNone )
58+ } else {
59+ return ;
60+ } ;
4061
41- if !default_arg_is_none {
42- // nothing to lint!
43- return ;
44- }
62+ if !default_arg_is_none {
63+ // nothing to lint!
64+ return ;
65+ }
4566
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- } ;
67+ let f_arg_is_some = if let hir:: ExprKind :: Path ( ref qpath) = map_arg. kind {
68+ is_lang_ctor ( cx, qpath, OptionSome )
69+ } else {
70+ false
71+ } ;
72+
73+ if is_option {
74+ let self_snippet = snippet ( cx, recv. span , ".." ) ;
75+ if_chain ! {
76+ if let hir:: ExprKind :: Closure ( _, _, id, span, _) = map_arg. kind;
77+ let arg_snippet = snippet( cx, span, ".." ) ;
78+ let body = cx. tcx. hir( ) . body( id) ;
79+ if let Some ( ( func, arg_char) ) = reduce_unit_expression( cx, & body. value) ;
80+ if arg_char. len( ) == 1 ;
81+ if let hir:: ExprKind :: Path ( ref qpath) = func. kind;
82+ if let Some ( segment) = single_segment_path( qpath) ;
83+ if segment. ident. name == sym:: Some ;
84+ then {
85+ let func_snippet = snippet( cx, arg_char[ 0 ] . span, ".." ) ;
86+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
87+ `map(..)` instead";
88+ return span_lint_and_sugg(
89+ cx,
90+ OPTION_MAP_OR_NONE ,
91+ expr. span,
92+ msg,
93+ "try using `map` instead" ,
94+ format!( "{0}.map({1} {2})" , self_snippet, arg_snippet, func_snippet) ,
95+ Applicability :: MachineApplicable ,
96+ ) ;
97+ }
5198
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 \
56- `and_then(..)` instead";
57- (
58- OPTION_MAP_OR_NONE ,
59- msg,
60- "try using `and_then` instead" ,
61- format ! ( "{0}.and_then({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 ;
7699 }
77- } ;
78100
79- span_lint_and_sugg (
80- cx,
81- lint_name,
82- expr. span ,
83- msg,
84- instead,
85- hint,
86- Applicability :: MachineApplicable ,
87- ) ;
101+ let func_snippet = snippet ( cx, map_arg. span , ".." ) ;
102+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
103+ `and_then(..)` instead";
104+ return span_lint_and_sugg (
105+ cx,
106+ OPTION_MAP_OR_NONE ,
107+ expr. span ,
108+ msg,
109+ "try using `and_then` instead" ,
110+ format ! ( "{0}.and_then({1})" , self_snippet, func_snippet) ,
111+ Applicability :: MachineApplicable ,
112+ ) ;
113+ } else if f_arg_is_some {
114+ let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
115+ `ok()` instead";
116+ let self_snippet = snippet ( cx, recv. span , ".." ) ;
117+ return span_lint_and_sugg (
118+ cx,
119+ RESULT_MAP_OR_INTO_OPTION ,
120+ expr. span ,
121+ msg,
122+ "try using `ok` instead" ,
123+ format ! ( "{0}.ok()" , self_snippet) ,
124+ Applicability :: MachineApplicable ,
125+ ) ;
126+ }
88127}
0 commit comments