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::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::higher; 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_ast::ast::RangeLimits;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -111,7 +112,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
&& deref.any(|l| { && deref.any(|l| {
l.peel_refs().is_slice() l.peel_refs().is_slice()
|| l.peel_refs().is_array() || 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"; 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) (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::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_as_impl; use clippy_utils::get_parent_as_impl;
use clippy_utils::source::snippet; 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_ast::Mutability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
@ -139,7 +139,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
} }
&& !deref_chain(cx, ty).any(|ty| { && !deref_chain(cx, ty).any(|ty| {
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method // 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| { && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
if item.ident.name == sym!(IntoIter) { if item.ident.name == sym!(IntoIter) {

View File

@ -17,9 +17,9 @@
use rustc_middle::traits::EvaluationResult; use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, GenericArgsRef, GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
}; };
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP}; 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. /// Checks if a Ty<'_> has some inherent method Symbol.
/// This does not look for impls in the type's `Deref::Target` type. /// 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(...)`. /// 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) { 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
cx.tcx .inherent_impls(ty_did)
.associated_items(did) .into_iter()
.filter_by_name_unhygienic(method_name) .flatten()
.next() .map(|&did| {
.is_some_and(|item| item.kind == ty::AssocKind::Fn) cx.tcx
}) .associated_items(did)
.filter_by_name_unhygienic(method_name)
.next()
.filter(|item| item.kind == ty::AssocKind::Fn)
})
.next()
.flatten()
} else { } 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 also check the out_of_bounds_indexing lint here, because it lints similar things and
// we want to avoid false positives. // we want to avoid false positives.
#![warn(clippy::out_of_bounds_indexing)] #![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)] #![warn(clippy::indexing_slicing)]
use std::ops::Index; use std::ops::Index;
@ -19,6 +25,28 @@ fn index(&self, index: bool) -> &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() { fn main() {
let x = [1, 2, 3, 4]; let x = [1, 2, 3, 4];
let index: usize = 1; let index: usize = 1;
@ -73,4 +101,12 @@ fn main() {
}; };
map[true]; // Ok, because `get` does not exist (custom indexing) 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];
} }