diff options
44 files changed, 645 insertions, 314 deletions
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5a29379ba48..80a311a74ab 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1294,7 +1294,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" dependencies = [ "anyhow", "crossbeam-channel", @@ -1310,9 +1310,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9462c4dc73e17f971ec1f171d44bfffb72e65a130117233388a0ebc7ec5656f9" +checksum = "7d6ada348dbc2703cbe7637b2dda05cff84d3da2819c24abcb305dd613e0ba2e" dependencies = [ "crossbeam-channel", "log", @@ -2007,7 +2007,7 @@ dependencies = [ "intern", "itertools 0.14.0", "load-cargo", - "lsp-server 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e7cf0212bf2..01a13a39f7b 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -100,7 +100,7 @@ ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } # in-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.8" } +lsp-server = { version = "0.7.9" } # non-local crates anyhow = "1.0.98" @@ -125,11 +125,11 @@ memmap2 = "0.9.5" nohash-hasher = "0.2.0" oorandom = "11.1.5" object = { version = "0.36.7", default-features = false, features = [ - "std", - "read_core", - "elf", - "macho", - "pe", + "std", + "read_core", + "elf", + "macho", + "pe", ] } process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" @@ -139,9 +139,9 @@ rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.23.0", default-features = true, features = [ - "rayon", - "salsa_unstable", - "macros", + "rayon", + "salsa_unstable", + "macros", ] } salsa-macros = "0.23.0" semver = "1.0.26" @@ -151,9 +151,9 @@ serde_json = "1.0.140" rustc-hash = "2.1.1" rustc-literal-escaper = "0.0.4" smallvec = { version = "1.15.1", features = [ - "const_new", - "union", - "const_generics", + "const_new", + "union", + "const_generics", ] } smol_str = "0.3.2" temp-dir = "0.1.16" @@ -161,12 +161,12 @@ text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" tracing-subscriber = { version = "0.3.19", default-features = false, features = [ - "registry", - "fmt", - "local-time", - "std", - "time", - "tracing-log", + "registry", + "fmt", + "local-time", + "std", + "time", + "tracing-log", ] } triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" @@ -176,7 +176,7 @@ xshell = "0.2.7" dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] } # We need to freeze the version of the crate, as it needs to match with dashmap hashbrown = { version = "0.14.*", features = [ - "inline-more", + "inline-more", ], default-features = false } [workspace.lints.rust] diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d207305b4c6..05f06f97fdc 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1241,29 +1241,27 @@ impl<'db> SemanticsImpl<'db> { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index f73b8c4fd0f..c208c8bf789 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -13,12 +13,7 @@ use ide_db::{ 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 node_to_insert_before(target_node: SyntaxNode) -> SyntaxNode { } 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 @@ fn make_bool_enum(make_pub: bool) -> ast::Enum { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 916bb67ebb4..5f526ec8994 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -236,6 +236,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) }; let mut fn_ = make::fn_( None, + None, closure_name_or_default.clone(), closure_type_params, closure_where_clause, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index f1cc3d90b9c..7d8b763d8b8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -96,6 +96,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> } let error_type = ast::AssocItem::TypeAlias(make::ty_alias( + None, "Error", None, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 66552dd65f5..4b6d0b37854 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -9,7 +9,6 @@ use stdx::never; use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, ast::{self, Use, UseTree, VisibilityKind, make}, - ted, }; use crate::{ @@ -165,8 +164,6 @@ fn build_expanded_import( 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 @@ fn build_expanded_import( })) .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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 890b8dd6412..d88e3311bd7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -1642,6 +1642,7 @@ fn format_function( make::fn_( None, + None, fun_name, generic_params, where_clause, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index c6a6b97df82..e783b869345 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -1,6 +1,5 @@ -use std::iter; +use std::ops::RangeInclusive; -use either::Either; use hir::{HasSource, ModuleSource}; use ide_db::{ FileId, FxHashMap, FxHashSet, @@ -82,7 +81,15 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti curr_parent_module = ast::Module::cast(mod_syn_opt); } - let mut module = extract_target(&node, ctx.selection_trimmed())?; + let selection_range = ctx.selection_trimmed(); + let (mut module, module_text_range) = if let Some(item) = ast::Item::cast(node.clone()) { + let module = extract_single_target(&item); + (module, node.text_range()) + } else { + let (module, range) = extract_child_target(&node, selection_range)?; + let module_text_range = range.start().text_range().cover(range.end().text_range()); + (module, module_text_range) + }; if module.body_items.is_empty() { return None; } @@ -92,7 +99,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti acc.add( AssistId::refactor_extract("extract_module"), "Extract Module", - module.text_range, + module_text_range, |builder| { //This takes place in three steps: // @@ -110,17 +117,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti //for change_visibility and usages for first point mentioned above in the process let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = - module.get_usages_and_record_fields(ctx); + module.get_usages_and_record_fields(ctx, module_text_range); builder.edit_file(ctx.vfs_file_id()); use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); }); - let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); + let import_items = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); + let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string(); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -157,15 +164,12 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { - for import_path_text_range in import_paths_to_be_removed { - if module.text_range.intersect(import_path_text_range).is_some() { - module.text_range = module.text_range.cover(import_path_text_range); - } else { - builder.delete(import_path_text_range); + for import_item in import_items { + if !module_text_range.contains_range(import_item) { + builder.delete(import_item); } } - - builder.replace(module.text_range, module_def) + builder.replace(module_text_range, module_def) } }, ) @@ -173,38 +177,50 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn generate_module_def( parent_impl: &Option<ast::Impl>, - module: &mut Module, + module: Module, old_indent: IndentLevel, -) -> String { - let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { - (Either::Left(module.body_items.iter()), old_indent + 2) +) -> ast::Module { + let Module { name, body_items, use_items } = module; + let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let assoc_items = body_items + .into_iter() + .map(|item| item.syntax().clone()) + .filter_map(ast::AssocItem::cast) + .map(|it| it.indent(IndentLevel(1))) + .collect_vec(); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list)); + // Add the import for enum/struct corresponding to given impl block + let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); + let mut module_body_items = use_items; + module_body_items.insert(0, use_impl); + module_body_items.push(ast::Item::Impl(impl_)); + module_body_items } else { - (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + [use_items, body_items].concat() }; - let mut body = items_to_be_processed - .map(|item| item.indent(IndentLevel(1))) - .map(|item| format!("{new_item_indent}{item}")) - .join("\n\n"); + let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); + let module_body = make::item_list(Some(items)); - if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { - let impl_indent = old_indent + 1; - body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + let module_name = make::name(name); + make::mod_(module_name, Some(module_body)).indent(old_indent) +} - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items.iter() { - body = format!("{impl_indent}{item}\n\n{body}"); - } - } +fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { + let super_path = make::ext::ident_path("super"); + 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), + ); - let module_name = module.name; - format!("mod {module_name} {{\n{body}\n{old_indent}}}") + ast::Item::from(use_) } #[derive(Debug)] struct Module { - text_range: TextRange, name: &'static str, /// All items except use items. body_items: Vec<ast::Item>, @@ -214,22 +230,37 @@ struct Module { use_items: Vec<ast::Item>, } -fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> { +fn extract_single_target(node: &ast::Item) -> Module { + let (body_items, use_items) = if matches!(node, ast::Item::Use(_)) { + (Vec::new(), vec![node.clone()]) + } else { + (vec![node.clone()], Vec::new()) + }; + let name = "modname"; + Module { name, body_items, use_items } +} + +fn extract_child_target( + node: &SyntaxNode, + selection_range: TextRange, +) -> Option<(Module, RangeInclusive<SyntaxNode>)> { let selected_nodes = node .children() .filter(|node| selection_range.contains_range(node.text_range())) - .chain(iter::once(node.clone())); - let (use_items, body_items) = selected_nodes .filter_map(ast::Item::cast) - .partition(|item| matches!(item, ast::Item::Use(..))); - - Some(Module { text_range: selection_range, name: "modname", body_items, use_items }) + .collect_vec(); + let start = selected_nodes.first()?.syntax().clone(); + let end = selected_nodes.last()?.syntax().clone(); + let (use_items, body_items): (Vec<ast::Item>, Vec<ast::Item>) = + selected_nodes.into_iter().partition(|item| matches!(item, ast::Item::Use(..))); + Some((Module { name: "modname", body_items, use_items }, start..=end)) } impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, ) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>, FxHashMap<TextSize, ast::Use>) { let mut adt_fields = Vec::new(); @@ -247,7 +278,7 @@ impl Module { ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx, replace_range,node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -281,30 +312,30 @@ impl Module { ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -318,6 +349,7 @@ impl Module { fn expand_and_group_usages_file_wise( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>, use_stmts_to_be_inserted: &mut FxHashMap<TextSize, ast::Use>, @@ -327,7 +359,7 @@ impl Module { syntax::NodeOrToken::Node(node) => node, syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic }; - let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let out_of_sel = |node: &SyntaxNode| !replace_range.contains_range(node.text_range()); let mut use_stmts_set = FxHashSet::default(); for (file_id, refs) in node_def.usages(&ctx.sema).all() { @@ -527,7 +559,8 @@ impl Module { // mod -> ust_stmt transversal // true | false -> super import insertion // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } None => {} } @@ -556,7 +589,8 @@ impl Module { use_tree_paths = Some(use_tree_str); } else if def_in_mod && def_out_sel { - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } } @@ -586,6 +620,7 @@ impl Module { 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_)); @@ -595,19 +630,6 @@ impl Module { import_path_to_be_removed } - fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item { - let super_path = make::ext::ident_path("super"); - let node_path = make::ext::ident_path(&node_syntax.to_string()); - let use_ = make::use_( - None, - make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), - ); - - let item = ast::Item::from(use_); - self.use_items.insert(0, item.clone()); - item - } - fn process_use_stmt_for_import_resolve( &self, use_stmt: Option<ast::Use>, @@ -1422,10 +1444,10 @@ $0fn foo(x: B) {}$0 struct B {} mod modname { - use super::B; - use super::A; + use super::B; + impl A { pub(crate) fn foo(x: B) {} } @@ -1737,4 +1759,27 @@ fn main() { "#, ); } + + #[test] + fn test_miss_select_item() { + check_assist( + extract_module, + r#" +mod foo { + mod $0bar { + fn foo(){}$0 + } +} +"#, + r#" +mod foo { + mod modname { + pub(crate) mod bar { + fn foo(){} + } + } +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 79f22381952..7f93506685e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -69,8 +69,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c9c1969b9e0..bd88e8b09ce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -200,7 +200,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 2c81e2883a3..d8a2e038d33 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -155,6 +155,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ret_type = method_source.ret_type(); let f = make::fn_( + None, vis, fn_name, type_params, @@ -195,6 +196,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index e96250f3c50..e87dde5b8e4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -20,7 +20,6 @@ use syntax::{ HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred, edit::{self, AstNodeEdit}, - edit_in_place::AttrsOwnerEdit, make, }, ted::{self, Position}, @@ -266,6 +265,7 @@ fn generate_impl( 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 @@ fn generate_impl( 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 @@ fn process_assoc_item( 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 @@ fn process_assoc_item( 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 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option // 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 @@ fn func_assoc_item( 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 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<Ass 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 @@ impl T for B { } #[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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 73a69c82fbc..06fef4af223 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/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 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt )) .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 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt .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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 3c327a63b0f..0b7eca2290f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -94,6 +94,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) // Insert new alias let ty_alias = make::ty_alias( + None, &alias_name, generic_params, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 613b32fcc16..efdb20c1e93 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -189,7 +189,7 @@ fn add_func_to_accumulator( ))); // 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 @@ impl FunctionBuilder { Visibility::Pub => Some(make::visibility_pub()), }; let fn_def = make::fn_( + None, visibility, self.fn_name, self.generic_param_list, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 807b9194b2d..e42d0ed1b00 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -263,6 +263,7 @@ fn generate_getter_from_info( let body = make::block_expr([], Some(body)); make::fn_( + None, strukt.visibility(), fn_name, None, @@ -299,6 +300,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI // Make the setter fn make::fn_( + None, strukt.visibility(), fn_name, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index b38ee6f7dce..77eb8efc6f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -174,6 +174,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> let make_impl_ = |body| { make::impl_trait( + None, trait_.unsafe_token().is_some(), None, trait_gen_args.clone(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 351f134612f..ac4a54a89ef 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -134,6 +134,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4e95ceb2e85..6076d5cb5ce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -6,13 +6,12 @@ use ide_db::{ }; 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 @@ pub(crate) fn generate_single_field_struct_from( let fn_ = make::fn_( None, + None, make::name("from"), None, None, @@ -110,8 +110,12 @@ pub(crate) fn generate_single_field_struct_from( .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 @@ pub(crate) fn generate_single_field_struct_from( 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 603be4d6673..547d3686e39 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -88,7 +88,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 9356d02706c..414f6746d44 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -112,6 +112,16 @@ fn compute_dbg_replacement( } } } + // 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 compute_dbg_replacement( }) } +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 @@ mod tests { } #[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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 3cd7b58f4dd..df7057835c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -7,11 +7,8 @@ use ide_db::{ }; 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 @@ pub(crate) fn replace_named_generic_with_impl( "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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index c066f41ca47..accb5c28d6e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/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 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt 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/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 91aac9cf7b6..20e0302b57d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -736,8 +736,11 @@ fn generate_impl_inner( 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 @@ fn generate_impl_inner( adt.where_clause(), body, ), - None => make::impl_(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()); + None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), } + .clone_for_update() } pub(crate) fn add_method_to_adt( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 2eabf99fc69..9d246017131 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -559,6 +559,7 @@ fn expected_type_and_name<'db>( 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 @@ fn expected_type_and_name<'db>( .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 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> { } 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/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 75c20968e1e..7a8c70f190e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -434,3 +434,53 @@ fn f(thing: u32) -> &u32 { 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/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 08cd8f28608..b174adfd7e4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -194,7 +194,7 @@ fn insert_use_with_alias_option( 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/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index a5d9a10d2e5..d47cc650793 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -390,7 +390,8 @@ fn get_doc_links( 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/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 6af156fa668..c2f08f8ca2e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -415,6 +415,30 @@ fn foo() { } #[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/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index ac64413effe..ac64413effe 100755..100644 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 84e41277390..f768d4b68f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -83,14 +83,14 @@ pub(crate) fn goto_definition( 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 @@ fn f() { } #[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/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 7a8514c47af..8c2a2f6f72f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -228,9 +228,9 @@ fn hints( chaining::hints(hints, famous_defs, config, display_target, &expr); adjustment::hints(hints, famous_defs, config, display_target, &expr); match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, ast::Expr::from(it)), + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, famous_defs, config, ast::Expr::from(it)) + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { closure_captures::hints(hints, famous_defs, config, it.clone()); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index ec0a4c46c7f..75470778405 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,7 +7,7 @@ use std::iter::zip; use either::Either; -use hir::Semantics; +use hir::{EditionedFileId, Semantics}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use stdx::to_lower_snake_case; @@ -19,6 +19,7 @@ pub(super) fn hints( acc: &mut Vec<InlayHint>, FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -39,6 +40,9 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; + if range.file_id != file_id { + return None; + } let param_name = p.name(sema.db)?; Some((p, param_name, arg, range)) }) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index aea4ae0fd97..8922a8eb485 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -27,6 +27,27 @@ pub use ide_db::rename::RenameError; 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 @@ pub(crate) fn rename( 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(); - - // 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), - ) - })); - - 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); - } + 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!(), + }; + + 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) + }), + ), + 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 @@ fn find_definitions( }) }); - 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/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 5bea74bed7e..203173c11be 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -347,9 +347,7 @@ impl WorkspaceBuildScripts { 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 @@ impl WorkspaceBuildScripts { } 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/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 61c758d5e86..f5932d8cff0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -812,7 +812,7 @@ impl GlobalState { }; 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/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index b50ce644243..160f000387b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -273,28 +273,6 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { } } } - - 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/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index daeb79cf081..9897fd09415 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -190,6 +190,7 @@ fn ty_from_text(text: &str) -> ast::Type { } 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 @@ pub fn ty_alias( let assignment_where = assignment_where.flatten(); quote! { TypeAlias { + #(#attrs "\n")* [type] " " Name { [IDENT ident] } #generic_param_list @@ -229,6 +231,23 @@ pub fn ty_fn_ptr<I: Iterator<Item = Param>>( } } +pub fn item_list(body: Option<Vec<ast::Item>>) -> ast::ItemList { + let is_break_braces = body.is_some(); + let body_newline = if is_break_braces { "\n" } else { "" }; + let body_indent = if is_break_braces { " " } else { "" }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), + None => String::new(), + }; + ast_from_text(&format!("mod C {{{body_newline}{body_indent}{body}{body_newline}}}")) +} + +pub fn mod_(name: ast::Name, body: Option<ast::ItemList>) -> ast::Module { + let body = body.map_or(";".to_owned(), |body| format!(" {body}")); + ast_from_text(&format!("mod {name}{body}")) +} + pub fn assoc_item_list(body: Option<Vec<ast::AssocItem>>) -> ast::AssocItemList { let is_break_braces = body.is_some(); let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() }; @@ -277,12 +296,16 @@ fn merge_where_clause( } 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 +318,11 @@ pub fn impl_( }; 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 +335,8 @@ pub fn impl_trait( 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 +360,7 @@ pub fn impl_trait( 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 +478,18 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast:: 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 +978,19 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { } 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 +1197,7 @@ pub fn variant( } pub fn fn_( + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, fn_name: ast::Name, type_params: Option<ast::GenericParamList>, @@ -1174,6 +1210,8 @@ pub fn fn_( 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 +1235,7 @@ pub fn fn_( 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 +1255,15 @@ pub fn struct_( } 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 +1273,7 @@ pub fn enum_( 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/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 738a26fed5d..8bf27f96748 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/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 @@ impl SyntaxFactory { 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 @@ impl SyntaxFactory { 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 @@ impl SyntaxFactory { 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 @@ impl SyntaxFactory { variant_list: ast::VariantList, ) -> ast::Enum { let ast = make::enum_( + attrs, visibility.clone(), name.clone(), generic_param_list.clone(), @@ -1182,6 +1193,7 @@ impl SyntaxFactory { 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 @@ impl SyntaxFactory { 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 @@ impl SyntaxFactory { 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/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 18f5015e9ea..0b358878fcf 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -619,6 +619,7 @@ mod tests { fn test_replace_token_in_parent() { let parent_fn = make::fn_( None, + None, make::name("it"), None, None, diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 534c24be52e..1dc11ee4e4b 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -5588,9 +5588,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "dev": true, "license": "MIT", "engines": { diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 1fc1da50a0a..f56a0de6163 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -16,9 +16,9 @@ crossbeam-channel.workspace = true [dev-dependencies] lsp-types = "=0.95" ctrlc = "3.4.7" -anyhow.workspace = true +anyhow.workspace = true rustc-hash.workspace = true -toolchain.workspace = true +toolchain.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 399d674e41d..305008e69ae 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -84,9 +84,9 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub result: Option<serde_json::Value>, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub error: Option<ResponseError>, } @@ -94,7 +94,7 @@ pub struct Response { pub struct ResponseError { pub code: i32, pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub data: Option<serde_json::Value>, } @@ -175,7 +175,7 @@ impl Message { let msg = match serde_json::from_str(&text) { Ok(msg) => msg, Err(e) => { - return Err(invalid_data!("malformed LSP payload: {:?}", e)); + return Err(invalid_data!("malformed LSP payload `{e:?}`: {text:?}")); } }; |
