about summary refs log tree commit diff
path: root/clippy_lints/src/init_numbered_fields.rs
diff options
context:
space:
mode:
authorAndre Bogus <bogusandre@gmail.com>2021-12-25 16:52:58 +0100
committerAndre Bogus <bogusandre@gmail.com>2021-12-26 16:19:22 +0100
commit3ebd2bc2e4fac8f56e1399872ea449594a280daa (patch)
tree4ebe86e999fc72653647f338f7f54925104017a0 /clippy_lints/src/init_numbered_fields.rs
parent547efad9454123b5d37e627eb8643666aa3ddf3b (diff)
downloadrust-3ebd2bc2e4fac8f56e1399872ea449594a280daa.tar.gz
rust-3ebd2bc2e4fac8f56e1399872ea449594a280daa.zip
new lint: `init-numbered-fields`
Diffstat (limited to 'clippy_lints/src/init_numbered_fields.rs')
-rw-r--r--clippy_lints/src/init_numbered_fields.rs80
1 files changed, 80 insertions, 0 deletions
diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs
new file mode 100644
index 00000000000..5fe6725b581
--- /dev/null
+++ b/clippy_lints/src/init_numbered_fields.rs
@@ -0,0 +1,80 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use std::borrow::Cow;
+use std::cmp::Reverse;
+use std::collections::BinaryHeap;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for tuple structs initialized with field syntax.
+    /// It will however not lint if a base initializer is present.
+    /// The lint will also ignore code in macros.
+    ///
+    /// ### Why is this bad?
+    /// This may be confusing to the uninitiated and adds no
+    /// benefit as opposed to tuple initializers
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct TupleStruct(u8, u16);
+    ///
+    /// let _ = TupleStruct {
+    ///     0: 1,
+    ///     1: 23,
+    /// };
+    ///
+    /// // should be written as
+    /// let base = TupleStruct(1, 23);
+    ///
+    /// // This is OK however
+    /// let _ = TupleStruct { 0: 42, ..base };
+    /// ```
+    #[clippy::version = "1.59.0"]
+    pub INIT_NUMBERED_FIELDS,
+    style,
+    "numbered fields in tuple struct initializer"
+}
+
+declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
+
+impl<'tcx> LateLintPass<'tcx> for NumberedFields {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Struct(path, fields, None) = e.kind {
+            if !fields.is_empty()
+                && !in_macro(e.span)
+                && fields
+                    .iter()
+                    .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
+            {
+                let expr_spans = fields
+                    .iter()
+                    .map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
+                    .collect::<BinaryHeap<_>>();
+                let mut appl = Applicability::MachineApplicable;
+                let snippet = format!(
+                    "{}({})",
+                    snippet_with_applicability(cx, path.span(), "..", &mut appl),
+                    expr_spans
+                        .into_iter_sorted()
+                        .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
+                        .intersperse(Cow::Borrowed(", "))
+                        .collect::<String>()
+                );
+                span_lint_and_sugg(
+                    cx,
+                    INIT_NUMBERED_FIELDS,
+                    e.span,
+                    "used a field initializer for a tuple struct",
+                    "try this instead",
+                    snippet,
+                    appl,
+                );
+            }
+        }
+    }
+}