Skip to content

Commit 59cd37c

Browse files
committed
Optimize some usages of !! and -- in suggestions
When an expression is made of a `!` or `-` unary operator which does not change the type of the expression, use a new variant in `Sugg` to denote it. This allows replacing an extra application of the same operator by the removal of the original operator instead. Some suggestions will now be shown as `x` instead of `!!x`. Right now, no suggestion seems to produce `--x`.
1 parent c7dd98c commit 59cd37c

File tree

4 files changed

+91
-30
lines changed

4 files changed

+91
-30
lines changed

clippy_lints/src/floating_point_arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
148148
.into();
149149

150150
suggestion = match suggestion {
151-
Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
151+
Sugg::MaybeParen(_) | Sugg::UnOp(UnOp::Neg, _) => Sugg::MaybeParen(op),
152152
_ => Sugg::NonParen(op),
153153
};
154154
}

clippy_utils/src/sugg.rs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context};
55
use crate::ty::expr_sig;
66
use crate::{get_parent_expr_for_hir, higher};
7-
use rustc_ast::ast;
87
use rustc_ast::util::parser::AssocOp;
8+
use rustc_ast::{UnOp, ast};
99
use rustc_data_structures::fx::FxHashSet;
1010
use rustc_errors::Applicability;
1111
use rustc_hir::{self as hir, Closure, ExprKind, HirId, MutTy, Node, TyKind};
@@ -29,6 +29,11 @@ pub enum Sugg<'a> {
2929
/// A binary operator expression, including `as`-casts and explicit type
3030
/// coercion.
3131
BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>),
32+
/// A unary operator expression. This is used to sometimes represent `!`
33+
/// or `-`, but only if the type with and without the operator is kept identical.
34+
/// It means that doubling the operator can be used to remove it instead, in
35+
/// order to provide better suggestions.
36+
UnOp(UnOp, Box<Sugg<'a>>),
3237
}
3338

3439
/// Literal constant `0`, for convenience.
@@ -40,9 +45,10 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
4045

4146
impl Display for Sugg<'_> {
4247
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
43-
match *self {
44-
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f),
45-
Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f),
48+
match self {
49+
Sugg::NonParen(s) | Sugg::MaybeParen(s) => s.fmt(f),
50+
Sugg::BinOp(op, lhs, rhs) => binop_to_string(*op, lhs, rhs).fmt(f),
51+
Sugg::UnOp(op, expr) => write!(f, "{}{}", op.as_str(), expr.clone().maybe_inner_paren(*op)),
4652
}
4753
}
4854
}
@@ -100,9 +106,19 @@ impl<'a> Sugg<'a> {
100106
applicability: &mut Applicability,
101107
) -> Self {
102108
if expr.span.ctxt() == ctxt {
103-
Self::hir_from_snippet(expr, |span| {
104-
snippet_with_context(cx, span, ctxt, default, applicability).0
105-
})
109+
if let ExprKind::Unary(op, inner) = expr.kind
110+
&& matches!(op, UnOp::Neg | UnOp::Not)
111+
&& cx.typeck_results().expr_ty(expr) == cx.typeck_results().expr_ty(inner)
112+
{
113+
Sugg::UnOp(
114+
op,
115+
Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)),
116+
)
117+
} else {
118+
Self::hir_from_snippet(expr, |span| {
119+
snippet_with_context(cx, span, ctxt, default, applicability).0
120+
})
121+
}
106122
} else {
107123
let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability);
108124
Sugg::NonParen(snip)
@@ -281,8 +297,9 @@ impl<'a> Sugg<'a> {
281297
}
282298

283299
/// Convenience method to create the `*<expr>` suggestion.
284-
pub fn deref(self) -> Sugg<'static> {
285-
make_unop("*", self)
300+
#[must_use]
301+
pub fn deref(self) -> Self {
302+
Sugg::UnOp(UnOp::Deref, Box::new(self))
286303
}
287304

288305
/// Convenience method to create the `&*<expr>` suggestion. Currently this
@@ -341,13 +358,24 @@ impl<'a> Sugg<'a> {
341358
let sugg = binop_to_string(op, &lhs, &rhs);
342359
Sugg::NonParen(format!("({sugg})").into())
343360
},
361+
Sugg::UnOp(op, expr) => Sugg::NonParen(format!("({}{})", op.as_str(), expr.maybe_inner_paren(op)).into()),
344362
}
345363
}
346364

