check return type of get and indexing

This commit is contained in:
Jacherr 2024-03-19 10:41:56 +00:00 committed by Jacher
parent 46b3264131
commit e186ed2ad1
4 changed files with 94 additions and 18 deletions

View file

@ -3,12 +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::{adt_has_inherent_method, deref_chain};
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
@ -111,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
&& deref.any(|l| {
l.peel_refs().is_slice()
|| l.peel_refs().is_array()
|| adt_has_inherent_method(cx, l.peel_refs(), sym!(get))
|| ty_has_appliciable_get_function(cx, l.peel_refs(), expr_ty, expr)
})
{
let note = "the suggestion might not be applicable in constant blocks";
@ -240,3 +241,36 @@ 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_appliciable_get_function<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
array_ty: Ty<'tcx>,
index_expr: &Expr<'_>,
) -> bool {
if let ty::Adt(_, array_args) = 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)
.instantiate(cx.tcx, array_args)
.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.get(0)
&& let generic_ty = option_generic_param.expect_ty().peel_refs()
&& let _ = println!(
"{}, {}",
cx.typeck_results().expr_ty(index_expr).peel_refs(),
generic_ty.peel_refs()
)
&& cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs()
{
true
} else {
false
}
}

View file

@ -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::{adt_has_inherent_method, deref_chain, 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};
@ -139,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
}
&& !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) {

View file

@ -17,9 +17,9 @@ use rustc_middle::mir::ConstValue;
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, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
@ -1345,16 +1345,22 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
/// 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 adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
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(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)
})
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 == ty::AssocKind::Fn)
})
.next()
.flatten()
} else {
false
None
}
}

View file

@ -2,7 +2,13 @@
// 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;
@ -19,6 +25,28 @@ impl<T> Index<bool> for BoolMap<T> {
}
}
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)
}
}
}
fn main() {
let x = [1, 2, 3, 4];
let index: usize = 1;
@ -73,4 +101,12 @@ fn main() {
};
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];
}