]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/init_numbered_fields.rs
Auto merge of #8221 - Jarcho:while_let_on_iterator_8113, r=llogiq
[rust.git] / clippy_lints / src / init_numbered_fields.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
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()
48                 && !e.span.from_expansion()
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 }