347365
pub fn into_string(self) -> String {
348366
match self {
349367
Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(),
350368
Sugg::BinOp(b, l, r) => binop_to_string(b, &l, &r),
369+
Sugg::UnOp(op, expr) => format!("{}{}", op.as_str(), expr.maybe_inner_paren(op)),
370+
}
371+
}
372+
373+
/// Call `maybe_paren` on `self` if it doesn't start with the same unary operator as `op`,
374+
/// don't touch it otherwise.
375+
fn maybe_inner_paren(self, op: UnOp) -> Self {
376+
match &self {
377+
Sugg::UnOp(o, _) if *o == op => self,
378+
_ => self.maybe_paren(),
351379
}
352380
}
353381
}
@@ -430,10 +458,11 @@ impl Sub for &Sugg<'_> {
430458
forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
431459
forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
432460

433-
impl Neg for Sugg<'_> {
434-
type Output = Sugg<'static>;
435-
fn neg(self) -> Sugg<'static> {
436-
match &self {
461+
impl<'a> Neg for Sugg<'a> {
462+
type Output = Sugg<'a>;
463+
fn neg(self) -> Self::Output {
464+
match self {
465+
Self::UnOp(UnOp::Neg, sugg) => *sugg,
437466
Self::BinOp(AssocOp::Cast, ..) => Sugg::MaybeParen(format!("-({self})").into()),
438467
_ => make_unop("-", self),
439468
}
@@ -446,19 +475,21 @@ impl<'a> Not for Sugg<'a> {
446475
use AssocOp::Binary;
447476
use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne};
448477

449-
if let Sugg::BinOp(op, lhs, rhs) = self {
450-
let to_op = match op {
451-
Binary(Eq) => Binary(Ne),
452-
Binary(Ne) => Binary(Eq),
453-
Binary(Lt) => Binary(Ge),
454-
Binary(Ge) => Binary(Lt),
455-
Binary(Gt) => Binary(Le),
456-
Binary(Le) => Binary(Gt),
457-
_ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
458-
};
459-
Sugg::BinOp(to_op, lhs, rhs)
460-
} else {
461-
make_unop("!", self)
478+
match self {
479+
Sugg::BinOp(op, lhs, rhs) => {
480+
let to_op = match op {
481+
Binary(Eq) => Binary(Ne),
482+
Binary(Ne) => Binary(Eq),
483+
Binary(Lt) => Binary(Ge),
484+
Binary(Ge) => Binary(Lt),
485+
Binary(Gt) => Binary(Le),
486+
Binary(Le) => Binary(Gt),
487+
_ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
488+
};
489+
Sugg::BinOp(to_op, lhs, rhs)
490+
},
491+
Sugg::UnOp(UnOp::Not, expr) => *expr,
492+
_ => make_unop("!", self),
462493
}
463494
}
464495
}

tests/ui/nonminimal_bool.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,21 @@ mod issue14404 {
236236
}
237237
}
238238
}
239+
240+
fn dont_simplify_double_not_if_types_differ() {
241+
struct S;
242+
243+
impl std::ops::Not for S {
244+
type Output = bool;
245+
fn not(self) -> bool {
246+
true
247+
}
248+
}
249+
250+
// The lint must propose `if !!S`, not `if S`.
251+
// FIXME: `bool_comparison` will propose to use `S == true`
252+
// which is invalid.
253+
if !S != true {}
254+
//~^ nonminimal_bool
255+
//~| bool_comparison
256+
}

tests/ui/nonminimal_bool.stderr

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ error: inequality checks against true can be replaced by a negation
179179
--> tests/ui/nonminimal_bool.rs:186:8
180180
|
181181
LL | if !b != true {}
182-
| ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
182+
| ^^^^^^^^^^ help: try simplifying it as shown: `b`
183183

184184
error: this boolean expression can be simplified
185185
--> tests/ui/nonminimal_bool.rs:189:8
@@ -209,7 +209,7 @@ error: inequality checks against true can be replaced by a negation
209209
--> tests/ui/nonminimal_bool.rs:193:8
210210
|
211211
LL | if true != !b {}
212-
| ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
212+
| ^^^^^^^^^^ help: try simplifying it as shown: `b`
213213

214214
error: this boolean expression can be simplified
215215
--> tests/ui/nonminimal_bool.rs:196:8
@@ -235,5 +235,17 @@ error: this boolean expression can be simplified
235235
LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) {
236236
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)`
237237

238-
error: aborting due to 31 previous errors
238+
error: this boolean expression can be simplified
239+
--> tests/ui/nonminimal_bool.rs:253:8
240+
|
241+
LL | if !S != true {}
242+
| ^^^^^^^^^^ help: try: `S == true`
243+
244+
error: inequality checks against true can be replaced by a negation
245+
--> tests/ui/nonminimal_bool.rs:253:8
246+
|
247+
LL | if !S != true {}
248+
| ^^^^^^^^^^ help: try simplifying it as shown: `!!S`
249+
250+
error: aborting due to 33 previous errors
239251

0 commit comments

Comments
 (0)