Auto merge of #12488 - Jacherr:issue-11525, r=llogiq
Disable `indexing_slicing` for custom Index impls Fixes https://github.com/rust-lang/rust-clippy/issues/11525 Disables `indexing_slicing` for custom Index impls, specifically any implementations that also do not have a `get` method anywhere along the deref chain (so, for example, it still lints on Vec, which has its `get` method as part of the deref chain). Thanks `@y21` for pointing me in the right direction with a couple of handy util functions for deref chain and inherent methods, saved a headache there! changelog: FP: Disable `indexing_slicing` for custom Index impls
This commit is contained in:
commit
28e887fe71
@ -3,11 +3,13 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -104,7 +106,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Index(array, index, _) = &expr.kind {
|
||||
if let ExprKind::Index(array, index, _) = &expr.kind
|
||||
&& let expr_ty = cx.typeck_results().expr_ty(array)
|
||||
&& let mut deref = deref_chain(cx, expr_ty)
|
||||
&& deref.any(|l| {
|
||||
l.peel_refs().is_slice()
|
||||
|| l.peel_refs().is_array()
|
||||
|| ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr)
|
||||
})
|
||||
{
|
||||
let note = "the suggestion might not be applicable in constant blocks";
|
||||
let ty = cx.typeck_results().expr_ty(array).peel_refs();
|
||||
if let Some(range) = higher::Range::hir(index) {
|
||||
@ -231,3 +241,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1
|
||||
|
||||
(start, end)
|
||||
}
|
||||
|
||||
/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the
|
||||
/// indexing operation (if any).
|
||||
fn ty_has_applicable_get_function<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
array_ty: Ty<'tcx>,
|
||||
index_expr: &Expr<'_>,
|
||||
) -> bool {
|
||||
if let ty::Adt(_, _) = array_ty.kind()
|
||||
&& let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| {
|
||||
cx.tcx
|
||||
.fn_sig(m.def_id)
|
||||
.skip_binder()
|
||||
.output()
|
||||
.skip_binder()
|
||||
})
|
||||
&& let ty::Adt(def, args) = get_output_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Option, def.0.did)
|
||||
&& let Some(option_generic_param) = args.first()
|
||||
&& let generic_ty = option_generic_param.expect_ty().peel_refs()
|
||||
// FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type
|
||||
&& (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs()
|
||||
|| matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _)))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::get_parent_as_impl;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, make_normalized_projection};
|
||||
use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
|
||||
@ -9,8 +9,7 @@
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use std::iter;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
.is_some_and(|did| cx.effective_visibilities.is_exported(did))
|
||||
}
|
||||
|
||||
/// Returns the deref chain of a type, starting with the type itself.
|
||||
fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
|
||||
iter::successors(Some(ty), |&ty| {
|
||||
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
|
||||
&& implements_trait(cx, ty, deref_did, &[])
|
||||
{
|
||||
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
|
||||
if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
|
||||
cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| {
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.filter_by_name_unhygienic(method_name)
|
||||
.next()
|
||||
.is_some_and(|item| item.kind == ty::AssocKind::Fn)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
||||
if !in_external_macro(cx.sess(), item.span)
|
||||
@ -167,7 +139,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
||||
}
|
||||
&& !deref_chain(cx, ty).any(|ty| {
|
||||
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
|
||||
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
|
||||
ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
|
||||
})
|
||||
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
|
||||
if item.ident.name == sym!(IntoIter) {
|
||||
|
@ -17,9 +17,9 @@
|
||||
use rustc_middle::traits::EvaluationResult;
|
||||
use rustc_middle::ty::layout::ValidityRequirement;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
||||
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
|
||||
self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
|
||||
GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
|
||||
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
|
||||
};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||
@ -1328,3 +1328,39 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>
|
||||
pub fn is_manually_drop(ty: Ty<'_>) -> bool {
|
||||
ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
|
||||
}
|
||||
|
||||
/// Returns the deref chain of a type, starting with the type itself.
|
||||
pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
|
||||
iter::successors(Some(ty), |&ty| {
|
||||
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
|
||||
&& implements_trait(cx, ty, deref_did, &[])
|
||||
{
|
||||
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if a Ty<'_> has some inherent method Symbol.
|
||||
/// This does not look for impls in the type's `Deref::Target` type.
|
||||
/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
|
||||
pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
|
||||
if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) {
|
||||
cx.tcx
|
||||
.inherent_impls(ty_did)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|&did| {
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.filter_by_name_unhygienic(method_name)
|
||||
.next()
|
||||
.filter(|item| item.kind == AssocKind::Fn)
|
||||
})
|
||||
.next()
|
||||
.flatten()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,89 @@
|
||||
// We also check the out_of_bounds_indexing lint here, because it lints similar things and
|
||||
// we want to avoid false positives.
|
||||
#![warn(clippy::out_of_bounds_indexing)]
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec)]
|
||||
#![allow(
|
||||
clippy::no_effect,
|
||||
clippy::unnecessary_operation,
|
||||
clippy::useless_vec,
|
||||
unused_must_use,
|
||||
unused
|
||||
)]
|
||||
#![warn(clippy::indexing_slicing)]
|
||||
|
||||
use std::ops::Index;
|
||||
|
||||
struct BoolMap<T> {
|
||||
false_value: T,
|
||||
true_value: T,
|
||||
}
|
||||
|
||||
impl<T> Index<bool> for BoolMap<T> {
|
||||
type Output = T;
|
||||
fn index(&self, index: bool) -> &T {
|
||||
if index { &self.true_value } else { &self.false_value }
|
||||
}
|
||||
}
|
||||
|
||||
struct BoolMapWithGet<T> {
|
||||
false_value: T,
|
||||
true_value: T,
|
||||
}
|
||||
|
||||
impl<T> Index<bool> for BoolMapWithGet<T> {
|
||||
type Output = T;
|
||||
fn index(&self, index: bool) -> &Self::Output {
|
||||
if index { &self.true_value } else { &self.false_value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BoolMapWithGet<T> {
|
||||
fn get(&self, index: bool) -> Option<&T> {
|
||||
if index {
|
||||
Some(&self.true_value)
|
||||
} else {
|
||||
Some(&self.false_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct S<T>(T);
|
||||
impl S<i32> {
|
||||
fn get() -> Option<i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<T> Index<i32> for S<T> {
|
||||
type Output = T;
|
||||
fn index(&self, _index: i32) -> &Self::Output {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
struct Y<T>(T);
|
||||
impl Y<i32> {
|
||||
fn get<U>() -> Option<U> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<T> Index<i32> for Y<T> {
|
||||
type Output = T;
|
||||
fn index(&self, _index: i32) -> &Self::Output {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
struct Z<T>(T);
|
||||
impl<T> Z<T> {
|
||||
fn get<T2>() -> T2 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<T> Index<i32> for Z<T> {
|
||||
type Output = T;
|
||||
fn index(&self, _index: i32) -> &Self::Output {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = [1, 2, 3, 4];
|
||||
@ -51,4 +133,28 @@ fn main() {
|
||||
//~^ ERROR: slicing may panic
|
||||
|
||||
&v[..]; // Ok, should not produce stderr.
|
||||
|
||||
let map = BoolMap {
|
||||
false_value: 2,
|
||||
true_value: 4,
|
||||
};
|
||||
|
||||
map[true]; // Ok, because `get` does not exist (custom indexing)
|
||||
|
||||
let map_with_get = BoolMapWithGet {
|
||||
false_value: 2,
|
||||
true_value: 4,
|
||||
};
|
||||
|
||||
// Lint on this, because `get` does exist with same signature
|
||||
map_with_get[true];
|
||||
|
||||
let s = S::<i32>(1);
|
||||
s[0];
|
||||
|
||||
let y = Y::<i32>(1);
|
||||
y[0];
|
||||
|
||||
let z = Z::<i32>(1);
|
||||
z[0];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:12:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:94:6
|
||||
|
|
||||
LL | &x[index..];
|
||||
| ^^^^^^^^^^
|
||||
@ -9,7 +9,7 @@ LL | &x[index..];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]`
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:14:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:96:6
|
||||
|
|
||||
LL | &x[..index];
|
||||
| ^^^^^^^^^^
|
||||
@ -17,7 +17,7 @@ LL | &x[..index];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:16:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:98:6
|
||||
|
|
||||
LL | &x[index_from..index_to];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -25,7 +25,7 @@ LL | &x[index_from..index_to];
|
||||
= help: consider using `.get(n..m)` or `.get_mut(n..m)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:18:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:100:6
|
||||
|
|
||||
LL | &x[index_from..][..index_to];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -33,7 +33,7 @@ LL | &x[index_from..][..index_to];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:18:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:100:6
|
||||
|
|
||||
LL | &x[index_from..][..index_to];
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@ -41,7 +41,7 @@ LL | &x[index_from..][..index_to];
|
||||
= help: consider using `.get(n..)` or .get_mut(n..)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:21:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:103:6
|
||||
|
|
||||
LL | &x[5..][..10];
|
||||
| ^^^^^^^^^^^^
|
||||
@ -49,7 +49,7 @@ LL | &x[5..][..10];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: range is out of bounds
|
||||
--> tests/ui/indexing_slicing_slice.rs:21:8
|
||||
--> tests/ui/indexing_slicing_slice.rs:103:8
|
||||
|
|
||||
LL | &x[5..][..10];
|
||||
| ^
|
||||
@ -58,7 +58,7 @@ LL | &x[5..][..10];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]`
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:25:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:107:6
|
||||
|
|
||||
LL | &x[0..][..3];
|
||||
| ^^^^^^^^^^^
|
||||
@ -66,7 +66,7 @@ LL | &x[0..][..3];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:27:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:109:6
|
||||
|
|
||||
LL | &x[1..][..5];
|
||||
| ^^^^^^^^^^^
|
||||
@ -74,19 +74,19 @@ LL | &x[1..][..5];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: range is out of bounds
|
||||
--> tests/ui/indexing_slicing_slice.rs:35:12
|
||||
--> tests/ui/indexing_slicing_slice.rs:117:12
|
||||
|
|
||||
LL | &y[0..=4];
|
||||
| ^
|
||||
|
||||
error: range is out of bounds
|
||||
--> tests/ui/indexing_slicing_slice.rs:37:11
|
||||
--> tests/ui/indexing_slicing_slice.rs:119:11
|
||||
|
|
||||
LL | &y[..=4];
|
||||
| ^
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:43:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:125:6
|
||||
|
|
||||
LL | &v[10..100];
|
||||
| ^^^^^^^^^^
|
||||
@ -94,7 +94,7 @@ LL | &v[10..100];
|
||||
= help: consider using `.get(n..m)` or `.get_mut(n..m)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:45:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:127:6
|
||||
|
|
||||
LL | &x[10..][..100];
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -102,13 +102,13 @@ LL | &x[10..][..100];
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: range is out of bounds
|
||||
--> tests/ui/indexing_slicing_slice.rs:45:8
|
||||
--> tests/ui/indexing_slicing_slice.rs:127:8
|
||||
|
|
||||
LL | &x[10..][..100];
|
||||
| ^^
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:48:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:130:6
|
||||
|
|
||||
LL | &v[10..];
|
||||
| ^^^^^^^
|
||||
@ -116,12 +116,36 @@ LL | &v[10..];
|
||||
= help: consider using `.get(n..)` or .get_mut(n..)` instead
|
||||
|
||||
error: slicing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:50:6
|
||||
--> tests/ui/indexing_slicing_slice.rs:132:6
|
||||
|
|
||||
LL | &v[..100];
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: consider using `.get(..n)`or `.get_mut(..n)` instead
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
error: indexing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:150:5
|
||||
|
|
||||
LL | map_with_get[true];
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider using `.get(n)` or `.get_mut(n)` instead
|
||||
|
||||
error: indexing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:153:5
|
||||
|
|
||||
LL | s[0];
|
||||
| ^^^^
|
||||
|
|
||||
= help: consider using `.get(n)` or `.get_mut(n)` instead
|
||||
|
||||
error: indexing may panic
|
||||
--> tests/ui/indexing_slicing_slice.rs:156:5
|
||||
|
|
||||
LL | y[0];
|
||||
| ^^^^
|
||||
|
|
||||
= help: consider using `.get(n)` or `.get_mut(n)` instead
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user