|
1 | 1 | use crate::utils::paths;
|
2 | 2 | use crate::utils::{is_copy, match_type, snippet, span_lint, span_note_and_lint};
|
3 |
| -use rustc::hir; |
| 3 | +use rustc::hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; |
| 4 | +use rustc::hir::{self, *}; |
| 5 | +use rustc::hir::def_id::DefId; |
4 | 6 | use rustc::lint::LateContext;
|
| 7 | +use rustc_data_structures::fx::FxHashSet; |
5 | 8 |
|
6 | 9 | use super::OPTION_MAP_UNWRAP_OR;
|
7 | 10 |
|
8 | 11 | /// lint use of `map().unwrap_or()` for `Option`s
|
9 |
| -pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, map_args: &[hir::Expr], unwrap_args: &[hir::Expr]) { |
| 12 | +pub(super) fn lint<'a, 'tcx>( |
| 13 | + cx: &LateContext<'a, 'tcx>, |
| 14 | + expr: &hir::Expr, |
| 15 | + map_args: &'tcx [hir::Expr], |
| 16 | + unwrap_args: &'tcx [hir::Expr], |
| 17 | +) { |
10 | 18 | // lint if the caller of `map()` is an `Option`
|
11 |
| - let unwrap_ty = cx.tables.expr_ty(&unwrap_args[1]); |
12 |
| - if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) && is_copy(cx, unwrap_ty) { |
| 19 | + if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) { |
| 20 | + |
| 21 | + if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) { |
| 22 | + // Do not lint if the `map` argument uses identifiers in the `map` |
| 23 | + // argument that are also used in the `unwrap_or` argument |
| 24 | + |
| 25 | + let mut unwrap_visitor = UnwrapVisitor { |
| 26 | + cx, |
| 27 | + identifiers: FxHashSet::default(), |
| 28 | + }; |
| 29 | + unwrap_visitor.visit_expr(&unwrap_args[1]); |
| 30 | + |
| 31 | + let mut map_expr_visitor = MapExprVisitor { |
| 32 | + cx, |
| 33 | + identifiers: unwrap_visitor.identifiers, |
| 34 | + found_identifier: false, |
| 35 | + }; |
| 36 | + map_expr_visitor.visit_expr(&map_args[1]); |
| 37 | + |
| 38 | + if map_expr_visitor.found_identifier { |
| 39 | + return; |
| 40 | + } |
| 41 | + } |
| 42 | + |
13 | 43 | // get snippets for args to map() and unwrap_or()
|
14 | 44 | let map_snippet = snippet(cx, map_args[1].span, "..");
|
15 | 45 | let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
|
@@ -47,3 +77,43 @@ pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, map_args: &[hir::
|
47 | 77 | };
|
48 | 78 | }
|
49 | 79 | }
|
| 80 | + |
| 81 | +struct UnwrapVisitor<'a, 'tcx: 'a> { |
| 82 | + cx: &'a LateContext<'a, 'tcx>, |
| 83 | + identifiers: FxHashSet<DefId>, |
| 84 | +} |
| 85 | + |
| 86 | +impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { |
| 87 | + fn visit_path(&mut self, path: &'tcx Path, _id: HirId) { |
| 88 | + if let Some(def_id) = path.def.opt_def_id() { |
| 89 | + self.identifiers.insert(def_id); |
| 90 | + } |
| 91 | + walk_path(self, path); |
| 92 | + } |
| 93 | + |
| 94 | + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
| 95 | + NestedVisitorMap::All(&self.cx.tcx.hir()) |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +struct MapExprVisitor<'a, 'tcx: 'a> { |
| 100 | + cx: &'a LateContext<'a, 'tcx>, |
| 101 | + identifiers: FxHashSet<DefId>, |
| 102 | + found_identifier: bool, |
| 103 | +} |
| 104 | + |
| 105 | +impl<'a, 'tcx: 'a> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> { |
| 106 | + fn visit_path(&mut self, path: &'tcx Path, _id: HirId) { |
| 107 | + if let Some(def_id) = path.def.opt_def_id() { |
| 108 | + if self.identifiers.contains(&def_id) { |
| 109 | + self.found_identifier = true; |
| 110 | + return; |
| 111 | + } |
| 112 | + } |
| 113 | + walk_path(self, path); |
| 114 | + } |
| 115 | + |
| 116 | + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
| 117 | + NestedVisitorMap::All(&self.cx.tcx.hir()) |
| 118 | + } |
| 119 | +} |
0 commit comments