|
1 |
| -use clippy_utils::diagnostics::span_lint_and_sugg; |
| 1 | +use clippy_utils::diagnostics::span_lint_and_then; |
2 | 2 | use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
3 | 3 | use rustc_errors::Applicability;
|
4 | 4 | use rustc_hir::def::{DefKind, Res};
|
5 | 5 | use rustc_hir::{Expr, ExprKind};
|
6 | 6 | use rustc_lint::{LateContext, LateLintPass};
|
7 | 7 | use rustc_session::declare_lint_pass;
|
| 8 | +use rustc_span::SyntaxContext; |
8 | 9 | use std::borrow::Cow;
|
9 |
| -use std::cmp::Reverse; |
10 |
| -use std::collections::BinaryHeap; |
11 | 10 |
|
12 | 11 | declare_clippy_lint! {
|
13 | 12 | /// ### What it does
|
@@ -44,38 +43,56 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
|
44 | 43 |
|
45 | 44 | impl<'tcx> LateLintPass<'tcx> for NumberedFields {
|
46 | 45 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
47 |
| - if let ExprKind::Struct(path, fields, None) = e.kind { |
48 |
| - if !fields.is_empty() |
49 |
| - && !e.span.from_expansion() |
50 |
| - && fields |
51 |
| - .iter() |
52 |
| - .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) |
53 |
| - && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) |
54 |
| - { |
55 |
| - let expr_spans = fields |
56 |
| - .iter() |
57 |
| - .map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span)) |
58 |
| - .collect::<BinaryHeap<_>>(); |
59 |
| - let mut appl = Applicability::MachineApplicable; |
60 |
| - let snippet = format!( |
61 |
| - "{}({})", |
62 |
| - snippet_with_applicability(cx, path.span(), "..", &mut appl), |
63 |
| - expr_spans |
64 |
| - .into_iter_sorted() |
65 |
| - .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0) |
66 |
| - .intersperse(Cow::Borrowed(", ")) |
67 |
| - .collect::<String>() |
68 |
| - ); |
69 |
| - span_lint_and_sugg( |
70 |
| - cx, |
71 |
| - INIT_NUMBERED_FIELDS, |
72 |
| - e.span, |
73 |
| - "used a field initializer for a tuple struct", |
74 |
| - "try", |
75 |
| - snippet, |
76 |
| - appl, |
77 |
| - ); |
78 |
| - } |
| 46 | + if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind |
| 47 | + // If the first character of any field is a digit it has to be a tuple. |
| 48 | + && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit) |
| 49 | + // Type aliases can't be used as functions. |
| 50 | + && !matches!( |
| 51 | + cx.qpath_res(path, e.hir_id), |
| 52 | + Res::Def(DefKind::TyAlias | DefKind::AssocTy, _) |
| 53 | + ) |
| 54 | + // This is the only syntax macros can use that works for all struct types. |
| 55 | + && !e.span.from_expansion() |
| 56 | + && let mut has_side_effects = false |
| 57 | + && let Ok(mut expr_spans) = fields |
| 58 | + .iter() |
| 59 | + .map(|f| { |
| 60 | + has_side_effects |= f.expr.can_have_side_effects(); |
| 61 | + f.ident.as_str().parse::<usize>().map(|x| (x, f.expr.span)) |
| 62 | + }) |
| 63 | + .collect::<Result<Vec<_>, _>>() |
| 64 | + // We can only reorder the expressions if there are no side effects. |
| 65 | + && (!has_side_effects || expr_spans.is_sorted_by_key(|&(idx, _)| idx)) |
| 66 | + { |
| 67 | + span_lint_and_then( |
| 68 | + cx, |
| 69 | + INIT_NUMBERED_FIELDS, |
| 70 | + e.span, |
| 71 | + "used a field initializer for a tuple struct", |
| 72 | + |diag| { |
| 73 | + if !has_side_effects { |
| 74 | + // We already checked the order if there are side effects. |
| 75 | + expr_spans.sort_by_key(|&(idx, _)| idx); |
| 76 | + } |
| 77 | + let mut app = Applicability::MachineApplicable; |
| 78 | + diag.span_suggestion( |
| 79 | + e.span, |
| 80 | + "use tuple initialization", |
| 81 | + format!( |
| 82 | + "{}({})", |
| 83 | + snippet_with_applicability(cx, path.span(), "..", &mut app), |
| 84 | + expr_spans |
| 85 | + .into_iter() |
| 86 | + .map( |
| 87 | + |(_, span)| snippet_with_context(cx, span, SyntaxContext::root(), "..", &mut app).0 |
| 88 | + ) |
| 89 | + .intersperse(Cow::Borrowed(", ")) |
| 90 | + .collect::<String>() |
| 91 | + ), |
| 92 | + app, |
| 93 | + ); |
| 94 | + }, |
| 95 | + ); |
79 | 96 | }
|
80 | 97 | }
|
81 | 98 | }
|
0 commit comments