-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 clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_copy;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_data_structures::fx::FxHashSet;
-use syntax::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_middle::hir::map::Map;
+use rustc_span::source_map::Span;
+use rustc_span::{sym, Symbol};
-use super::OPTION_MAP_UNWRAP_OR;
+use super::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],
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ 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) {
- if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
+ if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) {
// Do not lint if the `map` argument uses identifiers in the `map`
// argument that are also used in the `unwrap_or` argument
}
}
- // 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" {
- "and_then(f)"
+ let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
+ let unwrap_snippet_none = unwrap_snippet == "None";
+ let suggest = if unwrap_snippet_none {
+ "and_then(<f>)"
} else {
- "map_or(a, f)"
+ "map_or(<a>, <f>)"
};
let msg = &format!(
- "called `map(f).unwrap_or({})` on an Option value. \
- This can be done more directly by calling `{}` instead",
+ "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, ¬e);
- } else if same_span && multiline {
- span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
- };
+
+ span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
+ 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)));
+ }
+
+ diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
+ });
}
}
-struct UnwrapVisitor<'a, 'tcx: 'a> {
- cx: &'a LateContext<'a, 'tcx>,
+struct UnwrapVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<Symbol>,
}
-impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
- fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
+impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
+ 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())
}
}
-struct MapExprVisitor<'a, 'tcx: 'a> {
- cx: &'a LateContext<'a, 'tcx>,
+struct MapExprVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<Symbol>,
found_identifier: bool,
}
-impl<'a, 'tcx: 'a> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
- fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
+impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
+ 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;
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")