|
4 | 4 |
|
5 | 5 | use crate::line_span;
|
6 | 6 | use rustc_errors::Applicability;
|
7 |
| -use rustc_hir::{Expr, ExprKind}; |
| 7 | +use rustc_hir::{BinOpKind, Expr, ExprKind}; |
8 | 8 | use rustc_lint::{LateContext, LintContext};
|
9 | 9 | use rustc_span::hygiene;
|
10 | 10 | use rustc_span::{BytePos, Pos, Span, SyntaxContext};
|
@@ -306,6 +306,89 @@ pub fn snippet_with_context(
|
306 | 306 | )
|
307 | 307 | }
|
308 | 308 |
|
| 309 | +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
| 310 | +pub enum TargetPrecedence { |
| 311 | + Closure, |
| 312 | + Assignment, |
| 313 | + Range, |
| 314 | + Or, |
| 315 | + And, |
| 316 | + Eq, |
| 317 | + BitOr, |
| 318 | + BitAnd, |
| 319 | + Shift, |
| 320 | + BitXor, |
| 321 | + Add, |
| 322 | + Mul, |
| 323 | + As, |
| 324 | + Prefix, |
| 325 | + Postfix, |
| 326 | +} |
| 327 | + |
| 328 | +/// Extracts a snippet of the given expression taking into account the `SyntaxContext` the snippet |
| 329 | +/// needs to be taken from. Parenthesis will be added if needed to place the snippet in the target |
| 330 | +/// precedence level. Returns a placeholder (`(..)`) if a snippet can't be extracted (e.g. an |
| 331 | +/// invalid span). |
| 332 | +/// |
| 333 | +/// The `SyntaxContext` of the expression will be walked up to the given target context (usually |
| 334 | +/// from the parent expression) before extracting a snippet. This allows getting the call to a macro |
| 335 | +/// rather than the expression from expanding the macro. e.g. In the expression `&vec![]` taking a |
| 336 | +/// snippet of the chile of the borrow expression will get a snippet of what `vec![]` expands in to. |
| 337 | +/// With the target context set to the same as the borrow expression, this will get a snippet of the |
| 338 | +/// call to the macro. |
| 339 | +/// |
| 340 | +/// The applicability will be modified in two ways: |
| 341 | +/// * If a snippet can't be extracted it will be changed from `MachineApplicable` or |
| 342 | +/// `MaybeIncorrect` to `HasPlaceholders`. |
| 343 | +/// * If the snippet is taken from a macro expansion then it will be changed from |
| 344 | +/// `MachineApplicable` to `MaybeIncorrect`. |
| 345 | +pub fn snippet_expr( |
| 346 | + cx: &LateContext<'_>, |
| 347 | + expr: &Expr<'_>, |
| 348 | + target_precedence: TargetPrecedence, |
| 349 | + ctxt: SyntaxContext, |
| 350 | + app: &mut Applicability, |
| 351 | +) -> String { |
| 352 | + let (snip, is_mac_call) = snippet_with_context(cx, expr.span, ctxt, "(..)", app); |
| 353 | + |
| 354 | + match snip { |
| 355 | + Cow::Borrowed(snip) => snip.to_owned(), |
| 356 | + Cow::Owned(snip) if is_mac_call => snip, |
| 357 | + Cow::Owned(mut snip) => { |
| 358 | + let needs_paren = match expr.kind { |
| 359 | + ExprKind::Binary(op, ..) => match op.node { |
| 360 | + BinOpKind::Add | BinOpKind::Sub => target_precedence > TargetPrecedence::Add, |
| 361 | + BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem => target_precedence > TargetPrecedence::Mul, |
| 362 | + BinOpKind::And => target_precedence > TargetPrecedence::And, |
| 363 | + BinOpKind::Or => target_precedence > TargetPrecedence::Or, |
| 364 | + BinOpKind::BitXor => target_precedence > TargetPrecedence::BitXor, |
| 365 | + BinOpKind::BitAnd => target_precedence > TargetPrecedence::BitAnd, |
| 366 | + BinOpKind::BitOr => target_precedence > TargetPrecedence::BitOr, |
| 367 | + BinOpKind::Shl | BinOpKind::Shr => target_precedence > TargetPrecedence::Shift, |
| 368 | + BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Gt | BinOpKind::Ge => { |
| 369 | + target_precedence > TargetPrecedence::Eq |
| 370 | + }, |
| 371 | + }, |
| 372 | + ExprKind::Unary(..) | ExprKind::AddrOf(..) => target_precedence > TargetPrecedence::Prefix, |
| 373 | + ExprKind::Cast(..) => target_precedence > TargetPrecedence::As, |
| 374 | + ExprKind::Box(..) |
| 375 | + | ExprKind::Closure(..) |
| 376 | + | ExprKind::Break(..) |
| 377 | + | ExprKind::Ret(..) |
| 378 | + | ExprKind::Yield(..) => target_precedence > TargetPrecedence::Closure, |
| 379 | + ExprKind::Assign(..) | ExprKind::AssignOp(..) => target_precedence > TargetPrecedence::Assignment, |
| 380 | + _ => false, |
| 381 | + }; |
| 382 | + |
| 383 | + if needs_paren { |
| 384 | + snip.insert(0, '('); |
| 385 | + snip.push(')'); |
| 386 | + } |
| 387 | + snip |
| 388 | + }, |
| 389 | + } |
| 390 | +} |
| 391 | + |
309 | 392 | /// Walks the span up to the target context, thereby returning the macro call site if the span is
|
310 | 393 | /// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
|
311 | 394 | /// case of the span being in a macro expansion, but the target context is from expanding a macro
|
|
0 commit comments