@@ -193,6 +193,24 @@ declare_lint! {
193
193
`map_or_else(g, f)`"
194
194
}
195
195
196
+ /// **What it does:** Checks for usage of `_.map_or(None, _)`.
197
+ ///
198
+ /// **Why is this bad?** Readability, this can be written more concisely as
199
+ /// `_.and_then(_)`.
200
+ ///
201
+ /// **Known problems:** None.
202
+ ///
203
+ /// **Example:**
204
+ /// ```rust
205
+ /// opt.map_or(None, |a| a + 1)
206
+ /// ```
207
+ declare_lint ! {
208
+ pub OPTION_MAP_OR_NONE ,
209
+ Warn ,
210
+ "using `Option.map_or(None, f)`, which is more succinctly expressed as \
211
+ `and_then(f)`"
212
+ }
213
+
196
214
/// **What it does:** Checks for usage of `_.filter(_).next()`.
197
215
///
198
216
/// **Why is this bad?** Readability, this can be written more concisely as
@@ -574,6 +592,7 @@ impl LintPass for Pass {
574
592
OK_EXPECT ,
575
593
OPTION_MAP_UNWRAP_OR ,
576
594
OPTION_MAP_UNWRAP_OR_ELSE ,
595
+ OPTION_MAP_OR_NONE ,
577
596
OR_FUN_CALL ,
578
597
CHARS_NEXT_CMP ,
579
598
CHARS_LAST_CMP ,
@@ -620,6 +639,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
620
639
lint_map_unwrap_or ( cx, expr, arglists[ 0 ] , arglists[ 1 ] ) ;
621
640
} else if let Some ( arglists) = method_chain_args ( expr, & [ "map" , "unwrap_or_else" ] ) {
622
641
lint_map_unwrap_or_else ( cx, expr, arglists[ 0 ] , arglists[ 1 ] ) ;
642
+ } else if let Some ( arglists) = method_chain_args ( expr, & [ "map_or" ] ) {
643
+ lint_map_or_none ( cx, expr, arglists[ 0 ] ) ;
623
644
} else if let Some ( arglists) = method_chain_args ( expr, & [ "filter" , "next" ] ) {
624
645
lint_filter_next ( cx, expr, arglists[ 0 ] ) ;
625
646
} else if let Some ( arglists) = method_chain_args ( expr, & [ "filter" , "map" ] ) {
@@ -1220,6 +1241,35 @@ fn lint_map_unwrap_or_else<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir
1220
1241
}
1221
1242
}
1222
1243
1244
+ /// lint use of `_.map_or(None, _)` for `Option`s
1245
+ fn lint_map_or_none < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , map_or_args : & ' tcx [ hir:: Expr ] ) {
1246
+
1247
+ if match_type ( cx, cx. tables . expr_ty ( & map_or_args[ 0 ] ) , & paths:: OPTION ) {
1248
+ // check if the first non-self argument to map_or() is None
1249
+ let map_or_arg_is_none = if let hir:: Expr_ :: ExprPath ( ref qpath) = map_or_args[ 1 ] . node {
1250
+ match_qpath ( qpath, & paths:: OPTION_NONE )
1251
+ } else {
1252
+ false
1253
+ } ;
1254
+
1255
+ if map_or_arg_is_none {
1256
+ // lint message
1257
+ let msg = "called `map_or(None, f)` on an Option value. This can be done more directly by calling \
1258
+ `and_then(f)` instead";
1259
+ let map_or_self_snippet = snippet ( cx, map_or_args[ 0 ] . span , ".." ) ;
1260
+ let map_or_func_snippet = snippet ( cx, map_or_args[ 2 ] . span , ".." ) ;
1261
+ let hint = format ! ( "{0}.and_then({1})" , map_or_self_snippet, map_or_func_snippet) ;
1262
+ span_lint_and_then (
1263
+ cx,
1264
+ OPTION_MAP_OR_NONE ,
1265
+ expr. span ,
1266
+ msg,
1267
+ |db| { db. span_suggestion ( expr. span , "try using and_then instead" , hint) ; } ,
1268
+ ) ;
1269
+ }
1270
+ }
1271
+ }
1272
+
1223
1273
/// lint use of `filter().next()` for `Iterators`
1224
1274
fn lint_filter_next < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , filter_args : & ' tcx [ hir:: Expr ] ) {
1225
1275
// lint if caller of `.filter().next()` is an Iterator
0 commit comments