]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/methods/option_map_unwrap_or.rs
rustup https://github.com/rust-lang/rust/pull/68944
[rust.git] / clippy_lints / src / methods / option_map_unwrap_or.rs
index 769392a6fd8ff8d550282e6ff2eb6e1944e5e2dd..c40ab7beba442e4bbf13dff49fbfb0b8d8e9719d 100644 (file)
@@ -1,19 +1,23 @@
-use crate::utils::paths;
-use crate::utils::{is_copy, match_type, snippet, span_lint, span_note_and_lint};
-use rustc::hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
-use rustc::hir::{self, *};
-use rustc::lint::LateContext;
+use crate::utils::{differing_macro_contexts, paths, snippet_with_applicability, span_lint_and_then};
+use crate::utils::{is_copy, match_type};
+use rustc::hir::map::Map;
 use rustc_data_structures::fx::FxHashSet;
-use syntax_pos::symbol::Symbol;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
+use rustc_hir::{self, HirId, Path};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
 
 use super::OPTION_MAP_UNWRAP_OR;
 
 /// lint use of `map().unwrap_or()` for `Option`s
 pub(super) fn lint<'a, 'tcx>(
     cx: &LateContext<'a, 'tcx>,
-    expr: &hir::Expr,
-    map_args: &'tcx [hir::Expr],
-    unwrap_args: &'tcx [hir::Expr],
+    expr: &rustc_hir::Expr<'_>,
+    map_args: &'tcx [rustc_hir::Expr<'_>],
+    unwrap_args: &'tcx [rustc_hir::Expr<'_>],
+    map_span: Span,
 ) {
     // lint if the caller of `map()` is an `Option`
     if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) {
@@ -39,41 +43,46 @@ pub(super) fn lint<'a, 'tcx>(
             }
         }
 
-        // get snippets for args to map() and unwrap_or()
-        let map_snippet = snippet(cx, map_args[1].span, "..");
-        let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
+        if differing_macro_contexts(unwrap_args[1].span, map_span) {
+            return;
+        }
+
+        let mut applicability = Applicability::MachineApplicable;
+        // get snippet for unwrap_or()
+        let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability);
         // lint message
         // comparing the snippet from source to raw text ("None") below is safe
         // because we already have checked the type.
         let arg = if unwrap_snippet == "None" { "None" } else { "a" };
-        let suggest = if unwrap_snippet == "None" {
+        let unwrap_snippet_none = unwrap_snippet == "None";
+        let suggest = if unwrap_snippet_none {
             "and_then(f)"
         } else {
             "map_or(a, f)"
         };
         let msg = &format!(
-            "called `map(f).unwrap_or({})` on an Option value. \
+            "called `map(f).unwrap_or({})` on an `Option` value. \
              This can be done more directly by calling `{}` instead",
             arg, suggest
         );
-        // lint, with note if neither arg is > 1 line and both map() and
-        // unwrap_or() 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 suggest = if unwrap_snippet == "None" {
-                format!("and_then({})", map_snippet)
-            } else {
-                format!("map_or({}, {})", unwrap_snippet, map_snippet)
-            };
-            let note = format!(
-                "replace `map({}).unwrap_or({})` with `{}`",
-                map_snippet, unwrap_snippet, suggest
-            );
-            span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, &note);
-        } else if same_span && multiline {
-            span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
-        };
+
+        span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |db| {
+            let map_arg_span = map_args[1].span;
+
+            let mut suggestion = vec![
+                (
+                    map_span,
+                    String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
+                ),
+                (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")),
+            ];
+
+            if !unwrap_snippet_none {
+                suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet)));
+            }
+
+            db.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
+        });
     }
 }
 
@@ -83,13 +92,15 @@ struct UnwrapVisitor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
-    fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
+    type Map = Map<'tcx>;
+
+    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
         self.identifiers.insert(ident(path));
         walk_path(self, path);
     }
 
-    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::All(&self.cx.tcx.hir())
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::All(self.cx.tcx.hir())
     }
 }
 
@@ -100,7 +111,9 @@ struct MapExprVisitor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
-    fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
+    type Map = Map<'tcx>;
+
+    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
         if self.identifiers.contains(&ident(path)) {
             self.found_identifier = true;
             return;
@@ -108,12 +121,12 @@ fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
         walk_path(self, path);
     }
 
-    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::All(&self.cx.tcx.hir())
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::All(self.cx.tcx.hir())
     }
 }
 
-fn ident(path: &Path) -> Symbol {
+fn ident(path: &Path<'_>) -> Symbol {
     path.segments
         .last()
         .expect("segments should be composed of at least 1 element")