2021-03-25 13:29:11 -05:00
|
|
|
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;
|
2019-01-21 11:29:35 -06:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2019-10-05 07:23:52 -05:00
|
|
|
use rustc_errors::Applicability;
|
2020-01-09 01:13:22 -06:00
|
|
|
use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
|
2020-02-21 02:39:38 -06:00
|
|
|
use rustc_hir::{self, HirId, Path};
|
2020-01-12 00:08:41 -06:00
|
|
|
use rustc_lint::LateContext;
|
2020-03-30 04:02:14 -05:00
|
|
|
use rustc_middle::hir::map::Map;
|
2020-01-04 04:00:00 -06:00
|
|
|
use rustc_span::source_map::Span;
|
2020-11-05 07:29:48 -06:00
|
|
|
use rustc_span::{sym, Symbol};
|
2019-01-19 05:53:21 -06:00
|
|
|
|
2020-05-17 10:36:26 -05:00
|
|
|
use super::MAP_UNWRAP_OR;
|
2019-01-19 05:53:21 -06:00
|
|
|
|
|
|
|
/// lint use of `map().unwrap_or()` for `Option`s
|
2021-03-12 08:30:50 -06:00
|
|
|
pub(super) fn check<'tcx>(
|
2020-06-25 15:41:36 -05:00
|
|
|
cx: &LateContext<'tcx>,
|
2020-01-06 10:39:50 -06:00
|
|
|
expr: &rustc_hir::Expr<'_>,
|
2021-04-08 10:50:13 -05:00
|
|
|
recv: &rustc_hir::Expr<'_>,
|
|
|
|
map_arg: &'tcx rustc_hir::Expr<'_>,
|
|
|
|
unwrap_recv: &rustc_hir::Expr<'_>,
|
|
|
|
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
|
2019-10-05 07:23:52 -05:00
|
|
|
map_span: Span,
|
2019-01-21 11:29:35 -06:00
|
|
|
) {
|
2019-01-19 05:53:21 -06:00
|
|
|
// lint if the caller of `map()` is an `Option`
|
2021-10-02 18:51:01 -05:00
|
|
|
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) {
|
2021-04-08 10:50:13 -05:00
|
|
|
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
|
2019-01-21 11:29:35 -06:00
|
|
|
// Do not lint if the `map` argument uses identifiers in the `map`
|
|
|
|
// argument that are also used in the `unwrap_or` argument
|
|
|
|
|
|
|
|
let mut unwrap_visitor = UnwrapVisitor {
|
|
|
|
cx,
|
|
|
|
identifiers: FxHashSet::default(),
|
|
|
|
};
|
2021-04-08 10:50:13 -05:00
|
|
|
unwrap_visitor.visit_expr(unwrap_arg);
|
2019-01-21 11:29:35 -06:00
|
|
|
|
|
|
|
let mut map_expr_visitor = MapExprVisitor {
|
|
|
|
cx,
|
|
|
|
identifiers: unwrap_visitor.identifiers,
|
|
|
|
found_identifier: false,
|
|
|
|
};
|
2021-04-08 10:50:13 -05:00
|
|
|
map_expr_visitor.visit_expr(map_arg);
|
2019-01-21 11:29:35 -06:00
|
|
|
|
|
|
|
if map_expr_visitor.found_identifier {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 10:50:13 -05:00
|
|
|
if differing_macro_contexts(unwrap_arg.span, map_span) {
|
2019-10-05 07:23:52 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
|
|
|
// get snippet for unwrap_or()
|
2021-04-08 10:50:13 -05:00
|
|
|
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
|
2019-01-19 05:53:21 -06:00
|
|
|
// lint message
|
|
|
|
// comparing the snippet from source to raw text ("None") below is safe
|
|
|
|
// because we already have checked the type.
|
2020-11-05 07:29:48 -06:00
|
|
|
let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
|
2019-10-05 07:23:52 -05:00
|
|
|
let unwrap_snippet_none = unwrap_snippet == "None";
|
|
|
|
let suggest = if unwrap_snippet_none {
|
2020-11-05 07:29:48 -06:00
|
|
|
"and_then(<f>)"
|
2019-01-19 05:53:21 -06:00
|
|
|
} else {
|
2020-11-05 07:29:48 -06:00
|
|
|
"map_or(<a>, <f>)"
|
2019-01-19 05:53:21 -06:00
|
|
|
};
|
|
|
|
let msg = &format!(
|
2020-11-05 07:29:48 -06:00
|
|
|
"called `map(<f>).unwrap_or({})` on an `Option` value. \
|
2020-05-17 10:36:26 -05:00
|
|
|
This can be done more directly by calling `{}` instead",
|
2019-01-19 05:53:21 -06:00
|
|
|
arg, suggest
|
|
|
|
);
|
2019-10-05 07:23:52 -05:00
|
|
|
|
2020-05-17 10:36:26 -05:00
|
|
|
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
|
2021-04-08 10:50:13 -05:00
|
|
|
let map_arg_span = map_arg.span;
|
2019-10-05 07:23:52 -05:00
|
|
|
|
|
|
|
let mut suggestion = vec![
|
|
|
|
(
|
|
|
|
map_span,
|
|
|
|
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
|
|
|
),
|
2021-04-08 10:50:13 -05:00
|
|
|
(expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
|
2019-10-05 07:23:52 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
if !unwrap_snippet_none {
|
|
|
|
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet)));
|
|
|
|
}
|
|
|
|
|
2020-04-17 01:08:00 -05:00
|
|
|
diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
|
2019-10-05 07:23:52 -05:00
|
|
|
});
|
2019-01-19 05:53:21 -06:00
|
|
|
}
|
|
|
|
}
|
2019-01-21 11:29:35 -06:00
|
|
|
|
2019-06-19 13:36:23 -05:00
|
|
|
struct UnwrapVisitor<'a, 'tcx> {
|
2020-06-25 15:41:36 -05:00
|
|
|
cx: &'a LateContext<'tcx>,
|
2019-02-05 09:59:23 -06:00
|
|
|
identifiers: FxHashSet<Symbol>,
|
2019-01-21 11:29:35 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 13:36:23 -05:00
|
|
|
impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
|
2020-01-09 01:13:22 -06:00
|
|
|
type Map = Map<'tcx>;
|
|
|
|
|
2019-12-29 22:02:10 -06:00
|
|
|
fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
|
2019-02-05 09:59:23 -06:00
|
|
|
self.identifiers.insert(ident(path));
|
2019-01-21 11:29:35 -06:00
|
|
|
walk_path(self, path);
|
|
|
|
}
|
|
|
|
|
2020-03-15 17:41:20 -05:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
|
|
NestedVisitorMap::All(self.cx.tcx.hir())
|
2019-01-21 11:29:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-19 13:36:23 -05:00
|
|
|
struct MapExprVisitor<'a, 'tcx> {
|
2020-06-25 15:41:36 -05:00
|
|
|
cx: &'a LateContext<'tcx>,
|
2019-02-05 09:59:23 -06:00
|
|
|
identifiers: FxHashSet<Symbol>,
|
2019-01-21 11:29:35 -06:00
|
|
|
found_identifier: bool,
|
|
|
|
}
|
|
|
|
|
2019-06-19 13:36:23 -05:00
|
|
|
impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
|
2020-01-09 01:13:22 -06:00
|
|
|
type Map = Map<'tcx>;
|
|
|
|
|
2019-12-29 22:02:10 -06:00
|
|
|
fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
|
2019-02-05 09:59:23 -06:00
|
|
|
if self.identifiers.contains(&ident(path)) {
|
|
|
|
self.found_identifier = true;
|
|
|
|
return;
|
2019-01-21 11:29:35 -06:00
|
|
|
}
|
|
|
|
walk_path(self, path);
|
|
|
|
}
|
|
|
|
|
2020-03-15 17:41:20 -05:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
|
|
NestedVisitorMap::All(self.cx.tcx.hir())
|
2019-01-21 11:29:35 -06:00
|
|
|
}
|
|
|
|
}
|
2019-02-05 09:59:23 -06:00
|
|
|
|
2019-12-29 22:02:10 -06:00
|
|
|
fn ident(path: &Path<'_>) -> Symbol {
|
2019-02-05 09:59:23 -06:00
|
|
|
path.segments
|
|
|
|
.last()
|
|
|
|
.expect("segments should be composed of at least 1 element")
|
|
|
|
.ident
|
|
|
|
.name
|
|
|
|
}
|