add structured suggestions

This commit is contained in:
Ada Alakbarova 2025-08-22 18:18:03 +02:00
parent 6a6b4a3ba8
commit 82b2eb13f8
No known key found for this signature in database
4 changed files with 126 additions and 32 deletions

View file

@ -1,5 +1,7 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{HasSession, snippet_with_applicability};
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
@ -24,7 +26,7 @@ declare_clippy_lint! {
/// Use instead:
/// ```no_run
/// fn simple_no_parens() -> i32 {
/// 0
/// (0)
/// }
///
/// # fn foo(bar: usize) {}
@ -40,23 +42,51 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]);
impl EarlyLintPass for DoubleParens {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
let span = match &expr.kind {
ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span,
ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. })
if let [args] = &**args
&& let ExprKind::Paren(_) = args.kind =>
{
args.span
if expr.span.from_expansion() {
return;
}
match &expr.kind {
// ((..))
// ^^^^^^ expr
// ^^^^ inner
ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => {
// suggest removing the outer parens
let mut applicability = Applicability::MachineApplicable;
let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability);
span_lint_and_sugg(
cx,
DOUBLE_PARENS,
expr.span,
"unnecessary parentheses",
"remove them",
sugg.to_string(),
applicability,
);
},
_ => return,
};
if !expr.span.from_expansion() {
span_lint(
cx,
DOUBLE_PARENS,
span,
"consider removing unnecessary double parentheses",
);
// func((n))
// ^^^^^^^^^ expr
// ^^^ arg
// ^ inner
ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. })
if let [arg] = &**args
&& let ExprKind::Paren(inner) = &arg.kind =>
{
// suggest removing the inner parens
let mut applicability = Applicability::MachineApplicable;
let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability);
span_lint_and_sugg(
cx,
DOUBLE_PARENS,
arg.span,
"unnecessary parentheses",
"remove them",
sugg.to_string(),
applicability,
);
},
_ => {},
}
}
}

View file

@ -0,0 +1,63 @@
#![warn(clippy::double_parens)]
#![expect(clippy::eq_op)]
#![feature(custom_inner_attributes)]
#![rustfmt::skip]
fn dummy_fn<T>(_: T) {}
struct DummyStruct;
impl DummyStruct {
fn dummy_method<T>(self, _: T) {}
}
fn simple_double_parens() -> i32 {
(0)
//~^ double_parens
}
fn fn_double_parens() {
dummy_fn(0);
//~^ double_parens
}
fn method_double_parens(x: DummyStruct) {
x.dummy_method(0);
//~^ double_parens
}
fn tuple_double_parens() -> (i32, i32) {
(1, 2)
//~^ double_parens
}
#[allow(clippy::unused_unit)]
fn unit_double_parens() {
()
//~^ double_parens
}
fn fn_tuple_ok() {
dummy_fn((1, 2));
}
fn method_tuple_ok(x: DummyStruct) {
x.dummy_method((1, 2));
}
fn fn_unit_ok() {
dummy_fn(());
}
fn method_unit_ok(x: DummyStruct) {
x.dummy_method(());
}
// Issue #3206
fn inside_macro() {
assert_eq!((1, 2), (1, 2), "Error");
assert_eq!((1, 2), (1, 2), "Error");
//~^ double_parens
}
fn main() {}

View file

@ -31,6 +31,7 @@ fn tuple_double_parens() -> (i32, i32) {
//~^ double_parens
}
#[allow(clippy::unused_unit)]
fn unit_double_parens() {
(())
//~^ double_parens

View file

@ -1,41 +1,41 @@
error: consider removing unnecessary double parentheses
error: unnecessary parentheses
--> tests/ui/double_parens.rs:15:5
|
LL | ((0))
| ^^^^^
| ^^^^^ help: remove them: `(0)`
|
= note: `-D clippy::double-parens` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::double_parens)]`
error: consider removing unnecessary double parentheses
error: unnecessary parentheses
--> tests/ui/double_parens.rs:20:14
|
LL | dummy_fn((0));
| ^^^
| ^^^ help: remove them: `0`
error: consider removing unnecessary double parentheses
error: unnecessary parentheses
--> tests/ui/double_parens.rs:25:20
|
LL | x.dummy_method((0));
| ^^^
| ^^^ help: remove them: `0`
error: consider removing unnecessary double parentheses
error: unnecessary parentheses
--> tests/ui/double_parens.rs:30:5
|
LL | ((1, 2))
| ^^^^^^^^
| ^^^^^^^^ help: remove them: `(1, 2)`
error: consider removing unnecessary double parentheses
--> tests/ui/double_parens.rs:35:5
error: unnecessary parentheses
--> tests/ui/double_parens.rs:36:5
|
LL | (())
| ^^^^
| ^^^^ help: remove them: `()`
error: consider removing unnecessary double parentheses
--> tests/ui/double_parens.rs:58:16
error: unnecessary parentheses
--> tests/ui/double_parens.rs:59:16
|
LL | assert_eq!(((1, 2)), (1, 2), "Error");
| ^^^^^^^^
| ^^^^^^^^ help: remove them: `(1, 2)`
error: aborting due to 6 previous errors