4
4
use crate :: source:: { snippet, snippet_opt, snippet_with_applicability, snippet_with_context} ;
5
5
use crate :: ty:: expr_sig;
6
6
use crate :: { get_parent_expr_for_hir, higher} ;
7
- use rustc_ast:: ast;
8
7
use rustc_ast:: util:: parser:: AssocOp ;
8
+ use rustc_ast:: { UnOp , ast} ;
9
9
use rustc_data_structures:: fx:: FxHashSet ;
10
10
use rustc_errors:: Applicability ;
11
11
use rustc_hir:: { self as hir, Closure , ExprKind , HirId , MutTy , Node , TyKind } ;
@@ -29,6 +29,11 @@ pub enum Sugg<'a> {
29
29
/// A binary operator expression, including `as`-casts and explicit type
30
30
/// coercion.
31
31
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 > > ) ,
32
37
}
33
38
34
39
/// Literal constant `0`, for convenience.
@@ -40,9 +45,10 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
40
45
41
46
impl Display for Sugg < ' _ > {
42
47
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, inner) => write ! ( f, "{}{}" , op. as_str( ) , inner. clone( ) . maybe_inner_paren( ) ) ,
46
52
}
47
53
}
48
54
}
@@ -100,9 +106,19 @@ impl<'a> Sugg<'a> {
100
106
applicability : & mut Applicability ,
101
107
) -> Self {
102
108
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
+ }
106
122
} else {
107
123
let ( snip, _) = snippet_with_context ( cx, expr. span , ctxt, default, applicability) ;
108
124
Sugg :: NonParen ( snip)
@@ -341,13 +357,34 @@ impl<'a> Sugg<'a> {
341
357
let sugg = binop_to_string ( op, & lhs, & rhs) ;
342
358
Sugg :: NonParen ( format ! ( "({sugg})" ) . into ( ) )
343
359
} ,
360
+ Sugg :: UnOp ( op, inner) => Sugg :: NonParen ( format ! ( "({}{})" , op. as_str( ) , inner. maybe_inner_paren( ) ) . into ( ) ) ,
344
361
}
345
362
}
346
363
347
364
pub fn into_string ( self ) -> String {
348
365
match self {
349
366
Sugg :: NonParen ( p) | Sugg :: MaybeParen ( p) => p. into_owned ( ) ,
350
367
Sugg :: BinOp ( b, l, r) => binop_to_string ( b, & l, & r) ,
368
+ Sugg :: UnOp ( op, inner) => format ! ( "{}{}" , op. as_str( ) , inner. maybe_inner_paren( ) ) ,
369
+ }
370
+ }
371
+
372
+ /// Checks if `self` starts with a unary operator.
373
+ fn starts_with_unary_op ( & self ) -> bool {
374
+ match self {
375
+ Sugg :: UnOp ( ..) => true ,
376
+ Sugg :: BinOp ( ..) => false ,
377
+ Sugg :: MaybeParen ( s) | Sugg :: NonParen ( s) => s. starts_with ( [ '*' , '!' , '-' , '&' ] ) ,
378
+ }
379
+ }
380
+
381
+ /// Call `maybe_paren` on `self` if it doesn't start with a unary operator,
382
+ /// don't touch it otherwise.
383
+ fn maybe_inner_paren ( self ) -> Self {
384
+ if self . starts_with_unary_op ( ) {
385
+ self
386
+ } else {
387
+ self . maybe_paren ( )
351
388
}
352
389
}
353
390
}
@@ -430,10 +467,11 @@ impl Sub for &Sugg<'_> {
430
467
forward_binop_impls_to_ref ! ( impl Add , add for Sugg <' _>, type Output = Sugg <' static >) ;
431
468
forward_binop_impls_to_ref ! ( impl Sub , sub for Sugg <' _>, type Output = Sugg <' static >) ;
432
469
433
- impl Neg for Sugg < ' _ > {
434
- type Output = Sugg < ' static > ;
435
- fn neg ( self ) -> Sugg < ' static > {
436
- match & self {
470
+ impl < ' a > Neg for Sugg < ' a > {
471
+ type Output = Sugg < ' a > ;
472
+ fn neg ( self ) -> Self :: Output {
473
+ match self {
474
+ Self :: UnOp ( UnOp :: Neg , sugg) => * sugg,
437
475
Self :: BinOp ( AssocOp :: Cast , ..) => Sugg :: MaybeParen ( format ! ( "-({self})" ) . into ( ) ) ,
438
476
_ => make_unop ( "-" , self ) ,
439
477
}
@@ -446,19 +484,21 @@ impl<'a> Not for Sugg<'a> {
446
484
use AssocOp :: Binary ;
447
485
use ast:: BinOpKind :: { Eq , Ge , Gt , Le , Lt , Ne } ;
448
486
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 )
487
+ match self {
488
+ Sugg :: BinOp ( op, lhs, rhs) => {
489
+ let to_op = match op {
490
+ Binary ( Eq ) => Binary ( Ne ) ,
491
+ Binary ( Ne ) => Binary ( Eq ) ,
492
+ Binary ( Lt ) => Binary ( Ge ) ,
493
+ Binary ( Ge ) => Binary ( Lt ) ,
494
+ Binary ( Gt ) => Binary ( Le ) ,
495
+ Binary ( Le ) => Binary ( Gt ) ,
496
+ _ => return make_unop ( "!" , Sugg :: BinOp ( op, lhs, rhs) ) ,
497
+ } ;
498
+ Sugg :: BinOp ( to_op, lhs, rhs)
499
+ } ,
500
+ Sugg :: UnOp ( UnOp :: Not , expr) => * expr,
501
+ _ => make_unop ( "!" , self ) ,
462
502
}
463
503
}
464
504
}
@@ -491,20 +531,11 @@ impl<T: Display> Display for ParenHelper<T> {
491
531
/// Builds the string for `<op><expr>` adding parenthesis when necessary.
492
532
///
493
533
/// For convenience, the operator is taken as a string because all unary
494
- /// operators have the same
495
- /// precedence.
534
+ /// operators have the same precedence.
496
535
pub fn make_unop ( op : & str , expr : Sugg < ' _ > ) -> Sugg < ' static > {
497
- // If the `expr` starts with `op` already, do not add wrap it in
536
+ // If the `expr` starts with a unary operator already, do not wrap it in
498
537
// parentheses.
499
- let expr = if let Sugg :: MaybeParen ( ref sugg) = expr
500
- && !has_enclosing_paren ( sugg)
501
- && sugg. starts_with ( op)
502
- {
503
- expr
504
- } else {
505
- expr. maybe_paren ( )
506
- } ;
507
- Sugg :: MaybeParen ( format ! ( "{op}{expr}" ) . into ( ) )
538
+ Sugg :: MaybeParen ( format ! ( "{op}{}" , expr. maybe_inner_paren( ) ) . into ( ) )
508
539
}
509
540
510
541
/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
0 commit comments