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,26 @@ 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
+ 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
+
14
34
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
15
35
pub ( super ) fn check < ' tcx > (
16
36
cx : & LateContext < ' tcx > ,
@@ -31,58 +51,78 @@ pub(super) fn check<'tcx>(
31
51
return ;
32
52
}
33
53
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
+ } ;
40
60
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
+ }
45
65
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
+ } ;
51
71
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 \
56
85
`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
+ }
76
97
}
77
- } ;
78
98
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
+ }
88
128
}
0 commit comments