Skip to content

Commit 8eafeeb

Browse files
committed
Auto merge of #12691 - Alexendoo:ignore-interior-mutability-indirect, r=llogiq
Rework interior mutability detection Replaces the existing interior mutability detection, the two main changes being - It now follows references/pointers e.g. `struct S(&Cell)` - `mutable_key_type` ignores pointers as it did before - The `ignore_interior_mutability` config now applies to types containing the ignored type, e.g. `http::HeaderName` Fixes #7752 Fixes #9776 Fixes #9801 changelog: [`mutable_key_type`], [`declare_interior_mutable_const`]: now considers types that have references to interior mutable types as interior mutable
2 parents c2a98cb + f7aef63 commit 8eafeeb

File tree

15 files changed

+225
-223
lines changed

15 files changed

+225
-223
lines changed

book/src/lint_configuration.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -506,13 +506,14 @@ The maximum byte size a `Future` can have, before it triggers the `clippy::large
506506

507507

508508
## `ignore-interior-mutability`
509-
A list of paths to types that should be treated like `Arc`, i.e. ignored but
510-
for the generic parameters for determining interior mutability
509+
A list of paths to types that should be treated as if they do not contain interior mutability
511510

512511
**Default Value:** `["bytes::Bytes"]`
513512

514513
---
515514
**Affected lints:**
515+
* [`borrow_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const)
516+
* [`declare_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const)
516517
* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
517518
* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
518519

clippy_config/src/conf.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,9 @@ define_Conf! {
467467
///
468468
/// The maximum size of the `Err`-variant in a `Result` returned from a function
469469
(large_error_threshold: u64 = 128),
470-
/// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND.
470+
/// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST.
471471
///
472-
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
473-
/// for the generic parameters for determining interior mutability
472+
/// A list of paths to types that should be treated as if they do not contain interior mutability
474473
(ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
475474
/// Lint: UNINLINED_FORMAT_ARGS.
476475
///

clippy_lints/src/copies.rs

+20-24
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
22
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
3-
use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop};
3+
use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
44
use clippy_utils::visitors::for_each_expr;
55
use clippy_utils::{
6-
capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
7-
if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
6+
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
7+
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
88
};
99
use core::iter;
1010
use core::ops::ControlFlow;
1111
use rustc_errors::Applicability;
12-
use rustc_hir::def_id::DefIdSet;
1312
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
1413
use rustc_lint::{LateContext, LateLintPass};
1514
use rustc_session::impl_lint_pass;
@@ -159,40 +158,36 @@ declare_clippy_lint! {
159158
"`if` statement with shared code in all blocks"
160159
}
161160

162-
pub struct CopyAndPaste {
161+
pub struct CopyAndPaste<'tcx> {
163162
ignore_interior_mutability: Vec<String>,
164-
ignored_ty_ids: DefIdSet,
163+
interior_mut: InteriorMut<'tcx>,
165164
}
166165

167-
impl CopyAndPaste {
166+
impl CopyAndPaste<'_> {
168167
pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
169168
Self {
170169
ignore_interior_mutability,
171-
ignored_ty_ids: DefIdSet::new(),
170+
interior_mut: InteriorMut::default(),
172171
}
173172
}
174173
}
175174

176-
impl_lint_pass!(CopyAndPaste => [
175+
impl_lint_pass!(CopyAndPaste<'_> => [
177176
IFS_SAME_COND,
178177
SAME_FUNCTIONS_IN_IF_CONDITION,
179178
IF_SAME_THEN_ELSE,
180179
BRANCHES_SHARING_CODE
181180
]);
182181

183-
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
182+
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
184183
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
185-
for ignored_ty in &self.ignore_interior_mutability {
186-
let path: Vec<&str> = ignored_ty.split("::").collect();
187-
for id in def_path_def_ids(cx, path.as_slice()) {
188-
self.ignored_ty_ids.insert(id);
189-
}
190-
}
184+
self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
191185
}
186+
192187
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
193188
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
194189
let (conds, blocks) = if_sequence(expr);
195-
lint_same_cond(cx, &conds, &self.ignored_ty_ids);
190+
lint_same_cond(cx, &conds, &mut self.interior_mut);
196191
lint_same_fns_in_if_cond(cx, &conds);
197192
let all_same =
198193
!is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
@@ -570,13 +565,14 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
570565
})
571566
}
572567

573-
fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool {
568+
fn method_caller_is_mutable<'tcx>(
569+
cx: &LateContext<'tcx>,
570+
caller_expr: &Expr<'_>,
571+
interior_mut: &mut InteriorMut<'tcx>,
572+
) -> bool {
574573
let caller_ty = cx.typeck_results().expr_ty(caller_expr);
575-
// Check if given type has inner mutability and was not set to ignored by the configuration
576-
let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
577-
&& !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did()));
578574

579-
is_inner_mut_ty
575+
interior_mut.is_interior_mut_ty(cx, caller_ty)
580576
|| caller_ty.is_mutable_ptr()
581577
// `find_binding_init` will return the binding iff its not mutable
582578
|| path_to_local(caller_expr)
@@ -585,15 +581,15 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore
585581
}
586582

587583
/// Implementation of `IFS_SAME_COND`.
588-
fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) {
584+
fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) {
589585
for (i, j) in search_same(
590586
conds,
591587
|e| hash_expr(cx, e),
592588
|lhs, rhs| {
593589
// Ignore eq_expr side effects iff one of the expression kind is a method call
594590
// and the caller is not a mutable, including inner mutable type.
595591
if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
596-
if method_caller_is_mutable(cx, caller, ignored_ty_ids) {
592+
if method_caller_is_mutable(cx, caller, interior_mut) {
597593
false
598594
} else {
599595
SpanlessEq::new(cx).eq_expr(lhs, rhs)

clippy_lints/src/mut_key.rs

+20-47
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use clippy_utils::diagnostics::span_lint;
2-
use clippy_utils::ty::is_interior_mut_ty;
3-
use clippy_utils::{def_path_def_ids, trait_ref_of_method};
4-
use rustc_data_structures::fx::FxHashSet;
2+
use clippy_utils::trait_ref_of_method;
3+
use clippy_utils::ty::InteriorMut;
54
use rustc_hir as hir;
65
use rustc_lint::{LateContext, LateLintPass};
76
use rustc_middle::ty::{self, Ty};
@@ -23,41 +22,28 @@ declare_clippy_lint! {
2322
/// ### Known problems
2423
///
2524
/// #### False Positives
26-
/// It's correct to use a struct that contains interior mutability as a key, when its
25+
/// It's correct to use a struct that contains interior mutability as a key when its
2726
/// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
2827
/// However, this lint is unable to recognize this, so it will often cause false positives in
29-
/// theses cases. The `bytes` crate is a great example of this.
28+
/// these cases.
3029
///
3130
/// #### False Negatives
32-
/// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
33-
/// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
34-
/// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
35-
///
36-
/// This lint does check a few cases for indirection. Firstly, using some standard library
37-
/// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
38-
/// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
39-
/// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
40-
/// contained type.
41-
///
42-
/// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
43-
/// apply only to the **address** of the contained value. Therefore, interior mutability
44-
/// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
45-
/// or `Ord`, and therefore will not trigger this link. For more info, see issue
46-
/// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
31+
/// This lint does not follow raw pointers (`*const T` or `*mut T`) as `Hash` and `Ord`
32+
/// apply only to the **address** of the contained value. This can cause false negatives for
33+
/// custom collections that use raw pointers internally.
4734
///
4835
/// ### Example
4936
/// ```no_run
5037
/// use std::cmp::{PartialEq, Eq};
5138
/// use std::collections::HashSet;
5239
/// use std::hash::{Hash, Hasher};
5340
/// use std::sync::atomic::AtomicUsize;
54-
///# #[allow(unused)]
5541
///
5642
/// struct Bad(AtomicUsize);
5743
/// impl PartialEq for Bad {
5844
/// fn eq(&self, rhs: &Self) -> bool {
5945
/// ..
60-
/// ; unimplemented!();
46+
/// # ; true
6147
/// }
6248
/// }
6349
///
@@ -66,7 +52,7 @@ declare_clippy_lint! {
6652
/// impl Hash for Bad {
6753
/// fn hash<H: Hasher>(&self, h: &mut H) {
6854
/// ..
69-
/// ; unimplemented!();
55+
/// # ;
7056
/// }
7157
/// }
7258
///
@@ -80,25 +66,16 @@ declare_clippy_lint! {
8066
"Check for mutable `Map`/`Set` key type"
8167
}
8268

83-
#[derive(Clone)]
84-
pub struct MutableKeyType {
69+
pub struct MutableKeyType<'tcx> {
8570
ignore_interior_mutability: Vec<String>,
86-
ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>,
71+
interior_mut: InteriorMut<'tcx>,
8772
}
8873

89-
impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
74+
impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]);
9075

