about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-04 22:17:50 +0000
committerbors <bors@rust-lang.org>2024-04-04 22:17:50 +0000
commit8253040b3f991bd87ad3b1d5ce175bd3ebe599f4 (patch)
treef269a55a9ca6d2a89a9efc27b352404be118bd40
parenta73e751d1939fdf5b14df28f53704616e63963f3 (diff)
parent9f5d31ef862489ea87bf3e52812d05b1a700de47 (diff)
downloadrust-8253040b3f991bd87ad3b1d5ce175bd3ebe599f4.tar.gz
rust-8253040b3f991bd87ad3b1d5ce175bd3ebe599f4.zip
Auto merge of #12591 - y21:issue12585, r=Jarcho
type certainty: clear `DefId` when an expression's type changes to non-adt

Fixes #12585

The root cause of the ICE in the linked issue was in the expression `one.x`, in the array literal.

The type of `one` is the `One` struct: an adt with a DefId, so its certainty is `Certain(def_id_of_one)`. However, the field access `.x` can then change the type (to `i32` here) and that should update that `DefId` accordingly. It does do that correctly when `one.x` would be another adt with a DefId:

https://github.com/rust-lang/rust-clippy/blob/97ba291d5aa026353ad93e48cf00e06f08c73830/clippy_utils/src/ty/type_certainty/mod.rs#L90-L91

but when it *isn't* an adt and there is no def id (which is the case in the linked issue: `one.x` is an i32), it keeps the `DefId` of `One`, even though that's the wrong type (which would then lead to a contradiction later when joining `Certainty`s):
https://github.com/rust-lang/rust-clippy/blob/97ba291d5aa026353ad93e48cf00e06f08c73830/clippy_utils/src/ty/type_certainty/mod.rs#L92-L93

In particular, in the linked issue, `from_array([one.x, two.x])` would try to join the `Certainty` of the two array elements, which *should* have been `[Certain(None), Certain(None)]`, because `i32`s have no `DefId`, but instead it was `[Certain(One), Certain(Two)]`, because the DefId wasn't cleared from when it was visiting `one` and `two`. This is the "contradiction" that could be seen in the ICE message

... so this changes it to clear the `DefId` when it isn't an adt.

cc `@smoelius` you implemented this initially in #11135, does this change make sense to you?

changelog: none
-rw-r--r--clippy_utils/src/ty/type_certainty/mod.rs2
-rw-r--r--tests/ui/crashes/ice-12585.rs26
2 files changed, 27 insertions, 1 deletions
diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs
index 762830ffd78..2241494b484 100644
--- a/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/clippy_utils/src/ty/type_certainty/mod.rs
@@ -90,7 +90,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
     if let Some(def_id) = adt_def_id(expr_ty) {
         certainty.with_def_id(def_id)
     } else {
-        certainty
+        certainty.clear_def_id()
     }
 }
 
diff --git a/tests/ui/crashes/ice-12585.rs b/tests/ui/crashes/ice-12585.rs
new file mode 100644
index 00000000000..7928115c0a9
--- /dev/null
+++ b/tests/ui/crashes/ice-12585.rs
@@ -0,0 +1,26 @@
+#![allow(clippy::unit_arg)]
+
+struct One {
+    x: i32,
+}
+struct Two {
+    x: i32,
+}
+
+struct Product {}
+
+impl Product {
+    pub fn a_method(self, _: ()) {}
+}
+
+fn from_array(_: [i32; 2]) -> Product {
+    todo!()
+}
+
+pub fn main() {
+    let one = One { x: 1 };
+    let two = Two { x: 2 };
+
+    let product = from_array([one.x, two.x]);
+    product.a_method(<()>::default());
+}