1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: is_lang_ctor;
3
2
use clippy_utils:: source:: snippet;
4
3
use clippy_utils:: ty:: is_type_diagnostic_item;
4
+ use clippy_utils:: { is_lang_ctor, single_segment_path} ;
5
5
use rustc_errors:: Applicability ;
6
6
use rustc_hir as hir;
7
7
use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
@@ -11,6 +11,28 @@ use rustc_span::symbol::sym;
11
11
use super :: OPTION_MAP_OR_NONE ;
12
12
use super :: RESULT_MAP_OR_INTO_OPTION ;
13
13
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
+
14
36
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
15
37
pub ( super ) fn check < ' tcx > (
16
38
cx : & LateContext < ' tcx > ,
@@ -31,58 +53,75 @@ pub(super) fn check<'tcx>(
31
53
return ;
32
54
}
33
55
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
+ } ;
40
61
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
+ }
45
66
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
+ }
51
98
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 ;
76
99
}
77
- } ;
78
100
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
+ }
88
127
}
0 commit comments