about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDropDemBits <r3usrlnd@gmail.com>2023-12-10 21:23:21 -0500
committerDropDemBits <r3usrlnd@gmail.com>2024-02-08 19:09:33 -0500
commit3924a0ef7c86540d4c84919bf1e0054e30c34711 (patch)
tree308abb135515b3fb2dff82994265766e41ad7ca2
parent8c0661b2de9cab1251c7a3c53e3375cbafce4fa0 (diff)
downloadrust-3924a0ef7c86540d4c84919bf1e0054e30c34711.tar.gz
rust-3924a0ef7c86540d4c84919bf1e0054e30c34711.zip
Add ast versions of `generate{_trait}_impl_text{_intransitive}`
-rw-r--r--crates/ide-assists/src/utils.rs100
-rw-r--r--crates/syntax/src/ast/make.rs7
2 files changed, 105 insertions, 2 deletions
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 3bc585d323e..0ec3f68c8ff 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -470,6 +470,7 @@ pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Opti
 
 /// Generates the surrounding `impl Type { <code> }` including type and lifetime
 /// parameters.
+// FIXME: migrate remaining uses to `generate_impl`
 pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
     generate_impl_text_inner(adt, None, true, code)
 }
@@ -478,6 +479,7 @@ pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
 /// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds.
 ///
 /// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
+// FIXME: migrate remaining uses to `generate_trait_impl`
 pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
     generate_impl_text_inner(adt, Some(trait_text), true, code)
 }
@@ -486,6 +488,7 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
 /// and lifetime parameters, with `impl`'s generic parameters' bounds kept as-is.
 ///
 /// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
+// FIXME: migrate remaining uses to `generate_trait_impl_intransitive`
 pub(crate) fn generate_trait_impl_text_intransitive(
     adt: &ast::Adt,
     trait_text: &str,
@@ -516,7 +519,7 @@ fn generate_impl_text_inner(
                         // Add the current trait to `bounds` if the trait is transitive,
                         // meaning `impl<T> Trait for U<T>` requires `T: Trait`.
                         if trait_is_transitive {
-                            bounds.push(make::type_bound(trait_));
+                            bounds.push(make::type_bound_text(trait_));
                         }
                     };
                     // `{ty_param}: {bounds}`
@@ -574,6 +577,101 @@ fn generate_impl_text_inner(
     buf
 }
 
+/// Generates the corresponding `impl Type {}` including type and lifetime
+/// parameters.
+pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
+    generate_impl_inner(adt, None, true)
+}
+
+/// Generates the corresponding `impl <trait> for Type {}` including type
+/// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds.
+///
+/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
+pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
+    generate_impl_inner(adt, Some(trait_), true)
+}
+
+/// Generates the corresponding `impl <trait> for Type {}` including type
+/// and lifetime parameters, with `impl`'s generic parameters' bounds kept as-is.
+///
+/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
+pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
+    generate_impl_inner(adt, Some(trait_), false)
+}
+
+fn generate_impl_inner(
+    adt: &ast::Adt,
+    trait_: Option<ast::Type>,
+    trait_is_transitive: bool,
+) -> ast::Impl {
+    // Ensure lifetime params are before type & const params
+    let generic_params = adt.generic_param_list().map(|generic_params| {
+        let lifetime_params =
+            generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
+        let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
+            match param {
+                ast::TypeOrConstParam::Type(param) => {
+                    let param = param.clone_for_update();
+                    // remove defaults since they can't be specified in impls
+                    param.remove_default();
+                    let mut bounds =
+                        param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
+                    if let Some(trait_) = &trait_ {
+                        // Add the current trait to `bounds` if the trait is transitive,
+                        // meaning `impl<T> Trait for U<T>` requires `T: Trait`.
+                        if trait_is_transitive {
+                            bounds.push(make::type_bound(trait_.clone()));
+                        }
+                    };
+                    // `{ty_param}: {bounds}`
+                    let param =
+                        make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
+                    ast::GenericParam::TypeParam(param)
+                }
+                ast::TypeOrConstParam::Const(param) => {
+                    let param = param.clone_for_update();
+                    // remove defaults since they can't be specified in impls
+                    param.remove_default();
+                    ast::GenericParam::ConstParam(param)
+                }
+            }
+        });
+
+        make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
+    });
+    let generic_args =
+        generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update());
+    let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text()));
+
+    let impl_ = match trait_ {
+        Some(trait_) => make::impl_trait(
+            false,
+            None,
+            None,
+            generic_params,
+            generic_args,
+            false,
+            trait_,
+            ty,
+            None,
+            adt.where_clause(),
+            None,
+        ),
+        None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), None),
+    }
+    .clone_for_update();
+
+    // Copy any cfg attrs from the original adt
+    let cfg_attrs = adt
+        .attrs()
+        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
+    for attr in cfg_attrs {
+        impl_.add_attr(attr.clone_for_update());
+    }
+
+    impl_
+}
+
 pub(crate) fn add_method_to_adt(
     builder: &mut SourceChangeBuilder,
     adt: &ast::Adt,
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index e479aec30f8..764221fc8bf 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -895,7 +895,12 @@ pub fn trait_(
     ast_from_text(&text)
 }
 
-pub fn type_bound(bound: &str) -> ast::TypeBound {
+// FIXME: remove when no one depends on `generate_impl_text_inner`
+pub fn type_bound_text(bound: &str) -> ast::TypeBound {
+    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
+}
+
+pub fn type_bound(bound: ast::Type) -> ast::TypeBound {
     ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
 }