Skip to content

Commit 7b849af

Browse files
committed
New lint: copy_then_borrow_mut
1 parent a8b1782 commit 7b849af

11 files changed

+259
-67
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5497,6 +5497,7 @@ Released 2018-09-13
54975497
[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty
54985498
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
54995499
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
5500+
[`copy_then_borrow_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_then_borrow_mut
55005501
[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
55015502
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
55025503
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use clippy_utils::diagnostics::span_lint_and_note;
2+
use clippy_utils::ty::is_copy;
3+
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::declare_lint_pass;
6+
7+
declare_clippy_lint! {
8+
/// ### What it does
9+
/// Checks for mutable reference on a freshly copied data due to
10+
/// the use of a block to return an value implementing `Copy`.
11+
///
12+
/// ### Why is this bad?
13+
/// Using a block will make a copy of the block result if its type
14+
/// implements `Copy`. This might be an indication of a failed attempt
15+
/// to borrow the original data instead.
16+
///
17+
/// ### Example
18+
/// ```no_run
19+
/// # fn f(_: &mut [i32]) {}
20+
/// let arr = &mut [10, 20, 30];
21+
/// f(&mut { *arr });
22+
/// ```
23+
/// If you intend to modify `arr` in `f`, use instead:
24+
/// ```no_run
25+
/// # fn f(_: &mut [i32]) {}
26+
/// let arr = &mut [10, 20, 30];
27+
/// f(arr);
28+
/// ```
29+
#[clippy::version = "1.86.0"]
30+
pub COPY_THEN_BORROW_MUT,
31+
suspicious,
32+
"mutable borrow of a data which was just copied"
33+
}
34+
35+
declare_lint_pass!(CopyThenBorrowMut => [COPY_THEN_BORROW_MUT]);
36+
37+
impl LateLintPass<'_> for CopyThenBorrowMut {
38+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
39+
if !expr.span.from_expansion()
40+
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, sub_expr) = expr.kind
41+
&& let ExprKind::Block(block, _) = sub_expr.kind
42+
&& block.span.eq_ctxt(expr.span)
43+
&& let Some(block_expr) = block.expr
44+
&& let block_ty = cx.typeck_results().expr_ty_adjusted(block_expr)
45+
&& is_copy(cx, block_ty)
46+
{
47+
span_lint_and_note(
48+
cx,
49+
COPY_THEN_BORROW_MUT,
50+
expr.span,
51+
"mutable borrow of a value which was just copied",
52+
(!block.targeted_by_break).then_some(block_expr.span),
53+
"the return value of the block implements `Copy` and will be copied",
54+
);
55+
}
56+
}
57+
}

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
113113
crate::copies::IF_SAME_THEN_ELSE_INFO,
114114
crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO,
115115
crate::copy_iterator::COPY_ITERATOR_INFO,
116+
crate::copy_then_borrow_mut::COPY_THEN_BORROW_MUT_INFO,
116117
crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO,
117118
crate::create_dir::CREATE_DIR_INFO,
118119
crate::dbg_macro::DBG_MACRO_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ mod collection_is_never_read;
103103
mod comparison_chain;
104104
mod copies;
105105
mod copy_iterator;
106+
mod copy_then_borrow_mut;
106107
mod crate_in_macro_def;
107108
mod create_dir;
108109
mod dbg_macro;
@@ -980,5 +981,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
980981
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
981982
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
982983
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
984+
store.register_late_pass(|_| Box::new(copy_then_borrow_mut::CopyThenBorrowMut));
983985
// add lints here, do not remove this comment, it's used in `new_lint`
984986
}

tests/ui-toml/excessive_nesting/excessive_nesting.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
clippy::collapsible_if,
1414
clippy::blocks_in_conditions,
1515
clippy::single_match,
16+
clippy::copy_then_borrow_mut
1617
)]
1718

1819
#[macro_use]

0 commit comments

Comments
 (0)