diff options
| author | flip1995 <philipp.krones@embecosm.com> | 2021-03-12 15:30:50 +0100 |
|---|---|---|
| committer | flip1995 <philipp.krones@embecosm.com> | 2021-03-12 15:30:50 +0100 |
| commit | f2f2a005b4efd3e44ac6a02ea2b9660d28401679 (patch) | |
| tree | c4ece65dffee2aa79eaa3b7f190765a95055f815 /clippy_lints/src/methods/map_unwrap_or.rs | |
| parent | 36a27ecaacad74f69b21a12bc66b826f11f2d44e (diff) | |
Merge commit '6ed6f1e6a1a8f414ba7e6d9b8222e7e5a1686e42' into clippyup
Diffstat (limited to 'clippy_lints/src/methods/map_unwrap_or.rs')
| -rw-r--r-- | clippy_lints/src/methods/map_unwrap_or.rs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs new file mode 100644 index 00000000000..63b2cf87f32 --- /dev/null +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -0,0 +1,76 @@ +use crate::utils::usage::mutated_variables; +use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; +use rustc_span::symbol::sym; + +use super::MAP_UNWRAP_OR; + +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + +/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + map_args: &'tcx [hir::Expr<'_>], + unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } + // lint if the caller of `map()` is an `Option` + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); + + if is_option || is_result { + // Don't make a suggestion that may fail to compile due to mutably borrowing + // the same variable twice. + let map_mutated_vars = mutated_variables(&map_args[0], cx); + let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); + if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { + if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { + return false; + } + } else { + return false; + } + + // lint message + let msg = if is_option { + "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \ + `map_or_else(<g>, <f>)` instead" + } else { + "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(<g>, <f>)` instead" + }; + // get snippets for args to map() and unwrap_or_else() + let map_snippet = snippet(cx, map_args[1].span, ".."); + let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); + // lint, with note if neither arg is > 1 line and both map() and + // unwrap_or_else() have the same span + let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; + let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); + if same_span && !multiline { + let var_snippet = snippet(cx, map_args[0].span, ".."); + span_lint_and_sugg( + cx, + MAP_UNWRAP_OR, + expr.span, + msg, + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, + ); + return true; + } else if same_span && multiline { + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); + return true; + } + } + + false +} |
