Replace some iteration with get_[type_]diagnostic_name

This commit is contained in:
Alex Macleod 2024-08-17 18:42:21 +00:00
parent 9aa656df9f
commit 6993752607
6 changed files with 60 additions and 75 deletions

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
use clippy_utils::visitors::{for_each_expr, Visitable};
use clippy_utils::{get_enclosing_block, path_to_local_id};
use core::ops::ControlFlow;
@ -7,7 +7,6 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
/// ### What it does
@ -44,24 +43,11 @@
}
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
// Add `String` here when it is added to diagnostic items
static COLLECTIONS: [Symbol; 9] = [
sym::BTreeMap,
sym::BTreeSet,
sym::BinaryHeap,
sym::HashMap,
sym::HashSet,
sym::LinkedList,
sym::Option,
sym::Vec,
sym::VecDeque,
];
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
// Look for local variables whose type is a container. Search surrounding block for read access.
if let PatKind::Binding(_, local_id, _, _) = local.pat.kind
&& match_acceptable_type(cx, local, &COLLECTIONS)
&& match_acceptable_type(cx, local)
&& let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
&& has_no_read_access(cx, local_id, enclosing_block)
{
@ -70,11 +56,22 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
}
}
fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool {
fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
let ty = cx.typeck_results().pat_ty(local.pat);
collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
// String type is a lang item but not a diagnostic item for now so we need a separate check
|| is_type_lang_item(cx, ty, LangItem::String)
matches!(
get_type_diagnostic_name(cx, ty),
Some(
sym::BTreeMap
| sym::BTreeSet
| sym::BinaryHeap
| sym::HashMap
| sym::HashSet
| sym::LinkedList
| sym::Option
| sym::Vec
| sym::VecDeque
)
) || is_type_lang_item(cx, ty, LangItem::String)
}
fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {

View File

@ -8,7 +8,7 @@
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span, Symbol};
use rustc_span::{sym, Span};
use clippy_utils::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
@ -193,17 +193,13 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
}
}
static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
match *ty.kind() {
// primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
ty::Adt(adt, args) => {
tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
|| KNOWN_WRAPPER_TYS
.iter()
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
|| matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
&& args.types().any(|ty| is_mutable_ty(cx, ty, tys))
},
ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)),

View File

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::higher;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
@ -210,18 +210,6 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
("product", 0),
];
/// the paths of types that are known to be infinitely allocating
const INFINITE_COLLECTORS: &[Symbol] = &[
sym::BinaryHeap,
sym::BTreeMap,
sym::BTreeSet,
sym::HashMap,
sym::HashSet,
sym::LinkedList,
sym::Vec,
sym::VecDeque,
];
fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
match expr.kind {
ExprKind::MethodCall(method, receiver, args, _) => {
@ -248,10 +236,19 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
}
} else if method.ident.name == sym!(collect) {
let ty = cx.typeck_results().expr_ty(expr);
if INFINITE_COLLECTORS
.iter()
.any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
{
if matches!(
get_type_diagnostic_name(cx, ty),
Some(
sym::BinaryHeap
| sym::BTreeMap
| sym::BTreeSet
| sym::HashMap
| sym::HashSet
| sym::LinkedList
| sym::Vec
| sym::VecDeque,
)
) {
return is_infinite(cx, receiver);
}
}

View File

@ -2,14 +2,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
use clippy_utils::{match_def_path, paths, SpanlessEq};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::ExprKind::Assign;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{impl_lint_pass, RustcVersion};
use rustc_session::impl_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Span;
@ -20,16 +20,6 @@
&paths::SLICE_INTO,
&paths::VEC_DEQUE_ITER,
];
const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
(sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)),
(sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
(sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
(sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
(sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
(sym::Vec, None),
(sym::VecDeque, None),
];
const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap];
declare_clippy_lint! {
/// ### What it does
@ -264,16 +254,22 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
}
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
is_type_diagnostic_item(cx, expr_ty, *ty)
&& acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
})
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
let required = match get_type_diagnostic_name(cx, ty) {
Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN,
Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN,
Some(sym::HashSet) => msrvs::HASH_SET_RETAIN,
Some(sym::HashMap) => msrvs::HASH_MAP_RETAIN,
Some(sym::Vec | sym::VecDeque) => return true,
_ => return false,
};
msrv.meets(required)
}
fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty))
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap))
}
fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) {

View File

@ -2,7 +2,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
use clippy_utils::{
can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
CaptureKind,
@ -88,9 +88,10 @@ pub(super) fn check<'tcx>(
Node::LetStmt(l) => {
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
.into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& matches!(
get_type_diagnostic_name(cx, ty),
Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
)
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))

View File

@ -125,14 +125,12 @@ fn check_sig(&mut self, cx: &LateContext<'tcx>, fn_def_id: LocalDefId, decl: &hi
// generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs();
if let ty::Adt(def, args) = ty.kind() {
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
if !is_keyed_type {
return;
}
if let ty::Adt(def, args) = ty.kind()
&& matches!(
cx.tcx.get_diagnostic_name(def.did()),
Some(sym::HashMap | sym::BTreeMap | sym::HashSet | sym::BTreeSet)
)
{
let subst_ty = args.type_at(0);
if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");