91-
impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
76+
impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> {
9277
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
93-
self.ignore_mut_def_ids.clear();
94-
let mut path = Vec::new();
95-
for ty in &self.ignore_interior_mutability {
96-
path.extend(ty.split("::"));
97-
for id in def_path_def_ids(cx, &path[..]) {
98-
self.ignore_mut_def_ids.insert(id);
99-
}
100-
path.clear();
101-
}
78+
self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability);
10279
}
10380

10481
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
@@ -121,23 +98,23 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
12198
}
12299
}
123100

124-
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) {
101+
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &hir::LetStmt<'tcx>) {
125102
if let hir::PatKind::Wild = local.pat.kind {
126103
return;
127104
}
128105
self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat));
129106
}
130107
}
131108

132-
impl MutableKeyType {
109+
impl<'tcx> MutableKeyType<'tcx> {
133110
pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
134111
Self {
135112
ignore_interior_mutability,
136-
ignore_mut_def_ids: FxHashSet::default(),
113+
interior_mut: InteriorMut::default(),
137114
}
138115
}
139116

140-
fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
117+
fn check_sig(&mut self, cx: &LateContext<'tcx>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'tcx>) {
141118
let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
142119
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
143120
self.check_ty_(cx, hir_ty.span, *ty);
@@ -151,7 +128,7 @@ impl MutableKeyType {
151128

152129
// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
153130
// generics (because the compiler cannot ensure immutability for unknown types).
154-
fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
131+
fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
155132
let ty = ty.peel_refs();
156133
if let ty::Adt(def, args) = ty.kind() {
157134
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
@@ -162,11 +139,7 @@ impl MutableKeyType {
162139
}
163140

164141
let subst_ty = args.type_at(0);
165-
// Determines if a type contains interior mutability which would affect its implementation of
166-
// [`Hash`] or [`Ord`].
167-
if is_interior_mut_ty(cx, subst_ty)
168-
&& !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
169-
{
142+
if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
170143
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
171144
}
172145
}

0 commit comments

Comments
 (0)