]>
Commit | Line | Data |
---|---|---|
a2a8927a | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
31ef2f64 | 2 | use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; |
a2a8927a | 3 | use rustc_errors::Applicability; |
04454e1e | 4 | use rustc_hir::def::{DefKind, Res}; |
a2a8927a XL |
5 | use rustc_hir::{Expr, ExprKind}; |
6 | use rustc_lint::{LateContext, LateLintPass}; | |
4b012472 | 7 | use rustc_session::declare_lint_pass; |
a2a8927a XL |
8 | use std::borrow::Cow; |
9 | use std::cmp::Reverse; | |
10 | use std::collections::BinaryHeap; | |
11 | ||
12 | declare_clippy_lint! { | |
13 | /// ### What it does | |
14 | /// Checks for tuple structs initialized with field syntax. | |
15 | /// It will however not lint if a base initializer is present. | |
16 | /// The lint will also ignore code in macros. | |
17 | /// | |
18 | /// ### Why is this bad? | |
19 | /// This may be confusing to the uninitiated and adds no | |
20 | /// benefit as opposed to tuple initializers | |
21 | /// | |
22 | /// ### Example | |
ed00b5ec | 23 | /// ```no_run |
a2a8927a XL |
24 | /// struct TupleStruct(u8, u16); |
25 | /// | |
26 | /// let _ = TupleStruct { | |
27 | /// 0: 1, | |
28 | /// 1: 23, | |
29 | /// }; | |
30 | /// | |
31 | /// // should be written as | |
32 | /// let base = TupleStruct(1, 23); | |
33 | /// | |
34 | /// // This is OK however | |
35 | /// let _ = TupleStruct { 0: 42, ..base }; | |
36 | /// ``` | |
37 | #[clippy::version = "1.59.0"] | |
38 | pub INIT_NUMBERED_FIELDS, | |
39 | style, | |
40 | "numbered fields in tuple struct initializer" | |
41 | } | |
42 | ||
43 | declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]); | |
44 | ||
45 | impl<'tcx> LateLintPass<'tcx> for NumberedFields { | |
46 | 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() | |
5099ac24 | 49 | && !e.span.from_expansion() |
a2a8927a XL |
50 | && fields |
51 | .iter() | |
52 | .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) | |
781aab86 | 53 | && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) |
a2a8927a XL |
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() | |
31ef2f64 | 65 | .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0) |
a2a8927a XL |
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", | |
add651ee | 74 | "try", |
a2a8927a XL |
75 | snippet, |
76 | appl, | |
77 | ); | |
78 | } | |
79 | } | |
80 | } | |
81 | } |