Merge pull request #20392 from rust-lang/veykril/push-pxplxplxvvyy
Report the incorrect payload when failing to deserialize lsp messages
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d207305..05f06f9 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1241,29 +1241,27 @@
adt,
))
})?;
- let mut res = None;
for (_, derive_attr, derives) in derives {
// as there may be multiple derives registering the same helper
// name, we gotta make sure to call this for all of them!
// FIXME: We need to call `f` for all of them as well though!
- res = res.or(process_expansion_for_token(
- ctx,
- &mut stack,
- derive_attr,
- ));
+ process_expansion_for_token(ctx, &mut stack, derive_attr);
for derive in derives.into_iter().flatten() {
- res = res
- .or(process_expansion_for_token(ctx, &mut stack, derive));
+ process_expansion_for_token(ctx, &mut stack, derive);
}
}
// remove all tokens that are within the derives expansion
filter_duplicates(tokens, adt.syntax().text_range());
- Some(res)
+ Some(())
});
// if we found derives, we can early exit. There is no way we can be in any
// macro call at this point given we are not in a token tree
- if let Some(res) = res {
- return res;
+ if let Some(()) = res {
+ // Note: derives do not remap the original token. Furthermore, we want
+ // the original token to be before the derives in the list, because if they
+ // upmap to the same token and we deduplicate them (e.g. in rename), we
+ // want the original token to remain, not the derive.
+ return None;
}
}
// Then check for token trees, that means we are either in a function-like macro or
diff --git a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
index f73b8c4..c208c8b 100644
--- a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -13,12 +13,7 @@
use itertools::Itertools;
use syntax::{
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T,
- ast::{
- self, HasName,
- edit::IndentLevel,
- edit_in_place::{AttrsOwnerEdit, Indent},
- make,
- },
+ ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make},
};
use crate::{
@@ -506,18 +501,6 @@
}
fn make_bool_enum(make_pub: bool) -> ast::Enum {
- let enum_def = make::enum_(
- if make_pub { Some(make::visibility_pub()) } else { None },
- make::name("Bool"),
- None,
- None,
- make::variant_list(vec![
- make::variant(None, make::name("True"), None, None),
- make::variant(None, make::name("False"), None, None),
- ]),
- )
- .clone_for_update();
-
let derive_eq = make::attr_outer(make::meta_token_tree(
make::ext::ident_path("derive"),
make::token_tree(
@@ -529,11 +512,19 @@
NodeOrToken::Token(make::tokens::ident("Eq")),
],
),
- ))
- .clone_for_update();
- enum_def.add_attr(derive_eq);
-
- enum_def
+ ));
+ make::enum_(
+ [derive_eq],
+ if make_pub { Some(make::visibility_pub()) } else { None },
+ make::name("Bool"),
+ None,
+ None,
+ make::variant_list(vec![
+ make::variant(None, make::name("True"), None, None),
+ make::variant(None, make::name("False"), None, None),
+ ]),
+ )
+ .clone_for_update()
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 916bb67..5f526ec 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -236,6 +236,7 @@
};
let mut fn_ = make::fn_(
None,
+ None,
closure_name_or_default.clone(),
closure_type_params,
closure_where_clause,
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index f1cc3d9..7d8b763 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -96,6 +96,7 @@
}
let error_type = ast::AssocItem::TypeAlias(make::ty_alias(
+ None,
"Error",
None,
None,
diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs
index 66552dd..4b6d0b3 100644
--- a/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -9,7 +9,6 @@
use syntax::{
AstNode, Direction, SyntaxNode, SyntaxToken, T,
ast::{self, Use, UseTree, VisibilityKind, make},
- ted,
};
use crate::{
@@ -165,8 +164,6 @@
let filtered_defs =
if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) };
- let use_tree = builder.make_mut(use_tree);
-
let names_to_import = find_names_to_import(filtered_defs, imported_defs);
let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
let path = make::ext::ident_path(
@@ -176,22 +173,24 @@
}))
.clone_for_update();
+ let mut editor = builder.make_editor(use_tree.syntax());
match use_tree.star_token() {
Some(star) => {
let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
if needs_braces {
- ted::replace(star, expanded.syntax())
+ editor.replace(star, expanded.syntax())
} else {
let without_braces = expanded
.syntax()
.children_with_tokens()
.filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
.collect();
- ted::replace_with_many(star, without_braces)
+ editor.replace_with_many(star, without_braces)
}
}
None => never!(),
}
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
}
fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind {
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 890b8dd..d88e331 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -1642,6 +1642,7 @@
make::fn_(
None,
+ None,
fun_name,
generic_params,
where_clause,
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs
index c6a6b97..da91d0a 100644
--- a/crates/ide-assists/src/handlers/extract_module.rs
+++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -586,6 +586,7 @@
if (def_out_sel || !is_item) && use_stmt_not_in_sel {
let use_ = make::use_(
None,
+ None,
make::use_tree(make::join_paths(use_tree_paths), None, None, false),
);
self.use_items.insert(0, ast::Item::from(use_));
@@ -600,6 +601,7 @@
let node_path = make::ext::ident_path(&node_syntax.to_string());
let use_ = make::use_(
None,
+ None,
make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
);
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs
index 79f2238..7f93506 100644
--- a/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -69,8 +69,9 @@
edit.replace(ty.syntax(), new_ty.syntax());
// Insert new alias
- let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None)))
- .clone_for_update();
+ let ty_alias =
+ make::ty_alias(None, "Type", generic_params, None, None, Some((ty, None)))
+ .clone_for_update();
if let Some(cap) = ctx.config.snippet_cap
&& let Some(name) = ty_alias.name()
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index c9c1969..bd88e8b 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -200,7 +200,7 @@
}
ExtractionKind::Constant => {
let ast_ty = make.ty(&ty_string);
- ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer))
+ ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer))
.into()
}
ExtractionKind::Static => {
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 2c81e28..d8a2e03 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -155,6 +155,7 @@
let ret_type = method_source.ret_type();
let f = make::fn_(
+ None,
vis,
fn_name,
type_params,
@@ -195,6 +196,7 @@
let assoc_item_list = make::assoc_item_list(Some(vec![item]));
let impl_def = make::impl_(
+ None,
ty_params,
ty_args,
make::ty_path(make::ext::ident_path(name)),
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index e96250f..e87dde5 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -20,7 +20,6 @@
HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path,
WherePred,
edit::{self, AstNodeEdit},
- edit_in_place::AttrsOwnerEdit,
make,
},
ted::{self, Position},
@@ -266,6 +265,7 @@
let bound_params = bound_def.generic_param_list();
let delegate = make::impl_trait(
+ None,
delegee.is_unsafe(db),
bound_params.clone(),
bound_params.map(|params| params.to_generic_args()),
@@ -379,6 +379,7 @@
let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?;
// 3) Generate delegate trait impl
let delegate = make::impl_trait(
+ None,
trait_.is_unsafe(db),
trait_gen_params,
trait_gen_args,
@@ -652,8 +653,7 @@
qual_path_ty: ast::Path,
base_name: &str,
) -> Option<ast::AssocItem> {
- let attrs = item.attrs();
- let assoc = match item {
+ match item {
AssocItem::Const(c) => const_assoc_item(c, qual_path_ty),
AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name),
AssocItem::MacroCall(_) => {
@@ -662,18 +662,7 @@
None
}
AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty),
- };
- if let Some(assoc) = &assoc {
- attrs.for_each(|attr| {
- assoc.add_attr(attr.clone());
- // fix indentations
- if let Some(tok) = attr.syntax().next_sibling_or_token() {
- let pos = Position::after(tok);
- ted::insert(pos, make::tokens::whitespace(" "));
- }
- })
}
- assoc
}
fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> {
@@ -687,6 +676,7 @@
// make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap());
let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
let inner = make::item_const(
+ item.attrs(),
item.visibility(),
item.name()?,
item.ty()?,
@@ -755,6 +745,7 @@
let body = make::block_expr(vec![], Some(call.into())).clone_for_update();
let func = make::fn_(
+ item.attrs(),
item.visibility(),
item.name()?,
item.generic_param_list(),
@@ -779,13 +770,14 @@
let ident = item.name()?.to_string();
let alias = make::ty_alias(
+ item.attrs(),
ident.as_str(),
item.generic_param_list(),
None,
item.where_clause(),
Some((ty, None)),
)
- .clone_for_update();
+ .indent(edit::IndentLevel(1));
Some(AssocItem::TypeAlias(alias))
}
@@ -1814,6 +1806,63 @@
}
#[test]
+ fn test_ty_alias_attrs() {
+ check_assist(
+ generate_delegate_trait,
+ r#"
+struct A;
+
+trait T {
+ #[cfg(test)]
+ type t;
+ #[cfg(not(test))]
+ type t;
+}
+
+impl T for A {
+ #[cfg(test)]
+ type t = u32;
+ #[cfg(not(test))]
+ type t = bool;
+}
+
+struct B {
+ a$0: A,
+}
+"#,
+ r#"
+struct A;
+
+trait T {
+ #[cfg(test)]
+ type t;
+ #[cfg(not(test))]
+ type t;
+}
+
+impl T for A {
+ #[cfg(test)]
+ type t = u32;
+ #[cfg(not(test))]
+ type t = bool;
+}
+
+struct B {
+ a: A,
+}
+
+impl T for B {
+ #[cfg(test)]
+ type t = <A as T>::t;
+
+ #[cfg(not(test))]
+ type t = <A as T>::t;
+}
+"#,
+ );
+ }
+
+ #[test]
fn assoc_items_attributes_mutably_cloned() {
check_assist(
generate_delegate_trait,
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 73a69c8..06fef4a 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -1,6 +1,8 @@
use syntax::{
+ SyntaxKind::{ATTR, COMMENT, WHITESPACE},
T,
- ast::{self, AstNode, HasAttrs, edit_in_place::AttrsOwnerEdit, make},
+ ast::{self, AstNode, HasAttrs, edit::IndentLevel, make},
+ syntax_editor::{Element, Position},
};
use crate::{AssistContext, AssistId, Assists};
@@ -48,8 +50,20 @@
))
.clone_for_update();
- let nominal = edit.make_mut(nominal);
- nominal.add_attr(derive.clone());
+ let mut editor = edit.make_editor(nominal.syntax());
+ let indent = IndentLevel::from_node(nominal.syntax());
+ let after_attrs_and_comments = nominal
+ .syntax()
+ .children_with_tokens()
+ .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
+ .map_or(Position::first_child_of(nominal.syntax()), Position::before);
+ editor.insert_all(
+ after_attrs_and_comments,
+ vec![
+ derive.syntax().syntax_element(),
+ make::tokens::whitespace(&format!("\n{indent}")).syntax_element(),
+ ],
+ );
let delimiter = derive
.meta()
@@ -58,8 +72,9 @@
.expect("failed to get token tree out of Meta")
.r_paren_token()
.expect("make::attr_outer was expected to have a R_PAREN");
-
- edit.add_tabstop_before_token(cap, delimiter);
+ let tabstop_before = edit.make_tabstop_before(cap);
+ editor.add_annotation(delimiter, tabstop_before);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
}
Some(_) => {
// Just move the cursor.
diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
index 3c327a6..0b7eca2 100644
--- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
+++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -94,6 +94,7 @@
// Insert new alias
let ty_alias = make::ty_alias(
+ None,
&alias_name,
generic_params,
None,
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 613b32f..efdb20c 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -189,7 +189,7 @@
)));
// FIXME: adt may have generic params.
- let impl_ = make::impl_(None, None, name, None, None).clone_for_update();
+ let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update();
func.indent(IndentLevel(1));
impl_.get_or_create_assoc_item_list().add_item(func.into());
@@ -365,6 +365,7 @@
Visibility::Pub => Some(make::visibility_pub()),
};
let fn_def = make::fn_(
+ None,
visibility,
self.fn_name,
self.generic_param_list,
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index 807b919..e42d0ed 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -263,6 +263,7 @@
let body = make::block_expr([], Some(body));
make::fn_(
+ None,
strukt.visibility(),
fn_name,
None,
@@ -299,6 +300,7 @@
// Make the setter fn
make::fn_(
+ None,
strukt.visibility(),
fn_name,
None,
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index b38ee6f..77eb8ef 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -174,6 +174,7 @@
let make_impl_ = |body| {
make::impl_trait(
+ None,
trait_.unsafe_token().is_some(),
None,
trait_gen_args.clone(),
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 351f134..ac4a54a 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -134,6 +134,7 @@
let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
let fn_ = make::fn_(
+ None,
strukt.visibility(),
make::name("new"),
None,
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
index 4e95ceb..6076d5c 100644
--- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
+++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -6,13 +6,12 @@
};
use syntax::{
TokenText,
- ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent},
+ ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit, edit_in_place::Indent},
};
use crate::{
AssistId,
assist_context::{AssistContext, Assists},
- utils::add_cfg_attrs_to,
};
// Assist: generate_single_field_struct_from
@@ -90,6 +89,7 @@
let fn_ = make::fn_(
None,
+ None,
make::name("from"),
None,
None,
@@ -110,8 +110,12 @@
.clone_for_update();
fn_.indent(1.into());
+ let cfg_attrs = strukt
+ .attrs()
+ .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
let impl_ = make::impl_trait(
+ cfg_attrs,
false,
None,
trait_gen_args,
@@ -128,8 +132,6 @@
impl_.get_or_create_assoc_item_list().add_item(fn_.into());
- add_cfg_attrs_to(&strukt, &impl_);
-
impl_.reindent_to(indent);
builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}"));
diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs
index 603be4d..547d368 100644
--- a/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -88,7 +88,7 @@
}
}
- let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer);
+ let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer);
if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) {
let tabstop = edit.make_tabstop_before(cap);
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 9356d02..414f674 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -112,6 +112,16 @@
}
}
}
+ // dbg!(2, 'x', &x, x, ...);
+ exprs if ast::ExprStmt::can_cast(parent.kind()) && exprs.iter().all(pure_expr) => {
+ let mut replace = vec![parent.clone().into()];
+ if let Some(prev_sibling) = parent.prev_sibling_or_token()
+ && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE
+ {
+ replace.push(prev_sibling);
+ }
+ (replace, None)
+ }
// dbg!(expr0)
[expr] => {
// dbg!(expr, &parent);
@@ -163,6 +173,20 @@
})
}
+fn pure_expr(expr: &ast::Expr) -> bool {
+ match_ast! {
+ match (expr.syntax()) {
+ ast::Literal(_) => true,
+ ast::RefExpr(it) => {
+ matches!(it.expr(), Some(ast::Expr::PathExpr(p))
+ if p.path().and_then(|p| p.as_single_name_ref()).is_some())
+ },
+ ast::PathExpr(it) => it.path().and_then(|it| it.as_single_name_ref()).is_some(),
+ _ => false,
+ }
+ }
+}
+
fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
if let ast::Expr::MacroExpr(mac) = &expanded {
// Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree
@@ -232,6 +256,32 @@
}
#[test]
+ fn test_remove_simple_dbg_statement() {
+ check_assist(
+ remove_dbg,
+ r#"
+fn foo() {
+ let n = 2;
+ $0dbg!(3);
+ dbg!(2.6);
+ dbg!(1, 2.5);
+ dbg!('x');
+ dbg!(&n);
+ dbg!(n);
+ // needless comment
+ dbg!("foo");$0
+}
+"#,
+ r#"
+fn foo() {
+ let n = 2;
+ // needless comment
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_remove_dbg_not_applicable() {
check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
index 3cd7b58..df70578 100644
--- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -7,11 +7,8 @@
};
use syntax::{
AstNode,
- ast::{
- self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType,
- make::impl_trait_type,
- },
- match_ast, ted,
+ ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, make},
+ match_ast,
};
use crate::{AssistContext, AssistId, Assists};
@@ -74,26 +71,31 @@
"Replace named generic with impl trait",
target,
|edit| {
- let type_param = edit.make_mut(type_param);
- let fn_ = edit.make_mut(fn_);
-
- let path_types_to_replace = path_types_to_replace
- .into_iter()
- .map(|param| edit.make_mut(param))
- .collect::<Vec<_>>();
+ let mut editor = edit.make_editor(type_param.syntax());
// remove trait from generic param list
if let Some(generic_params) = fn_.generic_param_list() {
- generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param));
- if generic_params.generic_params().count() == 0 {
- ted::remove(generic_params.syntax());
+ let params: Vec<ast::GenericParam> = generic_params
+ .clone()
+ .generic_params()
+ .filter(|it| it.syntax() != type_param.syntax())
+ .collect();
+ if params.is_empty() {
+ editor.delete(generic_params.syntax());
+ } else {
+ let new_generic_param_list = make::generic_param_list(params);
+ editor.replace(
+ generic_params.syntax(),
+ new_generic_param_list.syntax().clone_for_update(),
+ );
}
}
- let new_bounds = impl_trait_type(type_bound_list);
+ let new_bounds = make::impl_trait_type(type_bound_list);
for path_type in path_types_to_replace.iter().rev() {
- ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax());
+ editor.replace(path_type.syntax(), new_bounds.clone_for_update().syntax());
}
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/unmerge_imports.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs
index c066f41..accb5c2 100644
--- a/crates/ide-assists/src/handlers/unmerge_imports.rs
+++ b/crates/ide-assists/src/handlers/unmerge_imports.rs
@@ -1,9 +1,6 @@
use syntax::{
AstNode, SyntaxKind,
- ast::{
- self, HasAttrs, HasVisibility, edit::IndentLevel, edit_in_place::AttrsOwnerEdit, make,
- syntax_factory::SyntaxFactory,
- },
+ ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
syntax_editor::{Element, Position, Removable},
};
@@ -46,13 +43,10 @@
acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| {
let make = SyntaxFactory::with_mappings();
let new_use = make.use_(
+ use_.attrs(),
use_.visibility(),
make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()),
);
- // Add any attributes that are present on the use tree
- use_.attrs().for_each(|attr| {
- new_use.add_attr(attr.clone_for_update());
- });
let mut editor = builder.make_editor(use_.syntax());
// Remove the use tree from the current use item
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 91aac9c..20e0302 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -736,8 +736,11 @@
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_ {
+ let cfg_attrs =
+ adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ match trait_ {
Some(trait_) => make::impl_trait(
+ cfg_attrs,
is_unsafe,
None,
None,
@@ -750,26 +753,9 @@
adt.where_clause(),
body,
),
- None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body),
+ None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body),
}
- .clone_for_update();
-
- // Copy any cfg attrs from the original adt
- add_cfg_attrs_to(adt, &impl_);
-
- impl_
-}
-
-pub(crate) fn add_cfg_attrs_to<T, U>(from: &T, to: &U)
-where
- T: HasAttrs,
- U: AttrsOwnerEdit,
-{
- let cfg_attrs =
- from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
- for attr in cfg_attrs {
- to.add_attr(attr.clone_for_update());
- }
+ .clone_for_update()
}
pub(crate) fn add_method_to_adt(
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 2eabf99..9d24601 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -559,6 +559,7 @@
token: &SyntaxToken,
name_like: &ast::NameLike,
) -> (Option<Type<'db>>, Option<NameOrNameRef>) {
+ let token = prev_assign_token_at_trivia(token.clone());
let mut node = match token.parent() {
Some(it) => it,
None => return (None, None),
@@ -629,6 +630,17 @@
.map(TypeInfo::original);
(ty, None)
},
+ ast::BinExpr(it) => {
+ if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() {
+ let ty = it.lhs()
+ .and_then(|lhs| sema.type_of_expr(&lhs))
+ .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs)))
+ .map(TypeInfo::original);
+ (ty, None)
+ } else {
+ (None, None)
+ }
+ },
ast::ArgList(_) => {
cov_mark::hit!(expected_type_fn_param);
ActiveParameter::at_token(
@@ -1856,3 +1868,23 @@
}
None
}
+
+fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken {
+ while token.kind().is_trivia()
+ && let Some(prev) = token.prev_token()
+ && let T![=]
+ | T![+=]
+ | T![/=]
+ | T![*=]
+ | T![%=]
+ | T![>>=]
+ | T![<<=]
+ | T![-=]
+ | T![|=]
+ | T![&=]
+ | T![^=] = prev.kind()
+ {
+ token = prev
+ }
+ token
+}
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index 75c2096..7a8c70f 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -434,3 +434,53 @@
expect!["ty: u32, name: ?"],
);
}
+
+#[test]
+fn expected_type_assign() {
+ check_expected_type_and_name(
+ r#"
+enum State { Stop }
+fn foo() {
+ let x: &mut State = &mut State::Stop;
+ x = $0;
+}
+"#,
+ expect![[r#"ty: &'_ mut State, name: ?"#]],
+ );
+}
+
+#[test]
+fn expected_type_deref_assign() {
+ check_expected_type_and_name(
+ r#"
+enum State { Stop }
+fn foo() {
+ let x: &mut State = &mut State::Stop;
+ match x {
+ State::Stop => {
+ *x = $0;
+ },
+ }
+}
+"#,
+ expect![[r#"ty: State, name: ?"#]],
+ );
+}
+
+#[test]
+fn expected_type_deref_assign_at_block_end() {
+ check_expected_type_and_name(
+ r#"
+enum State { Stop }
+fn foo() {
+ let x: &mut State = &mut State::Stop;
+ match x {
+ State::Stop => {
+ *x = $0
+ },
+ }
+}
+"#,
+ expect![[r#"ty: State, name: ?"#]],
+ );
+}
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index 08cd8f2..b174adf 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -194,7 +194,7 @@
use_tree = use_tree.clone_for_update();
use_tree.wrap_in_tree_list();
}
- let use_item = make::use_(None, use_tree).clone_for_update();
+ let use_item = make::use_(None, None, use_tree).clone_for_update();
for attr in
scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update())
{
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index a5d9a10..d47cc65 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -390,7 +390,8 @@
let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot);
- if let Some(path) = mod_path_of_def(db, target) {
+ let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db));
+ if append_mod && let Some(path) = mod_path_of_def(db, target) {
web_url = join_url(web_url, &path);
local_url = join_url(local_url, &path);
}
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index 6af156f..c2f08f8 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -415,6 +415,30 @@
}
#[test]
+fn external_docs_macro_export() {
+ check_external_docs(
+ r#"
+//- /lib.rs crate:foo
+pub mod inner {
+ #[macro_export]
+ macro_rules! my_macro {
+ () => {};
+ }
+}
+
+//- /main.rs crate:bar deps:foo
+fn main() {
+ foo::my_m$0acro!();
+}
+ "#,
+ Some("/home/user/project"),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/macro.my_macro.html"#]]),
+ Some(expect![[r#"file:///home/user/project/doc/foo/macro.my_macro.html"#]]),
+ Some("/sysroot"),
+ );
+}
+
+#[test]
fn doc_links_items_simple() {
check_doc_links(
r#"
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
old mode 100755
new mode 100644
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 84e4127..f768d4b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -83,14 +83,14 @@
return Some(RangeInfo::new(original_token.text_range(), navs));
}
- if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
- return Some(RangeInfo::new(original_token.text_range(), navs));
- }
-
let navs = sema
.descend_into_macros_no_opaque(original_token.clone(), false)
.into_iter()
.filter_map(|token| {
+ if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
+ return Some(navs);
+ }
+
let parent = token.value.parent()?;
let token_file_id = token.file_id;
@@ -3284,6 +3284,32 @@
}
#[test]
+ fn into_call_to_from_definition_within_macro() {
+ check(
+ r#"
+//- proc_macros: identity
+//- minicore: from
+struct A;
+
+struct B;
+
+impl From<A> for B {
+ fn from(value: A) -> Self {
+ //^^^^
+ B
+ }
+}
+
+#[proc_macros::identity]
+fn f() {
+ let a = A;
+ let b: B = a.into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
fn into_call_to_from_definition_with_trait_bounds() {
check(
r#"
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index aea4ae0..8922a8e 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -27,6 +27,27 @@
type RenameResult<T> = Result<T, RenameError>;
+/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item.
+fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
+ let mut err = None;
+ let oks = iter
+ .filter_map(|item| match item {
+ Ok(it) => Some(it),
+ Err(it) => {
+ err = Some(it);
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ if !oks.is_empty() {
+ Ok(oks)
+ } else if let Some(err) = err {
+ Err(err)
+ } else {
+ Ok(Vec::new())
+ }
+}
+
/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
/// being targeted for a rename.
pub(crate) fn prepare_rename(
@@ -95,58 +116,57 @@
alias_fallback(syntax, position, &new_name.display(db, edition).to_string());
let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
- Some(_) => defs
- // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
- // properly find "direct" usages/references.
- .map(|(.., def, new_name, _)| {
- match kind {
- IdentifierKind::Ident => (),
- IdentifierKind::Lifetime => {
- bail!("Cannot alias reference to a lifetime identifier")
- }
- IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
- IdentifierKind::LowercaseSelf => {
- bail!("Cannot rename alias reference to `self`")
- }
- };
- let mut usages = def.usages(&sema).all();
+ Some(_) => ok_if_any(
+ defs
+ // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
+ // properly find "direct" usages/references.
+ .map(|(.., def, new_name, _)| {
+ match kind {
+ IdentifierKind::Ident => (),
+ IdentifierKind::Lifetime => {
+ bail!("Cannot alias reference to a lifetime identifier")
+ }
+ IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
+ IdentifierKind::LowercaseSelf => {
+ bail!("Cannot rename alias reference to `self`")
+ }
+ };
+ let mut usages = def.usages(&sema).all();
- // FIXME: hack - removes the usage that triggered this rename operation.
- match usages.references.get_mut(&file_id).and_then(|refs| {
- refs.iter()
- .position(|ref_| ref_.range.contains_inclusive(position.offset))
- .map(|idx| refs.remove(idx))
- }) {
- Some(_) => (),
- None => never!(),
- };
+ // FIXME: hack - removes the usage that triggered this rename operation.
+ match usages.references.get_mut(&file_id).and_then(|refs| {
+ refs.iter()
+ .position(|ref_| ref_.range.contains_inclusive(position.offset))
+ .map(|idx| refs.remove(idx))
+ }) {
+ Some(_) => (),
+ None => never!(),
+ };
- let mut source_change = SourceChange::default();
- source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
- (
- position.file_id,
- source_edit_from_references(db, refs, def, &new_name, edition),
- )
- }));
+ let mut source_change = SourceChange::default();
+ source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
+ (
+ position.file_id,
+ source_edit_from_references(db, refs, def, &new_name, edition),
+ )
+ }));
- Ok(source_change)
- })
- .collect(),
- None => defs
- .map(|(.., def, new_name, rename_def)| {
- if let Definition::Local(local) = def {
- if let Some(self_param) = local.as_self_param(sema.db) {
- cov_mark::hit!(rename_self_to_param);
- return rename_self_to_param(&sema, local, self_param, &new_name, kind);
- }
- if kind == IdentifierKind::LowercaseSelf {
- cov_mark::hit!(rename_to_self);
- return rename_to_self(&sema, local);
- }
+ Ok(source_change)
+ }),
+ ),
+ None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| {
+ if let Definition::Local(local) = def {
+ if let Some(self_param) = local.as_self_param(sema.db) {
+ cov_mark::hit!(rename_self_to_param);
+ return rename_self_to_param(&sema, local, self_param, &new_name, kind);
}
- def.rename(&sema, new_name.as_str(), rename_def)
- })
- .collect(),
+ if kind == IdentifierKind::LowercaseSelf {
+ cov_mark::hit!(rename_to_self);
+ return rename_to_self(&sema, local);
+ }
+ }
+ def.rename(&sema, new_name.as_str(), rename_def)
+ })),
};
ops?.into_iter()
@@ -320,7 +340,7 @@
})
});
- let res: RenameResult<Vec<_>> = symbols.filter_map(Result::transpose).collect();
+ let res: RenameResult<Vec<_>> = ok_if_any(symbols.filter_map(Result::transpose));
match res {
Ok(v) => {
// remove duplicates, comparing `Definition`s
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs
index 5bea74b..203173c 100644
--- a/crates/project-model/src/build_dependencies.rs
+++ b/crates/project-model/src/build_dependencies.rs
@@ -347,9 +347,7 @@
match message {
Message::BuildScriptExecuted(mut message) => {
with_output_for(&message.package_id.repr, &mut |name, data| {
- progress(format!(
- "building compile-time-deps: build script {name} run"
- ));
+ progress(format!("build script {name} run"));
let cfgs = {
let mut acc = Vec::new();
for cfg in &message.cfgs {
@@ -380,9 +378,7 @@
}
Message::CompilerArtifact(message) => {
with_output_for(&message.package_id.repr, &mut |name, data| {
- progress(format!(
- "building compile-time-deps: proc-macro {name} built"
- ));
+ progress(format!("proc-macro {name} built"));
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 61c758d..f5932d8 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -812,7 +812,7 @@
};
if let Some(state) = state {
- self.report_progress("Building build-artifacts", state, msg, None, None);
+ self.report_progress("Building compile-time-deps", state, msg, None, None);
}
}
Task::LoadProcMacros(progress) => {
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index b50ce64..160f000 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -273,28 +273,6 @@
}
}
}
-
- fn add_attr(&self, attr: ast::Attr) {
- add_attr(self.syntax(), attr);
-
- fn add_attr(node: &SyntaxNode, attr: ast::Attr) {
- let indent = IndentLevel::from_node(node);
- attr.reindent_to(indent);
-
- let after_attrs_and_comments = node
- .children_with_tokens()
- .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
- .map_or(Position::first_child_of(node), Position::before);
-
- ted::insert_all(
- after_attrs_and_comments,
- vec![
- attr.syntax().clone().into(),
- make::tokens::whitespace(&format!("\n{indent}")).into(),
- ],
- )
- }
- }
}
impl<T: ast::HasAttrs> AttrsOwnerEdit for T {}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index daeb79c..c5ca609 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -190,6 +190,7 @@
}
pub fn ty_alias(
+ attrs: impl IntoIterator<Item = ast::Attr>,
ident: &str,
generic_param_list: Option<ast::GenericParamList>,
type_param_bounds: Option<ast::TypeParam>,
@@ -200,6 +201,7 @@
let assignment_where = assignment_where.flatten();
quote! {
TypeAlias {
+ #(#attrs "\n")*
[type] " "
Name { [IDENT ident] }
#generic_param_list
@@ -277,12 +279,16 @@
}
pub fn impl_(
+ attrs: impl IntoIterator<Item = ast::Attr>,
generic_params: Option<ast::GenericParamList>,
generic_args: Option<ast::GenericArgList>,
path_type: ast::Type,
where_clause: Option<ast::WhereClause>,
body: Option<ast::AssocItemList>,
) -> ast::Impl {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
+
let gen_args = generic_args.map_or_else(String::new, |it| it.to_string());
let gen_params = generic_params.map_or_else(String::new, |it| it.to_string());
@@ -295,10 +301,11 @@
};
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
- ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}"))
+ ast_from_text(&format!("{attrs}impl{gen_params} {path_type}{gen_args}{where_clause}{body}"))
}
pub fn impl_trait(
+ attrs: impl IntoIterator<Item = ast::Attr>,
is_unsafe: bool,
trait_gen_params: Option<ast::GenericParamList>,
trait_gen_args: Option<ast::GenericArgList>,
@@ -311,6 +318,8 @@
ty_where_clause: Option<ast::WhereClause>,
body: Option<ast::AssocItemList>,
) -> ast::Impl {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
let is_unsafe = if is_unsafe { "unsafe " } else { "" };
let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default();
@@ -334,7 +343,7 @@
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
ast_from_text(&format!(
- "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}"
+ "{attrs}{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}"
))
}
@@ -452,12 +461,18 @@
ast_from_text(&format!("use {{{use_trees}}};"))
}
-pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
+pub fn use_(
+ attrs: impl IntoIterator<Item = ast::Attr>,
+ visibility: Option<ast::Visibility>,
+ use_tree: ast::UseTree,
+) -> ast::Use {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
let visibility = match visibility {
None => String::new(),
Some(it) => format!("{it} "),
};
- ast_from_text(&format!("{visibility}use {use_tree};"))
+ ast_from_text(&format!("{attrs}{visibility}use {use_tree};"))
}
pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
@@ -946,16 +961,19 @@
}
pub fn item_const(
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
name: ast::Name,
ty: ast::Type,
expr: ast::Expr,
) -> ast::Const {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
let visibility = match visibility {
None => String::new(),
Some(it) => format!("{it} "),
};
- ast_from_text(&format!("{visibility}const {name}: {ty} = {expr};"))
+ ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};"))
}
pub fn item_static(
@@ -1162,6 +1180,7 @@
}
pub fn fn_(
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
fn_name: ast::Name,
type_params: Option<ast::GenericParamList>,
@@ -1174,6 +1193,8 @@
is_unsafe: bool,
is_gen: bool,
) -> ast::Fn {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
let type_params = match type_params {
Some(type_params) => format!("{type_params}"),
None => "".into(),
@@ -1197,7 +1218,7 @@
let gen_literal = if is_gen { "gen " } else { "" };
ast_from_text(&format!(
- "{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
+ "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
))
}
pub fn struct_(
@@ -1217,12 +1238,15 @@
}
pub fn enum_(
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
enum_name: ast::Name,
generic_param_list: Option<ast::GenericParamList>,
where_clause: Option<ast::WhereClause>,
variant_list: ast::VariantList,
) -> ast::Enum {
+ let attrs =
+ attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
let visibility = match visibility {
None => String::new(),
Some(it) => format!("{it} "),
@@ -1232,7 +1256,7 @@
let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default();
ast_from_text(&format!(
- "{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}"
+ "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}"
))
}
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 738a26f..8bf27f9 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -2,8 +2,8 @@
use crate::{
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
ast::{
- self, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds,
- HasVisibility, RangeItem, make,
+ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName,
+ HasTypeBounds, HasVisibility, RangeItem, make,
},
syntax_editor::SyntaxMappingBuilder,
};
@@ -107,8 +107,13 @@
ast
}
- pub fn use_(&self, visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
- make::use_(visibility, use_tree).clone_for_update()
+ pub fn use_(
+ &self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
+ visibility: Option<ast::Visibility>,
+ use_tree: ast::UseTree,
+ ) -> ast::Use {
+ make::use_(attrs, visibility, use_tree).clone_for_update()
}
pub fn use_tree(
@@ -840,16 +845,20 @@
pub fn item_const(
&self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
name: ast::Name,
ty: ast::Type,
expr: ast::Expr,
) -> ast::Const {
- let ast = make::item_const(visibility.clone(), name.clone(), ty.clone(), expr.clone())
- .clone_for_update();
+ let (attrs, attrs_input) = iterator_input(attrs);
+ let ast =
+ make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone())
+ .clone_for_update();
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone()));
if let Some(visibility) = visibility {
builder.map_node(
visibility.syntax().clone(),
@@ -1067,6 +1076,7 @@
pub fn item_enum(
&self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
name: ast::Name,
generic_param_list: Option<ast::GenericParamList>,
@@ -1074,6 +1084,7 @@
variant_list: ast::VariantList,
) -> ast::Enum {
let ast = make::enum_(
+ attrs,
visibility.clone(),
name.clone(),
generic_param_list.clone(),
@@ -1182,6 +1193,7 @@
pub fn fn_(
&self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
visibility: Option<ast::Visibility>,
fn_name: ast::Name,
type_params: Option<ast::GenericParamList>,
@@ -1194,7 +1206,9 @@
is_unsafe: bool,
is_gen: bool,
) -> ast::Fn {
+ let (attrs, input) = iterator_input(attrs);
let ast = make::fn_(
+ attrs,
visibility.clone(),
fn_name.clone(),
type_params.clone(),
@@ -1210,6 +1224,7 @@
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.attrs().map(|attr| attr.syntax().clone()));
if let Some(visibility) = visibility {
builder.map_node(
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index 18f5015..0b35887 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -619,6 +619,7 @@
fn test_replace_token_in_parent() {
let parent_fn = make::fn_(
None,
+ None,
make::name("it"),
None,
None,