44use crate :: source:: { snippet, snippet_opt, snippet_with_applicability, snippet_with_context} ;
55use crate :: ty:: expr_sig;
66use crate :: { get_parent_expr_for_hir, higher} ;
7- use rustc_ast:: ast;
87use rustc_ast:: util:: parser:: AssocOp ;
8+ use rustc_ast:: { UnOp , ast} ;
99use rustc_data_structures:: fx:: FxHashSet ;
1010use rustc_errors:: Applicability ;
1111use 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
4146impl 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, inner) => write ! ( f, "{}{}" , op. as_str( ) , inner. clone( ) . maybe_inner_paren( ) ) ,
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)
@@ -341,13 +357,34 @@ impl<'a> Sugg<'a> {
341357 let sugg = binop_to_string ( op, & lhs, & rhs) ;
342358 Sugg :: NonParen ( format ! ( "({sugg})" ) . into ( ) )
343359 } ,
360+ Sugg :: UnOp ( op, inner) => Sugg :: NonParen ( format ! ( "({}{})" , op. as_str( ) , inner. maybe_inner_paren( ) ) . into ( ) ) ,
344361 }
345362 }
346363
347364 pub fn into_string ( self ) -> String {
348365 match self {
349366 Sugg :: NonParen ( p) | Sugg :: MaybeParen ( p) => p. into_owned ( ) ,
350367 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 ( )
351388 }
352389 }
353390}
@@ -430,10 +467,11 @@ impl Sub for &Sugg<'_> {
430467forward_binop_impls_to_ref ! ( impl Add , add for Sugg <' _>, type Output = Sugg <' static >) ;
431468forward_binop_impls_to_ref ! ( impl Sub , sub for Sugg <' _>, type Output = Sugg <' static >) ;
432469
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,
437475 Self :: BinOp ( AssocOp :: Cast , ..) => Sugg :: MaybeParen ( format ! ( "-({self})" ) . into ( ) ) ,
438476 _ => make_unop ( "-" , self ) ,
439477 }
@@ -446,19 +484,21 @@ impl<'a> Not for Sugg<'a> {
446484 use AssocOp :: Binary ;
447485 use ast:: BinOpKind :: { Eq , Ge , Gt , Le , Lt , Ne } ;
448486
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 ) ,
462502 }
463503 }
464504}
@@ -491,20 +531,11 @@ impl<T: Display> Display for ParenHelper<T> {
491531/// Builds the string for `<op><expr>` adding parenthesis when necessary.
492532///
493533/// 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.
496535pub 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
498537 // 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 ( ) )
508539}
509540
510541/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
0 commit comments