Repository: rust-analyzer/rust-analyzer
Pull request #11957 (2022-04-11 05:46:37)

fix panic on GAT
Signed-off-by: Alex Chi <iskyzh@gmail.com>

This is still a workaround on GAT panic, and didn't solve the full problem. But at least we won't panic now. False positive is better than panicking and letting VSCode constantly pop out the warning 🤣

This PR is simple -- only apply the https://github.com/rust-analyzer/rust-analyzer/pull/11878 fix on const generics. For normal GATs, just follow the previous approach.

This PR fixes https://github.com/rust-analyzer/rust-analyzer/issues/11939, I've added it as a test case.

This PR didn't fully fix / https://github.com/rust-analyzer/rust-analyzer/issues/11923. But at least it won't panic now -- will only give a type mismatch error.

Not sure if it fixes / https://github.com/rust-analyzer/rust-analyzer/issues/11921, I'll test it later.

cc @flodiebold for review, thanks!


Patch:
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 0ffd428cff..bdb7677f5d 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -176,13 +176,22 @@ pub(super) fn associated_type_by_name_including_super_traits(
 pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
     let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
     if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
-        // XXX: treat generic associated types as not existing to avoid crashes (#)
-        //
-        // Chalk expects the inner associated type's parameters to come
-        // *before*, not after the trait's generics as we've always done it.
-        // Adapting to this requires a larger refactoring
-        cov_mark::hit!(ignore_gats);
-        return Generics { def, params: Interned::new(Default::default()), parent_generics };
+        let params = db.generic_params(def);
+        if params
+            .type_or_consts
+            .iter()
+            .any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)))
+        {
+            // XXX: treat const generic associated types as not existing to avoid crashes (#11769)
+            //
+            // Chalk expects the inner associated type's parameters to come
+            // *before*, not after the trait's generics as we've always done it.
+            // Adapting to this requires a larger refactoring
+            cov_mark::hit!(ignore_gats);
+            return Generics { def, params: Interned::new(Default::default()), parent_generics };
+        } else {
+            return Generics { def, params, parent_generics };
+        }
     }
     Generics { def, params: db.generic_params(def), parent_generics }
 }


Test patch:
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index a02ec6f85c..c3c934c468 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1471,7 +1471,7 @@ fn regression_11688_3() {
 }
 
 #[test]
-fn gat_crash() {
+fn gat_crash_1() {
     cov_mark::check!(ignore_gats);
     check_no_mismatches(
         r#"
@@ -1489,6 +1489,26 @@ fn test<T: Crash>() {
     );
 }
 
+#[test]
+fn gat_crash_2() {
+    check_no_mismatches(
+        r#"
+pub struct InlineStorage {}
+
+pub struct InlineStorageHandle<T: ?Sized> {}
+
+pub unsafe trait Storage {
+    type Handle<T: ?Sized>;
+    fn create<T: ?Sized>() -> Self::Handle<T>;
+}
+
+unsafe impl Storage for InlineStorage {
+    type Handle<T: ?Sized> = InlineStorageHandle<T>;
+}
+"#,
+    );
+}
+
 #[test]
 fn cfgd_out_self_param() {
     cov_mark::check!(cfgd_out_self_param);