Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/utils.rs')
-rw-r--r--crates/ide-assists/src/utils.rs247
1 files changed, 28 insertions, 219 deletions
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 10057f8681..bf1062d207 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -15,7 +15,6 @@ use ide_db::{
syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion},
};
use itertools::Itertools;
-use stdx::format_to;
use syntax::{
AstNode, AstToken, Direction, NodeOrToken, SourceFile,
SyntaxKind::*,
@@ -203,11 +202,9 @@ pub fn filter_assoc_items(
/// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it,
/// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got
/// inserted.
-///
-/// Legacy: prefer [`add_trait_assoc_items_to_impl_with_factory`] when a [`SyntaxFactory`] is
-/// available.
#[must_use]
pub fn add_trait_assoc_items_to_impl(
+ make: &SyntaxFactory,
sema: &Semantics<'_, RootDatabase>,
config: &AssistConfig,
original_items: &[InFile<ast::AssocItem>],
@@ -250,95 +247,23 @@ pub fn add_trait_assoc_items_to_impl(
})
.filter_map(|item| match item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let fn_ = fn_.clone_subtree();
- let new_body = make::block_expr(None, Some(expr_fill_default(config)));
- let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone());
- fn_.replace_or_insert_body(&mut fn_editor, new_body.clone_for_update());
- let new_fn_ = fn_editor.finish().new_root().clone();
- ast::AssocItem::cast(new_fn_)
- }
- ast::AssocItem::TypeAlias(type_alias) => {
- let type_alias = type_alias.clone_subtree();
- if let Some(type_bound_list) = type_alias.type_bound_list() {
- let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone());
- type_bound_list.remove(&mut type_alias_editor);
- let type_alias = type_alias_editor.finish().new_root().clone();
- ast::AssocItem::cast(type_alias)
- } else {
- Some(ast::AssocItem::TypeAlias(type_alias))
- }
- }
- item => Some(item),
- })
- .map(|item| AstNodeEdit::indent(&item, new_indent_level))
- .collect()
-}
-
-/// [`SyntaxFactory`]-based variant of [`add_trait_assoc_items_to_impl`].
-#[must_use]
-pub fn add_trait_assoc_items_to_impl_with_factory(
- make: &SyntaxFactory,
- sema: &Semantics<'_, RootDatabase>,
- config: &AssistConfig,
- original_items: &[InFile<ast::AssocItem>],
- trait_: hir::Trait,
- impl_: &ast::Impl,
- target_scope: &hir::SemanticsScope<'_>,
-) -> Vec<ast::AssocItem> {
- let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
- original_items
- .iter()
- .map(|InFile { file_id, value: original_item }| {
- let mut cloned_item = {
- if let Some(macro_file) = file_id.macro_file() {
- let span_map = sema.db.expansion_span_map(macro_file);
- let item_prettified = prettify_macro_expansion(
- sema.db,
- original_item.syntax().clone(),
- &span_map,
- target_scope.krate().into(),
- );
- if let Some(formatted) = ast::AssocItem::cast(item_prettified) {
- return formatted;
- } else {
- stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
- }
- }
- original_item
- }
- .reset_indent();
-
- if let Some(source_scope) = sema.scope(original_item.syntax()) {
- let transform =
- PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone());
- cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap();
- }
- cloned_item.remove_attrs_and_docs();
- cloned_item
- })
- .filter_map(|item| match item {
- ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let fn_ = fn_.clone_subtree();
+ let (fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_);
let fill_expr: ast::Expr = match config.expr_fill_default {
ExprFillDefaultMode::Todo | ExprFillDefaultMode::Default => make.expr_todo(),
ExprFillDefaultMode::Underscore => make.expr_underscore().into(),
};
let new_body = make.block_expr(None::<ast::Stmt>, Some(fill_expr));
- let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone());
- fn_.replace_or_insert_body(&mut fn_editor, new_body);
+ fn_.replace_or_insert_body(&fn_editor, new_body);
let new_fn_ = fn_editor.finish().new_root().clone();
ast::AssocItem::cast(new_fn_)
}
ast::AssocItem::TypeAlias(type_alias) => {
- let type_alias = type_alias.clone_subtree();
+ let (type_alias_editor, type_alias) = SyntaxEditor::with_ast_node(&type_alias);
if let Some(type_bound_list) = type_alias.type_bound_list() {
- let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone());
- type_bound_list.remove(&mut type_alias_editor);
- let type_alias = type_alias_editor.finish().new_root().clone();
- ast::AssocItem::cast(type_alias)
- } else {
- Some(ast::AssocItem::TypeAlias(type_alias))
- }
+ type_bound_list.remove(&type_alias_editor);
+ };
+ let type_alias = type_alias_editor.finish().new_root().clone();
+ ast::AssocItem::cast(type_alias)
}
item => Some(item),
})
@@ -404,10 +329,8 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Ex
Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into())
}
ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
- ast::Expr::ParenExpr(parexpr) => {
- parexpr.expr().map(|e| e.clone_subtree().clone_for_update())
- }
- _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()),
+ ast::Expr::ParenExpr(parexpr) => parexpr.expr(),
+ _ => pe.expr(),
},
ast::Expr::Literal(lit) => match lit.kind() {
ast::LiteralKind::Bool(b) => match b {
@@ -422,10 +345,10 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Ex
pub(crate) fn insert_attributes(
before: impl Element,
- edit: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
attrs: impl IntoIterator<Item = ast::Attr>,
- make: &SyntaxFactory,
) {
+ let make = editor.make();
let mut attrs = attrs.into_iter().peekable();
if attrs.peek().is_none() {
return;
@@ -433,12 +356,10 @@ pub(crate) fn insert_attributes(
let elem = before.syntax_element();
let indent = IndentLevel::from_element(&elem);
let whitespace = format!("\n{indent}");
- edit.insert_all(
- syntax::syntax_editor::Position::before(elem),
- attrs
- .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()])
- .collect(),
- );
+ let elements: Vec<syntax::SyntaxElement> = attrs
+ .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()])
+ .collect();
+ editor.insert_all(syntax::syntax_editor::Position::before(elem), elements);
}
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
@@ -608,104 +529,6 @@ fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool {
false
}
-/// Find the end of the `impl` block for the given `ast::Impl`.
-//
-// FIXME: this partially overlaps with `find_struct_impl`
-pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
- buf.push('\n');
- let end = impl_def
- .assoc_item_list()
- .and_then(|it| it.r_curly_token())?
- .prev_sibling_or_token()?
- .text_range()
- .end();
- Some(end)
-}
-
-/// 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)
-}
-
-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 param = match param {
- ast::TypeOrConstParam::Type(param) => {
- // remove defaults since they can't be specified in impls
- let mut bounds =
- param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
- if let Some(trait_) = trait_text {
- // 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_text(trait_));
- }
- };
- // `{ty_param}: {bounds}`
- let param = make::type_param(param.name()?, make::type_bound_list(bounds));
- ast::GenericParam::TypeParam(param)
- }
- ast::TypeOrConstParam::Const(param) => {
- // remove defaults since they can't be specified in impls
- let param = make::const_param(param.name()?, param.ty()?);
- ast::GenericParam::ConstParam(param)
- }
- };
- Some(param)
- });
-
- make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
- });
-
- // FIXME: use syntax::make & mutable AST apis instead
- // `trait_text` and `code` can't be opaque blobs of text
- let mut buf = String::with_capacity(code.len());
-
- // Copy any cfg attrs from the original adt
- buf.push_str("\n\n");
- let cfg_attrs = adt
- .attrs()
- .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
- cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
-
- // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
- buf.push_str("impl");
- if let Some(generic_params) = &generic_params {
- format_to!(buf, "{generic_params}");
- }
- buf.push(' ');
- if let Some(trait_text) = trait_text {
- buf.push_str(trait_text);
- buf.push_str(" for ");
- }
- buf.push_str(&adt.name().unwrap().text());
- if let Some(generic_params) = generic_params {
- format_to!(buf, "{}", generic_params.to_generic_args());
- }
-
- match adt.where_clause() {
- Some(where_clause) => {
- format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
- }
- None => {
- format_to!(buf, " {{\n{code}\n}}");
- }
- }
-
- buf
-}
-
/// Generates the corresponding `impl Type {}` including type and lifetime
/// parameters.
pub(crate) fn generate_impl_with_item(
@@ -758,6 +581,16 @@ pub(crate) fn generate_trait_impl_intransitive_with_item(
generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body))
}
+pub(crate) fn generate_trait_impl_with_item(
+ make: &SyntaxFactory,
+ is_unsafe: bool,
+ adt: &ast::Adt,
+ trait_: ast::Type,
+ body: ast::AssocItemList,
+) -> ast::Impl {
+ generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body))
+}
+
fn generate_impl_inner(
is_unsafe: bool,
adt: &ast::Adt,
@@ -806,8 +639,7 @@ fn generate_impl_inner(
let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text()));
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make::impl_trait(
cfg_attrs,
@@ -877,8 +709,7 @@ fn generate_impl_inner_with_factory(
let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into();
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make.impl_trait(
cfg_attrs,
@@ -989,28 +820,6 @@ fn generic_param_associated_bounds_with_factory(
trait_where_clause.peek().is_some().then(|| make.where_clause(trait_where_clause))
}
-pub(crate) fn add_method_to_adt(
- builder: &mut SourceChangeBuilder,
- adt: &ast::Adt,
- impl_def: Option<ast::Impl>,
- method: &str,
-) {
- let mut buf = String::with_capacity(method.len() + 2);
- if impl_def.is_some() {
- buf.push('\n');
- }
- buf.push_str(method);
-
- let start_offset = impl_def
- .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
- .unwrap_or_else(|| {
- buf = generate_impl_text(adt, &buf);
- adt.syntax().text_range().end()
- });
-
- builder.insert(start_offset, buf);
-}
-
#[derive(Debug)]
pub(crate) struct ReferenceConversion<'db> {
conversion: ReferenceConversionType,