check return type of get and indexing
This commit is contained in:
parent
46b3264131
commit
e186ed2ad1
@ -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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
&& 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
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ 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, 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
|
||||
.inherent_impls(ty_did)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|&did| {
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.filter_by_name_unhygienic(method_name)
|
||||
.next()
|
||||
.is_some_and(|item| item.kind == ty::AssocKind::Fn)
|
||||
.filter(|item| item.kind == ty::AssocKind::Fn)
|
||||
})
|
||||
.next()
|
||||
.flatten()
|
||||
} else {
|
||||
false
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ 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() {
|
||||
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];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user