Skip to content

Commit fec6e55

Browse files
committed
Attempt to fix false negative
1 parent 4cf720a commit fec6e55

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

clippy_lints/src/methods/option_map_unwrap_or.rs

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
11
use crate::utils::paths;
22
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;
46
use rustc::lint::LateContext;
7+
use rustc_data_structures::fx::FxHashSet;
58

69
use super::OPTION_MAP_UNWRAP_OR;
710

811
/// 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+
) {
1018
// 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+
1343
// get snippets for args to map() and unwrap_or()
1444
let map_snippet = snippet(cx, map_args[1].span, "..");
1545
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::
4777
};
4878
}
4979
}
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+
}

tests/ui/methods.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,12 @@ fn option_methods() {
179179
// macro case
180180
let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
181181

182+
// Should not lint if not copyable
182183
let id: String = "identifier".to_string();
183-
let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); // Should not lint if not copyable
184+
let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
185+
// ...but DO lint if the `unwrap_or` argument is not used in the `map`
186+
let id: String = "identifier".to_string();
187+
let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
184188

185189
// Check OPTION_MAP_UNWRAP_OR_ELSE
186190
// single line case

0 commit comments

Comments
 (0)