Skip to content

Commit c1b3bf0

Browse files
committed
Lint unused parentheses around method receiver
Extend the `unused_parens` lint to detect unnecessary parentheses around method receivers like `(x).method()`. Previously, the lint only checked method arguments but not the receiver. The fix adds a new `MethodReceiver` context to `UnusedDelimsCtx` and checks method call receivers in `check_expr()`. It also handles cases where parentheses are necessary, such as: - Range expressions: `(1..10).sum()` - Unary expressions: `(*ptr).method()`, `(-1).abs()` - Binary expressions: `(1 + 2).to_string()` - Cast expressions: `(x as i32).abs()` - Type expressions, assignments, and address-of expressions Also removes unnecessary parentheses from the standard library that were caught by the new lint. Fixes #151985
1 parent a60d12c commit c1b3bf0

File tree

9 files changed

+105
-11
lines changed

9 files changed

+105
-11
lines changed

‎compiler/rustc_lint/src/unused.rs‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements {
623623
enum UnusedDelimsCtx {
624624
FunctionArg,
625625
MethodArg,
626+
MethodReceiver,
626627
AssignedValue,
627628
AssignedValueLetElse,
628629
IfCond,
@@ -645,6 +646,7 @@ impl From<UnusedDelimsCtx> for &'static str {
645646
match ctx {
646647
UnusedDelimsCtx::FunctionArg => "function argument",
647648
UnusedDelimsCtx::MethodArg => "method argument",
649+
UnusedDelimsCtx::MethodReceiver => "method receiver",
648650
UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
649651
"assigned value"
650652
}
@@ -710,6 +712,22 @@ trait UnusedDelimLint {
710712
}
711713
}
712714

715+
// For method receivers, parentheses are necessary for certain expression types
716+
// to avoid parsing ambiguities, e.g. `(1..10).sum()`, `(*ptr).method()`, etc.
717+
if ctx == UnusedDelimsCtx::MethodReceiver {
718+
match inner.kind {
719+
ast::ExprKind::Range(..)
720+
| ast::ExprKind::Unary(..)
721+
| ast::ExprKind::Binary(..)
722+
| ast::ExprKind::Cast(..)
723+
| ast::ExprKind::Type(..)
724+
| ast::ExprKind::Assign(..)
725+
| ast::ExprKind::AssignOp(..)
726+
| ast::ExprKind::AddrOf(..) => return true,
727+
_ => {}
728+
}
729+
}
730+
713731
// Check it's range in LetScrutineeExpr
714732
if let ast::ExprKind::Range(..) = inner.kind
715733
&& matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
@@ -976,13 +994,15 @@ trait UnusedDelimLint {
976994
}
977995
// either function/method call, or something this lint doesn't care about
978996
ref call_or_other => {
979-
let (args_to_check, ctx) = match *call_or_other {
980-
Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
981-
MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
997+
let (args_to_check, ctx, receiver) = match *call_or_other {
998+
Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg, None),
999+
MethodCall(ref call) => {
1000+
(&call.args[..], UnusedDelimsCtx::MethodArg, Some(&call.receiver))
1001+
}
9821002
Closure(ref closure)
9831003
if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
9841004
{
985-
(&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
1005+
(&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody, None)
9861006
}
9871007
// actual catch-all arm
9881008
_ => {
@@ -999,6 +1019,17 @@ trait UnusedDelimLint {
9991019
for arg in args_to_check {
10001020
self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
10011021
}
1022+
if let Some(recv) = receiver {
1023+
self.check_unused_delims_expr(
1024+
cx,
1025+
recv,
1026+
UnusedDelimsCtx::MethodReceiver,
1027+
false,
1028+
None,
1029+
None,
1030+
false,
1031+
);
1032+
}
10021033
return;
10031034
}
10041035
};

‎library/alloc/src/rc.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ impl<T, A: Allocator> Rc<T, A> {
874874
},
875875
alloc,
876876
));
877-
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
877+
let uninit_ptr: NonNull<_> = unsafe { &mut *uninit_raw_ptr }.into();
878878
let init_ptr: NonNull<RcInner<T>> = uninit_ptr.cast();
879879

880880
let weak = Weak { ptr: init_ptr, alloc };
@@ -3242,7 +3242,7 @@ impl<T, A: Allocator> Weak<T, A> {
32423242
}
32433243

32443244
pub(crate) fn is_dangling<T: ?Sized>(ptr: *const T) -> bool {
3245-
(ptr.cast::<()>()).addr() == usize::MAX
3245+
ptr.cast::<()>().addr() == usize::MAX
32463246
}
32473247

32483248
/// Helper type to allow accessing the reference counts without

‎library/alloc/src/slice.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ impl<T> [T] {
534534
unsafe {
535535
ptr::copy_nonoverlapping::<T>(
536536
buf.as_ptr(),
537-
(buf.as_mut_ptr()).add(buf.len()),
537+
buf.as_mut_ptr().add(buf.len()),
538538
buf.len(),
539539
);
540540
// `buf` has capacity of `self.len() * n`.
@@ -555,7 +555,7 @@ impl<T> [T] {
555555
// This is non-overlapping since `2^expn > rem`.
556556
ptr::copy_nonoverlapping::<T>(
557557
buf.as_ptr(),
558-
(buf.as_mut_ptr()).add(buf.len()),
558+
buf.as_mut_ptr().add(buf.len()),
559559
rem_len,
560560
);
561561
// `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`).

‎library/alloc/src/sync.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ impl<T, A: Allocator> Arc<T, A> {
893893
},
894894
alloc,
895895
));
896-
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
896+
let uninit_ptr: NonNull<_> = unsafe { &mut *uninit_raw_ptr }.into();
897897
let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast();
898898

899899
let weak = Weak { ptr: init_ptr, alloc };

‎library/compiler-builtins/libm/src/math/cbrt.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub fn cbrt_round(x: f64, round: Round) -> FpResult<f64> {
194194
let mut cvt4: u64 = y1.to_bits();
195195
cvt4 = (cvt4 + (164 << 15)) & 0xffffffffffff0000u64;
196196

197-
if ((f64::from_bits(cvt4) - y1) - dy).abs() < hf64!("0x1p-60") || (zz).abs() == 1.0 {
197+
if ((f64::from_bits(cvt4) - y1) - dy).abs() < hf64!("0x1p-60") || zz.abs() == 1.0 {
198198
cvt3 = (cvt3 + (1u64 << 15)) & 0xffffffffffff0000u64;
199199
}
200200
}

‎library/core/src/ptr/const_ptr.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<T: PointeeSized> *const T {
2727
@capture { ptr: *const u8 } -> bool:
2828
// This use of `const_raw_ptr_comparison` has been explicitly blessed by t-lang.
2929
if const #[rustc_allow_const_fn_unstable(const_raw_ptr_comparison)] {
30-
match (ptr).guaranteed_eq(null_mut()) {
30+
match ptr.guaranteed_eq(null_mut()) {
3131
Some(res) => res,
3232
// To remain maximally conservative, we stop execution when we don't
3333
// know whether the pointer is null or not.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ run-rustfix
2+
3+
#![deny(unused_parens)]
4+
5+
struct Thing;
6+
impl Thing {
7+
fn method(self) {}
8+
}
9+
10+
fn main() {
11+
// Unnecessary parens - should warn
12+
let x = Thing;
13+
x.method(); //~ ERROR unnecessary parentheses around method receiver
14+
15+
// Necessary parens - should NOT warn
16+
let _ = (1..10).sum::<i32>(); // Range expression
17+
let _ = (1_i32 + 2).abs(); // Binary expression
18+
let _ = (-1_i32).abs(); // Unary expression
19+
let _ = (true as i32).abs(); // Cast expression
20+
let _ = (&42).clone(); // AddrOf expression
21+
let _ = (&mut 42).clone(); // AddrOf mut expression
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ run-rustfix
2+
3+
#![deny(unused_parens)]
4+
5+
struct Thing;
6+
impl Thing {
7+
fn method(self) {}
8+
}
9+
10+
fn main() {
11+
// Unnecessary parens - should warn
12+
let x = Thing;
13+
(x).method(); //~ ERROR unnecessary parentheses around method receiver
14+
15+
// Necessary parens - should NOT warn
16+
let _ = (1..10).sum::<i32>(); // Range expression
17+
let _ = (1_i32 + 2).abs(); // Binary expression
18+
let _ = (-1_i32).abs(); // Unary expression
19+
let _ = (true as i32).abs(); // Cast expression
20+
let _ = (&42).clone(); // AddrOf expression
21+
let _ = (&mut 42).clone(); // AddrOf mut expression
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: unnecessary parentheses around method receiver
2+
--> $DIR/unused-parens-method-receiver-issue-151985.rs:13:5
3+
|
4+
LL | (x).method();
5+
| ^ ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-parens-method-receiver-issue-151985.rs:3:9
9+
|
10+
LL | #![deny(unused_parens)]
11+
| ^^^^^^^^^^^^^
12+
help: remove these parentheses
13+
|
14+
LL - (x).method();
15+
LL + x.method();
16+
|
17+
18+
error: aborting due to 1 previous error
19+

0 commit comments

Comments
 (0)