2 use rustc::lint::{EarlyContext, LateContext};
6 use syntax::util::parser::AssocOp;
7 use utils::{higher, snippet};
9 /// A helper type to build suggestion correctly handling parenthesis.
11 /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
12 NonParen(Cow<'a, str>),
13 /// An expression that does not fit in other variants.
14 MaybeParen(Cow<'a, str>),
15 /// A binary operator expression, including `as`-casts and explicit type coercion.
16 BinOp(AssocOp, Cow<'a, str>),
19 impl<'a> std::fmt::Display for Sugg<'a> {
20 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
22 Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => {
30 pub fn hir(cx: &LateContext, expr: &'a hir::Expr, default: &'a str) -> Sugg<'a> {
31 let snippet = snippet(cx, expr.span, default);
36 hir::ExprClosure(..) |
39 hir::ExprMatch(..) => Sugg::MaybeParen(snippet),
46 hir::ExprInlineAsm(..) |
49 hir::ExprMethodCall(..) |
55 hir::ExprTupField(..) |
57 hir::ExprWhile(..) => Sugg::NonParen(snippet),
58 hir::ExprAssign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
59 hir::ExprAssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
60 hir::ExprBinary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
61 hir::ExprCast(..) => Sugg::BinOp(AssocOp::As, snippet),
62 hir::ExprType(..) => Sugg::BinOp(AssocOp::Colon, snippet),
66 pub fn ast(cx: &EarlyContext, expr: &'a ast::Expr, default: &'a str) -> Sugg<'a> {
67 use syntax::ast::RangeLimits;
69 let snippet = snippet(cx, expr.span, default);
72 ast::ExprKind::AddrOf(..) |
73 ast::ExprKind::Box(..) |
74 ast::ExprKind::Closure(..) |
75 ast::ExprKind::If(..) |
76 ast::ExprKind::IfLet(..) |
77 ast::ExprKind::InPlace(..) |
78 ast::ExprKind::Unary(..) |
79 ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
80 ast::ExprKind::Block(..) |
81 ast::ExprKind::Break(..) |
82 ast::ExprKind::Call(..) |
83 ast::ExprKind::Continue(..) |
84 ast::ExprKind::Field(..) |
85 ast::ExprKind::ForLoop(..) |
86 ast::ExprKind::Index(..) |
87 ast::ExprKind::InlineAsm(..) |
88 ast::ExprKind::Lit(..) |
89 ast::ExprKind::Loop(..) |
90 ast::ExprKind::Mac(..) |
91 ast::ExprKind::MethodCall(..) |
92 ast::ExprKind::Paren(..) |
93 ast::ExprKind::Path(..) |
94 ast::ExprKind::Repeat(..) |
95 ast::ExprKind::Ret(..) |
96 ast::ExprKind::Struct(..) |
97 ast::ExprKind::Try(..) |
98 ast::ExprKind::Tup(..) |
99 ast::ExprKind::TupField(..) |
100 ast::ExprKind::Vec(..) |
101 ast::ExprKind::While(..) |
102 ast::ExprKind::WhileLet(..) => Sugg::NonParen(snippet),
103 ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
104 ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotDot, snippet),
105 ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
106 ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
107 ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
108 ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
109 ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
113 /// Convenience method to create the `lhs && rhs` suggestion.
114 pub fn and(&self, rhs: &Self) -> Sugg<'static> {
115 make_binop(ast::BinOpKind::And, self, rhs)
118 /// Convenience method to create the `&<expr>` suggestion.
119 pub fn addr(&self) -> Sugg<'static> {
124 impl<'a, 'b> std::ops::Sub<&'b Sugg<'b>> for &'a Sugg<'a> {
125 type Output = Sugg<'static>;
126 fn sub(self, rhs: &'b Sugg<'b>) -> Sugg<'static> {
127 make_binop(ast::BinOpKind::Sub, self, rhs)
131 impl<'a> std::ops::Not for &'a Sugg<'a> {
132 type Output = Sugg<'static>;
133 fn not(self) -> Sugg<'static> {
138 struct ParenHelper<T> {
143 impl<T> ParenHelper<T> {
144 fn new(paren: bool, wrapped: T) -> Self {
152 impl<T: std::fmt::Display> std::fmt::Display for ParenHelper<T> {
153 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
155 write!(f, "({})", self.wrapped)
162 /// Build the string for `<op> <expr>` adding parenthesis when necessary.
164 /// For convenience, the operator is taken as a string because all unary operators have the same
166 pub fn make_unop(op: &str, expr: &Sugg) -> Sugg<'static> {
167 let needs_paren = !matches!(*expr, Sugg::NonParen(..));
168 Sugg::MaybeParen(format!("{}{}", op, ParenHelper::new(needs_paren, expr)).into())
171 /// Build the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
173 /// Precedence of shift operator relative to other arithmetic operation is often confusing so
174 /// parenthesis will always be added for a mix of these.
175 pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
176 fn is_shift(op: &AssocOp) -> bool {
177 matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
180 fn is_arith(op: &AssocOp) -> bool {
181 matches!(*op, AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus)
184 fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
185 other.precedence() < op.precedence() ||
186 (other.precedence() == op.precedence() &&
187 ((op != other && associativity(op) != dir) ||
188 (op == other && associativity(op) != Associativity::Both))) ||
189 is_shift(op) && is_arith(other) ||
190 is_shift(other) && is_arith(op)
193 let aop = AssocOp::from_ast_binop(op);
195 let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
196 needs_paren(&aop, lop, Associativity::Left)
201 let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
202 needs_paren(&aop, rop, Associativity::Right)
209 ParenHelper::new(lhs_paren, lhs),
211 ParenHelper::new(rhs_paren, rhs)).into())
214 #[derive(PartialEq, Eq)]
222 /// Return the associativity/fixity of an operator. The difference with `AssocOp::fixity` is that
223 /// an operator can be both left and right associative (such as `+`:
224 /// `a + b + c == (a + b) + c == a + (b + c)`.
226 /// Chained `as` and explicit `:` type coercion never need inner parenthesis so they are considered
228 fn associativity(op: &AssocOp) -> Associativity {
229 use syntax::util::parser::AssocOp::*;
232 Inplace | Assign | AssignOp(_) => Associativity::Right,
233 Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply |
234 As | Colon => Associativity::Both,
235 Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft |
236 ShiftRight | Subtract => Associativity::Left,
237 DotDot | DotDotDot => Associativity::None
241 /// Convert a `hir::BinOp` to the corresponding assigning binary operator.
242 fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
243 use rustc::hir::BinOp_::*;
244 use syntax::parse::token::BinOpToken::*;
246 AssocOp::AssignOp(match op.node {
257 BiAnd | BiEq | BiGe | BiGt | BiLe | BiLt | BiNe | BiOr => panic!("This operator does not exist"),
261 /// Convert an `ast::BinOp` to the corresponding assigning binary operator.
262 fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
263 use syntax::ast::BinOpKind::*;
264 use syntax::parse::token::BinOpToken;
266 AssocOp::AssignOp(match op.node {
267 Add => BinOpToken::Plus,
268 BitAnd => BinOpToken::And,
269 BitOr => BinOpToken::Or,
270 BitXor => BinOpToken::Caret,
271 Div => BinOpToken::Slash,
272 Mul => BinOpToken::Star,
273 Rem => BinOpToken::Percent,
274 Shl => BinOpToken::Shl,
275 Shr => BinOpToken::Shr,
276 Sub => BinOpToken::Minus,
277 And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),