Skip to content

Commit 167575c

Browse files
committed
feat(unnecessary_mut_passed): support &raw references
1 parent d1b51ea commit 167575c

File tree

4 files changed

+180
-44
lines changed

4 files changed

+180
-44
lines changed

clippy_lints/src/unnecessary_mut_passed.rs

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,30 @@ fn check_arguments<'tcx>(
8585
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
8686
for (argument, parameter) in iter::zip(arguments, parameters) {
8787
if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind()
88-
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind
88+
&& let ExprKind::AddrOf(borrow_kind, Mutability::Mut, arg) = argument.kind
8989
{
90-
let applicability = Applicability::MachineApplicable;
90+
emit(cx, name, fn_kind, argument, borrow_kind, arg);
91+
}
92+
}
93+
}
94+
}
95+
96+
fn emit(cx: &LateContext<'_>, name: &str, fn_kind: &str, argument: &Expr<'_>, borrow_kind: BorrowKind, arg: &Expr<'_>) {
97+
let applicability = Applicability::MachineApplicable;
9198

92-
let span_to_remove = {
93-
let span_until_arg = argument.span.until(arg.span);
94-
if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| {
99+
span_lint_and_then(
100+
cx,
101+
UNNECESSARY_MUT_PASSED,
102+
argument.span,
103+
format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
104+
|diag| {
105+
let span_until_arg = argument.span.until(arg.span);
106+
match borrow_kind {
107+
BorrowKind::Ref => {
108+
let span_to_remove = if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| {
95109
src
96-
// we don't use `strip_prefix` here, because `argument` might be enclosed in parens, in
97-
// which case `&` is no longer the prefix
110+
// we don't use `strip_prefix` here, because `argument` might be enclosed in
111+
// parens, in which case `&` is no longer the prefix
98112
.find('&')
99113
// just a sanity check, in case some proc-macro messes up the spans
100114
.filter(|ref_pos| src[*ref_pos..].contains("mut"))
@@ -103,19 +117,47 @@ fn check_arguments<'tcx>(
103117
span_until_arg.split_at(lo).1
104118
} else {
105119
return;
106-
}
107-
};
120+
};
121+
diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability);
122+
},
123+
BorrowKind::Raw => {
124+
let span_to_remove =
125+
if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src: &str| {
126+
// we don't use `strip_prefix` here, because `argument` might be enclosed in
127+
// parens, and there might be arbitrary whitespace between things
128+
let src_after_addr_raw = src.split_once('&')?.1.split_once("raw")?.1.trim_start();
108129

109-
span_lint_and_then(
110-
cx,
111-
UNNECESSARY_MUT_PASSED,
112-
argument.span,
113-
format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
114-
|diag| {
115-
diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability);
116-
},
117-
);
130+
Some(src_after_addr_raw)
131+
// just a sanity check, in case some proc-macro messes up the spans
132+
.filter(|trimmed| trimmed.contains("mut"))
133+
.map(|trimmed| {
134+
// SAFETY:
135+
// - `trimmed` is derived from `src` by trimming characters from the latter's start
136+
// - that also means that `trimmed` > `src`
137+
unsafe { trimmed.as_ptr().offset_from_unsigned(src.as_ptr()) }
138+
})
139+
}) && let Ok(lo) = u32::try_from(ref_pos)
140+
{
141+
span_until_arg.split_at(lo).1
142+
} else {
143+
return;
144+
};
145+
diag.span_suggestion_verbose(
146+
span_to_remove,
147+
"make this a `const` ptr",
148+
// the span points at `&raw mut x`
149+
// ^^^^
150+
// so we append a space to our suggestion
151+
String::from("const "),
152+
applicability,
153+
);
154+
},
155+
BorrowKind::Pin => {
156+
// it's fine to only "check" this after we've emitted the lint -- if the reference was an `&pin`,
157+
// passing it into a function requiring a ptr wouldn't have type-checked in the first place
158+
return;
159+
},
118160
}
119-
}
120-
}
161+
},
162+
)
121163
}

tests/ui/unnecessary_mut_passed.fixed

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ struct MyStruct;
4141

