]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/init_numbered_fields.rs
Auto merge of #9148 - arieluy:then_some_unwrap_or, r=Jarcho
[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::def::{DefKind, Res};
5 use rustc_hir::{Expr, ExprKind};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
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
23     /// ```rust
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()
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_applicability(cx, span, "..", &mut appl))
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 this instead",
75                     snippet,
76                     appl,
77                 );
78             }
79         }
80     }
81 }