Skip to content

Commit 950cd0d

Browse files
committed
Teach avoidable_slice_indexing about HirIds and Visitors
1 parent a7204cf commit 950cd0d

File tree

1 file changed

+42
-56
lines changed

1 file changed

+42
-56
lines changed

clippy_lints/src/avoidable_slice_indexing.rs

Lines changed: 42 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@ use clippy_utils::consts::{constant, Constant};
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::higher::IfLet;
44
use clippy_utils::ty::implements_trait;
5-
use clippy_utils::{in_macro, is_expn_of, is_lint_allowed, meets_msrv, msrvs};
5+
use clippy_utils::{in_macro, is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
66
use if_chain::if_chain;
77
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
88
use rustc_errors::Applicability;
99
use rustc_hir as hir;
10+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1011
use rustc_lint::{LateContext, LateLintPass, LintContext};
11-
use rustc_middle::mir::FakeReadCause;
12+
use rustc_middle::hir::map::Map;
1213
use rustc_middle::ty;
1314
use rustc_semver::RustcVersion;
1415
use rustc_session::{declare_tool_lint, impl_lint_pass};
15-
use rustc_span::{symbol::Ident, Span, Symbol};
16-
use rustc_trait_selection::infer::TyCtxtInferExt;
17-
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId};
16+
use rustc_span::{symbol::Ident, Span};
1817
use std::convert::TryInto;
1918

2019
declare_clippy_lint! {
@@ -89,24 +88,23 @@ impl LateLintPass<'_> for AvoidableSliceIndexing {
8988
}
9089