4242
impl MyStruct {
4343
fn takes_nothing(&self) {}
44+
// XXX: enable the tests for these if `arbitrary_self_types` is extended to support `*const Self`
45+
//
46+
// fn takes_nothing_raw(self: *const Self) {}
47+
// fn takes_nothing_raw_and_raw_const(self: *const Self, a: *const i32) {}
4448
fn takes_ref(&self, a: &i32) {}
4549
fn takes_refmut(&self, a: &mut i32) {}
4650
fn takes_ref_ref(&self, a: &&i32) {}
@@ -146,16 +150,24 @@ fn main() {
146150
my_struct.takes_raw_mut(a);
147151
}
148152

149-
// not supported currently
150153
fn raw_ptrs(my_struct: MyStruct) {
151154
let mut n = 42;
152155

153-
takes_raw_const(&raw mut n);
156+
takes_raw_const(&raw const n);
157+
//~^ unnecessary_mut_passed
158+
159+
// bad formatting
160+
#[rustfmt::skip]
161+
#[allow(clippy::double_parens)]
162+
takes_raw_const( & raw const n);
163+
//~^ unnecessary_mut_passed
154164

155165
let as_ptr: fn(*const i32) = takes_raw_const;
156-
as_ptr(&raw mut n);
166+
as_ptr(&raw const n);
167+
//~^ unnecessary_mut_passed
157168

158-
my_struct.takes_raw_const(&raw mut n);
169+
my_struct.takes_raw_const(&raw const n);
170+
//~^ unnecessary_mut_passed
159171

160172
// No error
161173

@@ -187,4 +199,15 @@ fn issue15722(mut my_struct: MyStruct) {
187199
#[expect(clippy::double_parens)]
188200
my_struct.takes_ref((&42));
189201
//~^ unnecessary_mut_passed
202+
203+
// XXX: enable these if `arbitrary_self_types` is extended to support `*const Self`
204+
//
205+
// let mut n = 42;
206+
// (&raw mut my_struct).takes_nothing_raw();
207+
// (&raw const my_struct).takes_nothing_raw();
208+
// (&raw mut my_struct).takes_nothing_raw_and_raw_const(&raw mut n);
209+
// #[expect(clippy::double_parens)]
210+
// (&raw mut my_struct).takes_nothing_raw_and_raw_const((&raw mut n));
211+
// #[expect(clippy::double_parens)]
212+
// my_struct.takes_nothing_raw_and_raw_const((&raw mut n));
190213
}

tests/ui/unnecessary_mut_passed.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ struct MyStruct;
4141

4242
impl MyStruct {
4343
fn takes_nothing(&self) {}
44+
// XXX: enable the tests for these if `arbitrary_self_types` is extended to support `*const Self`
45+
//
46+
// fn takes_nothing_raw(self: *const Self) {}
47+
// fn takes_nothing_raw_and_raw_const(self: *const Self, a: *const i32) {}
4448
fn takes_ref(&self, a: &i32) {}
4549
fn takes_refmut(&self, a: &mut i32) {}
4650
fn takes_ref_ref(&self, a: &&i32) {}
@@ -146,16 +150,24 @@ fn main() {
146150
my_struct.takes_raw_mut(a);
147151
}
148152

149-
// not supported currently
150153
fn raw_ptrs(my_struct: MyStruct) {
151154
let mut n = 42;
152155

153156
takes_raw_const(&raw mut n);
157+
//~^ unnecessary_mut_passed
158+
159+
// bad formatting
160+
#[rustfmt::skip]
161+
#[allow(clippy::double_parens)]
162+
takes_raw_const( & raw mut n);
163+
//~^ unnecessary_mut_passed
154164

155165
let as_ptr: fn(*const i32) = takes_raw_const;
156166
as_ptr(&raw mut n);
167+
//~^ unnecessary_mut_passed
157168

158169
my_struct.takes_raw_const(&raw mut n);
170+
//~^ unnecessary_mut_passed
159171

160172
// No error
161173

@@ -187,4 +199,15 @@ fn issue15722(mut my_struct: MyStruct) {
187199
#[expect(clippy::double_parens)]
188200
my_struct.takes_ref((&mut 42));
189201
//~^ unnecessary_mut_passed
202+
203+
// XXX: enable these if `arbitrary_self_types` is extended to support `*const Self`
204+
//
205+
// let mut n = 42;
206+
// (&raw mut my_struct).takes_nothing_raw();
207+
// (&raw const my_struct).takes_nothing_raw();
208+
// (&raw mut my_struct).takes_nothing_raw_and_raw_const(&raw mut n);
209+
// #[expect(clippy::double_parens)]
210+
// (&raw mut my_struct).takes_nothing_raw_and_raw_const((&raw mut n));
211+
// #[expect(clippy::double_parens)]
212+
// my_struct.takes_nothing_raw_and_raw_const((&raw mut n));
190213
}

0 commit comments

Comments
 (0)