]>
Commit | Line | Data |
---|---|---|
a2a8927a | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
a2a8927a XL |
2 | use clippy_utils::source::snippet_with_applicability; |
3 | use rustc_errors::Applicability; | |
4 | use rustc_hir::{Expr, ExprKind}; | |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | use std::borrow::Cow; | |
8 | use std::cmp::Reverse; | |
9 | use std::collections::BinaryHeap; | |
10 | ||
11 | declare_clippy_lint! { | |
12 | /// ### What it does | |
13 | /// Checks for tuple structs initialized with field syntax. | |
14 | /// It will however not lint if a base initializer is present. | |
15 | /// The lint will also ignore code in macros. | |
16 | /// | |
17 | /// ### Why is this bad? | |
18 | /// This may be confusing to the uninitiated and adds no | |
19 | /// benefit as opposed to tuple initializers | |
20 | /// | |
21 | /// ### Example | |
22 | /// ```rust | |
23 | /// struct TupleStruct(u8, u16); | |
24 | /// | |
25 | /// let _ = TupleStruct { | |
26 | /// 0: 1, | |
27 | /// 1: 23, | |
28 | /// }; | |
29 | /// | |
30 | /// // should be written as | |
31 | /// let base = TupleStruct(1, 23); | |
32 | /// | |
33 | /// // This is OK however | |
34 | /// let _ = TupleStruct { 0: 42, ..base }; | |
35 | /// ``` | |
36 | #[clippy::version = "1.59.0"] | |
37 | pub INIT_NUMBERED_FIELDS, | |
38 | style, | |
39 | "numbered fields in tuple struct initializer" | |
40 | } | |
41 | ||
42 | declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]); | |
43 | ||
44 | impl<'tcx> LateLintPass<'tcx> for NumberedFields { | |
45 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
46 | if let ExprKind::Struct(path, fields, None) = e.kind { | |
47 | if !fields.is_empty() | |
5099ac24 | 48 | && !e.span.from_expansion() |
a2a8927a XL |
49 | && fields |
50 | .iter() | |
51 | .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) | |
52 | { | |
53 | let expr_spans = fields | |
54 | .iter() | |
55 | .map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span)) | |
56 | .collect::<BinaryHeap<_>>(); | |
57 | let mut appl = Applicability::MachineApplicable; | |
58 | let snippet = format!( | |
59 | "{}({})", | |
60 | snippet_with_applicability(cx, path.span(), "..", &mut appl), | |
61 | expr_spans | |
62 | .into_iter_sorted() | |
63 | .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl)) | |
64 | .intersperse(Cow::Borrowed(", ")) | |
65 | .collect::<String>() | |
66 | ); | |
67 | span_lint_and_sugg( | |
68 | cx, | |
69 | INIT_NUMBERED_FIELDS, | |
70 | e.span, | |
71 | "used a field initializer for a tuple struct", | |
72 | "try this instead", | |
73 | snippet, | |
74 | appl, | |
75 | ); | |
76 | } | |
77 | } | |
78 | } | |
79 | } |