1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::source
::snippet_with_applicability
;
3 use clippy_utils
::ty
::is_copy
;
4 use clippy_utils
::ty
::is_type_diagnostic_item
;
5 use rustc_data_structures
::fx
::FxHashSet
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::intravisit
::{walk_path, Visitor}
;
8 use rustc_hir
::{self, HirId, Path}
;
9 use rustc_lint
::LateContext
;
10 use rustc_middle
::hir
::nested_filter
;
11 use rustc_span
::source_map
::Span
;
12 use rustc_span
::{sym, Symbol}
;
14 use super::MAP_UNWRAP_OR
;
16 /// lint use of `map().unwrap_or()` for `Option`s
17 pub(super) fn check
<'tcx
>(
18 cx
: &LateContext
<'tcx
>,
19 expr
: &rustc_hir
::Expr
<'_
>,
20 recv
: &rustc_hir
::Expr
<'_
>,
21 map_arg
: &'tcx rustc_hir
::Expr
<'_
>,
22 unwrap_recv
: &rustc_hir
::Expr
<'_
>,
23 unwrap_arg
: &'tcx rustc_hir
::Expr
<'_
>,
26 // lint if the caller of `map()` is an `Option`
27 if is_type_diagnostic_item(cx
, cx
.typeck_results().expr_ty(recv
), sym
::Option
) {
28 if !is_copy(cx
, cx
.typeck_results().expr_ty(unwrap_arg
)) {
29 // Do not lint if the `map` argument uses identifiers in the `map`
30 // argument that are also used in the `unwrap_or` argument
32 let mut unwrap_visitor
= UnwrapVisitor
{
34 identifiers
: FxHashSet
::default(),
36 unwrap_visitor
.visit_expr(unwrap_arg
);
38 let mut map_expr_visitor
= MapExprVisitor
{
40 identifiers
: unwrap_visitor
.identifiers
,
41 found_identifier
: false,
43 map_expr_visitor
.visit_expr(map_arg
);
45 if map_expr_visitor
.found_identifier
{
50 if unwrap_arg
.span
.ctxt() != map_span
.ctxt() {
54 let mut applicability
= Applicability
::MachineApplicable
;
55 // get snippet for unwrap_or()
56 let unwrap_snippet
= snippet_with_applicability(cx
, unwrap_arg
.span
, "..", &mut applicability
);
58 // comparing the snippet from source to raw text ("None") below is safe
59 // because we already have checked the type.
60 let arg
= if unwrap_snippet
== "None" { "None" }
else { "<a>" }
;
61 let unwrap_snippet_none
= unwrap_snippet
== "None";
62 let suggest
= if unwrap_snippet_none
{
68 "called `map(<f>).unwrap_or({})` on an `Option` value. \
69 This can be done more directly by calling `{}` instead",
73 span_lint_and_then(cx
, MAP_UNWRAP_OR
, expr
.span
, msg
, |diag
| {
74 let map_arg_span
= map_arg
.span
;
76 let mut suggestion
= vec
![
79 String
::from(if unwrap_snippet_none { "and_then" }
else { "map_or" }
),
81 (expr
.span
.with_lo(unwrap_recv
.span
.hi()), String
::new()),
84 if !unwrap_snippet_none
{
85 suggestion
.push((map_arg_span
.with_hi(map_arg_span
.lo()), format
!("{}, ", unwrap_snippet
)));
88 diag
.multipart_suggestion(&format
!("use `{}` instead", suggest
), suggestion
, applicability
);
93 struct UnwrapVisitor
<'a
, 'tcx
> {
94 cx
: &'a LateContext
<'tcx
>,
95 identifiers
: FxHashSet
<Symbol
>,
98 impl<'a
, 'tcx
> Visitor
<'tcx
> for UnwrapVisitor
<'a
, 'tcx
> {
99 type NestedFilter
= nested_filter
::All
;
101 fn visit_path(&mut self, path
: &'tcx Path
<'_
>, _id
: HirId
) {
102 self.identifiers
.insert(ident(path
));
103 walk_path(self, path
);
106 fn nested_visit_map(&mut self) -> Self::Map
{
111 struct MapExprVisitor
<'a
, 'tcx
> {
112 cx
: &'a LateContext
<'tcx
>,
113 identifiers
: FxHashSet
<Symbol
>,
114 found_identifier
: bool
,
117 impl<'a
, 'tcx
> Visitor
<'tcx
> for MapExprVisitor
<'a
, 'tcx
> {
118 type NestedFilter
= nested_filter
::All
;
120 fn visit_path(&mut self, path
: &'tcx Path
<'_
>, _id
: HirId
) {
121 if self.identifiers
.contains(&ident(path
)) {
122 self.found_identifier
= true;
125 walk_path(self, path
);
128 fn nested_visit_map(&mut self) -> Self::Map
{
133 fn ident(path
: &Path
<'_
>) -> Symbol
{
136 .expect("segments should be composed of at least 1 element")