9190
fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> Option<FxHashMap<hir::HirId, SliceLintInformation>> {
92-
let mut removed_pat: FxHashSet<Symbol> = FxHashSet::default();
93-
let mut ident_to_id: FxHashMap<Symbol, hir::HirId> = FxHashMap::default();
91+
let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
9492
let mut slices: FxHashMap<hir::HirId, SliceLintInformation> = FxHashMap::default();
9593
pat.walk_always(|pat| {
96-
if let hir::PatKind::Binding(binding, _, ident, sub_pat) = pat.kind {
94+
if let hir::PatKind::Binding(binding, value_hir_id, ident, sub_pat) = pat.kind {
9795
// We'll just ignore mut and ref mut for simplicity sake right now
9896
if let hir::BindingAnnotation::Mutable | hir::BindingAnnotation::RefMut = binding {
9997
return;
10098
}
10199

102100
// This block catches bindings with sub patterns. It would be hard to build a correct suggestion
103101
// for them and it's likely that the user knows what they are doing in such a case.
104-
if removed_pat.contains(&ident.name) {
102+
if removed_pat.contains(&value_hir_id) {
105103
return;
106104
}
107105
if sub_pat.is_some() {
108-
removed_pat.insert(ident.name);
109-
ident_to_id.remove(&ident.name).map(|key| slices.remove(&key));
106+
removed_pat.insert(value_hir_id);
107+
slices.remove(&value_hir_id);
110108
return;
111109
}
112110

@@ -121,9 +119,8 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> Option<FxHashM
121119
.copy_trait()
122120
.map_or(false, |trait_id| implements_trait(cx, inner_ty, trait_id, &[]));
123121

124-
let key = *ident_to_id.entry(ident.name).or_insert(pat.hir_id);
125122
let slice_info = slices
126-
.entry(key)
123+
.entry(value_hir_id)
127124
.or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
128125
slice_info.pattern_spans.push(pat.span);
129126
}
@@ -209,23 +206,21 @@ impl SliceLintInformation {
209206
}
210207
}
211208

212-
fn filter_lintable_slices(
213-
cx: &LateContext<'_>,
209+
fn filter_lintable_slices<'a, 'tcx>(
210+
cx: &'a LateContext<'tcx>,
214211
slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
215212
index_limit: u64,
216-
scope: &hir::Expr<'_>,
213+
scope: &'tcx hir::Expr<'tcx>,
217214
) -> Option<FxHashMap<hir::HirId, SliceLintInformation>> {
218-
let mut v = SliceIndexLintingDelegate {
215+
let mut visitor = SliceIndexLintingDelegate {
219216
cx,
220217
slice_lint_info,
221218
index_limit,
222219
};
223220

224-
cx.tcx.infer_ctxt().enter(|infcx| {
225-
ExprUseVisitor::new(&mut v, &infcx, scope.hir_id.owner, cx.param_env, cx.typeck_results()).consume_expr(scope);
226-
});
221+
intravisit::walk_expr(&mut visitor, scope);
227222

228-
let slices: FxHashMap<hir::HirId, SliceLintInformation> = v
223+
let slices: FxHashMap<hir::HirId, SliceLintInformation> = visitor
229224
.slice_lint_info
230225
.drain()
231226
.filter(|(_key, value)| !value.index_use.is_empty())
@@ -239,54 +234,45 @@ struct SliceIndexLintingDelegate<'a, 'tcx> {
239234
index_limit: u64,
240235
}
241236

242-
impl<'a, 'tcx> SliceIndexLintingDelegate<'a, 'tcx> {
243-
fn check_place(&mut self, place: &PlaceWithHirId<'tcx>, hir_id: hir::HirId) {
244-
if let PlaceBase::Local(slice_id) = place.place.base {
245-
if !self.slice_lint_info.contains_key(&slice_id) {
246-
return;
247-
}
237+
impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingDelegate<'a, 'tcx> {
238+
type Map = Map<'tcx>;
248239

249-
// Get the parent expression
250-
let map = self.cx.tcx.hir();
251-
let parent_id = map.get_parent_node(hir_id);
252-
let parent_expr = if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id) {
253-
parent_expr
254-
} else {
255-
return;
256-
};
240+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
241+
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
242+
}
257243

258-
// Checking for slice indexing
244+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
245+
if let Some(local_id) = path_to_local(expr) {
259246
if_chain! {
260-
if let hir::ExprKind::AddrOf(_kind, _mut, inner_expr) = parent_expr.kind;
261-
if let hir::ExprKind::Index(_, index_expr) = inner_expr.kind;
247+
// Check if this is even a local we're interested in
248+
if self.slice_lint_info.contains_key(&local_id);
249+
250+
let map = self.cx.tcx.hir();
251+
252+
// Checking for slice indexing
253+
let parent_id = map.get_parent_node(expr.hir_id);
254+
if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
255+
if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
262256
if let Some((Constant::Int(index_value), _)) = constant(self.cx, self.cx.typeck_results(), index_expr);
263257
if let Ok(index_value) = index_value.try_into();
264258
if index_value < self.index_limit;
265-
if let Some(use_info) = self.slice_lint_info.get_mut(&slice_id);
259+
260+
// Make sure that this slice index is read only
261+
let maybe_addrof_id = map.get_parent_node(parent_id);
262+
if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
263+
if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
264+
265+
// Get info to later lint this exporession
266+
if let Some(use_info) = self.slice_lint_info.get_mut(&local_id);
266267
then {
267268
use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
268269
return;
269270
}
270271
}
271272

272273
// The slice was used for something else than indexing
273-
self.slice_lint_info.remove(&slice_id);
274+
self.slice_lint_info.remove(&local_id);
274275
}
276+
intravisit::walk_expr(self, expr);
275277
}
276278
}
277-
278-
impl<'a, 'tcx> Delegate<'tcx> for SliceIndexLintingDelegate<'a, 'tcx> {
279-
fn consume(&mut self, place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) {
280-
self.check_place(place, expr_id);
281-
}
282-
283-
fn borrow(&mut self, place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _bk: ty::BorrowKind) {
284-
self.check_place(place, expr_id);
285-
}
286-
287-
fn mutate(&mut self, place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) {
288-
self.check_place(place, expr_id);
289-
}
290-
291-
fn fake_read(&mut self, _place: Place<'tcx>, _cause: FakeReadCause, _diag_expr_id: hir::HirId) {}
292-
}

0 commit comments

Comments
 (0)