Skip to content

Commit

Permalink
refactor: use generate_trait_impl_text_intransitive for From-like…
Browse files Browse the repository at this point in the history
… traits
  • Loading branch information
rami3l committed Dec 17, 2022
1 parent 12b05d2 commit cfa9149
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use ide_db::{famous_defs::FamousDefs, RootDatabase};
use syntax::ast::{self, AstNode, HasName};

use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
use crate::{
utils::generate_trait_impl_text_intransitive, AssistContext, AssistId, AssistKind, Assists,
};

// Assist: generate_from_impl_for_enum
//
Expand Down Expand Up @@ -70,7 +72,7 @@ pub(crate) fn generate_from_impl_for_enum(
}}"#
)
};
let from_impl = generate_trait_impl_text(&enum_, &from_trait, &impl_code);
let from_impl = generate_trait_impl_text_intransitive(&enum_, &from_trait, &impl_code);
edit.insert(start_offset, from_impl);
},
)
Expand Down
6 changes: 3 additions & 3 deletions crates/ide-assists/src/handlers/generate_impl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use syntax::ast::{self, AstNode, HasName};

use crate::{
utils::{generate_impl_text, generate_trait_impl_text},
utils::{generate_impl_text, generate_trait_impl_text_intransitive},
AssistContext, AssistId, AssistKind, Assists,
};

Expand Down Expand Up @@ -89,11 +89,11 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let start_offset = nominal.syntax().text_range().end();
match ctx.config.snippet_cap {
Some(cap) => {
let snippet = generate_trait_impl_text(&nominal, "$0", "");
let snippet = generate_trait_impl_text_intransitive(&nominal, "$0", "");
edit.insert_snippet(cap, start_offset, snippet);
}
None => {
let text = generate_trait_impl_text(&nominal, "", "");
let text = generate_trait_impl_text_intransitive(&nominal, "", "");
edit.insert(start_offset, text);
}
}
Expand Down
56 changes: 35 additions & 21 deletions crates/ide-assists/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::ops;
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
use hir::{db::HirDatabase, HirDisplay, Semantics};
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
use itertools::Itertools;
use stdx::format_to;
use syntax::{
ast::{
Expand Down Expand Up @@ -435,52 +434,67 @@ pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Opti
Some(end)
}

// Generates the surrounding `impl Type { <code> }` including type and lifetime
// parameters
/// Generates the surrounding `impl Type { <code> }` including type and lifetime
/// parameters.
pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
generate_impl_text_inner(adt, None, code)
generate_impl_text_inner(adt, None, true, code)
}

// Generates the surrounding `impl <trait> for Type { <code> }` including type
// and lifetime parameters
/// Generates the surrounding `impl <trait> for Type { <code> }` 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_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
generate_impl_text_inner(adt, Some(trait_text), code)
generate_impl_text_inner(adt, Some(trait_text), true, code)
}

/// Generates the surrounding `impl <trait> for Type { <code> }` 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_text_intransitive(
adt: &ast::Adt,
trait_text: &str,
code: &str,
) -> String {
generate_impl_text_inner(adt, Some(trait_text), false, code)
}

fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
fn generate_impl_text_inner(
adt: &ast::Adt,
trait_text: Option<&str>,
trait_is_transitive: bool,
code: &str,
) -> String {
// 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().filter_map(|param| {
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_vec());
// `{ty_param}: {trait_text}`
let mut bounds =
param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
if let Some(trait_) = trait_text {
// Defense against the following cases:
// - The trait is undetermined, e.g. `$0`.
// - The trait is a `From`, e.g. `From<T>`.
if !trait_.starts_with('$')
&& !matches!(trait_.split_once('<'), Some((left, _right)) if left.trim() == "From")
{
// 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_));
}
};
// `{ty_param}: {bounds}`
let param =
make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
Some(ast::GenericParam::TypeParam(param))
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();
Some(ast::GenericParam::ConstParam(param))
ast::GenericParam::ConstParam(param)
}
}
});
Expand Down
4 changes: 2 additions & 2 deletions crates/syntax/src/ast/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,8 +734,8 @@ pub fn type_bound_list(
}

pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::TypeParam {
let bound = bounds.map_or_else(String::new, |it| format!(": {it}"));
ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
let bounds = bounds.map_or_else(String::new, |it| format!(": {it}"));
ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}"))
}

pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
Expand Down

0 comments on commit cfa9149

Please sign in to comment.