about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-08 00:28:03 +0000
committerbors <bors@rust-lang.org>2024-07-08 00:28:03 +0000
commit1de41b18d2861d2baab01e45e44fe5612151d1a6 (patch)
tree9ae23e53e534b8a7d9797f0e242c1772fd45dfa3
parenta4132817fbdd6ca3b67c7b012b9751ea78cdba05 (diff)
parent5b7ffa1a63a4553e2946d70068629a6e7c9b7fdd (diff)
downloadrust-1de41b18d2861d2baab01e45e44fe5612151d1a6.tar.gz
rust-1de41b18d2861d2baab01e45e44fe5612151d1a6.zip
Auto merge of #13068 - Jarcho:init_numbered, r=Alexendoo
Rework `init_numbered_fields`

Two behaviour changes:
* Not linting in macros
* Not linting when side effects might be reordered

changelog: `init_numbered_fields`: Don't suggest reordering side effects.
-rw-r--r--clippy_lints/src/init_numbered_fields.rs87
-rw-r--r--clippy_lints/src/lib.rs1
-rw-r--r--tests/ui/init_numbered_fields.fixed (renamed from tests/ui/numbered_fields.fixed)9
-rw-r--r--tests/ui/init_numbered_fields.rs (renamed from tests/ui/numbered_fields.rs)9
-rw-r--r--tests/ui/init_numbered_fields.stderr (renamed from tests/ui/numbered_fields.stderr)12
5 files changed, 77 insertions, 41 deletions
diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs
index 1c8fd0a27f9..7f183bb601e 100644
--- a/clippy_lints/src/init_numbered_fields.rs
+++ b/clippy_lints/src/init_numbered_fields.rs
@@ -1,13 +1,12 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
+use rustc_span::SyntaxContext;
 use std::borrow::Cow;
-use std::cmp::Reverse;
-use std::collections::BinaryHeap;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,38 +43,56 @@ 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()
-                && !e.span.from_expansion()
-                && fields
-                    .iter()
-                    .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
-                && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
-            {
-                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_context(cx, span, path.span().ctxt(), "..", &mut appl).0)
-                        .intersperse(Cow::Borrowed(", "))
-                        .collect::<String>()
-                );
-                span_lint_and_sugg(
-                    cx,
-                    INIT_NUMBERED_FIELDS,
-                    e.span,
-                    "used a field initializer for a tuple struct",
-                    "try",
-                    snippet,
-                    appl,
-                );
-            }
+        if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
+            // If the first character of any field is a digit it has to be a tuple.
+            && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
+            // Type aliases can't be used as functions.
+            && !matches!(
+                cx.qpath_res(path, e.hir_id),
+                Res::Def(DefKind::TyAlias | DefKind::AssocTy, _)
+            )
+            // This is the only syntax macros can use that works for all struct types.
+            && !e.span.from_expansion()
+            && let mut has_side_effects = false
+            && let Ok(mut expr_spans) = fields
+                .iter()
+                .map(|f| {
+                    has_side_effects |= f.expr.can_have_side_effects();
+                    f.ident.as_str().parse::<usize>().map(|x| (x, f.expr.span))
+                })
+                .collect::<Result<Vec<_>, _>>()
+            // We can only reorder the expressions if there are no side effects.
+            && (!has_side_effects || expr_spans.is_sorted_by_key(|&(idx, _)| idx))
+        {
+            span_lint_and_then(
+                cx,
+                INIT_NUMBERED_FIELDS,
+                e.span,
+                "used a field initializer for a tuple struct",
+                |diag| {
+                    if !has_side_effects {
+                        // We already checked the order if there are side effects.
+                        expr_spans.sort_by_key(|&(idx, _)| idx);
+                    }
+                    let mut app = Applicability::MachineApplicable;
+                    diag.span_suggestion(
+                        e.span,
+                        "use tuple initialization",
+                        format!(
+                            "{}({})",
+                            snippet_with_applicability(cx, path.span(), "..", &mut app),
+                            expr_spans
+                                .into_iter()
+                                .map(
+                                    |(_, span)| snippet_with_context(cx, span, SyntaxContext::root(), "..", &mut app).0
+                                )
+                                .intersperse(Cow::Borrowed(", "))
+                                .collect::<String>()
+                        ),
+                        app,
+                    );
+                },
+            );
         }
     }
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index fbe895fa50e..54e19c1aa7f 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
+#![feature(is_sorted)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/init_numbered_fields.fixed
index 108520eed38..dca4e8da4d2 100644
--- a/tests/ui/numbered_fields.fixed
+++ b/tests/ui/init_numbered_fields.fixed
@@ -39,4 +39,13 @@ fn main() {
     struct TupleStructVec(Vec<usize>);
 
     let _ = TupleStructVec(vec![0, 1, 2, 3]);
+
+    {
+        struct S(i32, i32);
+        let mut iter = [1i32, 1i32].into_iter();
+        let _ = S {
+            1: iter.next().unwrap(),
+            0: iter.next().unwrap(),
+        };
+    }
 }
diff --git a/tests/ui/numbered_fields.rs b/tests/ui/init_numbered_fields.rs
index c718661a682..8cb34705b4f 100644
--- a/tests/ui/numbered_fields.rs
+++ b/tests/ui/init_numbered_fields.rs
@@ -47,4 +47,13 @@ fn main() {
     struct TupleStructVec(Vec<usize>);
 
     let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
+
+    {
+        struct S(i32, i32);
+        let mut iter = [1i32, 1i32].into_iter();
+        let _ = S {
+            1: iter.next().unwrap(),
+            0: iter.next().unwrap(),
+        };
+    }
 }
diff --git a/tests/ui/numbered_fields.stderr b/tests/ui/init_numbered_fields.stderr
index 9d3f59cd376..f176e0c2ff3 100644
--- a/tests/ui/numbered_fields.stderr
+++ b/tests/ui/init_numbered_fields.stderr
@@ -1,5 +1,5 @@
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:17:13
+  --> tests/ui/init_numbered_fields.rs:17:13
    |
 LL |       let _ = TupleStruct {
    |  _____________^
@@ -7,13 +7,13 @@ LL | |         0: 1u32,
 LL | |         1: 42,
 LL | |         2: 23u8,
 LL | |     };
-   | |_____^ help: try: `TupleStruct(1u32, 42, 23u8)`
+   | |_____^ help: use tuple initialization: `TupleStruct(1u32, 42, 23u8)`
    |
    = note: `-D clippy::init-numbered-fields` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::init_numbered_fields)]`
 
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:24:13
+  --> tests/ui/init_numbered_fields.rs:24:13
    |
 LL |       let _ = TupleStruct {
    |  _____________^
@@ -21,13 +21,13 @@ LL | |         0: 1u32,
 LL | |         2: 2u8,
 LL | |         1: 3u32,
 LL | |     };
-   | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)`
+   | |_____^ help: use tuple initialization: `TupleStruct(1u32, 3u32, 2u8)`
 
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:49:13
+  --> tests/ui/init_numbered_fields.rs:49:13
    |
 LL |     let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use tuple initialization: `TupleStructVec(vec![0, 1, 2, 3])`
 
 error: aborting due to 3 previous errors