diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e02b9e157bc5f..1a480d6911fec 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1156,6 +1156,7 @@ dependencies = [ "itertools 0.15.0", "paths", "serde_json", + "smallvec", "stdx", "syntax", "test-fixture", @@ -2061,9 +2062,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "ra-ap-rustc_abi" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f25a779e21ca3bba6795193b16508c8ab159f96ee4b07349893fd272065b525" +checksum = "e2cf1b1ffe31b6226c00b40cddfda65002b7729f9f4ed2d547b5856cdab0011c" dependencies = [ "bitflags", "ra-ap-rustc_hashes", @@ -2073,33 +2074,33 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0218ca6c7b096466e85a497e6150c39be5b7bc36637fe62c1cd20370a9d9aac7" +checksum = "2ef42605e36e1305e815ccfc8830eb870f74d78534bca19a61629149536d8e98" [[package]] name = "ra-ap-rustc_hashes" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b410bacf1a7c8038f376fa6283003784d568ac012e35fc0aeefa9a5ab11a2e" +checksum = "b9f5542968215c17275920791b2fa13a43014287506ed0450777c79845102e86" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2271b55e4a5d0cc0cbe9bdf8056c07ac69e32919a48ce66722ed0526d62588c3" +checksum = "1d9e47b9ca7d92cfb0d6653503adbabd41938b84474317397a664326b208d6c6" dependencies = [ "ra-ap-rustc_index_macros", ] [[package]] name = "ra-ap-rustc_index_macros" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a89e743fb881a1e13544e3395a5ad9ad9280d56384256a121066119abd7af2" +checksum = "4d744a7a2852a22f06210bcff9e4667ed0cacbfbe94894cc294044d25e876341" dependencies = [ "proc-macro2", "quote", @@ -2108,9 +2109,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d7c9cc05e0e6b72a214a455a106d9b22b0494164d50a657b17bd319534c218" +checksum = "527c12b3731b7d0692498012810b85b2b8dfdb8b514321ed6afc434bd1c70191" dependencies = [ "memchr", "unicode-ident", @@ -2119,9 +2120,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3017c2f0ace80b8e6068b9c613aa56ed50e0374bf44a891447511f1264e40d" +checksum = "a7a9663a8d7c369e934aac2b74a638537ad7eb4be75b4530d765384dc071c936" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -2132,9 +2133,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a737f844bdef8ac5ab54dadf2f34704b4d06beef9236d71080bb34db697220b" +checksum = "2c038b7a8b0f784d4e441ad8ab991fbbdaa5e0be482e59c639a846a1c8126951" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper", @@ -2142,9 +2143,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6de3d4c7d6078cce3c40c55717b8b15002a80b9fa8849faea496a365324861b4" +checksum = "42ca286f90e99bb97cd9274c088f3c874a05d1ee90cabf40a3928afedabe99fd" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.2", @@ -2155,9 +2156,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c5d9a4d3e7bee7313599bc6d794037247ac0165f03857379cf4fc3097199e05" +checksum = "26d6efb6008f665a9485e0afecf9f4950a6c4bedd8ddd330a9df8986a6c0160b" dependencies = [ "arrayvec", "bitflags", @@ -2176,9 +2177,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024598d1f54272acd83d28c121f8a2e82e216dd7be1e40158b66b2d12fa214c0" +checksum = "5f4fd2355e2bbf1f343c730f623596efc6e465b5e3685b606a437567ebb75bf8" dependencies = [ "proc-macro2", "quote", @@ -2295,9 +2296,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rowan" -version = "0.15.19" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2441aaccb50f4267d4f0f58b21e0138e96a449f361ed57a2673a83c9bca0772" +checksum = "14b574c58582fa59fa43a2feb6608b8744184659f08a2e0117e4b8224d95ed61" dependencies = [ "countme", "hashbrown 0.14.5", @@ -2713,6 +2714,7 @@ dependencies = [ "expect-test", "itertools 0.15.0", "parser", + "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash 2.1.2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 2a219c3ea485c..a56d9770f5b3e 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -86,14 +86,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.165", default-features = false } -ra-ap-rustc_parse_format = { version = "0.165", default-features = false } -ra-ap-rustc_index = { version = "0.165", default-features = false } -ra-ap-rustc_abi = { version = "0.165", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.165", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.165", default-features = false } -ra-ap-rustc_type_ir = { version = "0.165", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.165", default-features = false } +ra-ap-rustc_lexer = { version = "0.166", default-features = false } +ra-ap-rustc_parse_format = { version = "0.166", default-features = false } +ra-ap-rustc_index = { version = "0.166", default-features = false } +ra-ap-rustc_abi = { version = "0.166", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.166", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.166", default-features = false } +ra-ap-rustc_type_ir = { version = "0.166", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.166", default-features = false } # local crates that aren't published to crates.io. These should not have versions. @@ -123,7 +123,7 @@ process-wrap = { version = "9.1.0", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.15.19" +rowan = "0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.27.0", default-features = false, features = [ diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index d55509e2f0884..c330b374f71f9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -201,7 +201,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< let segment4 = segment4.and_then(|it| it.segment()?.name_ref()); segment1.text() == "test" && segment3.is_none_or(|it| it.text() == "prelude") - && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std")) + && segment4.is_none_or(|it| matches!(it.text(), "core" | "std")) }); if is_test { attr_flags.insert(AttrFlags::IS_TEST); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index bdd8ea84a0910..55cb6f8f81aa7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -44,13 +44,13 @@ pub struct Docs { docs: String, /// A sorted map from an offset in `docs` to an offset in the source code. docs_source_map: Vec, - /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated + /// If the item is an outlined module (`mod foo;`), `docs_source_map` stores the concatenated /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`] /// of the outline declaration, and the index in `docs` from which the inline docs /// begin. outline_mod: Option<(HirFileId, usize)>, inline_file: HirFileId, - /// The size the prepended prefix, which does not map to real doc comments. + /// The size of the prepended prefix, which does not map to real doc comments. prefix_len: TextSize, /// The offset in `docs` from which the docs are inner attributes/comments. inline_inner_docs_start: Option, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index df4fc6e531466..aaca830088e33 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -665,7 +665,7 @@ impl<'db> ExprCollector<'db> { lifetime: ast::Lifetime, ) -> LifetimeRefId { // FIXME: Keyword check? - let lifetime_ref = match &*lifetime.text() { + let lifetime_ref = match lifetime.text() { "" | "'" => LifetimeRef::Error, "'static" => LifetimeRef::Static, "'_" => LifetimeRef::Placeholder, @@ -1295,7 +1295,7 @@ impl<'db> ExprCollector<'db> { match binder.generic_param_list() { Some(gpl) => gpl .lifetime_params() - .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) + .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(lt.text()))) .collect(), None => ThinVec::default(), } @@ -3175,7 +3175,7 @@ impl<'db> ExprCollector<'db> { name: ast_label .lifetime() .as_ref() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())), + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())), }; self.alloc_label(label, AstPtr::new(&ast_label)) } @@ -3195,7 +3195,7 @@ impl<'db> ExprCollector<'db> { (hygiene_id.syntax_context().parent(self.db), expansion.def) }) }; - let name = Name::new_lifetime(&lifetime.text()); + let name = Name::new_lifetime(lifetime.text()); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { match &rib.kind { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs index 63a0594f74c1b..fb0a5b0bf7a71 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs @@ -39,7 +39,7 @@ impl ExprCollector<'_> { Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) } else { reg.name_ref().map(|name_ref| { - InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + InlineAsmRegOrRegClass::RegClass(Symbol::intern(name_ref.text())) }) } }; @@ -69,7 +69,7 @@ impl ExprCollector<'_> { continue; } ast::AsmPiece::AsmOperandNamed(op) => { - let name = op.name().map(|name| Symbol::intern(&name.text())); + let name = op.name().map(|name| Symbol::intern(name.text())); if let Some(name) = &name { named_args.insert(name.clone(), slot); named_pos.insert(slot, name.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index ce6e73670cba4..65877fb627f24 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -181,7 +181,7 @@ impl GenericParamsCollector { .map(|lifetime_param| { lifetime_param .lifetime() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())) }) .collect() }); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index d6568b8cfe6ae..cfbfde88765f0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -1,13 +1,15 @@ //! Name resolution for expressions. +use std::mem; + use base_db::SourceDatabase; use hir_expand::{MacroDefId, name::Name}; use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use crate::{ BlockId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, body::Param}, + expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, StoreVisitorExt, body::Param}, hir::{ - Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, + Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, generics::GenericParams, }, signatures::VariantFields, @@ -162,12 +164,13 @@ impl ExprScopes { body.expr_only.as_ref().map_or(0, |it| it.exprs.len()), ), }; - let mut root = scopes.root_scope(); + let root = scopes.root_scope(); if let Some(Param { formal: self_param, user_written: _ }) = body.self_param { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal)); - compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root); + ExprScopeVisitor { store: body, scopes: &mut scopes, scope: root, const_scope: root } + .on_expr(body.root_expr()); scopes } @@ -181,8 +184,9 @@ impl ExprScopes { }; let root = scopes.root_scope(); for root_expr in roots { - let mut scope = scopes.new_scope(root); - compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope); + let scope = scopes.new_scope(root); + ExprScopeVisitor { store, scopes: &mut scopes, scope, const_scope: scope } + .on_expr(root_expr); } scopes } @@ -278,178 +282,126 @@ impl ExprScopes { struct ExprScopeVisitor<'a> { store: &'a ExpressionStore, scopes: &'a mut ExprScopes, - scope: &'a mut ScopeId, - const_scope: &'a mut ScopeId, + scope: ScopeId, + const_scope: ScopeId, } -impl StoreVisitor for ExprScopeVisitor<'_> { - fn on_expr(&mut self, expr: ExprId) { - compute_expr_scopes(expr, self.store, self.scopes, self.scope, self.const_scope); - } - - fn on_anon_const_expr(&mut self, expr: ExprId) { - let mut scope = *self.const_scope; - compute_expr_scopes(expr, self.store, self.scopes, &mut scope, self.const_scope); +impl ExprScopeVisitor<'_> { + fn with_scope(&mut self, scope: ScopeId, f: impl FnOnce(&mut Self)) { + let old_scope = mem::replace(&mut self.scope, scope); + f(self); + self.scope = old_scope; } - fn on_pat(&mut self, pat: PatId) { - self.store.visit_pat_children(pat, &mut *self); - } + fn visit_block( + &mut self, + expr: ExprId, + id: Option, + statements: &[Statement], + tail: Option, + label: Option, + ) { + let scope = self.scopes.new_block_scope(self.scope, id, label); + self.with_scope(scope, |this| { + let old_const_scope = if id.is_some() { + let const_scope = this.scopes.new_block_scope(this.const_scope, id, None); + mem::replace(&mut this.const_scope, const_scope) + } else { + // We don't need to allocate a new scope, since only items matter to us. + this.const_scope + }; + // Overwrite the old scope for the block expr, so that every block scope can be found + // via the block itthis (important for blocks that only contain items, no expressions). + this.scopes.set_scope(expr, this.scope); + + for stmt in statements { + match stmt { + Statement::Let { pat, initializer, else_branch, type_ref } => { + this.on_type_opt(*type_ref); + this.on_expr_opt(*initializer); + this.on_expr_opt(*else_branch); + this.scope = this.scopes.new_scope(this.scope); + this.scopes.add_pat_bindings(this.store, this.scope, *pat); + } + Statement::Expr { expr, has_semi: _ } => this.on_expr(*expr), + Statement::Item(Item::MacroDef(macro_id)) => { + this.scope = this.scopes.new_macro_def_scope(this.scope, macro_id.clone()); + this.const_scope = + this.scopes.new_macro_def_scope(this.const_scope, macro_id.clone()); + } + Statement::Item(Item::Other) => (), + } + } + this.on_expr_opt(tail); - fn on_type(&mut self, ty: TypeRefId) { - self.store.visit_type_ref_children(ty, &mut *self); + this.const_scope = old_const_scope; + }); } } -fn compute_type_scopes( - ty: TypeRefId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - const_scope: &mut ScopeId, -) { - let mut scope = *const_scope; - ExprScopeVisitor { store, scopes, scope: &mut scope, const_scope }.on_type(ty); -} - -fn compute_block_scopes( - statements: &[Statement], - tail: Option, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - for stmt in statements { - match stmt { - Statement::Let { pat, initializer, else_branch, type_ref } => { - if let Some(type_ref) = type_ref { - compute_type_scopes(*type_ref, store, scopes, const_scope); - } - if let Some(expr) = initializer { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); - } - if let Some(expr) = else_branch { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); +impl StoreVisitor for ExprScopeVisitor<'_> { + fn on_expr(&mut self, expr: ExprId) { + self.scopes.set_scope(expr, self.scope); + match &self.store[expr] { + Expr::Block { statements, tail, id, label } => { + self.visit_block(expr, *id, statements, *tail, *label); + } + Expr::Const(expr) => self.on_anon_const_expr(*expr), + Expr::Unsafe { id, statements, tail } => { + self.visit_block(expr, *id, statements, *tail, None); + } + Expr::Loop { body, label, source: _ } => { + let scope = self.scopes.new_labeled_scope(self.scope, *label); + self.with_scope(scope, |this| this.on_expr(*body)); + } + Expr::Closure { args, arg_types, ret_type, body, capture_by: _, closure_kind: _ } => { + arg_types.iter().flatten().for_each(|type_ref| self.on_type(*type_ref)); + self.on_type_opt(*ret_type); + let scope = self.scopes.new_scope(self.scope); + args.iter().for_each(|arg| self.scopes.add_pat_bindings(self.store, scope, *arg)); + self.with_scope(scope, |this| this.on_expr(*body)); + } + Expr::Match { expr, arms } => { + self.on_expr(*expr); + for arm in arms.iter() { + let scope = self.scopes.new_scope(self.scope); + self.with_scope(scope, |this| { + this.scopes.add_pat_bindings(this.store, scope, arm.pat); + if let Some(guard) = arm.guard { + this.on_expr(guard); + } + this.on_expr(arm.expr); + }); } - - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, *pat); } - Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); + &Expr::If { condition, then_branch, else_branch } => { + let then_branch_scope = self.scopes.new_scope(self.scope); + self.with_scope(then_branch_scope, |this| { + this.on_expr(condition); + this.on_expr(then_branch); + }); + self.on_expr_opt(else_branch); } - Statement::Item(Item::MacroDef(macro_id)) => { - *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); - *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone()); + &Expr::Let { pat, expr } => { + self.on_expr(expr); + self.scope = self.scopes.new_scope(self.scope); + self.scopes.add_pat_bindings(self.store, self.scope, pat); } - Statement::Item(Item::Other) => (), + _ => self.store.visit_expr_children(expr, &mut *self), } } - if let Some(expr) = tail { - compute_expr_scopes(expr, store, scopes, scope, const_scope); + + fn on_anon_const_expr(&mut self, expr: ExprId) { + self.with_scope(self.const_scope, |this| this.on_expr(expr)); } -} -fn compute_expr_scopes( - expr: ExprId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - let compute_expr_scopes = - |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| { - compute_expr_scopes(expr, store, scopes, scope, const_scope) - }; - let handle_block = |id, - statements, - tail, - label, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId| { - let mut scope = scopes.new_block_scope(*scope, id, label); - let mut const_scope = if id.is_some() { - scopes.new_block_scope(*const_scope, id, None) - } else { - // We don't need to allocate a new scope, since only items matter to us. - *const_scope - }; - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - scopes.set_scope(expr, scope); - compute_block_scopes(statements, tail, store, scopes, &mut scope, &mut const_scope); - }; + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, self); + } - scopes.set_scope(expr, *scope); - match &store[expr] { - Expr::Block { statements, tail, id, label } => { - handle_block(*id, statements, *tail, *label, scopes, scope, const_scope); - } - Expr::Const(id) => { - let mut scope = *const_scope; - compute_expr_scopes(scopes, *id, &mut scope, const_scope); - } - Expr::Array(Array::Repeat { initializer, repeat }) => { - compute_expr_scopes(scopes, *initializer, scope, const_scope); - let mut repeat_scope = *const_scope; - compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope); - } - Expr::Unsafe { id, statements, tail } => { - handle_block(*id, statements, *tail, None, scopes, scope, const_scope); - } - Expr::Loop { body: body_expr, label, source: _ } => { - let mut scope = scopes.new_labeled_scope(*scope, *label); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Closure { - args, - arg_types, - ret_type, - body: body_expr, - capture_by: _, - closure_kind: _, - } => { - arg_types - .iter() - .flatten() - .for_each(|type_ref| compute_type_scopes(*type_ref, store, scopes, const_scope)); - if let Some(type_ref) = ret_type { - compute_type_scopes(*type_ref, store, scopes, const_scope); - } - let mut scope = scopes.new_scope(*scope); - args.iter().for_each(|arg| scopes.add_pat_bindings(store, scope, *arg)); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Match { expr, arms } => { - compute_expr_scopes(scopes, *expr, scope, const_scope); - for arm in arms.iter() { - let mut scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, scope, arm.pat); - if let Some(guard) = arm.guard { - scope = scopes.new_scope(scope); - compute_expr_scopes(scopes, guard, &mut scope, const_scope); - } - compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope); - } - } - &Expr::If { condition, then_branch, else_branch } => { - let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope); - compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope); - if let Some(else_branch) = else_branch { - compute_expr_scopes(scopes, else_branch, scope, const_scope); - } - } - &Expr::Let { pat, expr } => { - compute_expr_scopes(scopes, expr, scope, const_scope); - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, pat); - } - _ => { - store.visit_expr_children(expr, ExprScopeVisitor { store, scopes, scope, const_scope }) - } - }; + fn on_type(&mut self, ty: TypeRefId) { + self.with_scope(self.const_scope, |this| self.store.visit_type_ref_children(ty, this)); + } } #[cfg(test)] @@ -497,16 +449,27 @@ mod tests { let (file_id, _) = editioned_file_id.unpack(&db); let file_syntax = editioned_file_id.parse(&db).syntax_node(); - let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); + let marker: Option = find_node_at_offset(&file_syntax, offset); let function = find_function(&db, file_id); let scopes = ExprScopes::of(&db, DefWithBodyId::from(function)); - let (_body, source_map) = Body::with_source_map(&db, function.into()); - - let expr_id = source_map - .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) - .unwrap() - .as_expr() + let (body, source_map) = Body::with_source_map(&db, function.into()); + + let expr_id = marker + .and_then(|marker| { + source_map + .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) + .and_then(|expr| expr.as_expr()) + }) + .or_else(|| { + body.exprs().find_map(|(expr, value)| { + let crate::hir::Expr::Path(path) = value else { return None }; + path.mod_path() + .and_then(|path| path.as_ident()) + .is_some_and(|name| name.as_str() == "marker") + .then_some(expr) + }) + }) .unwrap(); let scope = scopes.scope_for(expr_id); @@ -533,6 +496,58 @@ fn f(param: usize) { ); } + #[test] + fn pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _: builtin#pattern_type (usize is 0..=$0) = 0; +} +"#, + &["param"], + ); + } + + #[test] + fn closure_pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = |_: builtin#pattern_type (usize is 0..=$0)| {}; +} +"#, + &["param"], + ); + } + + #[test] + fn array_repeat_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = [(); $0]; +} +"#, + &["param"], + ); + } + + #[test] + fn inline_const_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = const { $0 }; +} +"#, + &["param"], + ); + } + #[test] fn test_lambda_scope() { do_check( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 1443d3ea4be4c..61d578bfda7ff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -161,7 +161,7 @@ pub struct ItemScope { /// Module scoped macros will be inserted into `items` instead of here. // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will // be all resolved to the last one defined if shadowing happens. - legacy_macros: FxHashMap>, + legacy_macros: FxHashMap>, /// The attribute macro invocations in this scope. attr_macros: FxHashMap, MacroCallId>, /// The macro invocations in this scope. @@ -621,7 +621,11 @@ impl ItemScope { // for that. } _ => { - if glob_imports.types.remove(&lookup) { + // A non-glob import either shadows a glob import of the same + // name, or re-resolves a stale binding it recorded earlier. + if glob_imports.types.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( @@ -659,19 +663,22 @@ impl ItemScope { changed = true; } Entry::Occupied(mut entry) - if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.values.remove(&lookup) => + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => { - cov_mark::hit!(import_shadowed); - let import = import.and_then(ImportOrExternCrate::import_or_glob); - let prev = std::mem::replace(&mut fld.import, import); - if let Some(import) = import { - self.use_imports_values - .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + if glob_imports.values.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { + cov_mark::hit!(import_shadowed); + + let prev = std::mem::replace(&mut fld.import, import); + if let Some(import) = import { + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + } + entry.insert(fld); + changed = true; } - entry.insert(fld); - changed = true; } _ => {} } @@ -699,7 +706,8 @@ impl ItemScope { } Entry::Occupied(mut entry) if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.macros.remove(&lookup) => + && (glob_imports.macros.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import)) => { cov_mark::hit!(import_shadowed); let prev = std::mem::replace(&mut fld.import, import); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index fde1db4734a78..150b4eeb60f2b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -1,11 +1,11 @@ -//! This modules implements a function to resolve a path `foo::bar::baz` to a -//! def, which is used within the name resolution. +//! This module implements a function to resolve a path `foo::bar::baz` to a +//! def, which is used within name resolution. //! //! When name resolution is finished, the result of resolving a path is either -//! `Some(def)` or `None`. However, when we are in process of resolving imports +//! `Some(def)` or `None`. However, when we are in the process of resolving imports //! or macros, there's a third possibility: //! -//! I can't resolve this path right now, but I might be resolve this path +//! I can't resolve this path right now, but I might be able to resolve this path //! later, when more macros are expanded. //! //! `ReachedFixedPoint` signals about this. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 8721cd65dbac7..1cdc9b1f2c3d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -35,6 +35,16 @@ pub struct Item { pub import: Option, } +impl Item { + /// Whether `import` is the same import that produced `self`, now resolving to a different + /// `def`. This happens when an import is first recorded as an indeterminate resolution + /// (e.g. only one namespace was available at the time) and later re-resolves to another + /// def, such as an explicit import that shadows a glob only after the glob has been seen. + pub(crate) fn is_reresolved_by(&self, def: &Def, import: Option) -> bool { + import.is_some() && self.import == import && self.def != *def + } +} + pub type TypesItem = Item; pub type ValuesItem = Item; // May be Externcrate for `[macro_use]`'d macros @@ -109,16 +119,16 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { - self.values.map(|it| (it.def, it.import)) + pub fn take_values_full(self) -> Option { + self.values } pub fn take_macros(self) -> Option { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { - self.macros.map(|it| (it.def, it.import)) + pub fn take_macros_full(self) -> Option { + self.macros } pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 63ff384de021a..5b11f5ff8bbb6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -33,7 +33,7 @@ use crate::{ item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ItemScope}, lang_item::LangItemTarget, nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map}, - per_ns::PerNs, + per_ns::{MacrosItem, PerNs}, signatures::ImplSignature, src::HasSource, type_ref::LifetimeRef, @@ -174,7 +174,9 @@ impl<'db> Resolver<'db> { path: &Path, ) -> Option<(TypeNs, Option, Option)> { self.resolve_path_in_type_ns_with_prefix_info(db, path).map( - |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import), + |(resolution, remaining_segments, import, _, _)| { + (resolution, remaining_segments, import) + }, ) } @@ -182,8 +184,13 @@ impl<'db> Resolver<'db> { &self, db: &dyn SourceDatabase, path: &Path, - ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> - { + ) -> Option<( + TypeNs, + Option, + Option, + ResolvePathResultPrefixInfo, + Visibility, + )> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => &it.mod_path, @@ -206,6 +213,7 @@ impl<'db> Resolver<'db> { seg.as_ref().map(|_| 1), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } }; @@ -230,6 +238,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } else if let &GenericDefId::AdtId(adt) = def @@ -240,6 +249,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_type_by_name(first_name, *def) { @@ -248,6 +258,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -264,6 +275,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, ) } else { res @@ -323,7 +335,7 @@ impl<'db> Resolver<'db> { path: &Path, hygiene_id: HygieneId, ) -> Option { - self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it) + self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _, _)| it) } fn skip_to_mod<'this, T>( @@ -343,7 +355,7 @@ impl<'db> Resolver<'db> { db: &dyn SourceDatabase, path: &Path, mut hygiene_id: HygieneId, - ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo, Visibility)> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => &it.mod_path, @@ -363,6 +375,7 @@ impl<'db> Resolver<'db> { | LangItemTarget::MacroId(_) => return None, }), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } Path::LangItem(l, Some(_)) => { @@ -383,6 +396,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(type_ns, 0), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } }; @@ -408,6 +422,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.binding())), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -421,6 +436,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_const_by_name(first_name, *def) { @@ -428,6 +444,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(val), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -448,6 +465,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } else if let &GenericDefId::AdtId(adt) = def @@ -457,6 +475,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(ty, 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_type_by_name(first_name, *def) { @@ -464,6 +483,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(ty, 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -490,6 +510,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } @@ -513,7 +534,7 @@ impl<'db> Resolver<'db> { db: &dyn SourceDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option { let (item_map, item_local_map, module) = self.item_scope_(); item_map .resolve_path( @@ -525,7 +546,7 @@ impl<'db> Resolver<'db> { expected_macro_kind, ) .0 - .take_macros_import() + .take_macros_full() } pub fn resolve_path_as_macro_def( @@ -534,7 +555,7 @@ impl<'db> Resolver<'db> { path: &ModPath, expected_macro_kind: Option, ) -> Option { - self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| it.definition(db)) + self.resolve_path_as_macro(db, path, expected_macro_kind).map(|it| it.def.definition(db)) } pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { @@ -1166,7 +1187,7 @@ impl<'db> ModuleItemMap<'db> { &self, db: &'db dyn SourceDatabase, path: &ModPath, - ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo, Visibility)> { let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally( self.local_def_map, db, @@ -1176,12 +1197,12 @@ impl<'db> ModuleItemMap<'db> { ); match unresolved_idx { None => { - let value = to_value_ns(module_def, self.def_map)?; - Some((ResolveValueResult::ValueNs(value), prefix_info)) + let (value, vis) = to_value_ns(module_def, self.def_map)?; + Some((ResolveValueResult::ValueNs(value), prefix_info, vis)) } Some(unresolved_idx) => { - let def = module_def.take_types()?; - let ty = match def { + let res = module_def.take_types_full()?; + let ty = match res.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), @@ -1194,7 +1215,7 @@ impl<'db> ModuleItemMap<'db> { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info)) + Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info, res.vis)) } } } @@ -1203,8 +1224,13 @@ impl<'db> ModuleItemMap<'db> { &self, db: &dyn SourceDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> - { + ) -> Option<( + TypeNs, + Option, + Option, + ResolvePathResultPrefixInfo, + Visibility, + )> { let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally( self.local_def_map, db, @@ -1212,17 +1238,22 @@ impl<'db> ModuleItemMap<'db> { path, BuiltinShadowMode::Other, ); - let (res, import) = to_type_ns(module_def)?; - Some((res, idx, import, prefix_info)) + let (res, import, vis) = to_type_ns(module_def)?; + Some((res, idx, import, prefix_info, vis)) } } -fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option { - let def = per_ns.take_values().or_else(|| { - let Some(MacroId::ProcMacroId(proc_macro)) = per_ns.take_macros() else { return None }; +fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<(ValueNs, Visibility)> { + let (def, vis) = per_ns.take_values_full().map(|res| (res.def, res.vis)).or_else(|| { + let Some(MacrosItem { def: MacroId::ProcMacroId(proc_macro), vis, .. }) = + per_ns.take_macros_full() + else { + return None; + }; // If we cannot resolve to value ns, but we can resolve to a proc macro, and this is the crate // defining this proc macro - inside this crate, we should treat the macro as a function. - def_map.proc_macro_as_fn(proc_macro).map(ModuleDefId::FunctionId) + let def = ModuleDefId::FunctionId(def_map.proc_macro_as_fn(proc_macro)?); + Some((def, vis)) })?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), @@ -1238,10 +1269,10 @@ fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option { | ModuleDefId::MacroId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, vis)) } -fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { +fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option, Visibility)> { let def = per_ns.take_types_full()?; let res = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), @@ -1259,7 +1290,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some((res, def.import)) + Some((res, def.import, def.vis)) } #[derive(Default)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 10a38ec71e372..ab5d377387f49 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -52,7 +52,7 @@ pub struct StructSignature { bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct StructFlags: u8 { + pub struct StructFlags: u16 { /// Indicates whether this struct has `#[repr]`. const HAS_REPR = 1 << 0; /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. @@ -69,6 +69,8 @@ bitflags! { const IS_UNSAFE_CELL = 1 << 6; /// Indicates whether this struct is `UnsafePinned`. const IS_UNSAFE_PINNED = 1 << 7; + /// Indicates whether this struct is `CovariantUnsafeCell`. + const IS_COVARIANT_UNSAFE_CELL = 1 << 8; } } @@ -104,6 +106,9 @@ impl StructSignature { _ if lang == sym::owned_box => flags |= StructFlags::IS_BOX, _ if lang == sym::manually_drop => flags |= StructFlags::IS_MANUALLY_DROP, _ if lang == sym::unsafe_cell => flags |= StructFlags::IS_UNSAFE_CELL, + _ if lang == sym::covariant_unsafe_cell => { + flags |= StructFlags::IS_COVARIANT_UNSAFE_CELL + } _ if lang == sym::unsafe_pinned => flags |= StructFlags::IS_UNSAFE_PINNED, _ => (), } @@ -545,7 +550,7 @@ impl TraitSignature { let attrs = AttrFlags::query(db, id.into()); let source = loc.source(db); if source.value.auto_token().is_some() { - flags.insert(TraitFlags::AUTO); + flags.insert(TraitFlags::AUTO | TraitFlags::COINDUCTIVE); } if source.value.unsafe_token().is_some() { flags.insert(TraitFlags::UNSAFE); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 5e85a710e0855..63e57647283cf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -412,7 +412,7 @@ fn name_to_token( })?; let span = token_map.span_at(name.syntax().text_range().start()); - let name_token = tt::Ident::new(name.text().as_ref(), span); + let name_token = tt::Ident::new(name.text(), span); Ok(name_token) } @@ -1184,7 +1184,7 @@ fn coerce_pointee_expand( let new_bounds = bounds.bounds().filter_map(|bound| { let new_bound = substitute_type_bound( bound.clone(), - &pointee_param_name.text(), + pointee_param_name.text(), ADDED_PARAM, ); @@ -1197,7 +1197,7 @@ fn coerce_pointee_expand( let new_bounds_target = if is_pointee { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m_name.text()) + make.name_ref(param_name.text()) }; new_predicates.push(make.where_pred( Either::Right( @@ -1240,12 +1240,12 @@ fn coerce_pointee_expand( // If the target type references the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. if let Some(predicate_with_substituted_target) = - substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM) + substitute_where_pred(&predicate, pointee_param_name.text(), ADDED_PARAM) { new_predicates.push(predicate_with_substituted_target); } else if let Some(bounds) = predicate.type_bound_list() { let new_bounds = bounds.bounds().filter_map(|bound| { - substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM) + substitute_type_bound(bound, pointee_param_name.text(), ADDED_PARAM) }); new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds)); } @@ -1259,7 +1259,7 @@ fn coerce_pointee_expand( new_predicates.push( make.where_pred( Either::Right(make.ty_path_from_segments( - [make.path_segment(make.name_ref(&pointee_param_name.text()))], + [make.path_segment(make.name_ref(pointee_param_name.text()))], false, )), [make.type_bound( @@ -1294,7 +1294,7 @@ fn coerce_pointee_expand( .filter_map(|param| { Some(match param { ast::GenericParam::ConstParam(param) => { - ast::GenericArg::ConstArg(make.expr_const_value(¶m.name()?.text())) + ast::GenericArg::ConstArg(make.expr_const_value(param.name()?.text())) } ast::GenericParam::LifetimeParam(param) => { make.lifetime_arg(param.lifetime()?).into() @@ -1303,7 +1303,7 @@ fn coerce_pointee_expand( let name = if pointee_param_idx == type_param_idx { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m.name()?.text()) + make.name_ref(param.name()?.text()) }; type_param_idx += 1; make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false)) @@ -1314,7 +1314,7 @@ fn coerce_pointee_expand( make.path_from_segments( [make.generic_ty_path_segment( - make.name_ref(&struct_name.text()), + make.name_ref(struct_name.text()), self_params_for_traits, )], false, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index d91b0f378e191..7968adabbccf2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -246,14 +246,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::new_root(&self.text()), + None => Name::new_root(self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::new_root(&self.text()) + Name::new_root(self.text()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 15dd530312067..7aa6604ac09b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -361,7 +361,7 @@ pub(crate) fn create_anon_const<'a, 'db>( interner: DbInterner<'db>, owner: ExpressionStoreOwnerId, store: &ExpressionStore, - expr: ExprId, + expr_id: ExprId, resolver: &Resolver<'db>, expected_ty: Ty<'db>, generics: &dyn Fn() -> &'a Generics<'db>, @@ -369,10 +369,19 @@ pub(crate) fn create_anon_const<'a, 'db>( lowering_mode: LoweringMode, forbid_params_after: Option, ) -> Result, CreateConstError<'db>> { - match &store[expr] { + let mut expr = &store[expr_id]; + if let Expr::Block { statements, tail: Some(tail), .. } = expr + && statements.is_empty() + { + // rustc unwraps *one* layer of blocks, so we do too (this impacts whether the const can use generic parameters. + // Anon consts sometimes cannot while bare paths can). mGCA allows arbitrarily many blocks, but we don't implement + // it yet. + expr = &store[*tail]; + } + match expr { Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty), Expr::Underscore => match create_var { - Some(create_var) => Ok(create_var(expr.into())), + Some(create_var) => Ok(create_var(expr_id.into())), None => Err(CreateConstError::UnderscoreExpr), }, Expr::Path(path) @@ -395,7 +404,7 @@ pub(crate) fn create_anon_const<'a, 'db>( interner.db, AnonConstLoc { owner, - expr, + expr: expr_id, ty: StoredEarlyBinder::bind(expected_ty.store()), allow_using_generic_params, }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 8e7e55a77b349..9853f174eb76b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -27,7 +27,7 @@ use crate::{ consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds}, + lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds, WithDefinedOpaques}, mir::{MirBody, MirLowerError}, next_solver::{ Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, @@ -172,7 +172,7 @@ pub trait HirDatabase: SourceDatabase + 'static { fn type_for_type_alias_with_diagnostics<'db>( &'db self, def: TypeAliasId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder> { + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques>> { let db = self.as_dyn(); crate::lower::type_for_type_alias_with_diagnostics(db, def) } @@ -275,12 +275,12 @@ pub trait HirDatabase: SourceDatabase + 'static { crate::lower::callable_item_signature(db, def) } - fn callable_item_signature_with_diagnostics<'db>( + fn fn_sig_for_fn_with_diagnostics<'db>( &'db self, - def: CallableDefId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder> { + def: FunctionId, + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques>> { let db = self.as_dyn(); - crate::lower::callable_item_signature_with_diagnostics(db, def) + crate::lower::fn_sig_for_fn(db, def) } fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db> { @@ -513,8 +513,9 @@ impl<'db> AnonConstId<'db> { result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts()); result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts()); } - GenericDefId::FunctionId(id) => result - .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()), + GenericDefId::FunctionId(id) => { + result.push(db.fn_sig_for_fn_with_diagnostics(id).defined_anon_consts()) + } GenericDefId::ConstId(def) => { result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index bd00da84cafad..935b2f9ffaba3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -99,7 +99,7 @@ use crate::{ }, method_resolution::CandidateId, next_solver::{ - AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, + AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredFnSig, StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, @@ -410,6 +410,11 @@ pub enum InferenceDiagnostic { expected: usize, #[type_visitable(ignore)] found: usize, + /// True when the call goes through the `Fn`/`FnMut`/`FnOnce` trait + /// (i.e. arguments were bundled into a tuple). Determines whether the + /// diagnostic surface uses E0057 (Fn-trait call) or E0061 (regular call). + #[type_visitable(ignore)] + is_fn_trait_call: bool, }, MismatchedTupleStructPatArgCount { #[type_visitable(ignore)] @@ -820,7 +825,7 @@ pub struct InferenceResult<'db> { defined_anon_consts: ThinVec>, } -#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct ClosureData { /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. @@ -849,6 +854,42 @@ pub struct ClosureData { /// information on `t` in order to create place `t.0` and `t.1`. We can solve this /// issue by fake reading `t`. pub fake_reads: Box<[(Place, FakeReadCause, SmallVec<[CaptureSourceStack; 2]>)]>, + + /// For each fn, records the "liberated" types of its arguments + /// and return type. Liberated means that all bound regions + /// (including late-bound regions) are replaced with free + /// equivalents. This table is not used in codegen (since regions + /// are erased there) and hence is not serialized to metadata. + /// + /// This table also contains the "revealed" values for any `impl Trait` + /// that appear in the signature and whose values are being inferred + /// by this function. + /// + /// # Example + /// + /// ```rust + /// # use std::fmt::Debug; + /// fn foo(x: &u32) -> impl Debug { *x } + /// ``` + /// + /// The function signature here would be: + /// + /// ```ignore (illustrative) + /// for<'a> fn(&'a u32) -> Foo + /// ``` + /// + /// where `Foo` is an opaque type created for this function. + /// + /// + /// The *liberated* form of this would be + /// + /// ```ignore (illustrative) + /// fn(&'a u32) -> u32 + /// ``` + /// + /// Note that `'a` is not bound (it would be an `ReLateParam`) and + /// that the `Foo` opaque type is replaced by its hidden type. + pub liberated_sig: StoredFnSig, } /// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. @@ -1044,10 +1085,7 @@ impl<'db> InferenceResult<'db> { fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult<'_> { infer_query(db, def) } -} -#[salsa::tracked] -impl<'db> InferenceResult<'db> { /// Infer types for all const expressions in an item's signature. /// /// Returns an `InferenceResult` containing type information for array lengths, @@ -1400,7 +1438,7 @@ impl<'db> InferenceContext<'db> { lowering_mode: LoweringMode, ) -> Self { let trait_env = db.trait_environment(generic_def); - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), @@ -1677,7 +1715,7 @@ impl<'db> InferenceContext<'db> { } pat_adjustments.shrink_to_fit(); for closure_data in closures_data.values_mut() { - let ClosureData { min_captures, fake_reads } = closure_data; + let ClosureData { min_captures, fake_reads, liberated_sig } = closure_data; let dummy_place = || Place { base_ty: types.types.error.store(), base: closure::analysis::expr_use_visitor::PlaceBase::Rvalue, @@ -1706,6 +1744,8 @@ impl<'db> InferenceContext<'db> { min_capture.shrink_to_fit(); } min_captures.shrink_to_fit(); + + resolver.resolve_completely(liberated_sig); } closures_data.shrink_to_fit(); *tuple_field_access_types = tuple_field_accesses_rev diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index e2948a81ac7d9..9af182fc49288 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -9,6 +9,7 @@ use hir_def::{ hir::{ClosureKind, CoroutineKind, CoroutineSource, ExprId, PatId}, type_ref::TypeRefId, }; +use indexmap::IndexMap; use rustc_abi::ExternAbi; use rustc_type_ir::{ AliasTyKind, ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, @@ -21,11 +22,11 @@ use tracing::{debug, instrument}; use crate::{ Span, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, - infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, + infer::{BreakableKind, ClosureData, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArg, PolyFnSig, - PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, TermId, Ty, TyKind, - Unnormalized, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, StoredFnSig, TermId, Ty, + TyKind, Unnormalized, abi::Safety, infer::{ BoundRegionConversionTime, InferOk, InferResult, @@ -303,6 +304,15 @@ impl<'db> InferenceContext<'db> { } }; + self.result.closures_data.insert( + closure_expr, + ClosureData { + liberated_sig: StoredFnSig::new(liberated_sig), + fake_reads: Box::default(), + min_captures: IndexMap::default(), + }, + ); + // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(liberated_sig.inputs()) { self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 38e634eb7db49..0c24b82d1bde0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -515,7 +515,7 @@ impl<'db> InferenceContext<'db> { let fake_reads = delegate.fake_reads; - self.result.closures_data.entry(closure_expr_id).or_default().fake_reads = + self.result.closures_data.get_mut(&closure_expr_id).unwrap().fake_reads = fake_reads.into_boxed_slice(); // If we are also inferred the closure kind here, @@ -730,8 +730,7 @@ impl<'db> InferenceContext<'db> { return; } - let mut closure_data = - self.result.closures_data.remove(&closure_def_id).unwrap_or_default(); + let mut closure_data = self.result.closures_data.remove(&closure_def_id).unwrap(); let root_var_min_capture_list = &mut closure_data.min_captures; let mut dedup_sources_scratch = FxHashMap::default(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 20cfc9008a9e1..570df6b871df1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2015,10 +2015,9 @@ impl<'db> InferenceContext<'db> { match tuple_type.kind() { // We expected a tuple and got a tuple TyKind::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - // FIXME: Emit an error. - } + // Argument length differs. The mismatch is reported below by the + // shared `MismatchedArgCount` push (with `is_fn_trait_call = true`, + // which the diagnostic surface renders as E0057). let expected_input_tys = match expected_input_tys { Some(expected_input_tys) => match expected_input_tys.first() { Some(ty) => match ty.kind() { @@ -2068,6 +2067,7 @@ impl<'db> InferenceContext<'db> { call_expr, expected: expected_input_tys.len() + skip_indices.len(), found: provided_args.len(), + is_fn_trait_call: tuple_arguments == TupleArgumentsFlag::TupleArguments, }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index b226e5ca85de0..c2e39709e8949 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -235,7 +235,7 @@ impl<'db> InferenceContext<'db> { // We have to replace the operator with the mutable variant for the // program to compile, so we don't really have a choice here and want - // to just try using `DerefMut` even if its not in the item bounds + // to just try using `DerefMut` even if it's not in the item bounds // of the opaque. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6157f51500d9e..7b589efba2f63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,7 +3,7 @@ use std::fmt; use base_db::Crate; -use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ TyVid, TypeFoldable, TypeVisitableExt, @@ -14,7 +14,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::{ - InferenceDiagnostic, Span, + InferBodyId, InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, @@ -144,7 +144,7 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: ExpressionStoreOwnerId, + owner: InferBodyId<'db>, ) -> Self { let interner = DbInterner::new_with(db, krate); let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 1f321af79493f..777d0803fc799 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -142,8 +142,8 @@ fn repr_discr( Integer::I8 }; - // If there are no negative values, we can use the unsigned fit. - Ok(if min >= 0 { + // `min` and `max` are the ends of a wrapping range, so their sign is not a usable test. + Ok(if unsigned_fit <= signed_fit { (cmp::max(unsigned_fit, at_least), false) } else { (cmp::max(signed_fit, at_least), true) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b9ee38c44fe41..b5db24e98bc70 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -610,6 +610,13 @@ fn enums_with_discriminants() { A = 1, // This one is (perhaps surprisingly) zero sized. } } + size_and_align! { + #[allow(overflowing_literals, clippy::enum_clike_unportable_variant)] + enum Goal { + A = 0, + B = 0x8000_0000_0000_0001, // Wraps around to a negative discriminant. + } + } } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c631c87de5160..afd500a3e3343 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -111,9 +111,9 @@ pub use infer::{ infer_query_with_inspect, }; pub use lower::{ - FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, - LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, - TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*, + FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, LifetimeElisionKind, + LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, + TyLoweringResult, ValueTyDefId, diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; @@ -216,7 +216,7 @@ impl<'db> MemoryMap<'db> { } } -/// Return an index of a parameter in the generic type parameter list by it's id. +/// Returns the index of a parameter in the generic type parameter list by its id. pub fn type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> u32 { generics::generics(db, id.parent).type_or_const_param_idx(id) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index bdb882b70b259..098a43a876a7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -10,7 +10,6 @@ pub(crate) mod path; use std::{cell::OnceCell, iter, mem, sync::OnceLock}; -use base_db::salsa::update_fallback_db; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, @@ -80,13 +79,14 @@ use crate::{ pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); #[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTraits { - pub(crate) impl_traits: Arena, +pub struct WithDefinedOpaques { + value: T, + impl_traits: Option>>, } #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { - pub(crate) predicates: StoredClauses, + pub(crate) predicates: StoredEarlyBinder, pub(crate) assoc_ty_bounds_start: u32, } @@ -417,6 +417,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { BoundVarKinds::new_from_iter(interner, args) } + + fn take_defined_opaques(&mut self) -> Option>> { + if self.impl_trait_mode.opaque_type_data.is_empty() { + None + } else { + self.impl_trait_mode.opaque_type_data.shrink_to_fit(); + Some(Box::new(mem::take(&mut self.impl_trait_mode.opaque_type_data))) + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -631,7 +640,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // place even if we encounter more opaque types while // lowering the bounds let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { - predicates: Clauses::empty(interner).store(), + predicates: StoredEarlyBinder::bind(Clauses::empty(interner).store()), assoc_ty_bounds_start: 0, }); @@ -1319,7 +1328,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds; ImplTrait { - predicates: Clauses::new_from_slice(&predicates).store(), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), assoc_ty_bounds_start, } } @@ -1365,7 +1374,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { pub struct TyLoweringResult<'db, T> { #[update(fallback)] pub value: T, - #[update(bounds(TyLoweringResultInfo<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] info: Option>>, } @@ -1497,19 +1505,21 @@ pub(crate) fn impl_trait_with_diagnostics_cycle_result<'db>( impl ImplTraitId { #[inline] - pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + fn data(self, db: &dyn HirDatabase) -> &ImplTrait { let (impl_traits, idx) = match self { ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) + (ImplTrait::return_type_impl_traits(db, owner), idx) } ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) + (ImplTrait::type_alias_impl_traits(db, owner), idx) } }; - impl_traits - .as_deref() - .expect("owner should have opaque type") - .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) + &impl_traits[idx] + } + + #[inline] + pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.data(db).predicates.get().map_bound(|it| it.as_slice()) } #[inline] @@ -1517,24 +1527,8 @@ impl ImplTraitId { self, db: &'db dyn HirDatabase, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - let (impl_traits, idx) = match self { - ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) - } - ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) - } - }; - let predicates = - impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { - let impl_trait = &it.impl_traits[idx]; - ( - impl_trait.predicates.as_ref().as_slice(), - impl_trait.assoc_ty_bounds_start as usize, - ) - }); - - predicates.map_bound(|(preds, len)| &preds[..len]) + let data = self.data(db); + data.predicates.get().map_bound(|it| &it.as_slice()[..data.assoc_ty_bounds_start as usize]) } } @@ -1553,71 +1547,25 @@ impl InternedOpaqueTyId<'_> { } } -#[salsa::tracked] -impl ImplTraits { - #[salsa::tracked(returns(ref))] +impl ImplTrait { + #[inline] pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, - def: hir_def::FunctionId, - ) -> Option>> { - // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe - let data = FunctionSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::Infer, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(ret_type) = data.ret_type { - let _ret = ctx_ret.lower_ty(ret_type); - } - let mut return_type_impl_traits = - ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; - if return_type_impl_traits.impl_traits.is_empty() { - None - } else { - return_type_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(return_type_impl_traits))) - } + def: FunctionId, + ) -> &Arena { + fn_sig_for_fn(db, def).value.impl_traits.as_deref().unwrap_or(const { &Arena::new() }) } - #[salsa::tracked(returns(ref))] + #[inline] pub(crate) fn type_alias_impl_traits( db: &dyn HirDatabase, - def: hir_def::TypeAliasId, - ) -> Option>> { - let data = TypeAliasSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::AnonymousReportError, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(type_ref) = data.ty { - let _ty = ctx.lower_ty(type_ref); - } - let mut type_alias_impl_traits = - ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; - if type_alias_impl_traits.impl_traits.is_empty() { - None - } else { - type_alias_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(type_alias_impl_traits))) - } + def: TypeAliasId, + ) -> &Arena { + type_for_type_alias_with_diagnostics(db, def) + .value + .impl_traits + .as_deref() + .unwrap_or(const { &Arena::new() }) } } @@ -1666,7 +1614,7 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind it, GenericArgs::identity_for_item(interner, it.into()), )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.value.get(), } } @@ -1804,13 +1752,14 @@ pub(crate) fn value_ty<'db>( pub(crate) fn type_for_type_alias_with_diagnostics<'db>( db: &'db dyn HirDatabase, t: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { +) -> TyLoweringResult<'db, WithDefinedOpaques>> { let type_alias_data = TypeAliasSignature::of(db, t); let interner = DbInterner::new_no_crate(db); if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_foreign(interner, t.into()).store(), - )) + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store()), + impl_traits: None, + }) } else { let resolver = t.resolver(db); let generics = OnceCell::new(); @@ -1832,7 +1781,10 @@ pub(crate) fn type_for_type_alias_with_diagnostics<'db>( .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) .store(), ); - TyLoweringResult::from_ctx(res, ctx) + TyLoweringResult::from_ctx( + WithDefinedOpaques { value: res, impl_traits: ctx.take_defined_opaques() }, + ctx, + ) } } @@ -1840,10 +1792,13 @@ pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( db: &'db dyn HirDatabase, _: salsa::Id, _adt: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), - )) +) -> TyLoweringResult<'db, WithDefinedOpaques>> { + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind( + Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), + ), + impl_traits: None, + }) } pub(crate) fn impl_self_ty_query<'db>( @@ -2822,27 +2777,18 @@ pub(crate) fn callable_item_signature<'db>( db: &'db dyn HirDatabase, def: CallableDefId, ) -> EarlyBinder<'db, PolyFnSig<'db>> { - callable_item_signature_with_diagnostics(db, def).value.get() -} - -#[salsa::tracked(returns(ref))] -pub(crate) fn callable_item_signature_with_diagnostics<'db>( - db: &'db dyn HirDatabase, - def: CallableDefId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)), - CallableDefId::EnumVariantId(e) => { - TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e)) - } + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f).value.value.get(), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s).get(), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e).get(), } } -fn fn_sig_for_fn<'db>( +#[salsa::tracked(returns(ref))] +pub(crate) fn fn_sig_for_fn<'db>( db: &'db dyn HirDatabase, def: FunctionId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { +) -> TyLoweringResult<'db, WithDefinedOpaques>> { let data = FunctionSignature::of(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); @@ -2874,6 +2820,7 @@ fn fn_sig_for_fn<'db>( Some(ret_type) => ctx_ret.lower_ty(ret_type), None => Ty::new_unit(interner), }; + let impl_traits = ctx_ret.take_defined_opaques(); let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); ctx_params.diagnostics.extend(ctx_ret.diagnostics); @@ -2891,7 +2838,7 @@ fn fn_sig_for_fn<'db>( }, binder, ))); - TyLoweringResult::from_ctx(result, ctx_params) + TyLoweringResult::from_ctx(WithDefinedOpaques { value: result, impl_traits }, ctx_params) } fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { @@ -2901,13 +2848,14 @@ fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, T EarlyBinder::bind(ty) } -fn fn_sig_for_struct_constructor( +fn ctor_signature( db: &dyn HirDatabase, - def: StructId, + variant: VariantId, + adt: AdtId, ) -> StoredEarlyBinder { - let field_tys = db.field_types(def.into()); + let field_tys = db.field_types(variant); let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let ret = type_for_adt(db, def.into()).skip_binder(); + let ret = type_for_adt(db, adt).skip_binder(); let inputs_and_output = Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); @@ -2917,21 +2865,20 @@ fn fn_sig_for_struct_constructor( }))) } +#[salsa::tracked(returns(ref))] +fn fn_sig_for_struct_constructor( + db: &dyn HirDatabase, + def: StructId, +) -> StoredEarlyBinder { + ctor_signature(db, def.into(), def.into()) +} + +#[salsa::tracked(returns(ref))] fn fn_sig_for_enum_variant_constructor( db: &dyn HirDatabase, def: EnumVariantId, ) -> StoredEarlyBinder { - let field_tys = db.field_types(def.into()); - let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let parent = def.lookup(db).parent; - let ret = type_for_adt(db, parent.into()).skip_binder(); - - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); - StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), - inputs_and_output, - }))) + ctor_signature(db, def.into(), def.lookup(db).parent.into()) } // FIXME: Remove this. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 77037c5b12d13..c67c69520db10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -334,7 +334,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[tracing::instrument(skip(self), ret)] pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { - let (resolution, remaining_index, _, prefix_info) = + let (resolution, remaining_index, _, prefix_info, _) = self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; let segments = self.segments; @@ -385,7 +385,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { &mut self, hygiene_id: HygieneId, ) -> Option { - let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( + let (res, prefix_info, _) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( self.ctx.db, self.path, hygiene_id, @@ -1234,6 +1234,12 @@ pub(crate) fn substs_from_args_and_bindings<'db>( }; params.next(); substs.push(self_ty); + } else if has_self_arg { + // A qualified path `::Assoc` where `Trait` resolved to something without a + // `Self` parameter, e.g. a struct. `check_generic_args_len()` skips the self type + // unconditionally, so drop it here too instead of matching it against a real parameter. + // FIXME: Report a diagnostic here, rustc emits `E0404: expected trait, found struct`. + args.next(); } loop { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e8702bf2c99de..97e9d8bae1907 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -31,7 +31,7 @@ use hir_def::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - TypeVisitableExt, + TypeVisitableExt, VisitorResult, fast_reject::{TreatParams, simplify_type}, inherent::{BoundExistentialPredicates, IntoKind}, }; @@ -54,6 +54,7 @@ use crate::{ obligation_ctxt::ObligationCtxt, util::clauses_as_obligations, }, + ret, traits::ParamEnvAndCrate, }; @@ -835,27 +836,34 @@ impl<'db> TraitImpls<'db> { } } - pub fn for_each_crate_and_block( + pub fn for_each_crate_and_block( db: &'db dyn HirDatabase, krate: Crate, block: Option>, - for_each: &mut dyn FnMut(&TraitImpls<'db>), - ) { + for_each: &mut dyn FnMut(&TraitImpls<'db>) -> R, + ) -> R { let blocks = std::iter::successors(block, |block| block.module(db).block(db)); - blocks.filter_map(|block| Self::for_block(db, block)).for_each(&mut *for_each); - Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each); + for impl_ in blocks.filter_map(|block| Self::for_block(db, block)) { + ret!(for_each(impl_)); + } + for impl_ in Self::for_crate_and_deps(db, krate) { + ret!(for_each(impl_)); + } + R::output() } /// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type. - pub fn for_each_crate_and_block_trait_and_type( + pub fn for_each_crate_and_block_trait_and_type( db: &'db dyn HirDatabase, krate: Crate, type_block: Option>, trait_block: Option>, - for_each: &mut dyn FnMut(&TraitImpls<'db>), - ) { + for_each: &mut dyn FnMut(&TraitImpls<'db>) -> R, + ) -> R { let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate); - in_self_and_deps.iter().for_each(|impls| for_each(impls)); + for impl_ in in_self_and_deps { + ret!(for_each(impl_)); + } // We must not provide duplicate impls to the solver. Therefore we work with the following strategy: // start from each block, and walk ancestors until you meet the other block. If they never meet, @@ -874,13 +882,20 @@ impl<'db> TraitImpls<'db> { .filter_map(move |block| TraitImpls::for_block(db, block)) }; if trait_block == type_block { - blocks_iter(trait_block) - .filter_map(|block| TraitImpls::for_block(db, block)) - .for_each(for_each); + for impl_ in + blocks_iter(trait_block).filter_map(|block| TraitImpls::for_block(db, block)) + { + ret!(for_each(impl_)); + } } else { - for_each_block(trait_block, type_block).for_each(&mut *for_each); - for_each_block(type_block, trait_block).for_each(for_each); + for impl_ in for_each_block(trait_block, type_block) { + ret!(for_each(impl_)); + } + for impl_ in for_each_block(type_block, trait_block) { + ret!(for_each(impl_)); + } } + R::output() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e968da5111add..b08381603470c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2548,7 +2548,6 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty: Ty<'db>, locals: &Locals<'a, 'db>, ) -> Result<'db, ()> { - // FIXME: support indirect references let layout = self.layout(ty)?; let my_size = self.size_of_sized(ty, locals, "value to patch address")?; use rustc_type_ir::TyKind; @@ -2574,9 +2573,30 @@ impl<'a, 'db> Evaluator<'a, 'db> { )?; } None => { - let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); - if let Some(it) = patch_map.get(¤t) { - self.write_memory(addr, &it.to_le_bytes())?; + let bytes = self.read_memory(addr, my_size)?; + let (current, metadata) = bytes.split_at(my_size / 2); + let metadata = metadata.to_vec(); + let current = from_bytes!(usize, current); + let patched = match patch_map.get(¤t) { + Some(it) => { + self.write_memory(addr, &it.to_le_bytes())?; + *it + } + None => current, + }; + let patched = Address::from_usize(patched); + if let TyKind::Slice(inner) = t.kind() { + let len = from_bytes!(usize, metadata); + let size = self.size_of_sized(inner, locals, "slice item to patch")?; + for i in 0..len { + self.patch_addresses( + patch_map, + ty_of_bytes, + patched.offset(i * size), + inner, + locals, + )?; + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index e569b32bd779f..177b2c870f084 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1012,6 +1012,43 @@ impl<'a, 'db> Evaluator<'a, 'db> { let dst = Interval { addr: dst, size }; dst.write_from_interval(self, src) } + "slice_get_unchecked" => { + let [slice_ptr, index] = args else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked args are not provided".into(), + )); + }; + let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked item type is not provided".into(), + )); + }; + let slice_ptr = slice_ptr.get(self)?; + let ptr_size = self.ptr_size(); + let Some(data) = slice_ptr.get(..ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice pointer is too small".into(), + )); + }; + let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice metadata is missing".into(), + )); + }; + let slice_ptr = Address::from_bytes(data)?; + let len = from_bytes!(usize, len); + let index = from_bytes!(usize, index.get(self)?); + if index >= len { + return Err(MirEvalError::UndefinedBehavior(format!( + "slice_get_unchecked index {index} is out of bounds for slice of length {len}" + ))); + } + let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?; + let offset = index* size; + let addr = slice_ptr.to_usize() + offset; + let addr = Address::from_usize(addr); + destination.write_from_bytes(self, &addr.to_bytes()[..destination.size]) + } "offset" | "arith_offset" => { let [ptr, offset] = args else { return Err(MirEvalError::InternalError("offset args are not provided".into())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 68d19769d4811..7431ac8293e97 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -1114,6 +1114,109 @@ fn main() { ); } +#[test] +fn slice_get_unchecked_intrinsic() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn should_not_reach() { panic!() } + +fn main() { + let values = [10, 20, 30]; + let slice_ptr = &values as *const [i32]; + let item_ptr = unsafe { + slice_get_unchecked::<*const i32, *const [i32], i32>(slice_ptr, 1) + }; + if unsafe { *item_ptr } != 20 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] +fn slice_get_unchecked_out_of_bounds() { + check_error_with( + r#" +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn main() { + let values = [()]; + let slice_ptr = &values as *const [()]; + let _item = unsafe { + slice_get_unchecked::<*const (), *const [()], ()>(slice_ptr, 1) + }; +} +"#, + |e| { + let mut err = &e; + while let MirEvalError::InFunction(inner, _) = err { + err = inner; + } + matches!(err, MirEvalError::UndefinedBehavior(_)) + }, + ); +} + +#[test] +fn slice_get_unchecked_const_slice() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +struct Flag { + name: &'static str, + value: u16, +} + +const PURE: &str = "PURE"; +const NOMEM: &str = "NOMEM"; +const READONLY: &str = "READONLY"; +const PRESERVES_FLAGS: &str = "PRESERVES_FLAGS"; +const NORETURN: &str = "NORETURN"; +const NOSTACK: &str = "NOSTACK"; +const ATT_SYNTAX: &str = "ATT_SYNTAX"; +const FLAGS: &[Flag] = &[ + Flag { name: PURE, value: 1 }, + Flag { name: NOMEM, value: 2 }, + Flag { name: READONLY, value: 4 }, + Flag { name: PRESERVES_FLAGS, value: 8 }, + Flag { name: NORETURN, value: 16 }, + Flag { name: NOSTACK, value: 32 }, + Flag { name: ATT_SYNTAX, value: 64 }, +]; + +fn should_not_reach() { panic!() } + +fn main() { + let flag = unsafe { + slice_get_unchecked::<&Flag, &[Flag], Flag>(FLAGS, 6) + }; + let name = flag.name as *const str as *const u8; + if unsafe { *name } != b'A' || flag.value != 64 { + should_not_reach(); + } +} +"#, + ); +} + #[test] fn unreachable_intrinsic() { check_error_with( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1c7e5c2f51396..620d768cff422 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -53,7 +53,6 @@ use crate::{ next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, TypingMode, UnevaluatedConst, - abi::Safety, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -2147,11 +2146,10 @@ pub fn mir_body_for_closure_query<'db>( .store(), }); ctx.result.param_locals.push(closure_local); - - let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe); + let sig = infer.closures_data[&expr].liberated_sig.get(); let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr); let current = ctx.lower_params_and_bindings( - args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)), + args.iter().zip(sig.inputs().iter()).map(|(it, y)| (*it, *y)), None, |_| true, )?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index 8eb0a02694ce7..fdd67fc4fb626 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -150,3 +150,31 @@ fn caller(path: &PathBuf) { "#, ); } + +#[test] +fn borrowck_hrtb_closure_argument_does_not_panic() { + check_borrowck( + r#" +//- minicore: fn, copy +enum Res { + Ok(T), + Err(E), +} + +struct S; + +impl S { + fn set(&mut self, _: F) + where + F: for<'a> Fn(&mut (), &'a [u8]) -> Res<(), ()>, + { + } +} + +fn main() { + let mut s = S; + s.set(|_, _| Res::Err(())); +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs index 9585cced6b114..351d0c4cda47e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs @@ -71,17 +71,33 @@ impl StoredEarlyBinder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StoredPolyFnSig { bound_vars: StoredBoundVarKinds, - inputs_and_output: StoredTys, - fn_sig_kind: FnSigKind<'static>, + sig: StoredFnSig, } impl StoredPolyFnSig { #[inline] pub fn new(sig: PolyFnSig<'_>) -> Self { let bound_vars = sig.bound_vars().store(); - let sig = sig.skip_binder(); + Self { bound_vars, sig: StoredFnSig::new(sig.skip_binder()) } + } + + #[inline] + pub fn get(&self) -> PolyFnSig<'_> { + Binder::bind_with_vars(self.sig.get(), self.bound_vars.as_ref()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] +pub struct StoredFnSig { + inputs_and_output: StoredTys, + #[type_visitable(ignore)] + fn_sig_kind: FnSigKind<'static>, +} + +impl StoredFnSig { + #[inline] + pub fn new(sig: FnSig<'_>) -> Self { Self { - bound_vars, inputs_and_output: sig.inputs_and_output.store(), fn_sig_kind: FnSigKind::new( sig.fn_sig_kind.abi(), @@ -92,14 +108,8 @@ impl StoredPolyFnSig { } #[inline] - pub fn get(&self) -> PolyFnSig<'_> { - Binder::bind_with_vars( - FnSig { - inputs_and_output: self.inputs_and_output.as_ref(), - fn_sig_kind: self.fn_sig_kind, - }, - self.bound_vars.as_ref(), - ) + pub fn get(&self) -> FnSig<'_> { + FnSig { inputs_and_output: self.inputs_and_output.as_ref(), fn_sig_kind: self.fn_sig_kind } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 22b34b379dd86..483811f9e6f0c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -21,7 +21,8 @@ use rustc_type_ir::{ }; use crate::next_solver::{ - ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice, + ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, + impl_foldable_for_stored_type, interned_slice, }; use super::{ @@ -194,24 +195,7 @@ impl std::fmt::Debug for StoredGenericArg { } } -impl<'db> TypeVisitable> for StoredGenericArg { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - -impl<'db> TypeFoldable> for StoredGenericArg { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} +impl_foldable_for_stored_type!(StoredGenericArg); #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct GenericArg<'db> { @@ -473,28 +457,10 @@ interned_slice!( GenericArg<'static>, ); impl_foldable_for_interned_slice!(GenericArgs); +impl_foldable_for_stored_type!(StoredGenericArgs); impl<'db> rustc_type_ir::inherent::GenericArg> for GenericArg<'db> {} -impl<'db> TypeVisitable> for StoredGenericArgs { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - -impl<'db> TypeFoldable> for StoredGenericArgs { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} - trait GenericArgsBuilder<'db>: AsRef<[GenericArg<'db>]> { fn push(&mut self, arg: GenericArg<'db>); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 7e2dfb7112d3b..f251dcfdcfb6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -131,7 +131,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`. /// /// If this certainty is `Yes`, then we must be confident that the candidate - /// must hold iff it's nested goals hold. This is not true if the certainty is + /// must hold iff its nested goals hold. This is not true if the certainty is /// `Maybe(..)`, which suggests we forced ambiguity instead. /// /// This is *not* the certainty of the candidate's full nested evaluation, which diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index a7216a034cc56..02baf27e0bbf2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -29,7 +29,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ AliasTy, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder, FlagComputation, Flags, FnSigKind, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner, TraitRef, - TypeFlags, TypeVisitableExt, Upcast, Variance, + TypeFlags, TypeVisitableExt, Upcast, Variance, VisitorResult, elaborate::elaborate, error::TypeError, fast_reject, @@ -54,6 +54,7 @@ use crate::{ TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized, util::{explicit_item_bounds, explicit_item_self_bounds}, }, + ret, }; use super::{ @@ -264,6 +265,35 @@ macro_rules! impl_foldable_for_interned_slice { } pub(crate) use impl_foldable_for_interned_slice; +macro_rules! impl_foldable_for_stored_type { + ($name:ident) => { + impl<'db> ::rustc_type_ir::TypeVisitable> for $name { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_ref().visit_with(visitor) + } + } + + impl<'db> rustc_type_ir::TypeFoldable> for $name { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + self.as_ref().fold_with(folder).store() + } + } + }; +} +pub(crate) use impl_foldable_for_stored_type; + macro_rules! impl_stored_interned { ( $storage:ident, $name:ident, $stored_name:ident $(,)? ) => { #[derive(Clone, PartialEq, Eq, Hash)] @@ -1601,12 +1631,12 @@ impl<'db> Interner for DbInterner<'db> { def_id.0.trait_items(self.db()).associated_types().map(|id| id.into()) } - fn for_each_relevant_impl( + fn for_each_relevant_impl( self, trait_def_id: Self::TraitId, self_ty: Self::Ty, - mut f: impl FnMut(Self::ImplId), - ) { + mut f: impl FnMut(Self::ImplId) -> R, + ) -> R { let krate = self.krate.expect("trait solving requires setting `DbInterner::krate`"); let trait_block = trait_def_id.0.loc(self.db).container.block(self.db); let mut consider_impls_for_simplified_type = |simp: SimplifiedType<'_>| { @@ -1641,13 +1671,14 @@ impl<'db> Interner for DbInterner<'db> { let (regular_impls, builtin_derive_impls) = impls.for_trait_and_self_ty(trait_def_id.0, &simp); for &impl_ in regular_impls { - f(impl_.into()); + ret!(f(impl_.into())); } for &impl_ in builtin_derive_impls { - f(impl_.into()); + ret!(f(impl_.into())); } + R::output() }, - ); + ) }; match self_ty.kind() { @@ -1676,7 +1707,7 @@ impl<'db> Interner for DbInterner<'db> { let simp = fast_reject::simplify_type(self, self_ty, fast_reject::TreatParams::AsRigid) .unwrap(); - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } // HACK: For integer and float variables we have to manually look at all impls @@ -1704,7 +1735,7 @@ impl<'db> Interner for DbInterner<'db> { SimplifiedType::Uint(Usize), ]; for simp in possible_integers { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -1719,7 +1750,7 @@ impl<'db> Interner for DbInterner<'db> { ]; for simp in possible_floats { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -1748,15 +1779,22 @@ impl<'db> Interner for DbInterner<'db> { self.for_each_blanket_impl(trait_def_id, f) } - fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, mut f: impl FnMut(Self::ImplId)) { - let Some(krate) = self.krate else { return }; + fn for_each_blanket_impl( + self, + trait_def_id: Self::TraitId, + mut f: impl FnMut(Self::ImplId) -> R, + ) -> R { + let Some(krate) = self.krate else { + return R::output(); + }; let block = trait_def_id.0.loc(self.db).container.block(self.db); TraitImpls::for_each_crate_and_block(self.db, krate, block, &mut |impls| { for &impl_ in impls.blanket_impls(trait_def_id.0) { - f(impl_.into()); + ret!(f(impl_.into())); } - }); + R::output() + }) } fn has_item_definition(self, _def_id: Self::ImplOrTraitAssocTermId) -> bool { @@ -2047,13 +2085,19 @@ impl<'db> Interner for DbInterner<'db> { opaque: Self::LocalOpaqueTyId, ) -> EarlyBinder { let impl_trait_id = opaque.0.loc(self.db); - match impl_trait_id { + // The entry is missing when this call cycles back into the still-running inference + // of the defining body, as the cycle fallback is an empty result. + let hidden_type = match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - crate::opaques::rpit_hidden_types(self.db, func)[idx].get() + crate::opaques::rpit_hidden_types(self.db, func).get(idx) } crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => { - crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get() + crate::opaques::tait_hidden_types(self.db, type_alias).get(idx) } + }; + match hidden_type { + Some(hidden_type) => hidden_type.get(), + None => EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 05f559c0349e9..36c18ed772ca7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -33,7 +33,8 @@ use crate::{ CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate, Region, TraitRef, TypeAliasIdWrapper, Unnormalized, abi::Safety, - impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, + impl_foldable_for_interned_slice, impl_foldable_for_stored_type, impl_stored_interned, + interned_slice, util::{CoroutineArgsExt, IntegerTypeExt}, }, }; @@ -61,6 +62,7 @@ pub(super) struct TyInterned(WithCachedTypeInfo>); impl_internable!(gc; TyInterned); impl_stored_interned!(TyInterned, Ty, StoredTy); +impl_foldable_for_stored_type!(StoredTy); const _: () = { const fn is_copy() {} @@ -894,15 +896,6 @@ impl<'db> TypeVisitable> for Ty<'db> { } } -impl<'db> TypeVisitable> for StoredTy { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - impl<'db> TypeSuperVisitable> for Ty<'db> { fn super_visit_with>>( &self, @@ -969,18 +962,6 @@ impl<'db> TypeFoldable> for Ty<'db> { } } -impl<'db> TypeFoldable> for StoredTy { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} - impl<'db> TypeSuperFoldable> for Ty<'db> { fn try_super_fold_with>>( self, @@ -1422,6 +1403,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { interned_slice!(TysStorage, Tys, StoredTys, tys, Ty<'db>, Ty<'static>); impl_foldable_for_interned_slice!(Tys); +impl_foldable_for_stored_type!(StoredTys); impl<'db> Tys<'db> { #[inline] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 7e40e3c17d517..e384ac455213d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -723,3 +723,15 @@ pub(crate) fn clauses_as_obligations<'db>( recursion_depth: 0, }) } + +/// Copied from +/// +#[macro_export] +macro_rules! ret { + ($e: expr) => { + match $e.branch() { + ::std::ops::ControlFlow::Break(b) => return R::from_residual(b), + ::std::ops::ControlFlow::Continue(()) => {} + } + }; +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 9cb0022ca6bfc..194c866c68056 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -5,14 +5,14 @@ use hir_def::{ signatures::ImplSignature, }; use hir_expand::name::Name; -use la_arena::ArenaMap; +use la_arena::{Arena, ArenaMap}; use rustc_type_ir::inherent::Ty as _; use syntax::ast; use crate::{ ImplTraitId, InferBodyId, InferenceResult, db::{HirDatabase, InternedOpaqueTyId}, - lower::{ImplTraitIdx, ImplTraits}, + lower::{ImplTrait, ImplTraitIdx}, next_solver::{ DbInterner, ErrorGuaranteed, SolverDefId, StoredEarlyBinder, StoredTy, Ty, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, @@ -29,7 +29,7 @@ pub(crate) fn opaque_types_defined_by<'db>( // A function may define its own RPITs. extend_with_opaques( db, - ImplTraits::return_type_impl_traits(db, func), + ImplTrait::return_type_impl_traits(db, func), |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx), result, ); @@ -38,7 +38,7 @@ pub(crate) fn opaque_types_defined_by<'db>( let extend_with_taits = |type_alias| { extend_with_opaques( db, - ImplTraits::type_alias_impl_traits(db, type_alias), + ImplTrait::type_alias_impl_traits(db, type_alias), |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx), result, ); @@ -81,15 +81,13 @@ pub(crate) fn opaque_types_defined_by<'db>( fn extend_with_opaques<'db>( db: &'db dyn HirDatabase, - opaques: &Option>>, + opaques: &Arena, mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId, result: &mut Vec>, ) { - if let Some(opaques) = opaques { - for (opaque_idx, _) in (**opaques).as_ref().skip_binder().impl_traits.iter() { - let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); - result.push(opaque_id.into()); - } + for (opaque_idx, _) in opaques.iter() { + let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); + result.push(opaque_id.into()); } } } @@ -116,12 +114,7 @@ pub(crate) fn tait_hidden_types( type_alias: TypeAliasId, ) -> ArenaMap> { // Call this first, to not perform redundant work if there are no TAITs. - let Some(taits_count) = ImplTraits::type_alias_impl_traits(db, type_alias) - .as_deref() - .map(|taits| taits.as_ref().skip_binder().impl_traits.len()) - else { - return ArenaMap::new(); - }; + let taits_count = ImplTrait::type_alias_impl_traits(db, type_alias).len(); let loc = type_alias.loc(db); let module = loc.module(db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 08efef10ee78a..4574e095e91f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -47,7 +47,7 @@ fn foo() -> i32 { "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -136,7 +136,7 @@ fn baz() -> i32 { "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -147,7 +147,7 @@ fn baz() -> i32 { "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -158,7 +158,7 @@ fn baz() -> i32 { "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -599,21 +599,20 @@ fn main() { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::of_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_and_deps_", "TraitImpls < 'db >::for_crate_", "impl_trait_with_diagnostics", @@ -692,18 +691,17 @@ fn main() { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::with_source_map_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_", "ImplSignature::with_source_map_", "ImplSignature::of_", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c580841244f1b..59f33f0f1b29a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -3016,3 +3016,81 @@ fn f(s: S) { s.m(); } "#, ); } + +#[test] +fn regression_22799() { + check_no_mismatches( + r#" +struct S; +fn f() { + ::S; +} + "#, + ); +} + +#[test] +fn braced_const_path() { + check_types( + r#" +//- minicore: default, builtin_impls +trait ToNum { + type Num; +} +trait Bar { + type Ty; +} +struct Gen; +struct Int; + +impl ToNum for Gen<{ B }> { + type Num = Int; +} + +impl Bar for Int { + type Ty = i32; +} +impl Bar for Int { + type Ty = f32; +} + +type A = < as ToNum>::Num as Bar>::Ty; + +fn main() { + let x = A::default(); + // ^ i32 +} + "#, + ); +} + +#[test] +fn regression_22820() { + check_no_mismatches( + r#" +//- minicore: copy +trait MyTrait: Copy { + const ASSOC: usize; +} + +const fn output(_: T) -> usize { + ::ASSOC +} + +const fn yeet() -> impl Clone { + let x = [0u8; output(yeet())]; +} + "#, + ); +} + +#[test] +fn rpit_function_with_non_trivial_anon_const() { + check_no_mismatches( + r#" +fn f() -> impl Sized { + let x = [0u8; 1 + 2]; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index e8f378db3228a..3cdfe4edcb908 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4201,8 +4201,6 @@ fn foo() { 248..282 'LazyLo..._LOCK)': &'? [u32; 0] 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]> 265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]> - 197..202 '{ 0 }': usize - 199..200 '0': usize "#]], ); } @@ -4308,9 +4306,8 @@ enum Enum { } "#, expect![[r#" - 29..34 '{ 2 }': usize - 31..32 '2': usize - "#]], + +"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 8233a009816fc..fad944589dab6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -5377,3 +5377,37 @@ fn run_dyn<'b>(val: &dyn for<'a> Trait<'a, 'b>) {} "#]], ); } + +#[test] +fn recursive_auto_trait() { + check_types( + r#" +auto trait Send {} +impl !Send for *const T {} + +struct Vec(*const T); +impl Send for Vec {} + +struct Node { + children: Vec, +} + +struct Holder(T); + +trait Lock { + fn get(&self) -> &T; +} + +impl Lock for Holder { + fn get(&self) -> &T { + &self.0 + } +} + +fn probe(h: &Holder) { + h.get(); + // ^^^^^^^ &'? Node +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 9e04353087760..2690297283988 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -49,7 +49,9 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance let types = || crate::next_solver::default_types(db); if flags.contains(StructFlags::IS_UNSAFE_CELL) { return types().one_invariant.store(); - } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { + } else if flags.intersects( + StructFlags::IS_PHANTOM_DATA | StructFlags::IS_COVARIANT_UNSAFE_CELL, + ) { return types().one_covariant.store(); } } @@ -433,6 +435,7 @@ struct Covariant { check( r#" //- minicore: cell +#![feature(lang_items)] use core::cell::UnsafeCell; @@ -461,6 +464,10 @@ enum Enum { //~ ERROR [A: +, B: -, C: o] Bar(Contravariant),` Zed(Covariant,Contravariant) } + +#[repr(transparent)] +#[lang = "covariant_unsafe_cell"] +pub struct CovariantUnsafeCell(UnsafeCell); //~ ERROR [T: +] "#, expect![[r#" InvariantMut['a: covariant, A: invariant, B: invariant] @@ -469,6 +476,7 @@ enum Enum { //~ ERROR [A: +, B: -, C: o] Covariant[A: covariant] Contravariant[A: contravariant] Enum[A: covariant, B: contravariant, C: invariant] + CovariantUnsafeCell[T: covariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d0801d8efd03e..0c4191ce2dd6f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -441,6 +441,9 @@ pub struct MismatchedArgCount { pub call_expr: InFile, pub expected: usize, pub found: usize, + /// True when the call is through a `Fn`/`FnMut`/`FnOnce` trait (E0057) + /// rather than a regular function call (E0061). + pub is_fn_trait_call: bool, } #[derive(Debug)] @@ -885,9 +888,18 @@ impl<'db> AnyDiagnostic<'db> { }; DuplicateField { field: expr_or_pat, variant: variant.into() }.into() } - &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { - MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() - } + &InferenceDiagnostic::MismatchedArgCount { + call_expr, + expected, + found, + is_fn_trait_call, + } => MismatchedArgCount { + call_expr: expr_syntax(call_expr)?, + expected, + found, + is_fn_trait_call, + } + .into(), &InferenceDiagnostic::PrivateField { expr, field } => { let expr = expr_syntax(expr)?; let field = field.into(); diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index dc35a5c57bec4..61eda80fb4877 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -357,7 +357,7 @@ impl<'db> HirDisplay<'db> for Adt { impl<'db> HirDisplay<'db> for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { let module_id = self.module(f.db).id; - // FIXME: Render repr if its set explicitly? + // FIXME: Render repr if it's set explicitly? write_visibility(module_id, self.visibility(f.db), f)?; f.write_str("struct ")?; write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index fee6ae3d49527..f298e25489a59 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1563,7 +1563,7 @@ impl<'db> SemanticsImpl<'db> { } /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. + /// This only works for attribute expansions, as other ones do not have nodes as input. pub fn original_ast_node(&self, node: N) -> Option { self.wrap_node_infile(node).original_ast_node_rooted(self.db).map( |InRealFile { file_id, value }| { @@ -1574,7 +1574,7 @@ impl<'db> SemanticsImpl<'db> { } /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. + /// This only works for attribute expansions, as other ones do not have nodes as input. pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option { let InFile { file_id, .. } = self.find_file(node); InFile::new(file_id, node).original_syntax_node_rooted(self.db).map( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 3b39a489141e1..caa7b39885fec 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -10,7 +10,7 @@ //! This problem is a part of more-or-less every IDE feature implemented. Every //! IDE functionality (like goto to definition), conceptually starts with a //! specific cursor position in a file. Starting with this text offset, we first -//! figure out what syntactic construct are we at: is this a pattern, an +//! figure out what syntactic construct we are at: is this a pattern, an //! expression, an item definition. //! //! Knowing only the syntax gives us relatively little info. For example, @@ -32,11 +32,11 @@ //! Specifically, the algorithm goes like this: //! //! 1. Find the syntactic container for the syntax. For example, field's -//! container is the struct, and structs container is a module. +//! container is the struct, and the struct's container is a module. //! 2. Recursively get the def corresponding to container. //! 3. Ask the container def for all child defs. These child defs contain //! the answer and answer's siblings. -//! 4. For each child def, ask for it's source. +//! 4. For each child def, ask for its source. //! 5. The child def whose source is the syntax node we've started with //! is the answer. //! @@ -165,9 +165,21 @@ impl<'db> SourceToDefCache<'db> { self.expansion_info_cache.entry(macro_file).or_insert_with(|| { let exp_info = macro_file.expansion_info(db); + // Ensure that the cache contains syntax nodes from expanded macros, + // whose root may be in another file. let InMacroFile { file_id, value } = exp_info.expanded(); Self::cache(&mut self.root_to_file_cache, value, file_id.into()); + // include!("foo.rs") invocations are awkward: in addition to the + // expansion site there's the included file (foo.rs), so we need to + // ensure that it exists in the cache too. + if macro_file.is_include_macro(db) { + let arg = exp_info.arg(); + if let Some(arg_node) = arg.value { + Self::cache(&mut self.root_to_file_cache, arg_node.tree_top(), arg.file_id); + } + } + exp_info }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index fb27f9dec45ac..209091683a01b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -24,8 +24,9 @@ use hir_def::{ hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId, generics::GenericParams}, lang_item::LangItems, nameres::MacroSubNs, - resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope}, + resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs, resolver_for_scope}, type_ref::{Mutability, TypeRefId}, + visibility::Visibility, }; use hir_expand::{ HirFileId, InFile, @@ -1060,7 +1061,7 @@ impl<'db> SourceAnalyzer<'db> { }; let store_owner = self.resolver.expression_store_owner(); - let res = resolve_hir_value_path( + let (res, _) = resolve_hir_value_path( db, &self.resolver, store_owner, @@ -1319,7 +1320,7 @@ impl<'db> SourceAnalyzer<'db> { .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)), @@ -1360,7 +1361,7 @@ impl<'db> SourceAnalyzer<'db> { // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces if let Some(name_ref) = path.as_single_name_ref() { - let builtin = BuiltinAttr::builtin(&name_ref.text()); + let builtin = BuiltinAttr::builtin(name_ref.text()); if builtin.is_some() { return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } @@ -1410,7 +1411,7 @@ impl<'db> SourceAnalyzer<'db> { .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)), @@ -1643,7 +1644,8 @@ impl<'db> SourceAnalyzer<'db> { Some(name.clone()), )), hygiene, - ), + ) + .map(|(it, _)| it), ) }) } @@ -1683,7 +1685,8 @@ impl<'db> SourceAnalyzer<'db> { Some(name.clone()), )), hygiene, - ), + ) + .map(|(it, _)| it), ) })) } @@ -1865,7 +1868,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( ) -> Option { resolver .resolve_path_as_macro(db, path.mod_path()?, Some(MacroSubNs::Attr)) - .map(|(it, _)| it) + .map(|it| it.def) .map(Into::into) } @@ -1880,7 +1883,7 @@ fn resolve_hir_path_<'db>( resolve_per_ns: bool, ) -> PathResolutionPerNs<'db> { let types = || { - let (ty, unresolved) = match path.type_anchor() { + let (ty, unresolved, ty_is_visible) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { let generics = OnceCell::new(); let (_, res) = TyLoweringContext::new( @@ -1894,19 +1897,20 @@ fn resolve_hir_path_<'db>( LifetimeLoweringMode::LateParam, ) .lower_ty_ext(type_ref); - res.map(|ty_ns| (ty_ns, path.segments().first())) + res.map(|ty_ns| (ty_ns, path.segments().first(), Visibility::Public)) }), None => { - let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db, path)?; + let (ty, remaining_idx, _, _, vis) = + resolver.resolve_path_in_type_ns_with_prefix_info(db, path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { - Some((ty, path.segments().last())) + Some((ty, path.segments().last(), vis)) } else { None } } - None => Some((ty, None)), + None => Some((ty, None, vis)), } } }?; @@ -1917,7 +1921,10 @@ fn resolve_hir_path_<'db>( && let Some(type_alias_id) = trait_id.trait_items(db).associated_type_by_name(unresolved.name) { - return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); + return Some(( + PathResolution::Def(ModuleDefId::from(type_alias_id).into()), + ty_is_visible, + )); } let res = match ty { @@ -1945,8 +1952,8 @@ fn resolve_hir_path_<'db>( }) .map(TypeAlias::from) .map(Into::into) - .map(PathResolution::Def), - None => Some(res), + .map(|def| (PathResolution::Def(def), ty_is_visible)), + None => Some((res, ty_is_visible)), } }; @@ -1956,41 +1963,79 @@ fn resolve_hir_path_<'db>( let items = || { resolver .resolve_module_path_in_items(db, path.mod_path()?) - .take_types() - .map(|it| PathResolution::Def(it.into())) + .take_types_full() + .map(|it| (PathResolution::Def(it.def.into()), it.vis)) }; let macros = || { resolver .resolve_path_as_macro(db, path.mod_path()?, None) - .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into()))) + .map(|res| (PathResolution::Def(ModuleDef::Macro(res.def.into())), res.vis)) }; - if resolve_per_ns { - PathResolutionPerNs { - type_ns: types().or_else(items), - value_ns: values(), - macro_ns: macros(), - } - } else { - let res = if prefer_value_ns { - values() - .map(|value_ns| PathResolutionPerNs::new(None, Some(value_ns), None)) - .unwrap_or_else(|| PathResolutionPerNs::new(types(), None, None)) - } else { - types() - .map(|type_ns| PathResolutionPerNs::new(Some(type_ns), None, None)) - .unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None)) - }; + let mut types_ns: Option> = None; + let mut values_ns: Option> = None; + + let mut types_is_visible: Option = None; + let mut values_is_visible: Option = None; - if res.any().is_some() { - res - } else if let Some(type_ns) = items() { - PathResolutionPerNs::new(Some(type_ns), None, None) + if !resolve_per_ns { + if prefer_value_ns { + values_ns = Some(values().inspect(|(_, vis)| { + values_is_visible = Some(resolver.is_visible(db, *vis)); + })); + + if let Some(Some((res, _))) = values_ns + && values_is_visible.unwrap_or_default() + { + return PathResolutionPerNs::new(None, Some(res), None); + } } else { - PathResolutionPerNs::new(None, None, macros()) + types_ns = Some(types().or_else(items).inspect(|(_, vis)| { + types_is_visible = Some(resolver.is_visible(db, *vis)); + })); + + if let Some(Some((res, _))) = types_ns + && types_is_visible.unwrap_or_default() + { + return PathResolutionPerNs::new(Some(res), None, None); + } + } + } + + let mut macros_is_visible = false; + + let mut types = types_ns.unwrap_or_else(|| types().or_else(items)).map(|(res, vis)| { + types_is_visible = Some(types_is_visible.unwrap_or_else(|| resolver.is_visible(db, vis))); + res + }); + let mut values = values_ns.unwrap_or_else(values).map(|(res, vis)| { + values_is_visible = Some(values_is_visible.unwrap_or_else(|| resolver.is_visible(db, vis))); + res + }); + let mut macros = macros().map(|(res, vis)| { + macros_is_visible = resolver.is_visible(db, vis); + res + }); + + let types_is_visible = types_is_visible.unwrap_or_default(); + let values_is_visible = values_is_visible.unwrap_or_default(); + + // If there is a visible resolution and an invisible one, we only want to include the visible one. But if all are + // invisible, we want to include them all. + if types_is_visible || values_is_visible || macros_is_visible { + if !types_is_visible { + types = None; + } + if !values_is_visible { + values = None; + } + if !macros_is_visible { + macros = None; } } + + PathResolutionPerNs { type_ns: types, value_ns: values, macro_ns: macros } } fn resolve_hir_value_path<'db>( @@ -2000,23 +2045,26 @@ fn resolve_hir_value_path<'db>( infer_body: Option>, path: &Path, hygiene: HygieneId, -) -> Option> { - resolver.resolve_path_in_value_ns_fully(db, path, hygiene).and_then(|val| { - let res = match val { - ValueNs::LocalBinding(binding_id) => { - let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id }; - PathResolution::Local(var) - } - ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), - ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), - ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), - ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), - ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), - ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), - }; - Some(res) - }) +) -> Option<(PathResolution<'db>, Visibility)> { + resolver.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene).and_then( + |(val, _, vis)| { + let ResolveValueResult::ValueNs(val) = val else { return None }; + let res = match val { + ValueNs::LocalBinding(binding_id) => { + let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id }; + PathResolution::Local(var) + } + ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), + ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), + ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), + ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), + ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), + ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), + }; + Some((res, vis)) + }, + ) } /// Resolves a path where we know it is a qualifier of another path. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 10262445a2dfe..e2c1048bdb785 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -206,7 +206,7 @@ pub(crate) fn apply_demorgan_iterator( let closure_body = closure_expr.body()?; let op_range = method_call.syntax().text_range(); - let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); + let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text()); acc.add_group( &GroupLabel("Apply De Morgan's law".to_owned()), AssistId::refactor_rewrite("apply_demorgan_iterator"), @@ -216,7 +216,7 @@ pub(crate) fn apply_demorgan_iterator( let editor = builder.make_editor(method_call.syntax()); let make = editor.make(); // replace the method name - let new_name = match name.text().as_str() { + let new_name = match name.text() { "all" => make.name_ref("any"), "any" => make.name_ref("all"), "is_some_and" => make.name_ref("is_none_or"), 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 c9f5e0a4fbede..83effa11820b7 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 @@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed( capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - let capture_name = make.expr_path(make.path_from_text(&capture_name.text())); + let capture_name = make.expr_path(make.path_from_text(capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index a01a66e7b1a52..c8de14bed4bb8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -109,7 +109,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_, '_ editor.replace(into_fn_name.syntax(), make.name("from").syntax()); for s in selfs { - match s.text().as_ref() { + match s.text() { "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()), "Self" => { if let Some(path_segment) = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 9dffdf3f367c4..db084c6ea21d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -148,8 +148,7 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn if let Some(name_ref) = record_pat_field.field_name() { editor.replace( record_pat_field.syntax(), - make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) - .syntax(), + make.record_pat_field(make.name_ref(name_ref.text()), binding.clone()).syntax(), ); } } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index 7026b5bafdc7c..ae8f626c5da8a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -74,7 +74,7 @@ pub(crate) fn convert_range_for_to_while( let mut elements = vec![]; - let var_expr = make.expr_path(make.ident_path(&name.text())); + let var_expr = make.expr_path(make.ident_path(name.text())); let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: !inclusive, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index eb74e9107581b..0bb9bf12b12cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -194,7 +194,7 @@ fn process_struct_name_reference( let range = ctx.sema.original_range_opt(pat.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ]; @@ -237,7 +237,7 @@ fn process_struct_name_reference( let range = ctx.sema.original_range_opt(expr.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ]; 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 c2eb49dde5695..46333ed726388 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 @@ -934,7 +934,7 @@ impl FunctionBody { }; // FIXME: make trait arguments - let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into()); + let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(name.text())).into()); let parent = self.parent()?; let parents = generic_parents(&parent); @@ -1561,7 +1561,7 @@ fn format_function<'db>( old_indent: IndentLevel, make: &SyntaxFactory, ) -> ast::Fn { - let fun_name = make.name(&fun.name.text()); + let fun_name = make.name(fun.name.text()); let params = fun.make_param_list(make, ctx, module, fun.mods.edition); let ret_ty = fun.make_ret_ty(make, ctx, module); let body = make_body(make, ctx, old_indent, fun); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 5e6e74bc94429..c1ac4f1724893 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -241,7 +241,7 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b if matches!(token.kind(), T![lifetime_ident]) => { if let Some(lt) = lt.lifetime() - && lt.text().as_str() == token.text() + && lt.text() == token.text() { *tag = true; tagged_one = true; @@ -250,18 +250,15 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } param if matches!(token.kind(), T![ident]) => { if match param { - ast::GenericParam::ConstParam(konst) => konst - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::TypeParam(ty) => ty - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::LifetimeParam(lt) => lt - .lifetime() - .map(|lt| lt.text().as_str() == token.text()) - .unwrap_or_default(), + ast::GenericParam::ConstParam(konst) => { + konst.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::TypeParam(ty) => { + ty.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::LifetimeParam(lt) => { + lt.lifetime().map(|lt| lt.text() == token.text()).unwrap_or_default() + } } { *tag = true; tagged_one = true; @@ -338,7 +335,7 @@ fn update_variant( // FIXME: replace with a `ast::make` constructor let ty = match generic_args { Some(generic_args) => make.ty(&format!("{name}{generic_args}")), - None => make.ty(&name.text()), + None => make.ty(name.text()), }; // change from a record to a tuple field list 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 329f8325b4c12..a378256b598aa 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 @@ -145,7 +145,7 @@ fn collect_used_generics<'gp>( .filter_map(|it| match it { ast::GenericArg::LifetimeArg(lt) => { let lt = lt.lifetime()?; - known_generics.iter().find(find_lifetime(<.text())) + known_generics.iter().find(find_lifetime(lt.text())) } _ => None, }), @@ -157,7 +157,7 @@ fn collect_used_generics<'gp>( generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } @@ -166,13 +166,12 @@ fn collect_used_generics<'gp>( generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } ast::Type::RefType(ref_) => generics.extend( - ref_.lifetime() - .and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))), + ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ), ast::Type::ArrayType(ar) => { if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 1e3f2ac677254..738f461a1f2fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -75,12 +75,12 @@ pub(crate) fn generate_blanket_trait_impl( |builder| { let editor = builder.make_editor(traitd.syntax()); let make = editor.make(); - let namety = make.ty_path(make.path_from_text(&name.text())); + let namety = make.ty_path(make.path_from_text(name.text())); let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent()); let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list)); let is_unsafe = traitd.unsafe_token().is_some(); let thisname = this_name(make, &traitd); - let thisty = make.ty_path(make.path_from_text(&thisname.text())); + let thisty = make.ty_path(make.path_from_text(thisname.text())); let indent = traitd.indent_level(); let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param( @@ -141,7 +141,7 @@ pub(crate) fn generate_blanket_trait_impl( if let Some(cap) = ctx.config.snippet_cap && let Some(self_ty) = impl_.self_ty() { - builder.add_tabstop_before(cap, self_ty); + editor.add_annotation(self_ty.syntax(), builder.make_tabstop_before(cap)); } builder.add_file_edits(ctx.vfs_file_id(), editor); }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index 713d6a3fb708c..07c191b0eb666 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs @@ -76,7 +76,7 @@ pub(crate) fn generate_default_from_enum_variant( fn default_impl(variant_name: ast::Name, adt: &ast::Adt, make: &SyntaxFactory) -> ast::Impl { let impl_ = utils::generate_trait_impl_intransitive(make, adt, make.ty("Default")); - let fn_ = default_fn(&variant_name.text(), make); + let fn_ = default_fn(variant_name.text(), make); let (impl_editor, impl_) = SyntaxEditor::with_ast_node(&impl_); impl_ 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 f293e956bca5b..ba6bb5c70bcb1 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 @@ -42,9 +42,9 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> }; acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| { + let editor = edit.make_editor(nominal.syntax()); match derive_attr { None => { - let editor = edit.make_editor(nominal.syntax()); let make = editor.make(); let derive = make.attr_outer(make.meta_token_tree( @@ -79,16 +79,15 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> let tabstop_before = edit.make_tabstop_before(cap); editor.add_annotation(delimiter, tabstop_before); - edit.add_file_edits(ctx.vfs_file_id(), editor); } Some(_) => { + let delimiter = delimiter.expect("Right delim token could not be found."); + let tabstop_before = edit.make_tabstop_before(cap); // Just move the cursor. - edit.add_tabstop_before_token( - cap, - delimiter.expect("Right delim token could not be found."), - ); + editor.add_annotation(delimiter, tabstop_before); } }; + edit.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs index 53e77b49474c4..5e2ee772b9a51 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs @@ -141,7 +141,7 @@ impl Method { }; let variant_name = variant.name()?; - let fn_name = format!("is_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("is_{}", to_lower_snake_case(variant_name.text())); Some(Method { pattern_suffix, fn_name, variant_name }) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 8a194ae02bff0..479143c133353 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -219,7 +219,7 @@ impl Method { fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option { use itertools::Itertools as _; let variant_name = variant.name()?; - let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(variant_name.text())); match variant.kind() { ast::StructKind::Record(record) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index fb43e3eaa37e0..73837f486cb30 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -62,7 +62,7 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, ' let editor = builder.make_editor(enum_node.syntax()); let make = editor.make(); let field_list = parent.make_field_list(ctx, make); - let variant = make.variant(None, make.name(&name_ref.text()), field_list, None); + let variant = make.variant(None, make.name(name_ref.text()), field_list, None); if let Some(it) = enum_node.variant_list() { it.add_variant(&editor, &variant); } 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 3c3fde80f99e7..13096c6efc37a 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 @@ -67,7 +67,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { return None; } - let fn_name = &*name_ref.text(); + let fn_name = name_ref.text(); let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; @@ -159,7 +159,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (impl_, file) = if let Some(impl_) = cursor_impl { (Some(impl_), ctx.vfs_file_id()) } else { - get_adt_source(ctx, &adt, fn_name.text().as_str())? + get_adt_source(ctx, &adt, fn_name.text())? }; let target = get_method_target(ctx, &impl_, &adt)?; @@ -269,7 +269,7 @@ impl FunctionBuilder { // If generated function has the name "new" and is an associated function, we generate fn body // as a constructor and assume a "Self" return type. if let Some(body) = - make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition) + make_fn_body_as_new_function(make, ctx, fn_name.text(), adt_info, target_edition) { ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into())); should_focus_return_type = false; 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 7e5d5cec71bc5..b21e60876296f 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 @@ -225,7 +225,7 @@ fn generate_getter_from_info( ( make.ty_ref(record_field_info.field_ty.clone(), true), make.expr_ref( - make.expr_field(self_expr, &record_field_info.field_name.text()).into(), + make.expr_field(self_expr, record_field_info.field_name.text()).into(), true, ), ) @@ -250,7 +250,7 @@ fn generate_getter_from_info( make.expr_ref( make.expr_field( make.expr_path(make.ident_path("self")), - &record_field_info.field_name.text(), + record_field_info.field_name.text(), ) .into(), false, 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 ab0eb56fcf19d..ecff6267bb995 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 @@ -185,7 +185,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_> None, None, false, - make.ty(&name.text()), + make.ty(name.text()), make.ty_placeholder(), None, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index fd095dd9b2aab..6858b62f8d22d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -210,7 +210,7 @@ fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option } fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { - let new_name = match &*name.text() { + let new_name = match name.text() { "index" => "index_mut", "as_ref" => "as_mut", "borrow" => "borrow_mut", 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 d5629e2e7e073..242712ff2ed6d 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 @@ -4,14 +4,11 @@ use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, imports::import_assets::item_for_path_search, }; -use syntax::syntax_editor::{Position, SyntaxEditor}; -use syntax::{ - TokenText, - ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, - syntax_factory::SyntaxFactory, - }, +use syntax::ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, }; +use syntax::syntax_editor::{Position, SyntaxEditor}; use crate::{ AssistId, @@ -71,8 +68,7 @@ pub(crate) fn generate_single_field_struct_from( return None; } - let main_field_name = - names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text()); + let main_field_name = names.as_ref().map_or("value", |names| names[main_field_i].text()); let main_field_ty = types[main_field_i].clone(); acc.add( @@ -92,10 +88,10 @@ pub(crate) fn generate_single_field_struct_from( false, )); - let ty = make.ty(&strukt_name.text()); + let ty = make.ty(strukt_name.text()); let constructor = - make_adt_constructor(names.as_deref(), constructors, &main_field_name, make); + make_adt_constructor(names.as_deref(), constructors, main_field_name, make); let body = make.block_expr([], Some(constructor)); let fn_ = make @@ -108,7 +104,7 @@ pub(crate) fn generate_single_field_struct_from( make.param_list( None, [make.param( - make.path_pat(make.path_from_text(&main_field_name)), + make.path_pat(make.path_from_text(main_field_name)), main_field_ty, )], ), @@ -161,12 +157,12 @@ pub(crate) fn generate_single_field_struct_from( fn make_adt_constructor( names: Option<&[ast::Name]>, constructors: Vec>, - main_field_name: &TokenText<'_>, + main_field_name: &str, make: &SyntaxFactory, ) -> ast::Expr { if let Some(names) = names { let fields = make.record_expr_field_list(names.iter().zip(constructors).map( - |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer), + |(name, initializer)| make.record_expr_field(make.name_ref(name.text()), initializer), )); make.record_expr(make.path_from_text("Self"), fields).into() } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 12afd9ae6affa..354447cf3356e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -117,7 +117,7 @@ pub(crate) fn generate_trait_from_impl( let params = used_params(&impl_ast, make, ctx); let trait_ast = make.trait_( false, - &trait_name(&impl_assoc_items, make).text(), + trait_name(&impl_assoc_items, make).text(), params.clone(), impl_ast.where_clause(), trait_items, @@ -204,7 +204,7 @@ fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name { fn_names .next() .and_then(|name| { - fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(&name.text()))) + fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(name.text()))) }) .unwrap_or_else(|| make.name("NewTrait")) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 5a185637df4ef..d934fae9253f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -175,6 +175,23 @@ macro_rules! num { ); } + #[test] + fn inline_macro_in_included_file() { + // Regression test for climbing from the included file into an uncached includer root. + check_assist_not_applicable( + inline_macro, + r#" +//- minicore:include +//- /main.rs +include!("a.rs"); +//- /a.rs +fn foo() { + let x = 1$0; +} +"#, + ); + } + #[test] fn inline_macro_simple_not_applicable_broken_macro() { // FIXME: This is a bug. The macro should not expand, but it's diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index bb76e2743c377..f5d5400404cf0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -140,7 +140,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) let src = adt.source(ctx.db())?.value; let name = src.name()?; let generic_params = src.generic_param_list(); - let name_ref = make.name_ref(&name.text()); + let name_ref = make.name_ref(name.text()); let segment = match generic_params { Some(params) => { make.path_segment_generics(name_ref, params.to_generic_args(&make)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs index f41769150c042..5060886cfa9ce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -165,7 +165,7 @@ fn get_arm_types<'db>( { let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat); - map.insert(name.text().to_string(), pat_type); + map.insert(name.text().to_owned(), pat_type); } } _ => (), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index 658947abe135f..ed5a372e546db 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -82,7 +82,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::AssocItem::MacroCall(_) => None, }; - name.and_then(|n| ranks.get(n.text().as_str().trim_start_matches("r#")).copied()) + name.and_then(|n| ranks.get(n.text().trim_start_matches("r#")).copied()) .unwrap_or(usize::MAX) }) .collect(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 4e85b30b58188..707b3321c338d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -187,15 +187,18 @@ fn add_assist( && m.syntax().text() == "todo!()" { // Make the `todo!()` a placeholder - builder.add_placeholder_snippet(cap, m); + editor.add_annotation(m.syntax(), builder.make_placeholder_snippet(cap)); } else { // If we haven't already added a snippet, add a tabstop before the generated function - builder.add_tabstop_before(cap, first_assoc_item); + editor.add_annotation( + first_assoc_item.syntax(), + builder.make_tabstop_before(cap), + ); } } else if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) { - builder.add_tabstop_after_token(cap, l_curly); + editor.add_annotation(l_curly, builder.make_tabstop_after(cap)); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 9a61163e87e71..50d6f6d62cb3e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -102,7 +102,7 @@ pub(crate) fn replace_if_let_with_match( if !pat_seen && cond_bodies.len() != 1 { // Don't offer turning an if (chain) without patterns into a match, - // unless its a simple `if cond { .. } (else { .. })` + // unless it's a simple `if cond { .. } (else { .. })` return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 17ee8597c1020..a414db0a6c8d0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -39,7 +39,7 @@ pub(crate) fn replace_with_lazy_method( let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; let n_params = callable.n_params() + 1; - let method_name_lazy = lazy_method_name(&method_name.text()); + let method_name_lazy = lazy_method_name(method_name.text()); receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db, @@ -156,7 +156,7 @@ pub(crate) fn replace_with_eager_method( } let method_name_text = method_name.text(); - let method_name_eager = eager_method_name(&method_name_text)?; + let method_name_eager = eager_method_name(method_name_text)?; receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 135e750ca066c..3624099b13bc6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -354,7 +354,6 @@ fn check_with_config( handler(&mut acc, &ctx); }); let mut res = acc.finish(); - let assist = match assist_label { Some(label) => res.into_iter().find(|resolved| resolved.label == label), None if res.is_empty() => None, 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 670a030255cfc..388aac19b40e8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -344,7 +344,7 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option "is_none", "is_none" => "is_some", "is_ok" => "is_err", @@ -550,7 +550,7 @@ fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool { for item in il.assoc_items() { if let ast::AssocItem::Fn(f) = item && let Some(name) = f.name() - && names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) + && names.iter().any(|n| n.eq_ignore_ascii_case(name.text())) { return true; } @@ -664,7 +664,7 @@ fn generate_impl_inner( .zip(generic_params.as_ref()) .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params)); - let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); + let ty: ast::Type = make.ty_path(make.ident_path(adt.name().unwrap().text())).into(); let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); match trait_ { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c0ddcb950cbac..277b5bd8dfc5e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -21,7 +21,7 @@ pub(crate) fn gen_trait_fn_body( trait_ref: Option>, ) -> Option { let _ = func.body()?; - match trait_path.segment()?.name_ref()?.text().as_str() { + match trait_path.segment()?.name_ref()?.text() { "Clone" => { stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); gen_clone_impl(make, adt) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 109ebce01c9af..c2d7cb98cf9a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -40,7 +40,6 @@ pub(crate) fn complete_known_attribute_input( let path = attribute.path()?; let segments = path.segments().map(|s| s.name_ref()).collect::>>()?; let segments = segments.iter().map(|n| n.text()).collect::>(); - let segments = segments.iter().map(|t| t.as_str()).collect::>(); let tt = attribute.token_tree()?; match segments.as_slice() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 9e7dabbd01046..356c7e0087136 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -107,7 +107,7 @@ struct DeriveDependencies { } /// Standard Rust derives that have dependencies -/// (the dependencies are needed so that the main derive don't break the compilation when added) +/// (the dependencies are needed so that the main derive doesn't break the compilation when added) const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[ DeriveDependencies { label: "Copy", dependencies: &["Clone"] }, DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index 63cddb365e301..73ba847e7e61f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -23,7 +23,7 @@ pub(super) fn complete_repr( }) .any(|it| { let text = it.text(); - lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) + lookup.unwrap_or(label) == text || collides.contains(&text) }); if repr_already_annotated { continue; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 5a3a3ac39cb2f..34b53e5e5bfea 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -365,7 +365,7 @@ fn suggest_receiver_name( match receiver { ast::Expr::PathExpr(path) => { if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) { - return placeholder(name.text().as_str()); + return placeholder(name.text()); } } ast::Expr::RefExpr(it) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index c07c02e28538f..391152f438b61 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -26,7 +26,7 @@ pub(crate) fn complete_type_path<'db>( ScopeDef::ModuleDef(Function(_) | EnumVariant(_) | Static(_)) | ScopeDef::Local(_) => { false } - // unless its a constant in a generic arg list position + // unless it's a constant in a generic arg list position ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { location.complete_consts() } 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 b06f52c113459..7280fd1ad5751 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 @@ -1663,7 +1663,7 @@ fn classify_name_ref<'db>( let res = sema.resolve_path(&qualifier); // For understanding how and why super_chain_len is calculated the way it - // is check the documentation at it's definition + // is check the documentation at its definition let mut segment_count = 0; let super_count = iter::successors(Some(qualifier.clone()), |p| p.qualifier()) .take_while(|p| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 2ff726c6d03e3..675ffac040293 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -320,7 +320,7 @@ impl CompletionRelevance { } if let Some(trait_) = trait_ { - // lower rank trait methods unless its notable + // lower rank trait methods unless it's notable if !trait_.notable_trait { score -= 5; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 4f70a90affbdf..4698a9cd4a0cd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -233,7 +233,8 @@ pub(super) fn add_call_parens<'b>( Some(n) => { let smol_str = n.display_no_db(ctx.edition).to_smolstr(); let text = smol_str.as_str().trim_start_matches('_'); - let ref_ = ref_of_param(ctx, text, param.ty()); + let ref_ = + ref_of_param(ctx, text, ¶m.ty().instantiate_with_errors()); f(&format_args!("${{{}:{ref_}{text}}}", index + offset)) } None => { @@ -288,14 +289,16 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.as_reference_inner() { + if let Some((_, mutability)) = ty.as_reference() { + let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" }; + for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { - return if local.ty(ctx.db) == derefed_ty { - if ty.is_mutable_reference() { "&mut " } else { "&" } - } else { - "" - }; + let local_ty = local.ty(ctx.db); + let added_ref = local_ty.add_reference(ctx.db, mutability); + let needs_ref = + !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty); + return if needs_ref { ref_prefix } else { "" }; } } } @@ -473,7 +476,7 @@ fn bar(s: &S) { r#" struct S {} impl S { - fn foo(&self, x: i32) { + fn foo(&self, x: i32, y: &i32) { $0 } } @@ -481,8 +484,8 @@ impl S { r#" struct S {} impl S { - fn foo(&self, x: i32) { - self.foo(${1:x});$0 + fn foo(&self, x: i32, y: &i32) { + self.foo(${1:x}, ${2:y});$0 } } "#, @@ -561,6 +564,24 @@ fn main() { let x = Foo {}; ref_arg(${1:&x});$0 } +"#, + ); + check_edit( + "ref_arg", + r#" +//- minicore: coerce_unsized +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_ar$0 +} +"#, + r#" +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_arg(${1:&x});$0 +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index ee47c84708b46..20981433ae7ac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -38,13 +38,12 @@ // * `description` is an optional description of the snippet, if unset the snippet name will be used. // // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered. - // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if // the items aren't yet in scope. // // * `scope` is an optional filter for when the snippet should be applicable. Possible values are: -// ** for Snippet-Scopes: `expr`, `item` (default: `item`) -// ** for Postfix-Snippet-Scopes: `expr`, `type` (default: `expr`) +// * for Snippet-Scopes: `expr`, `item` (default: `item`) +// * for Postfix-Snippet-Scopes: `expr`, `type` (default: `expr`) // // The `body` field also has access to placeholders as visible in the example as `$0`. // These placeholders take the form of `$number` or `${number:placeholder_text}` which can be traversed as tabstop in ascending order starting from 1, @@ -98,7 +97,7 @@ // "scope": "expr" // } // } -// ```` +// ``` use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index f5dff47acf9ab..422648c8d6c55 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -979,7 +979,7 @@ impl<'db> ImportCandidate<'db> { return None; } let after = std::iter::successors(path.parent_path(), |it| it.parent_path()) - .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(name.text()))) .collect::>()?; path_import_candidate( sema, @@ -993,7 +993,7 @@ impl<'db> ImportCandidate<'db> { fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option { if sema .scope(name.syntax())? - .speculative_resolve(&make::ext::ident_path(&name.text())) + .speculative_resolve(&make::ext::ident_path(name.text())) .is_some() { return None; @@ -1033,7 +1033,7 @@ fn path_import_candidate<'db>( if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() - .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.name_ref().map(|name| Name::new_root(name.text()))) .collect::>>()?; ImportCandidate::Path(PathImportCandidate { qualifier, 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 27e3ed6bdb52e..0235389763080 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 @@ -314,7 +314,7 @@ impl ImportGroup { PathSegmentKind::SelfKw => ImportGroup::ThisModule, PathSegmentKind::SuperKw => ImportGroup::SuperModule, PathSegmentKind::CrateKw => ImportGroup::ThisCrate, - PathSegmentKind::Name(name) => match name.text().as_str() { + PathSegmentKind::Name(name) => match name.text() { "std" => ImportGroup::Std, "core" => ImportGroup::Std, _ => ImportGroup::ExternCrate, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 59099056f5c5b..f9251458fd026 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -582,8 +582,8 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(a_name), Some(b_name)) => { - let a_text = a_name.as_str().trim_start_matches("r#"); - let b_text = b_name.as_str().trim_start_matches("r#"); + let a_text = a_name.trim_start_matches("r#"); + let b_text = b_name.trim_start_matches("r#"); version_sort::version_sort(a_text, b_text) } } @@ -614,15 +614,13 @@ fn use_tree_cmp_by_tree_list_glob_or_alias( .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#")) + .map_or("_", |a_name| a_name.trim_start_matches("r#")) .cmp( b_rename .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")), + .map_or("_", |b_name| b_name.trim_start_matches("r#")), ), }, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 101046cf54436..ff32badd7f14a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -536,7 +536,7 @@ impl Ctx<'_> { let name = ident_pat.name()?; let make = editor.make(); - let temp_path = make.path_from_text(&name.text()); + let temp_path = make.path_from_text(name.text()); let resolution = self.source_scope.speculative_resolve(&temp_path)?; @@ -651,7 +651,7 @@ fn find_trait_for_assoc_item( }); for name in names { - if assoc_item_name.as_str() == name.as_str() { + if assoc_item_name == name.as_str() { // It is fine to return the first match because in case of // multiple possibilities, the exact trait must be disambiguated // in the definition of trait being implemented, so this search diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs index c8607a8099b91..09a270c143888 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs @@ -102,8 +102,7 @@ impl RaFixtureAnalysis { else { return false; }; - segment1.text_non_mutable() == "rust_analyzer" - && segment2.text_non_mutable() == "rust_fixture" + segment1.text() == "rust_analyzer" && segment2.text() == "rust_fixture" }) }); if !has_rust_fixture_attr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index b89c2fdf4ad87..16224ae5ee0b2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -459,7 +459,7 @@ fn rename_field_constructors( }; expr.record_expr_field_list()?.fields().find_map(|record_field| { if record_field.name_ref().is_none() - && Name::new_root(&record_field.field_name()?.text()) == old_name + && Name::new_root(record_field.field_name()?.text()) == old_name && let ast::Expr::PathExpr(field_name) = record_field.expr()? { field_name.path() @@ -734,7 +734,7 @@ fn source_edit_from_def<'db>( // special cases required for renaming fields/locals in Record patterns if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { if let Some(name_ref) = pat_field.name_ref() { - if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#") + if new_name.as_str() == name_ref.text().trim_start_matches("r#") && pat.at_token().is_none() { // Foo { field: ref mut local } -> Foo { ref mut field } @@ -747,7 +747,7 @@ fn source_edit_from_def<'db>( .text_range() .cover_offset(pat.syntax().text_range().start()), ); - edit.replace(name_range, name_ref.text().to_string()); + edit.replace(name_range, name_ref.text().to_owned()); } else { // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } // Foo { field: ref mut local } -> Foo { field: ref mut new_name } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index b688cb188d58d..6a492a54798ce 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -119,13 +119,13 @@ impl FileReferenceNode { _ => None, } } - pub fn text(&self) -> syntax::TokenText<'_> { + pub fn text(&self) -> &str { match self { FileReferenceNode::NameRef(name_ref) => name_ref.text(), FileReferenceNode::Name(name) => name.text(), FileReferenceNode::Lifetime(lifetime) => lifetime.text(), FileReferenceNode::FormatStringEntry(it, range) => { - syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()]) + &it.text()[*range - it.syntax().text_range().start()] } } } @@ -751,7 +751,7 @@ impl<'a, 'db> FindUsages<'a, 'db> { insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else { @@ -814,7 +814,7 @@ impl<'a, 'db> FindUsages<'a, 'db> { insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index a38154420af1a..58f459003ec46 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -1,5 +1,5 @@ -//! This modules defines type to represent changes to the source code, that flow -//! from the server to the client. +//! This module defines types that represent changes to source code flowing from +//! the server to the client. //! //! It can be viewed as a dual for [`Change`][vfs::Change]. @@ -15,7 +15,7 @@ use rustc_hash::FxHashMap; use span::FileId; use stdx::never; use syntax::{ - AstNode, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextSize, + AstNode, SyntaxNode, TextRange, TextSize, syntax_editor::{SyntaxAnnotation, SyntaxEditor}, }; @@ -68,7 +68,7 @@ impl SourceChange { /// Inserts a [`TextEdit`] and potentially a [`SnippetEdit`] for the given [`FileId`]. /// This properly handles merging existing edits for a file if some already exist. - pub fn insert_source_and_snippet_edit( + fn insert_source_and_snippet_edit( &mut self, file_id: impl Into, edit: TextEdit, @@ -105,7 +105,7 @@ impl SourceChange { pub fn merge(mut self, other: SourceChange) -> SourceChange { self.extend(other.source_file_edits); - self.extend(other.file_system_edits); + self.file_system_edits.extend(other.file_system_edits); self.is_snippet |= other.is_snippet; self } @@ -128,25 +128,6 @@ impl Extend<(FileId, (TextEdit, Option))> for SourceChange { } } -impl Extend for SourceChange { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|edit| self.push_file_system_edit(edit)); - } -} - -impl From> for SourceChange { - fn from(source_file_edits: IntMap) -> SourceChange { - let source_file_edits = - source_file_edits.into_iter().map(|(file_id, edit)| (file_id, (edit, None))).collect(); - SourceChange { - source_file_edits, - file_system_edits: Vec::new(), - is_snippet: false, - ..SourceChange::default() - } - } -} - impl FromIterator<(FileId, TextEdit)> for SourceChange { fn from_iter>(iter: T) -> Self { let mut this = SourceChange::default(); @@ -222,24 +203,15 @@ impl SnippetEdit { } pub struct SourceChangeBuilder { - pub edit: TextEditBuilder, + edit: TextEditBuilder, pub file_id: FileId, pub source_change: SourceChange, pub command: Option, /// Keeps track of all edits performed on each file - pub file_editors: FxHashMap, + file_editors: FxHashMap, /// Keeps track of which annotations correspond to which snippets - pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, - - /// Keeps track of where to place snippets - pub snippet_builder: Option, -} - -#[derive(Default)] -pub struct SnippetBuilder { - /// Where to place snippets at - places: Vec, + snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, } impl SourceChangeBuilder { @@ -251,7 +223,6 @@ impl SourceChangeBuilder { command: None, file_editors: FxHashMap::default(), snippet_annotations: vec![], - snippet_builder: None, } } @@ -329,15 +300,9 @@ impl SourceChangeBuilder { } // Apply mutable edits - let snippet_edit = self.snippet_builder.take().map(|builder| { - SnippetEdit::new( - builder.places.into_iter().flat_map(PlaceSnippet::finalize_position).collect(), - ) - }); - let edit = mem::take(&mut self.edit).finish(); - if !edit.is_empty() || snippet_edit.is_some() { - self.source_change.insert_source_and_snippet_edit(self.file_id, edit, snippet_edit); + if !edit.is_empty() { + self.source_change.insert_source_edit(self.file_id, edit); } } @@ -375,36 +340,6 @@ impl SourceChangeBuilder { self.command = Some(Command::Rename); } - /// Adds a tabstop snippet to place the cursor before `node` - pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { - assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); - } - - /// Adds a tabstop snippet to place the cursor before `token` - pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { - assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::Before(token.into())); - } - - /// Adds a tabstop snippet to place the cursor after `token` - pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { - assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::After(token.into())); - } - - /// Adds a snippet to move the cursor selected over `node` - pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { - assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into())) - } - - fn add_snippet(&mut self, snippet: PlaceSnippet) { - let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); - snippet_builder.places.push(snippet); - self.source_change.is_snippet = true; - } - fn add_snippet_annotation(&mut self, kind: AnnotationSnippet) -> SyntaxAnnotation { let annotation = SyntaxAnnotation::default(); self.snippet_annotations.push((kind, annotation)); @@ -425,7 +360,7 @@ impl SourceChangeBuilder { .is_err() ); - mem::take(&mut self.source_change) + self.source_change } } @@ -461,30 +396,11 @@ pub enum Snippet { PlaceholderGroup(Vec), } -pub enum AnnotationSnippet { +enum AnnotationSnippet { /// Place a tabstop before an element Before, - /// Place a tabstop before an element + /// Place a tabstop after an element After, /// Place a placeholder snippet in place of the element(s) Over, } - -enum PlaceSnippet { - /// Place a tabstop before an element - Before(SyntaxElement), - /// Place a tabstop before an element - After(SyntaxElement), - /// Place a placeholder snippet in place of the element - Over(SyntaxElement), -} - -impl PlaceSnippet { - fn finalize_position(self) -> Vec { - match self { - PlaceSnippet::Before(it) => vec![Snippet::Tabstop(it.text_range().start())], - PlaceSnippet::After(it) => vec![Snippet::Tabstop(it.text_range().end())], - PlaceSnippet::Over(it) => vec![Snippet::Placeholder(it.text_range())], - } - } -} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs index 7163c08e1e317..af893f1ea5f38 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs @@ -115,7 +115,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { } (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} (Some(lhs_ele), Some(rhs_ele)) => { - // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up + // nodes differ, look for lhs_ele in rhs, if it's found we can mark everything up // until that element as insertions. This is important to keep the diff minimal // in regards to insertions that have been actually done, this is important for // use insertions as we do not want to replace the entire module node. diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index ddf5999036d21..ce836197571a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -18,6 +18,7 @@ either.workspace = true itertools.workspace = true serde_json.workspace = true tracing.workspace = true +smallvec.workspace = true # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index 09f3e8bfb319b..9e38d8f1b9e43 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -6,14 +6,14 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: inactive-code // // This diagnostic is shown for code with inactive `#[cfg]` attributes. +// +// It can be disabled selectively with `#[allow(rust_analyzer::inactive_code)]`. pub(crate) fn inactive_code( ctx: &DiagnosticsContext<'_, '_>, d: &hir::InactiveCode, ) -> Option { - // If there's inactive code somewhere in a macro, don't propagate to the call-site. - if d.node.file_id.is_macro() { - return None; - } + // If there's inactive code somewhere in a macro that doesn't map to something in the call, don't propagate to the call-site. + d.node.map(|it| it.text_range()).original_node_file_range_rooted_opt(ctx.db())?; let inactive = DnfExpr::new(&d.cfg).why_inactive(&d.opts); let mut message = "code is inactive due to #[cfg] directives".to_owned(); @@ -28,10 +28,11 @@ pub(crate) fn inactive_code( } } // FIXME: This shouldn't be a diagnostic - let res = Diagnostic::new( - DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), + let res = Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RaLint("inactive_code", Severity::WeakWarning), message, - ctx.sema.diagnostics_display_range(d.node), + d.node, ) .stable() .with_unused(true); @@ -239,7 +240,7 @@ fn foo() {} }; assert_eq!( inactive_code.code, - DiagnosticCode::Ra("inactive-code", ide_db::Severity::WeakWarning) + DiagnosticCode::RaLint("inactive_code", ide_db::Severity::WeakWarning) ); assert_eq!( inactive_code.message, @@ -252,4 +253,76 @@ fn foo() {} ide_db::FileRange { file_id: file_id.file_id(&db), range: full_file_range }, ); } + + #[test] + fn cfg_in_macro_does_not_diagnose_the_whole_call() { + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + const _: () = (); + + $e + }; +} + +m! { + fn foo() {} +} + "#, + ); + } + + #[test] + fn in_macro() { + check( + r#" +macro_rules! m { + ($e:item) => { + $e + }; +} + +m! { + #[cfg(false)] fn foo() {} + // ^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled +} + "#, + ); + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + $e + }; +} + +m! { + fn foo() {} + // ^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled +} + "#, + ); + } + + #[test] + fn allow() { + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + #[allow(rust_analyzer::inactive_code)] + $e + }; +} + +m! { + fn foo() {} +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 81ea442e9e712..fb9095e0f4bd2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -38,8 +38,12 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); + // E0057 is the code rustc emits when calling something via the `Fn`/`FnMut`/`FnOnce` + // traits with the wrong number of arguments; E0061 is used for direct function calls. + // (Previously this used E0107, which is actually "wrong number of generic arguments".) + let code = if d.is_fn_trait_call { "E0057" } else { "E0061" }; Diagnostic::new( - DiagnosticCode::RustcHardError("E0107"), + DiagnosticCode::RustcHardError(code), message, invalid_args_range(ctx, d.call_expr, d.expected, d.found), ) @@ -395,10 +399,36 @@ fn main() { ) } + // A multi-argument closure exercises the same tuple-arguments code path in + // hir-ty (`TupleArgumentsFlag::TupleArguments` in `crates/hir-ty/src/infer/expr.rs`) + // as calls through `Fn`/`FnMut`/`FnOnce`. The mismatch is reported with error + // code E0057 (rustc's Fn-trait code), not E0061 which is reserved for direct + // function calls. `arg_count_lambda` above covers the 1-tuple case; this one + // covers the multi-argument case to make sure the tuple size is reported + // correctly. + #[test] + fn arg_count_multi_arg_closure() { + check_diagnostics( + r#" +//- minicore: fn +fn main() { + let f = |_a: u8, _b: u8| (); + f(); + //^^ error: expected 2 arguments, found 0 + f(1, 2); + f(1, 2, 3); + //^^ error: expected 2 arguments, found 3 +} +"#, + ) + } + #[test] fn cfgd_out_call_arguments() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + struct C(#[cfg(FALSE)] ()); impl C { fn new() -> Self { @@ -422,6 +452,8 @@ fn main() { fn cfgd_out_fn_params() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + fn foo(#[cfg(NEVER)] x: ()) {} struct S; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index cc900808b5bc5..d61c524341181 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -156,6 +156,8 @@ mod tests { fn dont_work_for_field_with_disabled_cfg() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + struct Test { #[cfg(feature = "hello")] test: u32, @@ -227,6 +229,8 @@ impl S { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + struct MyStruct { my_val: usize, #[cfg(feature = "foo")] @@ -252,6 +256,8 @@ impl MyStruct { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + enum Foo { #[cfg(not(feature = "foo"))] Buz, @@ -275,6 +281,8 @@ fn test_fn(f: Foo) { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + struct S { #[cfg(feature = "foo")] foo: u32, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 295f37ab1d6b3..df815d6322b63 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -45,18 +45,31 @@ pub(crate) fn type_mismatch( cov_mark::hit!(type_mismatch_range_adjustment); Some(salient_token_range) }); + + let expected = d + .expected + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + let actual = d + .actual + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + + // The types differ (that's why we're here), yet they render the same, e.g. `foo::S` and + // `bar::S` both render as `S`. Retry with qualified paths so the message isn't a useless + // "expected S, found S". + let (expected, actual) = if expected == actual { + qualified_display(ctx, d).unwrap_or((expected, actual)) + } else { + (expected, actual) + }; + Some( Diagnostic::new( DiagnosticCode::RustcHardError("E0308"), - format!( - "expected {}, found {}", - d.expected - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - d.actual - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - ), + format!("expected {expected}, found {actual}"), display_range, ) .stable() @@ -64,6 +77,20 @@ pub(crate) fn type_mismatch( ) } +/// Renders both sides of the mismatch with qualified paths, for when their plain names collide. +/// Returns `None` if either side has no renderable path, in which case both keep their plain +/// names — mixing a qualified and an unqualified name would be more confusing, not less. +fn qualified_display( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::TypeMismatch<'_>, +) -> Option<(String, String)> { + let root = d.expr_or_pat.file_id.parse_or_expand(ctx.db()); + let module = ctx.sema.scope(d.expr_or_pat.value.to_node(&root).syntax())?.module(); + let expected = d.expected.display_source_code(ctx.db(), module.into(), true).ok()?; + let actual = d.actual.display_source_code(ctx.db(), module.into(), true).ok()?; + Some((expected, actual)) +} + fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option> { let mut fixes = Vec::new(); @@ -1810,6 +1837,115 @@ fn main() { // ^^ error: type annotations needed // ^^^ error: the trait bound `i32: Foo` is not satisfied } +"#, + ); + } + + #[test] + fn regression_21668() { + check_diagnostics( + r#" +mod std { + pub enum Ordering { Less } +} +pub use std::*; + +pub mod evil { + pub struct Ordering(pub i32); +} + +pub mod oblivious { + use crate::Ordering; + + pub fn what() -> Ordering { + Ordering(2) + } +} +pub use evil::Ordering; +"#, + ); + } + + // Tests for qualified paths on name collision (issue #22331) + + #[test] + fn type_mismatch_collision_basic_structs() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn test(_: foo::S) { + test(bar::S); + //^^^^^^ error: expected foo::S, found bar::S +} +"#, + ); + } + + #[test] + fn type_mismatch_collision_in_generic() { + check_diagnostics( + r#" +//- minicore: option +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn make() -> Option { loop {} } +fn test(_: Option) { + test(make()); + //^^^^^^ error: expected Option, found Option +} +"#, + ); + } + + #[test] + fn type_mismatch_no_collision_unchanged() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct T; +} +fn test(_: foo::S) { + test(bar::T); + //^^^^^^ error: expected S, found T +} +"#, + ); + } + + #[test] + fn type_mismatch_multiple_collisions() { + check_diagnostics( + r#" +//- minicore: result +mod foo { + pub struct T; +} +mod bar { + pub struct T; +} +mod baz { + pub struct E; +} +mod qux { + pub struct E; +} +fn make() -> Result { loop {} } +fn test(_: Result) { + test(make()); + //^^^^^^ error: expected Result, found Result +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index dc6ae6f08ba5e..376fc4fbdf78d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -3,14 +3,15 @@ use std::iter; use hir::crate_def_map; -use hir::{InFile, ModuleSource}; +use hir::{EditionedFileId, InFile, ModuleSource}; use ide_db::text_edit::TextEdit; use ide_db::{FileId, FileRange, base_db::SourceDatabase, source_change::SourceChange}; use ide_db::{base_db, line_index}; use paths::Utf8Component; use syntax::{ - AstNode, TextRange, + AstNode, Edition, TextRange, ast::{self, HasModuleItem, HasName, edit::IndentLevel}, + utils::{is_identifier, is_raw_identifier}, }; use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fix}; @@ -22,10 +23,11 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fi pub(crate) fn unlinked_file( ctx: &DiagnosticsContext<'_, '_>, acc: &mut Vec, - file_id: FileId, + editioned_file_id: EditionedFileId, ) { + let file_id = editioned_file_id.file_id(ctx.sema.db); let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len()); - let fixes = fixes(ctx, file_id, range); + let fixes = fixes(ctx, editioned_file_id, range); // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics. // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523 let message = if fixes.is_none() { @@ -74,13 +76,15 @@ pub(crate) fn unlinked_file( fn fixes( ctx: &DiagnosticsContext<'_, '_>, - file_id: FileId, + editioned_file_id: EditionedFileId, trigger_range: TextRange, ) -> Option> { // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, // suggest that as a fix. let db = ctx.sema.db; + let file_id = editioned_file_id.file_id(db); + let edition = editioned_file_id.edition(db); let source_root = ctx.sema.db.file_source_root(file_id).source_root_id(db); let source_root = ctx.sema.db.source_root(source_root).source_root(db); @@ -136,6 +140,7 @@ fn fixes( return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -169,6 +174,7 @@ fn fixes( return make_fixes( parent_id, module.definition_source(ctx.sema.db).value, + edition, &module_name, trigger_range, ); @@ -193,6 +199,7 @@ fn fixes( return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -202,9 +209,26 @@ fn fixes( None } +/// Convert a module name along with its visibility to code. +/// In most cases, this just adds the visibility and keyword beforehand, +/// but the exceptions are non-identifiers and keywords. +fn format_mod_name(mod_name: &str, visibility_and_keyword: &str, edition: Edition) -> String { + if is_identifier(mod_name, edition) { + format!("{visibility_and_keyword} {mod_name};") + } else { + if is_raw_identifier(mod_name, edition) { + format!("{visibility_and_keyword} r#{mod_name};") + } else { + let file_name = format!("{mod_name}.rs"); + format!("#[path = {file_name:?}]\n{visibility_and_keyword} mod_name;") + } + } +} + fn make_fixes( parent_file_id: FileId, source: ModuleSource, + edition: Edition, new_mod_name: &str, trigger_range: TextRange, ) -> Option> { @@ -212,9 +236,9 @@ fn make_fixes( matches!(item, ast::Item::Module(m) if m.item_list().is_none()) } - let mod_decl = format!("mod {new_mod_name};"); - let pub_mod_decl = format!("pub mod {new_mod_name};"); - let pub_crate_mod_decl = format!("pub(crate) mod {new_mod_name};"); + let mod_decl = format_mod_name(new_mod_name, "mod", edition); + let pub_mod_decl = format_mod_name(new_mod_name, "pub mod", edition); + let pub_crate_mod_decl = format_mod_name(new_mod_name, "pub(crate) mod", edition); let mut mod_decl_builder = TextEdit::builder(); let mut pub_mod_decl_builder = TextEdit::builder(); @@ -545,6 +569,90 @@ mod bar { //- /main.rs include!("bar/foo/mod.rs"); //- /bar/foo/mod.rs +"#, + ); + } + + #[test] + fn unlinked_file_with_strict_keyword_move() { + check_fix( + r#" +//- /main.rs +//- /move.rs +$0 +"#, + r#" +mod r#move; +"#, + ); + } + + #[test] + fn unlinked_file_with_weak_keyword_safe() { + check_fix( + r#" +//- /main.rs +//- /safe.rs +$0 +"#, + r#" +mod safe; +"#, + ); + } + + #[test] + fn unlinked_file_with_reserved_keyword_abstract() { + check_fix( + r#" +//- /main.rs +//- /abstract.rs +$0 +"#, + r#" +mod r#abstract; +"#, + ); + } + + #[test] + fn unlinked_file_with_unescaped_keyword_crate() { + check_fix( + r#" +//- /main.rs +//- /crate.rs +$0 +"#, + r#"#[path = "crate.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_invalid_symbol_in_module_name() { + check_fix( + r#" +//- /main.rs +//- /my-file.rs +$0 +"#, + r#"#[path = "my-file.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_numeric_module_name() { + check_fix( + r#" +//- /main.rs +//- /0000.rs +$0 +"#, + r#"#[path = "0000.rs"] +mod mod_name; "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 819b644dfadde..d4d36e87f4709 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -129,9 +129,11 @@ use ide_db::{ rename::RenameConfig, source_change::SourceChange, }; +use smallvec::{SmallVec, smallvec}; use syntax::{ AstPtr, Edition, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ast::{self, AstNode}, + format_smolstr, }; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -141,6 +143,7 @@ pub enum DiagnosticCode { RustcLint(&'static str), Clippy(&'static str), Ra(&'static str, Severity), + RaLint(&'static str, Severity), } impl DiagnosticCode { @@ -158,7 +161,7 @@ impl DiagnosticCode { DiagnosticCode::Clippy(e) => { format!("https://rust-lang.github.io/rust-clippy/master/#/{e}") } - DiagnosticCode::Ra(e, _) => { + DiagnosticCode::Ra(e, _) | DiagnosticCode::RaLint(e, _) => { format!("https://rust-analyzer.github.io/book/diagnostics.html#{e}") } } @@ -169,7 +172,8 @@ impl DiagnosticCode { DiagnosticCode::RustcHardError(r) | DiagnosticCode::RustcLint(r) | DiagnosticCode::Clippy(r) - | DiagnosticCode::Ra(r, _) => r, + | DiagnosticCode::Ra(r, _) + | DiagnosticCode::RaLint(r, _) => r, DiagnosticCode::SyntaxError => "syntax-error", } } @@ -206,7 +210,7 @@ impl Diagnostic { // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can // make it normal warning. DiagnosticCode::Clippy(_) => Severity::WeakWarning, - DiagnosticCode::Ra(_, s) => s, + DiagnosticCode::Ra(_, s) | DiagnosticCode::RaLint(_, s) => s, }, unused: false, experimental: true, @@ -431,9 +435,7 @@ pub fn semantic_diagnostics( m.diagnostics(db, &mut diags, config.style_lints); } } - None => { - handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id.file_id(db)) - } + None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id), } for diag in diags { @@ -573,7 +575,14 @@ pub fn semantic_diagnostics( let mut lints = res .iter_mut() - .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_))) + .filter(|it| { + matches!( + it.code, + DiagnosticCode::Clippy(_) + | DiagnosticCode::RustcLint(_) + | DiagnosticCode::RaLint(..) + ) + }) .filter_map(|it| Some((it.main_node(&ctx.sema)?, it))) .collect::>(); @@ -646,7 +655,7 @@ fn handle_diag_from_macros( struct BuiltLint { lint: &'static Lint, - groups: Vec<&'static str>, + groups: SmallVec<[SmolStr; 5]>, } static RUSTC_LINTS: LazyLock> = @@ -667,12 +676,17 @@ fn build_lints_map( ) -> FxHashMap<&'static str, BuiltLint> { let mut map_with_prefixes: FxHashMap<_, _> = lints .iter() - .map(|lint| (lint.label, BuiltLint { lint, groups: vec![lint.label, "__RA_EVERY_LINT"] })) + .map(|lint| { + ( + lint.label, + BuiltLint { lint, groups: smallvec![lint.label.into(), "__RA_EVERY_LINT".into()] }, + ) + }) .collect(); for g in lint_group { let mut add_children = |label: &'static str| { for child in g.children { - map_with_prefixes.get_mut(child).unwrap().groups.push(label); + map_with_prefixes.get_mut(child).unwrap().groups.push(label.into()); } }; add_children(g.lint.label); @@ -693,12 +707,15 @@ fn handle_lints( edition: Edition, ) { for (node, diag) in diagnostics { - let lint = match diag.code { - DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint, - DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint, - _ => panic!("non-lint passed to `handle_lints()`"), + let default_severity = 'find_severity: { + let lint = match diag.code { + DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint, + DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint, + DiagnosticCode::RaLint(_, severity) => break 'find_severity severity, + _ => panic!("non-lint passed to `handle_lints()`"), + }; + default_lint_severity(lint, edition) }; - let default_severity = default_lint_severity(lint, edition); if !(default_severity == Severity::Allow && diag.severity == Severity::WeakWarning) { diag.severity = default_severity; } @@ -798,13 +815,13 @@ fn lint_attrs( #[derive(Debug)] struct LintGroups { - groups: &'static [&'static str], + groups: SmallVec<[SmolStr; 5]>, inside_warnings: bool, } impl LintGroups { fn contains(&self, group: &str) -> bool { - self.groups.contains(&group) || (self.inside_warnings && group == "warnings") + self.groups.iter().any(|g| g == group) || (self.inside_warnings && group == "warnings") } } @@ -813,12 +830,15 @@ fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups { DiagnosticCode::RustcLint(name) => { let lint = &RUSTC_LINTS[name]; let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning; - (&lint.groups, inside_warnings) + (lint.groups.clone(), inside_warnings) } DiagnosticCode::Clippy(name) => { let lint = &CLIPPY_LINTS[name]; let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning; - (&lint.groups, inside_warnings) + (lint.groups.clone(), inside_warnings) + } + DiagnosticCode::RaLint(name, severity) => { + (smallvec![format_smolstr!("rust_analyzer::{name}")], *severity == Severity::Warning) } _ => panic!("non-lint passed to `handle_lints()`"), }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs index 301613e920191..34cf80a85b98e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs @@ -2729,6 +2729,7 @@ tracing::error!(); "unresolved-macro-call", "syntax-error", "macro-error", + "inactive_code", ], ); } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index 3dbba0ff2dab4..9d6079202a5b6 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -155,7 +155,7 @@ impl<'db> Resolver<'_, 'db> { fn path_contains_placeholder(&self, path: &ast::Path) -> bool { if let Some(segment) = path.segment() && let Some(name_ref) = segment.name_ref() - && self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) + && self.placeholders_by_stand_in.contains_key(name_ref.text()) { return true; } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index c6a1d69672119..f76912b84085a 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -127,7 +127,7 @@ impl<'db> MatchFinder<'db> { usage_cache.find(&definition).unwrap() } - /// Returns the scope within which we want to search. We don't want un unrestricted search + /// Returns the scope within which we want to search. We don't want an unrestricted search /// scope, since we don't want to find references in external dependencies. fn search_scope(&self) -> SearchScope { // FIXME: We should ideally have a test that checks that we edit local roots and not library diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index b3d09cac42cf5..d8c15cabb2fb8 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -1070,7 +1070,7 @@ fn match_failure_reasons() { #[test] fn overlapping_possible_matches() { // There are three possible matches here, however the middle one, `foo(foo(foo(42)))` shouldn't - // match because it overlaps with the outer match. The inner match is permitted since it's is + // match because it overlaps with the outer match. The inner match is permitted since it is // contained entirely within the placeholder of the outer match. assert_matches( "foo(foo($a))", diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index f716f94d7141b..884bc111caeaf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -43,6 +43,8 @@ pub struct AnnotationConfig<'a> { pub annotate_references: bool, pub annotate_method_references: bool, pub annotate_enum_variant_references: bool, + pub references_exclude_imports: bool, + pub references_exclude_tests: bool, pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, pub ra_fixture: RaFixtureConfig<'a>, @@ -219,8 +221,8 @@ pub(crate) fn resolve_annotation( &FindAllRefsConfig { search_scope: None, ra_fixture: config.ra_fixture, - exclude_imports: false, - exclude_tests: false, + exclude_imports: config.references_exclude_imports, + exclude_tests: config.references_exclude_tests, }, ) .map(|result| { @@ -262,6 +264,8 @@ mod tests { annotate_references: true, annotate_method_references: true, annotate_enum_variant_references: true, + references_exclude_imports: false, + references_exclude_tests: false, location: AnnotationLocation::AboveName, ra_fixture: RaFixtureConfig::default(), filter_adjacent_derive_implementations: false, @@ -278,7 +282,7 @@ mod tests { .annotations(config, file_id) .unwrap() .into_iter() - .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap()) + .map(|annotation| analysis.resolve_annotation(config, annotation).unwrap()) .collect(); expect.assert_debug_eq(&annotations); @@ -1045,4 +1049,39 @@ struct Foo; &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG }, ); } + + #[test] + fn refs_exclude_tests() { + check_with_config( + r#" +fn foo() {} + +#[test] +fn bar() { foo() } + "#, + expect![[r#" + [ + Annotation { + range: 3..6, + kind: HasReferences { + pos: FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], + &AnnotationConfig { + references_exclude_tests: true, + annotate_runnables: false, + ..DEFAULT_CONFIG + }, + ); + } } 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 70d05cd3b5dcc..de8cf971da0eb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -282,7 +282,7 @@ pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option TextSize::try_from(comment.prefix().len()).ok(), ast::String(string) => { doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; - if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() { + if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) == Some("include_str")).is_some() { return None; } string.open_quote_text_range().map(|it| it.len()) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 21254fc4d6a22..1a85342dc905c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -106,7 +106,7 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option= 10 { format!("{it} ({it:#X})") } else { format!("{it}") }) } Err(err) => { - let res = it.value(db).map(|it| format!("{it:?}")); + let res = it.value(db).map(|it| it.to_string()); if env::var_os("RA_DEV").is_some() { let res = res.as_deref().unwrap_or(""); Some(format!( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 89f1cf2fc1e11..9dbf1136dcfc6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -5620,6 +5620,30 @@ enum E { This is a doc "#]], ); + // const eval failed test + check( + r#" +#[repr(u8)] +enum E { + A$0 = {}, +} +"#, + expect![[r#" + *A* + + ```rust + ra_test_fixture::E + ``` + + ```rust + A = {} + ``` + + --- + + size = 1, align = 1, no Drop + "#]], + ); } #[test] @@ -9508,7 +9532,7 @@ pub fn f(x$0: impl Tr<{ 0 }>) {} *x* ```rust - x: impl Tr<{const}> + ?Sized + x: impl Tr<0> + ?Sized ``` --- diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index 7a8a6eb84a5fa..89a2d9fa97eba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -204,7 +204,7 @@ fn hints_( mut is_trivial: bool, ) -> Option<()> { let is_elided = |lt: &Option| match lt { - Some(lt) => matches!(lt.text().as_str(), "'_"), + Some(lt) => matches!(lt.text(), "'_"), None => true, }; let self_param = self_param.and_then(|it| { @@ -298,12 +298,12 @@ fn hints_( potential_lt_refs.for_each(|(name, ..)| { let name = match name { Some(it) if config.param_names_for_lifetime_elision_hints => { - if let Some(c) = used_names.get_mut(it.text().as_str()) { + if let Some(c) = used_names.get_mut(it.text()) { *c += 1; - format_smolstr!("'{}{c}", it.text().as_str()) + format_smolstr!("'{}{c}", it.text()) } else { - used_names.insert(it.text().as_str().into(), 0); - format_smolstr!("'{}", it.text().as_str()) + used_names.insert(it.text().into(), 0); + format_smolstr!("'{}", it.text()) } } _ => gen_idx_name(), @@ -316,7 +316,7 @@ fn hints_( let output = match potential_lt_refs.as_slice() { [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { match lifetime { - Some(lt) => match lt.text().as_str() { + Some(lt) => match lt.text() { "'_" => allocated_lifetimes.first().cloned(), "'static" => None, name => Some(name.into()), 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 f1689e5f9dc8a..fcf1304d4b47f 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 @@ -294,7 +294,8 @@ pub(super) fn is_argument_similar_to_param_name( debug_assert!(!argument.is_empty()); debug_assert!(!param_name.is_empty()); let param_name = param_name.split('_'); - let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_')); + let argument = argument.iter().flat_map(|it| it.text().split('_')); + let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it)); let prefix_match = zip(argument.clone(), param_name.clone()) .all(|(arg, param)| arg.eq_ignore_ascii_case(param)); @@ -312,7 +313,7 @@ pub(super) fn get_segment_representation( let receiver = method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr)); let name_ref = method_call_expr.name_ref()?; - if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) { + if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text()) { return receiver; } Some(Either::Left(match receiver { @@ -374,16 +375,16 @@ fn is_adt_constructor_similar_to_param_name( ) -> bool { (|| match sema.resolve_path(path)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name) } hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => { - if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name { + if to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name { return Some(true); } let qual = path.qualifier()?; match sema.resolve_path(&qual)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(qual.segment()?.name_ref()?.text()) == param_name) } _ => None, } @@ -710,9 +711,12 @@ fn main() { let param_eter2 = 0; bar(param_eter2); //^^^^^^^^^^^ param_eter + let r#loop = true; let loop_level = 0; far(loop_level); faz(loop_level); + far(r#loop); + faz(r#loop); non_ident_pat((0, 0)); diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 125b2f495acca..c3d620a35700b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -190,7 +190,7 @@ impl NavigationTarget { kind: SymbolKind, ) -> UpmappingResult { let name = - value.name().map(|it| Symbol::intern(&it.text())).unwrap_or_else(|| sym::underscore); + value.name().map(|it| Symbol::intern(it.text())).unwrap_or_else(|| sym::underscore); orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( |(FileRange { file_id, range: full_range }, focus_range)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html new file mode 100644 index 0000000000000..b61f574f0e768 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html @@ -0,0 +1,50 @@ + + +
use foo::bar;
+
+fn main() {
+    bar!()
+}
+
+
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html new file mode 100644 index 0000000000000..4497e83a4d4cc --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html @@ -0,0 +1,49 @@ + + +
fn main() {
+    let here = 1;
+    format_args!(r"backslash \{here} arg");
+    format_args!(r#"hashed \{here} arg"#);
+    format_args!("plain {here} arg");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html new file mode 100644 index 0000000000000..06fc3f0772dbe --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html @@ -0,0 +1,46 @@ + + +
use foo::foo;
+
+
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index f4b103902499c..7a3d71f519f84 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -452,6 +452,23 @@ macro_rules! void_2024 { ); } +#[test] +fn test_raw_string_format_specifiers() { + check_highlighting( + r####" +//- minicore: fmt +fn main() { + let here = 1; + format_args!(r"backslash \{here} arg"); + format_args!(r#"hashed \{here} arg"#); + format_args!("plain {here} arg"); +} +"####, + expect_file!["./test_data/highlight_raw_string_format_specifiers.html"], + false, + ); +} + #[test] fn test_string_highlighting() { // The format string detection is based on macro-expansion, @@ -1601,3 +1618,51 @@ async fn get_double_async(num: u32) -> u32 { false, ); } + +#[test] +fn private_multi_namespace() { + check_highlighting( + r#" +//- /bar.rs crate:bar deps:foo +use foo::foo; + +//- /foo.rs crate:foo +struct foo; + +#[macro_export] +macro_rules! foo { + () => {}; +} + "#, + expect_file!["./test_data/private_multi_namespace.html"], + false, + ); +} + +#[test] +fn mod_and_macro_name_conflict() { + check_highlighting( + r#" +//- /main.rs crate:main deps:foo +use foo::bar; + +fn main() { + bar!() +} + +//- /foo.rs crate:foo +mod bar { + fn random() {} +} + +#[macro_export] +macro_rules! bar { + () => { + println!("Hello"); + }; +} +"#, + expect_file!["./test_data/highlight_module_macro_conflict.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index b06079d8acd13..79919dac5b195 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -365,7 +365,7 @@ fn on_left_angle_typed( ) -> Option { let file_text = reparsed.syntax().text(); - // Find the next non-whitespace char in the line, check if its a `>` + // Find the next non-whitespace char in the line, check if it's a `>` let mut next_offset = offset; while file_text.char_at(next_offset) == Some(' ') { next_offset += TextSize::of(' ') diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index fe303aa0e0c36..9a566ee687e0d 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -597,6 +597,7 @@ define_symbols! { unreachable_2021, unreachable, unsafe_cell, + covariant_unsafe_cell, unsafe_pinned, unsize, unstable, diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index fe01fb1f10637..c95e5a3ef8341 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -347,7 +347,7 @@ struct MatchState<'t> { /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// -/// For more info about the how this happens, see the module-level doc comments and the inline +/// For more info about how this happens, see the module-level doc comments and the inline /// comments of this function. /// /// # Parameters diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 3f341c2ab846e..9f3de3b921c6e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -299,8 +299,16 @@ fn expr_bp( // match 1.. { _ => () }; // match a.b()..S { _ => () }; // } + + // test closure_postfix_range_method_call + // fn foo() { + // || 1.. .method(); + // || 1.. .field; + // } + let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); + let struct_forbidden = r.forbid_structs && p.at(T!['{']); let has_trailing_expression = - p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])); + p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden; if !has_trailing_expression { // no RHS lhs = m.complete(p, RANGE_EXPR); @@ -382,6 +390,7 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik // } let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); let struct_forbidden = r.forbid_structs && p.at(T!['{']); + // NOTE: Similar logic `is_range` flag in expr_bp() if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden { expr_bp(p, None, r, 2); } diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index ce64db8adae90..4f4728a8c5a58 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -14,7 +14,7 @@ use crate::SyntaxKind; #[derive(Default)] pub struct Output { /// 32-bit encoding of events. If LSB is zero, then that's an index into the - /// error vector. Otherwise, it's one of the thee other variants, with data encoded as + /// error vector. Otherwise, it's one of the three other variants, with data encoded as /// /// ```text /// |16 bit kind|8 bit n_input_tokens|4 bit tag|4 bit leftover| diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 8104d28bafdf0..22b5684581252 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -121,6 +121,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/closure_params.rs"); } #[test] + fn closure_postfix_range_method_call() { + run_and_expect_no_errors("test_data/parser/inline/ok/closure_postfix_range_method_call.rs"); + } + #[test] fn closure_range_method_call() { run_and_expect_no_errors("test_data/parser/inline/ok/closure_range_method_call.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast new file mode 100644 index 0000000000000..555d312e33897 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast @@ -0,0 +1,53 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "method" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FIELD_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "field" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs new file mode 100644 index 0000000000000..71429be2caad5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs @@ -0,0 +1,4 @@ +fn foo() { + || 1.. .method(); + || 1.. .field; +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 3015bd0c0eccc..ae03be9aa7a26 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -31,7 +31,7 @@ //! ``` //! //! We probably should replace most of the code here with bincode someday, but, -//! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for +//! as we don't have bincode in Cargo.toml yet, let's stick with serde_json for //! the time being. #[cfg(feature = "in-rust-tree")] 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 926a9e327e8c4..9f84f632d5e44 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 @@ -473,6 +473,10 @@ impl WorkspaceBuildScripts { if let Some(lockfile_copy) = &lockfile_copy { requires_unstable_options = true; match lockfile_copy.usage { + LockfileUsage::WithFlag => { + cmd.arg("--lockfile-path"); + cmd.arg(lockfile_copy.path.as_str()); + } LockfileUsage::WithEnvVarUnstable => { cmd.arg("-Zlockfile-path"); cmd.env( diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index a6bfea8200c53..defd9f96ab5fb 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -143,6 +143,8 @@ pub(crate) struct LockfileCopy { } pub(crate) enum LockfileUsage { + /// Rust [1.82.0, 1.95.0). `cargo --lockfile-path ` + WithFlag, /// Rust [1.95.0, 1.97.0). `CARGO_RESOLVER_LOCKFILE_PATH= cargo -Zlockfile-path ` WithEnvVarUnstable, /// Rust >= 1.97.0. `CARGO_RESOLVER_LOCKFILE_PATH= cargo ` @@ -153,6 +155,15 @@ pub(crate) fn make_lockfile_copy( toolchain_version: &semver::Version, lockfile_path: &Utf8Path, ) -> Option { + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG: semver::Version = + semver::Version { + major: 1, + minor: 82, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV_UNSTABLE: semver::Version = semver::Version { major: 1, @@ -176,6 +187,8 @@ pub(crate) fn make_lockfile_copy( } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV_UNSTABLE { LockfileUsage::WithEnvVarUnstable + } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG { + LockfileUsage::WithFlag } else { return None; }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 3db5a0fffce7f..97375fe9ddd1a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -767,6 +767,10 @@ impl FetchMetadata { let mut using_lockfile_copy = false; if let Some(lockfile_copy) = &lockfile_copy { match lockfile_copy.usage { + LockfileUsage::WithFlag => { + other_options.push("--lockfile-path".to_owned()); + other_options.push(lockfile_copy.path.to_string()); + } LockfileUsage::WithEnvVarUnstable => { other_options.push("-Zlockfile-path".to_owned()); command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 6bd27c2621978..d3b2030205aa7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -166,8 +166,6 @@ fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { Ok(()) } -const STACK_SIZE: usize = 1024 * 1024 * 8; - /// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us /// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack /// space. @@ -176,8 +174,7 @@ fn with_extra_thread( thread_intent: stdx::thread::ThreadIntent, f: impl FnOnce() -> anyhow::Result<()> + Send + 'static, ) -> anyhow::Result<()> { - let handle = - stdx::thread::Builder::new(thread_intent, thread_name).stack_size(STACK_SIZE).spawn(f)?; + let handle = stdx::thread::Builder::new(thread_intent, thread_name).spawn(f)?; handle.join()?; @@ -189,6 +186,7 @@ fn run_server(startup_notice: Option) -> anyhow::Result<()> { rayon::ThreadPoolBuilder::new() .thread_name(|ix| format!("RayonWorker{}", ix)) + .stack_size(stdx::thread::DEFAULT_STACK_SIZE) .build_global() .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 79016921bfaaa..073f2875f5918 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1433,6 +1433,8 @@ impl flags::AnalysisStats { annotate_references: false, annotate_method_references: false, annotate_enum_variant_references: false, + references_exclude_imports: false, + references_exclude_tests: false, location: ide::AnnotationLocation::AboveName, filter_adjacent_derive_implementations: false, ra_fixture: RaFixtureConfig::default(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index e50e1c26bb971..8e24e0bd2ea06 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -13,13 +13,10 @@ use crate::cli::{flags, progress_report::ProgressReport}; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) .spawn(|| self.run_()) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs index 028311388c561..aff2c0d6ff52a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs @@ -65,31 +65,7 @@ impl<'a> ProgressReport<'a> { } fn update_text(&mut self, text: &str) { - // Get length of common portion - let mut common_prefix_length = 0; - let common_length = usize::min(self.text.len(), text.len()); - - while common_prefix_length < common_length - && text.chars().nth(common_prefix_length).unwrap() - == self.text.chars().nth(common_prefix_length).unwrap() - { - common_prefix_length += 1; - } - - // Backtrack to the first differing character - let mut output = String::new(); - output += &'\x08'.to_string().repeat(self.text.len() - common_prefix_length); - // Output new suffix, using chars() iter to ensure unicode compatibility - output.extend(text.chars().skip(common_prefix_length)); - - // If the new text is shorter than the old one: delete overlapping characters - if let Some(overlap_count) = self.text.len().checked_sub(text.len()) - && overlap_count > 0 - { - output += &" ".repeat(overlap_count); - output += &"\x08".repeat(overlap_count); - } - + let output = render_text_update(&self.text, text); let _ = io::stdout().write(output.as_bytes()); let _ = io::stdout().flush(); text.clone_into(&mut self.text); @@ -105,11 +81,86 @@ impl<'a> ProgressReport<'a> { } // Fill all last text to space and return the cursor - let spaces = " ".repeat(self.text.len()); - let backspaces = "\x08".repeat(self.text.len()); + let len = self.text.chars().count(); + let spaces = " ".repeat(len); + let backspaces = "\x08".repeat(len); print!("{backspaces}{spaces}{backspaces}"); let _ = io::stdout().flush(); self.text = String::new(); } } + +fn render_text_update(old: &str, new: &str) -> String { + let old_len = old.chars().count(); + let new_len = new.chars().count(); + + // Get length of common portion + let mut common_prefix_length = 0; + let common_length = usize::min(old_len, new_len); + + while common_prefix_length < common_length + && new.chars().nth(common_prefix_length).unwrap() + == old.chars().nth(common_prefix_length).unwrap() + { + common_prefix_length += 1; + } + + // Backtrack to the first differing character + let mut output = String::new(); + output += &'\x08'.to_string().repeat(old_len - common_prefix_length); + // Output new suffix, using chars() iter to ensure unicode compatibility + output.extend(new.chars().skip(common_prefix_length)); + + // If the new text is shorter than the old one: delete overlapping characters + if let Some(overlap_count) = old_len.checked_sub(new_len) + && overlap_count > 0 + { + output += &" ".repeat(overlap_count); + output += &"\x08".repeat(overlap_count); + } + + output +} + +#[cfg(test)] +mod tests { + use super::render_text_update; + + #[test] + fn ascii_prefix_reuse() { + let old = "1/7 14% processing: foo"; + let new = "1/7 28% processing: bar"; + let update = render_text_update(old, new); + + let common = "1/7 "; + let backspaces = old.chars().count() - common.chars().count(); + let expected = format!("{}{}", "\x08".repeat(backspaces), "28% processing: bar"); + assert_eq!(update, expected); + } + + #[test] + fn unicode_identifiers_do_not_panic() { + // Regression test for rust-lang/rust-analyzer#22844: previous code + // compared byte lengths with char indices, so `chars().nth(...).unwrap()` + // panicked on non-ASCII. + let old = "1/7 14% processing: f::消息"; + let new = "2/7 28% processing: f::消息内容"; + let update = render_text_update(old, new); + + let backspaces = old.chars().count(); + let expected = format!("{}{new}", "\x08".repeat(backspaces)); + assert_eq!(update, expected); + } + + #[test] + fn shorter_unicode_message_clears_overlap() { + let old = "processing: 消息内容"; + let new = "processing: 消息"; + let update = render_text_update(old, new); + + // Drop the last two chars, then blank/backspace the leftover width. + let expected = "\x08\x08 \x08\x08"; + assert_eq!(update, expected); + } +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 7b00aebbfc4a5..ad92ebea7adb1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -48,8 +48,8 @@ impl flags::Ssr { impl flags::Search { /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches - /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful - /// for much else. + /// `debug_snippet`. This is intended for debugging and probably isn't useful in its current + /// form for much else. pub fn run(self) -> anyhow::Result<()> { use ide_db::base_db::SourceDatabase; let cargo_config = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index f8eacbb670587..d9a56098bd9dc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -11,13 +11,10 @@ use crate::cli::flags; impl flags::UnresolvedReferences { pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) .spawn(|| self.run_()) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index ff2e21c865e9b..a79e78f3377b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -23,6 +23,7 @@ use stdx::process::streaming_output; /// well as custom discover commands. pub(crate) trait JsonLinesParser: Send + 'static { fn from_line(&self, line: &str, error: &mut String) -> Option; + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option; fn from_eof(&self) -> Option; } @@ -95,7 +96,8 @@ impl CommandActor { _ = stderr.write_all(line.as_bytes()); _ = stderr.write_all(b"\n"); } - if process_line(line, &mut stderr_errors) { + if let Some(t) = self.parser.from_stderr_line(line, &mut stderr_errors) { + self.sender.send(t).unwrap(); read_at_least_one_stderr_message = true; } }, @@ -172,7 +174,7 @@ impl CommandHandle { let mut child = child .spawn() .map(JodGroupChild) - .with_context(|| "Failed to spawn command: {child:?}")?; + .with_context(|| format!("Failed to spawn command: {child:?}"))?; let stdout = child.0.stdout().take().unwrap(); let stderr = child.0.stderr().take().unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index fcb34b743adbd..3ba0e47f14bcb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -562,9 +562,9 @@ config_data! { /// /// **Warning**: This format is provisional and subject to change. /// - /// The discover command should output JSON objects, one per - /// line (JSONL format). These objects should correspond to - /// this Rust data type: + /// The discover command should output JSON objects to stdout, + /// one per line (JSONL format). These objects should correspond + /// to this Rust data type: /// /// ```norun /// #[derive(Debug, Clone, Deserialize, Serialize)] @@ -604,6 +604,9 @@ config_data! { /// Only the finished event is required, but the other /// variants are encouraged to give users more feedback about /// progress or errors. + /// + /// Stderr is not parsed as JSONL. It is treated as command log + /// output and forwarded to rust-analyzer's own logs. workspace_discoverConfig: Option = None, } } @@ -1552,6 +1555,9 @@ pub struct LensConfig { pub refs_trait: bool, // for Struct, Enum, Union and Trait pub enum_variant_refs: bool, + pub refs_exclude_imports: bool, + pub refs_exclude_tests: bool, + // annotations pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, @@ -1595,10 +1601,6 @@ impl LensConfig { self.run || self.debug || self.update_test } - pub fn references(&self) -> bool { - self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs - } - pub fn into_annotation_config<'a>( self, binary_target: bool, @@ -1611,6 +1613,8 @@ impl LensConfig { annotate_references: self.refs_adt, annotate_method_references: self.method_refs, annotate_enum_variant_references: self.enum_variant_refs, + references_exclude_imports: self.refs_exclude_imports, + references_exclude_tests: self.refs_exclude_tests, location: self.location.into(), ra_fixture: RaFixtureConfig { minicore, disable_ra_fixture: self.disable_ra_fixture }, filter_adjacent_derive_implementations: self.filter_adjacent_derive_implementations, @@ -2703,18 +2707,19 @@ impl Config { } pub fn lens(&self) -> LensConfig { + let enable = *self.lens_enable(); LensConfig { - run: *self.lens_enable() && *self.lens_run_enable(), - debug: *self.lens_enable() && *self.lens_debug_enable(), - update_test: *self.lens_enable() - && *self.lens_updateTest_enable() - && *self.lens_run_enable(), - interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), - implementations: *self.lens_enable() && *self.lens_implementations_enable(), - method_refs: *self.lens_enable() && *self.lens_references_method_enable(), - refs_adt: *self.lens_enable() && *self.lens_references_adt_enable(), - refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(), - enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(), + run: enable && *self.lens_run_enable(), + debug: enable && *self.lens_debug_enable(), + update_test: enable && *self.lens_updateTest_enable() && *self.lens_run_enable(), + interpret: enable && *self.lens_run_enable() && *self.interpret_tests(), + implementations: enable && *self.lens_implementations_enable(), + method_refs: enable && *self.lens_references_method_enable(), + refs_adt: enable && *self.lens_references_adt_enable(), + refs_trait: enable && *self.lens_references_trait_enable(), + enum_variant_refs: enable && *self.lens_references_enumVariant_enable(), + refs_exclude_imports: *self.references_excludeImports(), + refs_exclude_tests: *self.references_excludeTests(), location: *self.lens_location(), filter_adjacent_derive_implementations: *self .gotoImplementations_filterAdjacentDerives(), @@ -2903,6 +2908,7 @@ enum SnippetScopeDef { #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(default)] +#[serde(try_from = "SnippetDefRepr")] pub(crate) struct SnippetDef { #[serde(with = "single_or_array")] #[serde(skip_serializing_if = "Vec::is_empty")] @@ -2926,6 +2932,46 @@ pub(crate) struct SnippetDef { scope: SnippetScopeDef, } +/// Plain deserialization target for [`SnippetDef`]. Both the client JSON +/// config and `rust-analyzer.toml` configs deserialize a `SnippetDef` per +/// map entry, so validating the field combination here (via `TryFrom`) +/// covers both config sources instead of only one. +#[derive(Deserialize, Default)] +#[serde(default)] +struct SnippetDefRepr { + #[serde(with = "single_or_array")] + prefix: Vec, + #[serde(with = "single_or_array")] + postfix: Vec, + #[serde(with = "single_or_array")] + body: Vec, + #[serde(with = "single_or_array")] + requires: Vec, + description: Option, + scope: SnippetScopeDef, +} + +impl TryFrom for SnippetDef { + type Error = String; + + fn try_from(repr: SnippetDefRepr) -> Result { + if repr.scope == SnippetScopeDef::Item && !repr.postfix.is_empty() { + return Err( + "'postfix' is not supported together with '\"scope\": \"item\"'; postfix snippets are not supported in item scope" + .to_owned(), + ); + } + Ok(SnippetDef { + prefix: repr.prefix, + postfix: repr.postfix, + body: repr.body, + requires: repr.requires, + description: repr.description, + scope: repr.scope, + }) + } +} + mod single_or_array { use serde::{Deserialize, Serialize}; @@ -4423,4 +4469,30 @@ mod tests { == Some(Utf8PathBuf::from("other_folder")) )); } + #[test] + fn postfix_snippet_item_scope_is_invalid() { + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None); + let mut change = ConfigChange::default(); + change.change_client_config(serde_json::json!({ + "completion":{ + "snippets": { + "custom":{ + "foo": { + "postfix": "foo", + "body": "foo", + "scope": "item" + } + } + } + } + })); + let errors; + (config, errors, _) = config.apply_change(change); + assert!(!errors.0.is_empty(), "expected a config error for postfix+item scope"); + assert!( + config.snippets.iter().all(|s| s.postfix_triggers.iter().all(|t| &**t != "foo")), + "invalid snippet should not have been registered" + ); + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 098b6a4d986d7..04d0cedb3eca2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -136,6 +136,11 @@ impl JsonLinesParser for DiscoverProjectParser { fn from_eof(&self) -> Option { None } + + fn from_stderr_line(&self, line: &str, _error: &mut String) -> Option { + tracing::info!(%line, "discover command stderr"); + None + } } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index f73ffb24eea32..b927a11604158 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1011,8 +1011,8 @@ enum CheckMessage { struct CheckParser; -impl JsonLinesParser for CheckParser { - fn from_line(&self, line: &str, error: &mut String) -> Option { +impl CheckParser { + fn parse_line(&self, line: &str, error: &mut String) -> Option { let mut deserializer = serde_json::Deserializer::from_str(line); deserializer.disable_recursion_limit(); if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { @@ -1042,6 +1042,16 @@ impl JsonLinesParser for CheckParser { error.push('\n'); None } +} + +impl JsonLinesParser for CheckParser { + fn from_line(&self, line: &str, error: &mut String) -> Option { + self.parse_line(line, error) + } + + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { + self.parse_line(line, error) + } fn from_eof(&self) -> Option { None 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 56490061a74f6..bd5c5ec87d968 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 @@ -998,7 +998,7 @@ impl GlobalState { let path = VfsPath::from(path); // If the file is in mem docs, it's managed by the client via - // notifications so only set it if its not in there. Library files are + // notifications so only set it if it's not in there. Library files are // exempt from that authority as they are considered immutable, for // them disk is always the source of truth. let is_library = self.source_root_config.path_is_library(&path); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 31f35df5c796d..c6f8a7c799c21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -72,6 +72,13 @@ impl JsonLinesParser for CargoTestOutputParser { }) } + fn from_stderr_line(&self, line: &str, _error: &mut String) -> Option { + Some(CargoTestMessage { + target: self.target.clone(), + output: CargoTestOutput::Custom { text: line.to_owned() }, + }) + } + fn from_eof(&self) -> Option { Some(CargoTestMessage { target: self.target.clone(), output: CargoTestOutput::Finished }) } diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index f6500a9b4dbea..369f383dbb935 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -1,8 +1,8 @@ //! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items //! and macro calls. //! -//! Specifically, it enumerates all items in a file and uses position of a an -//! item as an ID. That way, id's don't change unless the set of items itself +//! Specifically, it enumerates all items in a file and uses the position of an +//! item as an ID. That way, IDs don't change unless the set of items itself //! changes. //! //! These IDs are tricky. If one of them invalidates, its interned ID invalidates, @@ -381,8 +381,8 @@ fn impl_ast_id( let self_ty_name = type_as_name(node.self_ty()); let trait_name = type_as_name(node.trait_()); let data = ImplFileAstId { - self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()), - trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()), + self_ty_name: self_ty_name.as_ref().map(|it| it.text()), + trait_name: trait_name.as_ref().map(|it| it.text()), }; Some(index_map.new_id(ErasedFileAstIdKind::Impl, data)) } else { @@ -473,7 +473,7 @@ macro_rules! register_has_name_ast_id { $( ast::$ident(node) => { let name = node.$name_method(); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let result = ErasedHasNameFileAstId { name, }; @@ -519,7 +519,7 @@ macro_rules! register_assoc_item_ast_id { $( ast::$ident(node) => { let name = $name_callback(node); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let properties = ErasedHasNameFileAstId { name, }; diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs index 37b7a9f5edfa8..0000fd1954d40 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs @@ -34,6 +34,8 @@ where Builder::new(intent, name).spawn(f).expect("failed to spawn thread") } +pub const DEFAULT_STACK_SIZE: usize = 16 * 1024 * 1024; + pub struct Builder { intent: ThreadIntent, inner: jod_thread::Builder, @@ -43,7 +45,11 @@ pub struct Builder { impl Builder { #[must_use] pub fn new(intent: ThreadIntent, name: impl Into) -> Self { - Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false } + Self { + intent, + inner: jod_thread::Builder::new().name(name.into()).stack_size(DEFAULT_STACK_SIZE), + allow_leak: false, + } } #[must_use] diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs index 918b88d960f1a..1ef6954e5a6ce 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -45,7 +45,6 @@ impl Pool { /// Panics if job panics #[must_use] pub fn new(threads: usize) -> Self { - const STACK_SIZE: usize = 8 * 1024 * 1024; const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker; let (job_sender, job_receiver) = crossbeam_channel::unbounded(); @@ -54,7 +53,6 @@ impl Pool { let mut handles = Vec::with_capacity(threads); for idx in 0..threads { let handle = Builder::new(INITIAL_INTENT, format!("Worker{idx}",)) - .stack_size(STACK_SIZE) .allow_leak(true) .spawn({ let extant_tasks = Arc::clone(&extant_tasks); diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index e65836ed8dcb4..a9df1acdae9a7 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -15,6 +15,7 @@ doctest = false [dependencies] either.workspace = true itertools.workspace = true +ra-ap-rustc_lexer.workspace = true rowan.workspace = true rustc-hash.workspace = true rustc-literal-escaper.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index d8c7e1583031d..855b5a80a5f6d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -61,12 +61,6 @@ pub trait AstNode { Self: Sized; fn syntax(&self) -> &SyntaxNode; - fn clone_for_update(&self) -> Self - where - Self: Sized, - { - Self::cast(self.syntax().clone_for_update()).unwrap() - } fn clone_subtree(&self) -> Self where Self: Sized, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 080f9a7c6b175..852b13fc7a3d2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -360,7 +360,7 @@ impl ast::RecordExprField { // shorthand `{ x }` → expand to `{ x: expr }` let new_field = editor .make() - .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + .record_expr_field(editor.make().name_ref(name_ref.text()), Some(expr)); editor.replace(self.syntax(), new_field.syntax()); } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 1eb658f4b8d06..43bca6ed9e63d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -3,7 +3,7 @@ //! //! These methods should only do simple, shallow tasks related to the syntax of the node itself. -use std::{borrow::Cow, fmt, iter::successors}; +use std::{fmt, iter::successors}; use itertools::Itertools; use parser::SyntaxKind; @@ -11,7 +11,7 @@ use rowan::{GreenNodeData, GreenTokenData}; use smallvec::{SmallVec, smallvec}; use crate::{ - NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText, + NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, SyntaxNode, support, @@ -22,41 +22,21 @@ use crate::{ use super::{GenericParam, RangeItem, RangeOp}; impl ast::Lifetime { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } } impl ast::Name { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } - } } impl ast::NameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } - } pub fn as_tuple_field(&self) -> Option { self.text().parse().ok() @@ -67,15 +47,12 @@ impl ast::NameRef { } } -fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { +fn text_of_first_token(node: &SyntaxNode) -> &str { fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() } - match node.green() { - Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()), - Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()), - } + first_token(node.green()).text() } fn into_comma(it: NodeOrToken) -> Option { @@ -619,7 +596,7 @@ impl NameLike { _ => None, } } - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameLike::NameRef(name_ref) => name_ref.text(), NameLike::Name(name) => name.text(), @@ -691,7 +668,7 @@ impl ast::AstNode for NameOrNameRef { } impl NameOrNameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameOrNameRef::Name(name) => name.text(), NameOrNameRef::NameRef(name_ref) => name_ref.text(), 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 22c8c842d890c..23fb0e2e02782 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 @@ -14,49 +14,45 @@ use super::SyntaxFactory; impl SyntaxFactory { pub fn name(&self, name: &str) -> ast::Name { - make::name(name).clone_for_update() + make::name(name) } pub fn name_ref(&self, name: &str) -> ast::NameRef { - make::name_ref(name).clone_for_update() + make::name_ref(name) } pub fn name_ref_self_ty(&self) -> ast::NameRef { - make::name_ref_self_ty().clone_for_update() + make::name_ref_self_ty() } pub fn expr_todo(&self) -> ast::Expr { - make::ext::expr_todo().clone_for_update() + make::ext::expr_todo() } pub fn expr_self(&self) -> ast::Expr { - make::ext::expr_self().clone_for_update() + make::ext::expr_self() } pub fn expr_const_value(&self, text: &str) -> ast::ConstArg { - make::expr_const_value(text).clone_for_update() + make::expr_const_value(text) } pub fn lifetime(&self, text: &str) -> ast::Lifetime { - make::lifetime(text).clone_for_update() + make::lifetime(text) } pub fn ty(&self, text: &str) -> ast::Type { - make::ty(text).clone_for_update() + make::ty(text) } pub fn ty_infer(&self) -> ast::InferType { - let ast::Type::InferType(ast) = make::ty_placeholder().clone_for_update() else { - unreachable!() - }; + let ast::Type::InferType(ast) = make::ty_placeholder() else { unreachable!() }; ast } pub fn ty_path(&self, path: ast::Path) -> ast::PathType { - let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Type::PathType(ast) = make::ty_path(path.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -76,11 +72,11 @@ impl SyntaxFactory { } pub fn type_bound(&self, bound: ast::Type) -> ast::TypeBound { - make::type_bound(bound).clone_for_update() + make::type_bound(bound) } pub fn type_bound_text(&self, bound: &str) -> ast::TypeBound { - make::type_bound_text(bound).clone_for_update() + make::type_bound_text(bound) } pub fn use_tree_list( @@ -88,7 +84,7 @@ impl SyntaxFactory { use_trees: impl IntoIterator, ) -> ast::UseTreeList { let (use_trees, input) = iterator_input(use_trees); - let ast = make::use_tree_list(use_trees).clone_for_update(); + let ast = make::use_tree_list(use_trees); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -104,7 +100,7 @@ impl SyntaxFactory { bounds: impl IntoIterator, ) -> Option { let (bounds, input) = iterator_input(bounds); - let ast = make::type_bound_list(bounds)?.clone_for_update(); + let ast = make::type_bound_list(bounds)?; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -122,7 +118,7 @@ impl SyntaxFactory { name: ast::Name, bounds: Option, ) -> ast::TypeParam { - let ast = make::type_param(name.clone(), bounds.clone()).clone_for_update(); + let ast = make::type_param(name.clone(), bounds.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -140,23 +136,23 @@ impl SyntaxFactory { } pub fn path_from_text(&self, text: &str) -> ast::Path { - make::path_from_text(text).clone_for_update() + make::path_from_text(text) } pub fn path_from_text_with_edition(&self, text: &str, edition: Edition) -> ast::Path { - make::path_from_text_with_edition(text, edition).clone_for_update() + make::path_from_text_with_edition(text, edition) } pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { - make::path_concat(first, second).clone_for_update() + make::path_concat(first, second) } pub fn visibility_pub_crate(&self) -> ast::Visibility { - make::visibility_pub_crate().clone_for_update() + make::visibility_pub_crate() } pub fn visibility_pub(&self) -> ast::Visibility { - make::visibility_pub().clone_for_update() + make::visibility_pub() } pub fn struct_( @@ -171,8 +167,7 @@ impl SyntaxFactory { strukt_name.clone(), generic_param_list.clone(), field_list.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -198,7 +193,7 @@ impl SyntaxFactory { } pub fn unnamed_param(&self, ty: ast::Type) -> ast::Param { - let ast = make::unnamed_param(ty.clone()).clone_for_update(); + let ast = make::unnamed_param(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -210,7 +205,7 @@ impl SyntaxFactory { } pub fn untyped_param(&self, pat: ast::Pat) -> ast::Param { - let ast = make::untyped_param(pat.clone()).clone_for_update(); + let ast = make::untyped_param(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -229,8 +224,7 @@ impl SyntaxFactory { ret_type: Option, ) -> ast::FnPtrType { let (params, params_input) = iterator_input(params); - let ast = make::ty_fn_ptr(is_unsafe, abi.clone(), params.into_iter(), ret_type.clone()) - .clone_for_update(); + let ast = make::ty_fn_ptr(is_unsafe, abi.clone(), params.into_iter(), ret_type.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -257,7 +251,7 @@ impl SyntaxFactory { bounds: impl IntoIterator, ) -> ast::WherePred { let (bounds, bounds_input) = iterator_input(bounds); - let ast = make::where_pred(path.clone(), bounds).clone_for_update(); + let ast = make::where_pred(path.clone(), bounds); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -293,7 +287,7 @@ impl SyntaxFactory { predicates: impl IntoIterator, ) -> ast::WhereClause { let (predicates, input) = iterator_input(predicates); - let ast = make::where_clause(predicates).clone_for_update(); + let ast = make::where_clause(predicates); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -305,7 +299,7 @@ impl SyntaxFactory { } pub fn impl_trait_type(&self, bounds: ast::TypeBoundList) -> ast::ImplTraitType { - let ast = make::impl_trait_type(bounds.clone()).clone_for_update(); + let ast = make::impl_trait_type(bounds.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -318,9 +312,7 @@ impl SyntaxFactory { } pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { - let ast::Expr::FieldExpr(ast) = - make::expr_field(receiver.clone(), field).clone_for_update() - else { + let ast::Expr::FieldExpr(ast) = make::expr_field(receiver.clone(), field) else { unreachable!() }; @@ -362,8 +354,7 @@ impl SyntaxFactory { trait_where_clause.clone(), ty_where_clause.clone(), body.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -411,8 +402,7 @@ impl SyntaxFactory { type_param_bounds.clone(), where_clause.clone(), assignment.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -450,7 +440,7 @@ impl SyntaxFactory { params: impl IntoIterator, ) -> ast::ParamList { let (params, input) = iterator_input(params); - let ast = make::param_list(self_param.clone(), params).clone_for_update(); + let ast = make::param_list(self_param.clone(), params); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -467,7 +457,7 @@ impl SyntaxFactory { } pub fn const_param(&self, name: ast::Name, ty: ast::Type) -> ast::ConstParam { - let ast = make::const_param(name.clone(), ty.clone()).clone_for_update(); + let ast = make::const_param(name.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -480,7 +470,7 @@ impl SyntaxFactory { } pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { - let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + let ast = make::lifetime_param(lifetime.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -496,7 +486,7 @@ impl SyntaxFactory { params: impl IntoIterator, ) -> ast::GenericParamList { let (params, input) = iterator_input(params); - let ast = make::generic_param_list(params).clone_for_update(); + let ast = make::generic_param_list(params); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -508,7 +498,7 @@ impl SyntaxFactory { } pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment { - let ast = make::path_segment(name_ref.clone()).clone_for_update(); + let ast = make::path_segment(name_ref.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -520,15 +510,15 @@ impl SyntaxFactory { } pub fn path_segment_self(&self) -> ast::PathSegment { - make::path_segment_self().clone_for_update() + make::path_segment_self() } pub fn path_segment_super(&self) -> ast::PathSegment { - make::path_segment_super().clone_for_update() + make::path_segment_super() } pub fn path_segment_crate(&self) -> ast::PathSegment { - make::path_segment_crate().clone_for_update() + make::path_segment_crate() } pub fn generic_ty_path_segment( @@ -537,7 +527,7 @@ impl SyntaxFactory { generic_args: impl IntoIterator, ) -> ast::PathSegment { let (generic_args, input) = iterator_input(generic_args); - let ast = make::generic_ty_path_segment(name_ref.clone(), generic_args).clone_for_update(); + let ast = make::generic_ty_path_segment(name_ref.clone(), generic_args); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -555,7 +545,7 @@ impl SyntaxFactory { } pub fn tail_only_block_expr(&self, tail_expr: ast::Expr) -> ast::BlockExpr { - let ast = make::tail_only_block_expr(tail_expr.clone()).clone_for_update(); + let ast = make::tail_only_block_expr(tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -571,9 +561,7 @@ impl SyntaxFactory { } pub fn expr_bin_op(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr { - let ast::Expr::BinExpr(ast) = - make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() - else { + let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()) else { unreachable!() }; @@ -588,16 +576,16 @@ impl SyntaxFactory { } pub fn ty_placeholder(&self) -> ast::Type { - make::ty_placeholder().clone_for_update() + make::ty_placeholder() } pub fn ty_unit(&self) -> ast::Type { - make::ty_unit().clone_for_update() + make::ty_unit() } pub fn ty_tuple(&self, types: impl IntoIterator) -> ast::Type { let (types, input) = iterator_input(types); - let ast = make::ty_tuple(types).clone_for_update(); + let ast = make::ty_tuple(types); if let Some(mut mapping) = self.mappings() && let ast::Type::TupleType(tuple_ty) = &ast @@ -631,7 +619,7 @@ impl SyntaxFactory { unreachable!(); }; - let ast = path.path().unwrap().segment().unwrap().clone_for_update(); + let ast = path.path().unwrap().segment().unwrap(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -653,7 +641,7 @@ impl SyntaxFactory { use_tree: ast::UseTree, ) -> ast::Use { let (attrs, attrs_input) = iterator_input(attrs); - let ast = make::use_(attrs, visibility.clone(), use_tree.clone()).clone_for_update(); + let ast = make::use_(attrs, visibility.clone(), use_tree.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -678,8 +666,7 @@ impl SyntaxFactory { alias: Option, add_star: bool, ) -> ast::UseTree { - let ast = make::use_tree(path.clone(), use_tree_list.clone(), alias.clone(), add_star) - .clone_for_update(); + let ast = make::use_tree(path.clone(), use_tree_list.clone(), alias.clone(), add_star); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -700,11 +687,11 @@ impl SyntaxFactory { } pub fn use_tree_glob(&self) -> ast::UseTree { - make::use_tree_glob().clone_for_update() + make::use_tree_glob() } pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { - let ast = make::path_unqualified(segment.clone()).clone_for_update(); + let ast = make::path_unqualified(segment.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -716,7 +703,7 @@ impl SyntaxFactory { } pub fn path_qualified(&self, qual: ast::Path, segment: ast::PathSegment) -> ast::Path { - let ast = make::path_qualified(qual.clone(), segment.clone()).clone_for_update(); + let ast = make::path_qualified(qual.clone(), segment.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -738,7 +725,7 @@ impl SyntaxFactory { is_abs: bool, ) -> ast::Path { let (segments, input) = iterator_input(segments); - let ast = make::path_from_segments(segments, is_abs).clone_for_update(); + let ast = make::path_from_segments(segments, is_abs); if let Some(mut mapping) = self.mappings() { let mut current_path = Some(ast.clone()); @@ -757,7 +744,7 @@ impl SyntaxFactory { } pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat { - let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); + let ast = make::ident_pat(ref_, mut_, name.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -769,7 +756,7 @@ impl SyntaxFactory { } pub fn simple_ident_pat(&self, name: ast::Name) -> ast::IdentPat { - let ast = make::ext::simple_ident_pat(name.clone()).clone_for_update(); + let ast = make::ext::simple_ident_pat(name.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -781,16 +768,16 @@ impl SyntaxFactory { } pub fn wildcard_pat(&self) -> ast::WildcardPat { - make::wildcard_pat().clone_for_update() + make::wildcard_pat() } pub fn literal_pat(&self, text: &str) -> ast::LiteralPat { - make::literal_pat(text).clone_for_update() + make::literal_pat(text) } pub fn slice_pat(&self, pats: impl IntoIterator) -> ast::SlicePat { let (pats, input) = iterator_input(pats); - let ast = make::slice_pat(pats).clone_for_update(); + let ast = make::slice_pat(pats); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -803,7 +790,7 @@ impl SyntaxFactory { pub fn tuple_pat(&self, pats: impl IntoIterator) -> ast::TuplePat { let (pats, input) = iterator_input(pats); - let ast = make::tuple_pat(pats).clone_for_update(); + let ast = make::tuple_pat(pats); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -820,7 +807,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::TupleStructPat { let (fields, input) = iterator_input(fields); - let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update(); + let ast = make::tuple_struct_pat(path.clone(), fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -837,7 +824,7 @@ impl SyntaxFactory { path: ast::Path, fields: ast::RecordPatFieldList, ) -> ast::RecordPat { - let ast = make::record_pat_with_fields(path.clone(), fields.clone()).clone_for_update(); + let ast = make::record_pat_with_fields(path.clone(), fields.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -858,7 +845,7 @@ impl SyntaxFactory { rest_pat: Option, ) -> ast::RecordPatFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_pat_field_list(fields, rest_pat.clone()).clone_for_update(); + let ast = make::record_pat_field_list(fields, rest_pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -874,7 +861,7 @@ impl SyntaxFactory { } pub fn record_pat_field(&self, name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField { - let ast = make::record_pat_field(name_ref.clone(), pat.clone()).clone_for_update(); + let ast = make::record_pat_field(name_ref.clone(), pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -887,7 +874,7 @@ impl SyntaxFactory { } pub fn record_pat_field_shorthand(&self, pat: ast::Pat) -> ast::RecordPatField { - let ast = make::record_pat_field_shorthand(pat.clone()).clone_for_update(); + let ast = make::record_pat_field_shorthand(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -899,7 +886,7 @@ impl SyntaxFactory { } pub fn path_pat(&self, path: ast::Path) -> ast::Pat { - let ast = make::path_pat(path.clone()).clone_for_update(); + let ast = make::path_pat(path.clone()); match &ast { ast::Pat::PathPat(ast) => { @@ -923,7 +910,7 @@ impl SyntaxFactory { } pub fn rest_pat(&self) -> ast::RestPat { - make::rest_pat().clone_for_update() + make::rest_pat() } pub fn or_pat( @@ -932,7 +919,7 @@ impl SyntaxFactory { leading_pipe: bool, ) -> ast::OrPat { let (pats, input) = iterator_input(pats); - let ast = make::or_pat(pats, leading_pipe).clone_for_update(); + let ast = make::or_pat(pats, leading_pipe); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -944,7 +931,7 @@ impl SyntaxFactory { } pub fn box_pat(&self, pat: ast::Pat) -> ast::BoxPat { - let ast = make::box_pat(pat.clone()).clone_for_update(); + let ast = make::box_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -956,11 +943,11 @@ impl SyntaxFactory { } pub fn deref_pat(&self, pat: ast::Pat) -> ast::Pat { - make::deref_pat(pat.clone()).clone_for_update() + make::deref_pat(pat.clone()) } pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat { - let ast = make::paren_pat(pat.clone()).clone_for_update(); + let ast = make::paren_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -972,7 +959,7 @@ impl SyntaxFactory { } pub fn range_pat(&self, start: Option, end: Option) -> ast::RangePat { - let ast = make::range_pat(start.clone(), end.clone()).clone_for_update(); + let ast = make::range_pat(start.clone(), end.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -989,7 +976,7 @@ impl SyntaxFactory { } pub fn ref_pat(&self, pat: ast::Pat) -> ast::RefPat { - let ast = make::ref_pat(pat.clone()).clone_for_update(); + let ast = make::ref_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1007,7 +994,7 @@ impl SyntaxFactory { ) -> ast::BlockExpr { let (statements, mut input) = iterator_input(statements); - let ast = make::block_expr(statements, tail_expr.clone()).clone_for_update(); + let ast = make::block_expr(statements, tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -1040,7 +1027,7 @@ impl SyntaxFactory { ) -> ast::BlockExpr { let (statements, mut input) = iterator_input(statements); - let ast = make::async_move_block_expr(statements, tail_expr.clone()).clone_for_update(); + let ast = make::async_move_block_expr(statements, tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -1065,11 +1052,11 @@ impl SyntaxFactory { } pub fn expr_empty_block(&self) -> ast::BlockExpr { - make::expr_empty_block().clone_for_update() + make::expr_empty_block() } pub fn expr_paren(&self, expr: ast::Expr) -> ast::ParenExpr { - let ast = make::expr_paren(expr.clone()).clone_for_update(); + let ast = make::expr_paren(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1082,7 +1069,7 @@ impl SyntaxFactory { pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr { let (fields, input) = iterator_input(fields); - let ast = make::expr_tuple(fields).clone_for_update(); + let ast = make::expr_tuple(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1094,7 +1081,7 @@ impl SyntaxFactory { } pub fn expr_assignment(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { - let ast = make::expr_assignment(lhs.clone(), rhs.clone()).clone_for_update(); + let ast = make::expr_assignment(lhs.clone(), rhs.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1107,9 +1094,7 @@ impl SyntaxFactory { } pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr { - let ast::Expr::BinExpr(ast) = - make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() - else { + let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()) else { unreachable!() }; @@ -1124,13 +1109,11 @@ impl SyntaxFactory { } pub fn expr_literal(&self, text: &str) -> ast::Literal { - make::expr_literal(text).clone_for_update() + make::expr_literal(text) } pub fn expr_path(&self, path: ast::Path) -> ast::Expr { - let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1142,7 +1125,7 @@ impl SyntaxFactory { } pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { - let ast = make::expr_prefix(op, expr.clone()).clone_for_update(); + let ast = make::expr_prefix(op, expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1154,7 +1137,7 @@ impl SyntaxFactory { } pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr { - let ast = make::expr_call(expr.clone(), arg_list.clone()).clone_for_update(); + let ast = make::expr_call(expr.clone(), arg_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1172,8 +1155,7 @@ impl SyntaxFactory { method: ast::NameRef, arg_list: ast::ArgList, ) -> ast::MethodCallExpr { - let ast = make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone()) - .clone_for_update(); + let ast = make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1188,7 +1170,7 @@ impl SyntaxFactory { pub fn arg_list(&self, args: impl IntoIterator) -> ast::ArgList { let (args, input) = iterator_input(args); - let ast = make::arg_list(args).clone_for_update(); + let ast = make::arg_list(args); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone()); @@ -1200,8 +1182,7 @@ impl SyntaxFactory { } pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { - let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update() - else { + let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive) else { unreachable!() }; @@ -1215,9 +1196,7 @@ impl SyntaxFactory { } pub fn expr_reborrow(&self, expr: ast::Expr) -> ast::Expr { - let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { // Layout: RefExpr(&mut, PrefixExpr(*, expr)). Map `expr` to the @@ -1236,9 +1215,7 @@ impl SyntaxFactory { } pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { - let ast::Expr::RefExpr(ast) = - make::expr_raw_ref(expr.clone(), exclusive).clone_for_update() - else { + let ast::Expr::RefExpr(ast) = make::expr_raw_ref(expr.clone(), exclusive) else { unreachable!() }; @@ -1257,7 +1234,7 @@ impl SyntaxFactory { expr: ast::Expr, ) -> ast::ClosureExpr { let (args, input) = iterator_input(pats); - let ast = make::expr_closure(args, expr.clone()).clone_for_update(); + let ast = make::expr_closure(args, expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1275,9 +1252,7 @@ impl SyntaxFactory { } pub fn expr_return(&self, expr: Option) -> ast::ReturnExpr { - let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1291,9 +1266,7 @@ impl SyntaxFactory { } pub fn expr_underscore(&self) -> ast::UnderscoreExpr { - let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore().clone_for_update() else { - unreachable!() - }; + let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore() else { unreachable!() }; ast } @@ -1304,8 +1277,7 @@ impl SyntaxFactory { then_branch: ast::BlockExpr, else_branch: Option, ) -> ast::IfExpr { - let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()) - .clone_for_update(); + let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1328,9 +1300,7 @@ impl SyntaxFactory { } pub fn expr_loop(&self, body: ast::BlockExpr) -> ast::LoopExpr { - let ast::Expr::LoopExpr(ast) = make::expr_loop(body.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::LoopExpr(ast) = make::expr_loop(body.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1342,7 +1312,7 @@ impl SyntaxFactory { } pub fn expr_while_loop(&self, condition: ast::Expr, body: ast::BlockExpr) -> ast::WhileExpr { - let ast = make::expr_while_loop(condition.clone(), body.clone()).clone_for_update(); + let ast = make::expr_while_loop(condition.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1360,8 +1330,7 @@ impl SyntaxFactory { iterable: ast::Expr, body: ast::BlockExpr, ) -> ast::ForExpr { - let ast = - make::expr_for_loop(pat.clone(), iterable.clone(), body.clone()).clone_for_update(); + let ast = make::expr_for_loop(pat.clone(), iterable.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1375,7 +1344,7 @@ impl SyntaxFactory { } pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { - let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update(); + let ast = make::expr_let(pattern.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1388,7 +1357,7 @@ impl SyntaxFactory { } pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt { - let ast = make::expr_stmt(expr.clone()).clone_for_update(); + let ast = make::expr_stmt(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1400,7 +1369,7 @@ impl SyntaxFactory { } pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { - let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update(); + let ast = make::expr_match(expr.clone(), match_arm_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1416,7 +1385,7 @@ impl SyntaxFactory { } pub fn expr_macro(&self, path: ast::Path, tt: ast::TokenTree) -> ast::MacroExpr { - let ast = make::expr_macro(path.clone(), tt.clone()).clone_for_update(); + let ast = make::expr_macro(path.clone(), tt.clone()); if let Some(mut mapping) = self.mappings() { let macro_call = ast.macro_call().unwrap(); @@ -1436,7 +1405,7 @@ impl SyntaxFactory { guard: Option, expr: ast::Expr, ) -> ast::MatchArm { - let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update(); + let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1452,7 +1421,7 @@ impl SyntaxFactory { } pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard { - let ast = make::match_guard(condition.clone()).clone_for_update(); + let ast = make::match_guard(condition.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1468,7 +1437,7 @@ impl SyntaxFactory { match_arms: impl IntoIterator, ) -> ast::MatchArmList { let (match_arms, input) = iterator_input(match_arms); - let ast = make::match_arm_list(match_arms).clone_for_update(); + let ast = make::match_arm_list(match_arms); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1485,8 +1454,7 @@ impl SyntaxFactory { ty: Option, initializer: Option, ) -> ast::LetStmt { - let ast = - make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update(); + let ast = make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1516,8 +1484,7 @@ impl SyntaxFactory { ty.clone(), initializer.clone(), diverging.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1537,7 +1504,7 @@ impl SyntaxFactory { } pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg { - let ast = make::type_arg(ty.clone()).clone_for_update(); + let ast = make::type_arg(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1549,7 +1516,7 @@ impl SyntaxFactory { } pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg { - let ast = make::lifetime_arg(lifetime.clone()).clone_for_update(); + let ast = make::lifetime_arg(lifetime.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1570,8 +1537,7 @@ impl SyntaxFactory { ) -> ast::Const { let (attrs, attrs_input) = iterator_input(attrs); let ast = - make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()) - .clone_for_update(); + make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1607,8 +1573,7 @@ impl SyntaxFactory { name.clone(), ty.clone(), expr.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1651,9 +1616,9 @@ impl SyntaxFactory { ) -> ast::GenericArgList { let (generic_args, input) = iterator_input(generic_args); let ast = if is_turbo { - make::turbofish_generic_arg_list(generic_args).clone_for_update() + make::turbofish_generic_arg_list(generic_args) } else { - make::generic_arg_list(generic_args).clone_for_update() + make::generic_arg_list(generic_args) }; if let Some(mut mapping) = self.mappings() { @@ -1670,7 +1635,7 @@ impl SyntaxFactory { path: ast::Path, fields: ast::RecordExprFieldList, ) -> ast::RecordExpr { - let ast = make::record_expr(path.clone(), fields.clone()).clone_for_update(); + let ast = make::record_expr(path.clone(), fields.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); @@ -1688,7 +1653,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::RecordExprFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_expr_field_list(fields).clone_for_update(); + let ast = make::record_expr_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1704,7 +1669,7 @@ impl SyntaxFactory { name: ast::NameRef, expr: Option, ) -> ast::RecordExprField { - let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update(); + let ast = make::record_expr_field(name.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1738,7 +1703,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::RecordFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_field_list(fields).clone_for_update(); + let ast = make::record_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1757,8 +1722,7 @@ impl SyntaxFactory { name: ast::Name, ty: ast::Type, ) -> ast::RecordField { - let ast = - make::record_field(visibility.clone(), name.clone(), ty.clone()).clone_for_update(); + let ast = make::record_field(visibility.clone(), name.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1783,7 +1747,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::TupleFieldList { let (fields, input) = iterator_input(fields); - let ast = make::tuple_field_list(fields).clone_for_update(); + let ast = make::tuple_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1801,7 +1765,7 @@ impl SyntaxFactory { visibility: Option, ty: ast::Type, ) -> ast::TupleField { - let ast = make::tuple_field(visibility.clone(), ty.clone()).clone_for_update(); + let ast = make::tuple_field(visibility.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1836,8 +1800,7 @@ impl SyntaxFactory { generic_param_list.clone(), where_clause.clone(), variant_list.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1880,7 +1843,7 @@ impl SyntaxFactory { variants: impl IntoIterator, ) -> ast::VariantList { let (variants, input) = iterator_input(variants); - let ast = make::variant_list(variants).clone_for_update(); + let ast = make::variant_list(variants); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1905,8 +1868,7 @@ impl SyntaxFactory { name.clone(), field_list.clone(), discriminant.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2012,7 +1974,7 @@ impl SyntaxFactory { ) -> ast::AssocItemList { let (items, input) = iterator_input(items); let items_vec: Vec<_> = items.into_iter().collect(); - let ast = make::assoc_item_list(Some(items_vec)).clone_for_update(); + let ast = make::assoc_item_list(Some(items_vec)); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2024,13 +1986,13 @@ impl SyntaxFactory { } pub fn assoc_item_list_empty(&self) -> ast::AssocItemList { - make::assoc_item_list(None).clone_for_update() + make::assoc_item_list(None) } pub fn item_list(&self, items: impl IntoIterator) -> ast::ItemList { let (items, input) = iterator_input(items); let items_vec: Vec<_> = items.into_iter().collect(); - let ast = make::item_list(Some(items_vec)).clone_for_update(); + let ast = make::item_list(Some(items_vec)); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2042,7 +2004,7 @@ impl SyntaxFactory { } pub fn mod_(&self, name: ast::Name, body: Option) -> ast::Module { - let ast = make::mod_(name.clone(), body.clone()).clone_for_update(); + let ast = make::mod_(name.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2057,7 +2019,7 @@ impl SyntaxFactory { } pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr { - let ast = make::attr_outer(meta.clone()).clone_for_update(); + let ast = make::attr_outer(meta.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2069,7 +2031,7 @@ impl SyntaxFactory { } pub fn attr_inner(&self, meta: ast::Meta) -> ast::Attr { - let ast = make::attr_inner(meta.clone()).clone_for_update(); + let ast = make::attr_inner(meta.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2081,7 +2043,7 @@ impl SyntaxFactory { } pub fn meta_token_tree(&self, path: ast::Path, tt: ast::TokenTree) -> ast::Meta { - let ast = make::meta_token_tree(path.clone(), tt.clone()).clone_for_update(); + let ast = make::meta_token_tree(path.clone(), tt.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2095,7 +2057,7 @@ impl SyntaxFactory { } pub fn cfg_flag(&self, flag: &str) -> ast::CfgPredicate { - make::cfg_flag(flag).clone_for_update() + make::cfg_flag(flag) } pub fn cfg_attr_meta( @@ -2104,7 +2066,7 @@ impl SyntaxFactory { inner: impl IntoIterator, ) -> ast::CfgAttrMeta { let inner = Vec::from_iter(inner); - let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()).clone_for_update(); + let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2130,7 +2092,7 @@ impl SyntaxFactory { let tt: Vec<_> = tt.into_iter().collect(); let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect(); - let ast = make::token_tree(delimiter, tt).clone_for_update(); + let ast = make::token_tree(delimiter, tt); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2158,7 +2120,7 @@ impl SyntaxFactory { } pub fn mut_self_param(&self) -> ast::SelfParam { - let ast = make::mut_self_param().clone_for_update(); + let ast = make::mut_self_param(); if let Some(mut mapping) = self.mappings() { let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2169,7 +2131,7 @@ impl SyntaxFactory { } pub fn self_param(&self) -> ast::SelfParam { - let ast = make::self_param().clone_for_update(); + let ast = make::self_param(); if let Some(mut mapping) = self.mappings() { let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2196,8 +2158,7 @@ impl SyntaxFactory { path_type.clone(), where_clause.clone(), body.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2241,8 +2202,7 @@ impl SyntaxFactory { generic_param_list.clone(), where_clause.clone(), assoc_items.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2269,7 +2229,7 @@ impl SyntaxFactory { } pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { - let ast = make::ret_type(ty.clone()).clone_for_update(); + let ast = make::ret_type(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2280,7 +2240,7 @@ impl SyntaxFactory { } pub fn ty_ref(&self, ty: ast::Type, is_mut: bool) -> ast::Type { - let ast = make::ty_ref(ty.clone(), is_mut).clone_for_update(); + let ast = make::ty_ref(ty.clone(), is_mut); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2303,7 +2263,7 @@ impl SyntaxFactory { } pub fn ty_name(&self, name: ast::Name) -> ast::Type { - let ast = make::ext::ty_name(name.clone()).clone_for_update(); + let ast = make::ext::ty_name(name.clone()); if let Some(mut mapping) = self.mappings() && let ast::Type::PathType(path_ty) = &ast @@ -2318,9 +2278,7 @@ impl SyntaxFactory { } pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr { - let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2332,7 +2290,7 @@ impl SyntaxFactory { } pub fn expr_try(&self, expr: ast::Expr) -> ast::Expr { - let ast = make::expr_try(expr.clone()).clone_for_update(); + let ast = make::expr_try(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2353,8 +2311,7 @@ impl SyntaxFactory { tail_expr: Option, ) -> ast::BlockExpr { let elements = elements.into_iter().collect::>(); - let ast = - make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()).clone_for_update(); + let ast = make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()); if let Some(mut mapping) = self.mappings() && let Some(stmt_list) = ast.stmt_list() @@ -2379,9 +2336,7 @@ impl SyntaxFactory { } pub fn expr_break(&self, label: Option, expr: Option) -> ast::BreakExpr { - let ast::Expr::BreakExpr(ast) = - make::expr_break(label.clone(), expr.clone()).clone_for_update() - else { + let ast::Expr::BreakExpr(ast) = make::expr_break(label.clone(), expr.clone()) else { unreachable!() }; @@ -2400,8 +2355,7 @@ impl SyntaxFactory { } pub fn expr_continue(&self, label: Option) -> ast::ContinueExpr { - let ast::Expr::ContinueExpr(ast) = make::expr_continue(label.clone()).clone_for_update() - else { + let ast::Expr::ContinueExpr(ast) = make::expr_continue(label.clone()) else { unreachable!() }; @@ -2427,11 +2381,11 @@ impl SyntaxFactory { &self, parts: impl IntoIterator, ) -> Option { - make::ext::path_from_idents(parts).map(|path| path.clone_for_update()) + make::ext::path_from_idents(parts) } pub fn token_tree_from_node(&self, node: &SyntaxNode) -> ast::TokenTree { - make::ext::token_tree_from_node(node).clone_for_update() + make::ext::token_tree_from_node(node) } pub fn expr_unit(&self) -> ast::Expr { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index b5c4e1aa9d9f1..f8bffe9d47b36 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -183,6 +183,16 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; + if self.is_raw() { + let mut pos = offset; + for c in text.chars() { + let len = TextSize::of(c); + cb(TextRange::at(pos, len), Ok(c)); + pos += len; + } + return; + } + self.unescape(text, &mut |range: Range, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 614678536a512..dc7d7af7a2b92 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -21,8 +21,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; mod parsing; mod ptr; @@ -30,7 +34,6 @@ mod syntax_error; mod syntax_node; #[cfg(test)] mod tests; -mod token_text; mod validation; pub mod algo; @@ -54,7 +57,6 @@ pub use crate::{ PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder, }, - token_text::TokenText, }; pub use parser::{Edition, SyntaxKind, T}; pub use rowan::{ 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 7d15195c6f1f7..3ddc7914760eb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -1,6 +1,6 @@ //! Syntax Tree editor //! -//! Inspired by Roslyn's [`SyntaxEditor`], but is temporarily built upon mutable syntax tree editing. +//! Inspired by Roslyn's [`SyntaxEditor`]. //! //! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs @@ -39,13 +39,12 @@ impl SyntaxEditor { /// Creates a syntax editor from `root`. /// /// The returned `root` is guaranteed to be a detached, immutable node. - /// If the provided node is not a root (i.e., has a parent) or is already - /// mutable, it is cloned into a fresh subtree to satisfy syntax editor - /// invariants. + /// If the provided node is not a root (i.e., has a parent), it is cloned + /// into a fresh subtree to satisfy syntax editor invariants. pub fn new(root: SyntaxNode) -> (Self, SyntaxNode) { let mut root = root; - if root.parent().is_some() || root.is_mutable() { + if root.parent().is_some() { root = root.clone_subtree() }; @@ -603,7 +602,7 @@ mod tests { let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); let name = make::name("var_name"); - let name_ref = make::name_ref("var_name").clone_for_update(); + let name_ref = make::name_ref("var_name"); let placeholder_snippet = SyntaxAnnotation::default(); editor.add_annotation(name.syntax(), placeholder_snippet); @@ -884,7 +883,7 @@ mod tests { } #[test] - fn test_more_times_replace_node_to_mutable_token() { + fn test_more_times_replace_node_to_same_token() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); @@ -903,13 +902,13 @@ mod tests { } #[test] - fn test_more_times_replace_node_to_mutable() { + fn test_more_times_replace_node_to_same_node() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); - let target_expr = make::expr_literal("3").clone_for_update(); + let target_expr = make::expr_literal("3"); for arg in arg_list.args() { editor.replace(arg.syntax(), target_expr.syntax()); @@ -922,13 +921,13 @@ mod tests { } #[test] - fn test_more_times_insert_node_to_mutable() { + fn test_more_times_insert_node_to_same_node() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); - let target_expr = make::ext::expr_unit().clone_for_update(); + let target_expr = make::ext::expr_unit(); for arg in arg_list.args() { editor.insert(Position::before(arg.syntax()), target_expr.syntax()); diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 36f50e39186c6..24e7016f5bdee 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,415 +1,700 @@ //! Implementation of applying changes to a syntax tree. -use std::{ - cmp::Ordering, - collections::VecDeque, - ops::{Range, RangeInclusive}, -}; +use std::{cmp::Ordering, ops::Range}; use rowan::TextRange; use rustc_hash::FxHashMap; use stdx::format_to; -use crate::{ - SyntaxElement, SyntaxNode, SyntaxNodePtr, - syntax_editor::{Change, ChangeKind, PositionRepr, mapping::MissingMapping}, -}; - -use super::{SyntaxEdit, SyntaxEditor}; +use crate::{NodeOrToken, SyntaxElement, SyntaxNode}; -pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { - // Algorithm overview: - // - // - Sort changes by (range, type) - // - Ensures that parent edits are before child edits - // - Ensures that inserts will be guaranteed to be inserted at the right range - // - Validate changes - // - Checking for invalid changes is easy since the changes will be sorted by range - // - Fixup change targets - // - standalone change? map to original syntax tree - // - dependent change? - // - try to map to parent change (either independent or another dependent) - // - note: need to keep track of a parent change stack, since a change can be a parent of multiple changes - // - Apply changes - // - find changes to apply to real tree by applying nested changes first - // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) - // - Propagate annotations +use super::{ + Change, ChangeKind, PositionRepr, SyntaxAnnotation, SyntaxEdit, SyntaxEditor, SyntaxMapping, + mapping::MissingMapping, +}; - let SyntaxEditor { root, changes, annotations, make } = editor; - let mut changes = changes.into_inner(); - let annotations = annotations.into_inner(); - let mappings = make.take(); +/// A validated batch of changes in the exact order in which it must execute. +/// +/// Planning is deliberately separate from tree mutation. Once an `EditPlan` +/// exists, execution does not need to reason about overlaps, dependencies, or +/// source ordering. +struct EditPlan { + changes: Vec, +} - let mut node_depths = FxHashMap::::default(); - let mut get_node_depth = |node: SyntaxNode| { - *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) - }; +/// A change whose target tree and output tracking are fully known. +struct PlannedChange { + tree: SyntaxNode, + change: Change, + record_as_changed: bool, +} - // Sort changes by range, then depth, then change kind, so that we can: - // - ensure that parent edits are ordered before child edits - // - ensure that inserts will be guaranteed to be inserted at the right range - // - easily check for disjoint replace ranges - changes.sort_by(|a, b| { - a.target_range() - .start() - .cmp(&b.target_range().start()) - .then_with(|| { - let a_target = a.target_parent(); - let b_target = b.target_parent(); +impl PlannedChange { + /// Returns the immutable source elements that this change will slice in. + fn replacement_elements(&self) -> &[SyntaxElement] { + match &self.change { + Change::Insert(_, element) | Change::Replace(_, Some(element)) => { + std::slice::from_ref(element) + } + Change::InsertAll(_, elements) + | Change::ReplaceWithMany(_, elements) + | Change::ReplaceAll(_, elements) => elements, + Change::Replace(_, None) => &[], + } + } +} - if a_target == b_target { - return Ordering::Equal; - } +/// The dependency info accumulated from one source ordered changes. +/// +/// `parent` is an edge to the nearest containing node replacement. A discarded +/// entry has no executable graph node because an ancestor deletion or ambiguous +/// range replacement has mde its target unavailable. +#[derive(Clone, Copy, Default)] +struct PlanEntry { + parent: Option, + discarded: bool, +} - get_node_depth(a_target).cmp(&get_node_depth(b_target)) - }) - .then(a.change_kind().cmp(&b.change_kind())) - }); +/// Planning failure containing the source-ordered changes use for diag. +struct InvalidEditPlan { + changes: Vec, +} - let disjoint_replaces_ranges = changes - .iter() - .zip(changes.iter().skip(1)) - .filter(|(l, r)| { - // We only care about checking for disjoint replace ranges - matches!( - (l.change_kind(), r.change_kind()), - ( - ChangeKind::Replace | ChangeKind::ReplaceRange, - ChangeKind::Replace | ChangeKind::ReplaceRange - ) - ) - }) - .all(|(l, r)| { - get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) - || (l.target_range().end() <= r.target_range().start()) +impl EditPlan { + /// Validates raw editor changes and turns them into an execution schedule. + /// + /// The input is first sorted in source order, dependent targets are then + /// rewritten from their input trees into ancestor replacement trees. Finally, + /// discarded changes are removed and the dependency forest is traversed in + /// postorder. + /// Independent roots and sibling changes are prioritized right to left. + fn build( + mut changes: Vec, + mappings: &SyntaxMapping, + mut node_depth: impl FnMut(SyntaxNode) -> usize, + ) -> Result { + changes.sort_by(|left, right| { + left.target_range() + .start() + .cmp(&right.target_range().start()) + .then_with(|| { + let left_target = left.target_parent(); + let right_target = right.target_parent(); + if left_target == right_target { + Ordering::Equal + } else { + node_depth(left_target).cmp(&node_depth(right_target)) + } + }) + .then(left.change_kind().cmp(&right.change_kind())) }); - if !disjoint_replaces_ranges { - report_intersecting_changes(&changes, get_node_depth, &root); + if !Self::replacements_are_disjoint(&changes, &mut node_depth) { + return Err(InvalidEditPlan { changes }); + } - return SyntaxEdit { - old_root: root.clone(), - new_root: root, - annotations: Default::default(), - changed_elements: vec![], - }; - } + let mut entries = vec![PlanEntry::default(); changes.len()]; + let mut regions_by_tree = FxHashMap::>::default(); + + for (index, change) in changes.iter().enumerate() { + let target_tree = change.target_parent().tree_top(); + let regions = regions_by_tree.entry(target_tree).or_default(); + if let Some(region_index) = regions + .iter() + .rposition(|region| region.range.contains_range(change.target_range())) + { + regions.truncate(region_index + 1); + match regions[region_index].nested_changes { + NestedChanges::Remap => { + entries[index].parent = Some(regions[region_index].change_index); + } + NestedChanges::Discard => entries[index].discarded = true, + } + } else { + regions.clear(); + } - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - struct DependentChange { - parent: u32, - child: u32, - } + if let Some(region) = ChangedRegion::for_change(change, index, entries[index].discarded) + { + regions.push(region); + } + } - // Build change tree - let mut changed_ancestors: VecDeque = VecDeque::new(); - let mut dependent_changes = vec![]; - let mut independent_changes = vec![]; - let mut outdated_changes = vec![]; + // Work from the innermost dependency towards the outermost one. This + // lets a chain A -> B -> C rewrite C into B before B itself is mapped + // into A's replacement tree. + for (child, entry) in entries.iter().enumerate().rev() { + if let Some(parent) = entry.parent { + Self::rewrite_dependent_target(&mut changes, parent, child, mappings); + } + } - for (change_index, change) in changes.iter().enumerate() { - // Check if this change is dependent on another change (i.e. it's contained within another range) - if let Some(index) = changed_ancestors + let mut children = vec![Vec::new(); changes.len()]; + for (child, entry) in entries.iter().enumerate() { + if let Some(parent) = entry.parent { + children[parent].push(child); + } + } + for siblings in &mut children { + siblings.sort_by(|&left, &right| { + Self::execution_priority(&changes[left], &changes[right], &mut node_depth) + }); + } + + let mut roots = entries .iter() - .rposition(|ancestor| ancestor.affected_range().contains_range(change.target_range())) - { - // Pop off any ancestors that aren't applicable - changed_ancestors.drain((index + 1)..); + .enumerate() + .filter_map(|(index, entry)| { + (!entry.discarded && entry.parent.is_none()).then_some(index) + }) + .collect::>(); + roots.sort_by(|&left, &right| { + Self::execution_priority(&changes[left], &changes[right], &mut node_depth) + }); - // FIXME: Resolve changes that depend on a range of elements - let ancestor = &changed_ancestors[index]; + let mut planned = changes + .into_iter() + .zip(entries) + .map(|(change, entry)| { + (!entry.discarded).then_some(PlannedChange { + tree: change.target_parent().tree_top(), + change, + record_as_changed: entry.parent.is_none(), + }) + }) + .collect::>(); - if let Change::Replace(_, None) = changes[ancestor.change_index] { - outdated_changes.push(change_index as u32); - } else { - dependent_changes.push(DependentChange { - parent: ancestor.change_index as u32, - child: change_index as u32, - }); - } - } else { - // This change is independent of any other change + let mut ordered = Vec::new(); + for root in roots { + Self::append_postorder(root, &children, &mut planned, &mut ordered); + } + + Ok(Self { changes: ordered }) + } - // Drain the changed ancestors since we're no longer in a set of dependent changes - changed_ancestors.drain(..); + /// Orders disjoint changes from right to left, with deeper ties first. + fn execution_priority( + left: &Change, + right: &Change, + node_depth: &mut impl FnMut(SyntaxNode) -> usize, + ) -> Ordering { + right + .target_range() + .start() + .cmp(&left.target_range().start()) + .then_with(|| node_depth(right.target_parent()).cmp(&node_depth(left.target_parent()))) + .then(right.change_kind().cmp(&left.change_kind())) + } - independent_changes.push(change_index as u32); + /// Appends a dependency subtree in post order fashion. + fn append_postorder( + index: usize, + children: &[Vec], + planned: &mut [Option], + ordered: &mut Vec, + ) { + for &child in &children[index] { + Self::append_postorder(child, children, planned, ordered); } + ordered.push(planned[index].take().expect("reachable plan nodes are not discarded")); + } - // Add to changed ancestors, if applicable - match change { - Change::Replace(SyntaxElement::Node(target), _) - | Change::ReplaceWithMany(SyntaxElement::Node(target), _) => { - changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) + /// Checks that replacement at the same tree depth do not overlap + /// + /// `changes` is sorted by range start, so overlap is a single comparison against the + /// last range at that key, and `insert` can throw away the range it evicts. + fn replacements_are_disjoint( + changes: &[Change], + mut node_depth: impl FnMut(SyntaxNode) -> usize, + ) -> bool { + let mut previous = FxHashMap::<(SyntaxNode, usize), TextRange>::default(); + for change in changes { + if !matches!(change.change_kind(), ChangeKind::Replace | ChangeKind::ReplaceRange) { + continue; } - Change::ReplaceAll(range, _) => { - changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index)) + + let parent = change.target_parent(); + let key = (parent.tree_top(), node_depth(parent)); + if previous + .insert(key, change.target_range()) + .is_some_and(|range| range.end() > change.target_range().start()) + { + return false; } - _ => (), } + true } - // Map change targets to the correct syntax nodes - let tree_mutator = TreeMutator::new(&root); - let mut changed_elements = vec![]; - let mut changed_elements_set = rustc_hash::FxHashSet::default(); - let mut deduplicate_node = |node_or_token: &mut SyntaxElement| { - let node; - let node = match node_or_token { - SyntaxElement::Token(token) => match token.parent() { - None => return, - Some(parent) => { - node = parent; - &node - } - }, - SyntaxElement::Node(node) => node, + /// Maps one dependent change into its ancestor replacement tree. + fn rewrite_dependent_target( + changes: &mut [Change], + parent: usize, + child: usize, + mappings: &SyntaxMapping, + ) { + let (input_ancestor, output_ancestor) = match &changes[parent] { + Change::Replace( + SyntaxElement::Node(target), + Some(SyntaxElement::Node(replacement)), + ) => (target.clone(), replacement.clone()), + _ => unreachable!("only node replacements can own dependent changes"), }; - if changed_elements_set.contains(node) { - let new_node = node.clone_subtree().clone_for_update(); - match node_or_token { - SyntaxElement::Node(node) => *node = new_node, - SyntaxElement::Token(token) => { - *token = new_node - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|it| it.kind() == token.kind() && it.text() == token.text()) - .unwrap(); - } - } - } else { - changed_elements_set.insert(node.clone()); - } - }; - for index in independent_changes { - match &mut changes[index as usize] { - Change::Insert(target, _) | Change::InsertAll(target, _) => { - match &mut target.repr { - PositionRepr::FirstChild(parent) => { - *parent = tree_mutator.make_syntax_mut(parent); - } - PositionRepr::After(child) => { - *child = tree_mutator.make_element_mut(child); - } - }; - } - Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(_))) => { - *target = tree_mutator.make_syntax_mut(target); + let upmap_node = |target: &SyntaxNode| { + mappings.upmap_child(target, &input_ancestor, &output_ancestor).unwrap_or_else( + |MissingMapping(current)| { + panic!( + "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" + ) + }, + ) + }; + let upmap_element = |target: &SyntaxElement| { + mappings.upmap_child_element(target, &input_ancestor, &output_ancestor).unwrap_or_else( + |MissingMapping(current)| { + panic!( + "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" + ) + }, + ) + }; + + match &mut changes[child] { + Change::Insert(position, _) | Change::InsertAll(position, _) => { + match &mut position.repr { + PositionRepr::FirstChild(parent) => *parent = upmap_node(parent), + PositionRepr::After(child) => *child = upmap_element(child), + } } Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { - *target = tree_mutator.make_element_mut(target); + *target = upmap_element(target); } Change::ReplaceAll(range, _) => { - let start = tree_mutator.make_element_mut(range.start()); - let end = tree_mutator.make_element_mut(range.end()); - - *range = start..=end; + *range = upmap_element(range.start())..=upmap_element(range.end()); } } + } +} - match &mut changes[index as usize] { - Change::Insert(_, SyntaxElement::Node(node)) - | Change::Replace(_, Some(SyntaxElement::Node(node))) => { - if node.parent().is_some() { - *node = node.clone_subtree().clone_for_update(); - } else if !node.is_mutable() { - *node = node.clone_for_update(); - } - } - Change::Insert(_, SyntaxElement::Token(token)) - | Change::Replace(_, Some(SyntaxElement::Token(token))) => { - if let Some(parent) = token.parent() { - let idx = token.index(); - let new_parent = parent.clone_subtree().clone_for_update(); - *token = new_parent - .children_with_tokens() - .nth(idx) - .and_then(SyntaxElement::into_token) - .unwrap(); - } - } - Change::InsertAll(_, elements) - | Change::ReplaceWithMany(_, elements) - | Change::ReplaceAll(_, elements) => { - for element in elements { - match element { - SyntaxElement::Node(node) => { - if node.parent().is_some() { - *node = node.clone_subtree().clone_for_update(); - } else if !node.is_mutable() { - *node = node.clone_for_update(); - } - } - SyntaxElement::Token(token) => { - if let Some(parent) = token.parent() { - let idx = token.index(); - let new_parent = parent.clone_subtree().clone_for_update(); - *token = new_parent - .children_with_tokens() - .nth(idx) - .and_then(SyntaxElement::into_token) - .unwrap(); - } - } - } - } - } - _ => {} - } +/// A stable structural address expressed as `children_with_token` indices. +#[derive(Clone)] +struct SyntaxPath { + child_indices: Vec, +} - match &mut changes[index as usize] { - Change::Insert(_, element) | Change::Replace(_, Some(element)) => { - deduplicate_node(element); - } - Change::InsertAll(_, elements) - | Change::ReplaceWithMany(_, elements) - | Change::ReplaceAll(_, elements) => { - elements.iter_mut().for_each(&mut deduplicate_node); +impl SyntaxPath { + /// Builds the root-relative path of element in its current tree. + fn new(element: &SyntaxElement) -> Self { + let mut child_indices = Vec::new(); + let mut node = match element { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => { + child_indices.push(token.index()); + token.parent().unwrap() } - Change::Replace(_, None) => (), + }; + + while let Some(parent) = node.parent() { + child_indices.push(node.index()); + node = parent; } + child_indices.reverse(); + Self { child_indices } + } - // Collect changed elements - match &changes[index as usize] { - Change::Insert(_, element) => changed_elements.push(element.clone()), - Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), - Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), - Change::Replace(_, None) => {} - Change::ReplaceWithMany(_, elements) => { - changed_elements.extend(elements.iter().cloned()) - } - Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + /// Follows this path from root, returning None if the structure differs. + fn resolve(&self, root: &SyntaxNode) -> Option { + let mut current = SyntaxElement::Node(root.clone()); + for &index in &self.child_indices { + current = current.into_node()?.children_with_tokens().nth(index)?; } + Some(current) } - for DependentChange { parent, child } in dependent_changes.into_iter().rev() { - let (input_ancestor, output_ancestor) = match &changes[parent as usize] { - // No change will depend on an insert since changes can only depend on nodes in the root tree - Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), - Change::Replace(target, Some(new_target)) => { - (to_owning_node(target), to_owning_node(new_target)) - } - Change::Replace(_, None) => { - unreachable!("deletions should not generate dependent changes") - } - Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => { - unimplemented!("cannot resolve changes that depend on replacing many elements") - } - }; + /// Removes `ancestor`'s prefix, yielding this path within that subtree. + /// + /// Could have used LCA? + fn relative_to(&self, ancestor: &SyntaxPath) -> Option { + self.child_indices + .strip_prefix(ancestor.child_indices.as_slice()) + .map(|relative| SyntaxPath { child_indices: relative.to_vec() }) + } - let upmap_target_node = |target: &SyntaxNode| match mappings.upmap_child( - target, - &input_ancestor, - &output_ancestor, - ) { - Ok(it) => it, - Err(MissingMapping(current)) => unreachable!( - "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" - ), - }; + /// Appends an inserted child slot and a path relative to that child. + fn in_child(&self, index: usize, relative: &SyntaxPath) -> SyntaxPath { + let mut child_indices = + Vec::with_capacity(self.child_indices.len() + relative.child_indices.len() + 1); + child_indices.extend_from_slice(&self.child_indices); + child_indices.push(index); + child_indices.extend_from_slice(&relative.child_indices); + SyntaxPath { child_indices } + } - let upmap_target = |target: &SyntaxElement| match mappings.upmap_child_element( - target, - &input_ancestor, - &output_ancestor, - ) { - Ok(it) => it, - Err(MissingMapping(current)) => unreachable!( - "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" - ), + /// Updates this path for a splice and reports whether its element survives. + fn adjust_for_splice( + &mut self, + parent: &SyntaxPath, + deleted: &Range, + inserted: usize, + ) -> bool { + let Some(relative) = self.child_indices.strip_prefix(parent.child_indices.as_slice()) + else { + return true; }; + let Some((&child, _)) = relative.split_first() else { return true }; - match &mut changes[child as usize] { - Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { - PositionRepr::FirstChild(parent) => { - *parent = upmap_target_node(parent); - } - PositionRepr::After(child) => { - *child = upmap_target(child); + if deleted.contains(&child) { + return false; + } + if child >= deleted.end { + let new_child = child + inserted; + self.child_indices[parent.child_indices.len()] = + new_child - (deleted.end - deleted.start); + } + true + } +} + +/// An annotation paired with its structural location and registration order. +#[derive(Clone)] +struct TrackedAnnotation { + path: SyntaxPath, + annotation: SyntaxAnnotation, + order: usize, +} + +/// A structural edit used to translate original paths into a current tree. +enum PathEdit { + /// A child-list splice with all coordinates relative to the pre-edit tree. + Splice { parent: SyntaxPath, deleted: Range, inserted: usize }, + /// A root replacement, after which no path into the old root survives. + ReplaceRoot, +} + +/// The evolving immutable root and location metadata for one source tree. +/// +/// A syntax edit can involve the editor root plus several detached factory +/// trees. Each receives an independent state so dependent edits can be applied +/// before a generated tree is inserted elsewhere. +struct TreeState { + root: SyntaxNode, + edits: Vec, + changed: Vec, + original_annotations: Vec, + annotations: Vec, +} + +impl TreeState { + /// Starts tracking an unmodified immutable root. + fn new(root: SyntaxNode) -> Self { + Self { + root, + edits: Vec::new(), + changed: Vec::new(), + original_annotations: Vec::new(), + annotations: Vec::new(), + } + } + + /// Replay structural edits to translate an original path into this state. + fn map_original_path(&self, mut path: SyntaxPath) -> Option { + for edit in &self.edits { + match edit { + PathEdit::Splice { parent, deleted, inserted } => { + if !path.adjust_for_splice(parent, deleted, *inserted) { + return None; + } } - }, - Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { - *target = upmap_target(target); - } - Change::ReplaceAll(range, _) => { - *range = upmap_target(range.start())..=upmap_target(range.end()); + PathEdit::ReplaceRoot => return None, } } + Some(path) } - // We reverse here since we pushed to this in ascending order, - // and we want to remove elements in descending order - for idx in outdated_changes.into_iter().rev() { - changes.remove(idx as usize); + /// Finds a change target in the current root. + fn map_original_element(&self, element: &SyntaxElement) -> SyntaxElement { + self.map_original_path(SyntaxPath::new(element)) + .and_then(|path| path.resolve(&self.root)) + .expect("an edit target must still be present") } - // Apply changes - let mut root = tree_mutator.mutable_clone; + /// Applies one child-list splice and updates tracked structural path. + fn splice( + &mut self, + parent_path: SyntaxPath, + deleted: Range, + inserted: Vec, + track_as_changed: bool, + ) { + let inserted_count = inserted.len(); + self.changed + .retain_mut(|path| path.adjust_for_splice(&parent_path, &deleted, inserted_count)); + self.annotations + .retain_mut(|it| it.path.adjust_for_splice(&parent_path, &deleted, inserted_count)); + + for (offset, element) in inserted.iter().enumerate() { + let index = deleted.start + offset; + if track_as_changed { + self.changed + .push(parent_path.in_child(index, &SyntaxPath { child_indices: Vec::new() })); + } + self.annotations.extend(element.annotations.iter().map(|annotation| { + TrackedAnnotation { + path: parent_path.in_child(index, &annotation.path), + annotation: annotation.annotation, + order: annotation.order, + } + })); + } + + let parent = parent_path.resolve(&self.root).and_then(SyntaxElement::into_node).unwrap(); + let green = rowan::GreenNodeData::splice_children( + parent.green(), + deleted.clone(), + inserted.into_iter().map(PreparedElement::into_green), + ); + self.root = SyntaxNode::new_root(parent.replace_with(green)); + self.edits.push(PathEdit::Splice { + parent: parent_path, + deleted, + inserted: inserted_count, + }); + } + + /// Replaces the tree's root with a prepared node payload. + fn replace_root(&mut self, replacement: PreparedElement, track_as_changed: bool) { + let NodeOrToken::Node(node) = replacement.syntax else { + panic!("root node replacement should be a node") + }; + self.root = SyntaxNode::new_root(node.green().to_owned()); + self.changed.clear(); + if track_as_changed { + self.changed.push(SyntaxPath { child_indices: Vec::new() }); + } + self.annotations = replacement.annotations; + self.edits.push(PathEdit::ReplaceRoot); + } - for change in changes { + /// Applies a planned change to this tree using already prepared payloads. + fn apply( + &mut self, + change: &Change, + replacement: Vec, + record_as_changed: bool, + ) { match change { - Change::Insert(position, element) => { - let (parent, index) = position.place(); - parent.splice_children(index..index, vec![element]); - } - Change::InsertAll(position, elements) => { - let (parent, index) = position.place(); - parent.splice_children(index..index, elements); - } - Change::Replace(target, None) => { - target.detach(); - } - Change::Replace(SyntaxElement::Node(target), Some(new_target)) if target == root => { - root = new_target.into_node().expect("root node replacement should be a node"); + Change::Insert(position, _) | Change::InsertAll(position, _) => { + let (parent, index) = match &position.repr { + PositionRepr::FirstChild(parent) => { + let parent = self.map_original_element(&parent.clone().into()); + (parent.into_node().unwrap(), 0) + } + PositionRepr::After(child) => { + let child = self.map_original_element(child); + (child.parent().unwrap(), child.index() + 1) + } + }; + self.splice( + SyntaxPath::new(&parent.into()), + index..index, + replacement, + record_as_changed, + ); } - Change::Replace(target, Some(new_target)) => { - let parent = target.parent().unwrap(); - parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + Change::Replace(SyntaxElement::Node(target), Some(_)) if target.parent().is_none() => { + self.replace_root(replacement.into_iter().next().unwrap(), record_as_changed); } - Change::ReplaceWithMany(target, elements) => { + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + let target = self.map_original_element(target); let parent = target.parent().unwrap(); - parent.splice_children(target.index()..target.index() + 1, elements); + let index = target.index(); + self.splice( + SyntaxPath::new(&parent.into()), + index..index + 1, + replacement, + record_as_changed, + ); } - Change::ReplaceAll(range, elements) => { - let start = range.start().index(); - let end = range.end().index(); - let parent = range.start().parent().unwrap(); - parent.splice_children(start..end + 1, elements); + Change::ReplaceAll(range, _) => { + let start = self.map_original_element(range.start()); + let end = self.map_original_element(range.end()); + let parent = start.parent().unwrap(); + self.splice( + SyntaxPath::new(&parent.into()), + start.index()..end.index() + 1, + replacement, + record_as_changed, + ); } } } +} + +/// A replacement payload paired with the annotation below it. +/// +/// The syntax element remains an immutable snapshot of its source tree. +/// Annotation paths are relative to the payload root and are rebased by +/// splice +struct PreparedElement { + syntax: SyntaxElement, + annotations: Vec, +} - // Propagate annotations - let annotations = annotations.into_iter().filter_map(|(element, annotation)| { - match mappings.upmap_element(&element, &root) { - // Needed to follow the new tree to find the resulting element - Some(Ok(mapped)) => Some((mapped, annotation)), - // Element did not need to be mapped - None => Some((element, annotation)), - // Element did not make it to the final tree - Some(Err(_)) => None, +impl PreparedElement { + fn into_green(self) -> rowan::NodeOrToken { + match self.syntax { + SyntaxElement::Node(node) => NodeOrToken::Node(node.green().to_owned()), + SyntaxElement::Token(token) => NodeOrToken::Token(token.green().to_owned()), } - }); + } +} - let mut annotation_groups = FxHashMap::default(); +/// Owns all evolving trees involved in executing an edit plan. +struct TreeStore { + states: FxHashMap, +} - for (element, annotation) in annotations { - annotation_groups.entry(annotation).or_insert(vec![]).push(element); +impl TreeStore { + /// Creates per tree state for annotations after following factory mapping. + fn with_annotations( + annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + mappings: &SyntaxMapping, + ) -> Self { + let mut states = FxHashMap::::default(); + for (order, (element, annotation)) in annotations.into_iter().enumerate() { + let element = mappings.upmap_element(&element); + let tree = element.tree_top(); + let tracked = TrackedAnnotation { path: SyntaxPath::new(&element), annotation, order }; + let state = states.entry(tree.clone()).or_insert_with(|| TreeState::new(tree)); + state.original_annotations.push(tracked.clone()); + state.annotations.push(tracked); + } + Self { states } + } + + /// Execute an already ordered plan without performing further analysis. + fn execute(&mut self, plan: EditPlan) { + for planned in plan.changes { + self.states + .entry(planned.tree.clone()) + .or_insert_with(|| TreeState::new(planned.tree.clone())); + let replacement = planned + .replacement_elements() + .iter() + .map(|element| self.prepare_element(element)) + .collect(); + self.states.get_mut(&planned.tree).unwrap().apply( + &planned.change, + replacement, + planned.record_as_changed, + ); + } } - SyntaxEdit { - old_root: tree_mutator.immutable, - new_root: root, - changed_elements, - annotations: annotation_groups, + /// Captures the source element and annotations used by a replacement. + fn prepare_element(&self, element: &SyntaxElement) -> PreparedElement { + let tree = element.tree_top(); + let original_path = SyntaxPath::new(element); + let (element, annotations) = match self.states.get(&tree) { + Some(state) => { + let annotations_below = + |annotations: &[TrackedAnnotation], ancestor: &SyntaxPath| { + annotations + .iter() + .filter_map(|annotation| { + annotation.path.relative_to(ancestor).map(|path| { + TrackedAnnotation { + path, + annotation: annotation.annotation, + order: annotation.order, + } + }) + }) + .collect() + }; + match state.map_original_path(original_path.clone()) { + Some(path) => { + let element = path.resolve(&state.root).unwrap(); + let annotations = annotations_below(&state.annotations, &path); + (element, annotations) + } + None => { + let annotations = + annotations_below(&state.original_annotations, &original_path); + (element.clone(), annotations) + } + } + } + None => (element.clone(), Vec::new()), + }; + PreparedElement { syntax: element, annotations } + } + + /// Resolves the editor roots tracked paths and constructs the public edit. + fn finish(mut self, old_root: SyntaxNode) -> SyntaxEdit { + let state = + self.states.remove(&old_root).unwrap_or_else(|| TreeState::new(old_root.clone())); + let new_root = state.root; + + let mut changed_elements = state + .changed + .into_iter() + .filter_map(|path| path.resolve(&new_root)) + .collect::>(); + changed_elements.sort_by_key(|element| element.text_range().start()); + + let mut annotations = FxHashMap::>::default(); + for annotation in state.annotations { + if let Some(element) = annotation.path.resolve(&new_root) { + annotations + .entry(annotation.annotation) + .or_default() + .push((annotation.order, element)); + } + } + let annotations = annotations + .into_iter() + .map(|(annotation, mut elements)| { + elements.sort_by_key(|(order, element)| (*order, element.text_range().start())); + (annotation, elements.into_iter().map(|(_, element)| element).collect()) + }) + .collect(); + + SyntaxEdit { old_root, new_root, changed_elements, annotations } } } +/// Plans and executes all changes recorded by a SyntaxEditor. +pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { + let SyntaxEditor { root, changes, annotations, make } = editor; + let mappings = make.take(); + let mut node_depths = FxHashMap::::default(); + let mut node_depth = |node: SyntaxNode| { + *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) + }; + + let plan = match EditPlan::build(changes.into_inner(), &mappings, &mut node_depth) { + Ok(plan) => plan, + Err(InvalidEditPlan { changes }) => { + report_intersecting_changes(&changes, &mut node_depth, &root); + return SyntaxEdit { + old_root: root.clone(), + new_root: root, + annotations: FxHashMap::default(), + changed_elements: Vec::new(), + }; + } + }; + + let mut trees = TreeStore::with_annotations(annotations.into_inner(), &mappings); + trees.execute(plan); + trees.finish(root) +} + fn report_intersecting_changes( changes: &[Change], - mut get_node_depth: impl FnMut(rowan::SyntaxNode) -> usize, - root: &rowan::SyntaxNode, + mut get_node_depth: impl FnMut(SyntaxNode) -> usize, + root: &SyntaxNode, ) { let intersecting_changes = changes .iter() @@ -478,77 +763,49 @@ fn report_intersecting_changes( stdx::always!(false, "{}", error_msg); } -fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { - match element { - SyntaxElement::Node(node) => node.clone(), - SyntaxElement::Token(token) => token.parent().unwrap(), - } -} - -struct ChangedAncestor { - kind: ChangedAncestorKind, +/// A replacement region that can contain later source ordered changeds +struct ChangedRegion { + range: TextRange, change_index: usize, + nested_changes: NestedChanges, } -enum ChangedAncestorKind { - Single { node: SyntaxNode }, - Range { _changed_elements: RangeInclusive, _in_parent: SyntaxNode }, -} - -impl ChangedAncestor { - fn single(node: &SyntaxNode, change_index: usize) -> Self { - let kind = ChangedAncestorKind::Single { node: node.clone() }; - - Self { kind, change_index } - } - - fn multiple(range: &RangeInclusive, change_index: usize) -> Self { - Self { - kind: ChangedAncestorKind::Range { - _changed_elements: range.clone(), - _in_parent: range.start().parent().unwrap(), - }, - change_index, - } - } - - fn affected_range(&self) -> TextRange { - match &self.kind { - ChangedAncestorKind::Single { node } => node.text_range(), - ChangedAncestorKind::Range { _changed_elements: changed_nodes, _in_parent: _ } => { - TextRange::new( - changed_nodes.start().text_range().start(), - changed_nodes.end().text_range().end(), - ) - } - } - } -} - -struct TreeMutator { - immutable: SyntaxNode, - mutable_clone: SyntaxNode, +/// How changes nested within a replacement region are handled. +enum NestedChanges { + /// Map nested targets into a one-to-one node replacement. + Remap, + /// Drop nested changes because the replacement has no unique counterpart. + Discard, } -impl TreeMutator { - fn new(immutable: &SyntaxNode) -> TreeMutator { - let immutable = immutable.clone(); - let mutable_clone = immutable.clone_for_update(); - TreeMutator { immutable, mutable_clone } - } - - fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { - match element { - SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(node)), - SyntaxElement::Token(token) => { - let parent = self.make_syntax_mut(&token.parent().unwrap()); - parent.children_with_tokens().nth(token.index()).unwrap() - } +impl ChangedRegion { + /// Describes a region replaced by change, if it can contain changes. + fn for_change(change: &Change, change_index: usize, discarded: bool) -> Option { + match change { + Change::Replace(SyntaxElement::Node(target), replacement) => Some(Self { + range: target.text_range(), + change_index, + nested_changes: if !discarded && matches!(replacement, Some(SyntaxElement::Node(_))) + { + NestedChanges::Remap + } else { + NestedChanges::Discard + }, + }), + Change::ReplaceWithMany(SyntaxElement::Node(target), _) => Some(Self { + range: target.text_range(), + change_index, + nested_changes: NestedChanges::Discard, + }), + Change::ReplaceAll(elements, _) => Some(Self { + range: TextRange::new( + elements.start().text_range().start(), + elements.end().text_range().end(), + ), + change_index, + nested_changes: NestedChanges::Discard, + }), + _ => None, } } - - fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { - let ptr = SyntaxNodePtr::new(node); - ptr.to_node(&self.mutable_clone) - } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 180c2e69fa3e9..17319368afc32 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -21,7 +21,7 @@ pub struct SyntaxMapping { impl SyntaxMapping { /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. - pub fn upmap_child_element( + pub(super) fn upmap_child_element( &self, child: &SyntaxElement, input_ancestor: &SyntaxNode, @@ -48,7 +48,7 @@ impl SyntaxMapping { /// Maps a child node of the input ancestor to the corresponding node in /// the output ancestor. - pub fn upmap_child( + pub(super) fn upmap_child( &self, child: &SyntaxNode, input_ancestor: &SyntaxNode, @@ -127,55 +127,22 @@ impl SyntaxMapping { Err(MissingMapping(current)) } - pub fn upmap_element( - &self, - input: &SyntaxElement, - output_root: &SyntaxNode, - ) -> Option> { - match input { - SyntaxElement::Node(node) => { - Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) - } - SyntaxElement::Token(token) => { - let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { - Ok(it) => it, - Err(err) => return Some(Err(err)), - }; + pub(super) fn upmap_element(&self, input: &SyntaxElement) -> SyntaxElement { + let mut current = input.clone(); - let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); - debug_assert!( - element.as_token().is_some_and(|it| it.kind() == token.kind()), - "token upmapping mapped to the wrong node ({token:?} -> {element:?})" - ); - - Some(Ok(element)) - } - } - } - - pub fn upmap_node( - &self, - input: &SyntaxNode, - output_root: &SyntaxNode, - ) -> Option> { - // Try to follow the mapping tree, if it exists - let input_mapping = self.upmap_node_single(input); - let input_ancestor = - input.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some()); - - match (input_mapping, input_ancestor) { - (Some(input_mapping), _) => { - // A mapping exists at the input, follow along the tree - Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) - } - (None, Some(input_ancestor)) => { - // A mapping exists at an ancestor, follow along the tree - Some(self.upmap_child(input, &input_ancestor, output_root)) - } - (None, None) => { - // No mapping exists at all, is the same position in the final tree - None - } + loop { + let node = match ¤t { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => token.parent().unwrap(), + }; + let Some((input_ancestor, output_ancestor)) = node.ancestors().find_map(|ancestor| { + self.upmap_node_single(&ancestor).map(|output_ancestor| (ancestor, output_ancestor)) + }) else { + return current; + }; + current = self + .upmap_child_element(¤t, &input_ancestor, &output_ancestor.parent().unwrap()) + .expect("the nearest mapped ancestor must map its descendants"); } } @@ -257,7 +224,7 @@ impl SyntaxMappingBuilder { } #[derive(Debug)] -pub struct MissingMapping(pub SyntaxNode); +pub(super) struct MissingMapping(pub SyntaxNode); #[derive(Debug, Clone, Copy)] struct MappingEntry { diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs deleted file mode 100644 index e69deb49ce142..0000000000000 --- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Yet another version of owned string, backed by a syntax tree token. - -use std::{cmp::Ordering, fmt, ops}; - -use rowan::GreenToken; -use smol_str::SmolStr; - -pub struct TokenText<'a>(pub(crate) Repr<'a>); - -pub(crate) enum Repr<'a> { - Borrowed(&'a str), - Owned(GreenToken), -} - -impl<'a> TokenText<'a> { - pub fn borrowed(text: &'a str) -> Self { - TokenText(Repr::Borrowed(text)) - } - - pub(crate) fn owned(green: GreenToken) -> Self { - TokenText(Repr::Owned(green)) - } - - pub fn as_str(&self) -> &str { - match &self.0 { - &Repr::Borrowed(it) => it, - Repr::Owned(green) => green.text(), - } - } -} - -impl ops::Deref for TokenText<'_> { - type Target = str; - - fn deref(&self) -> &str { - self.as_str() - } -} -impl AsRef for TokenText<'_> { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl From> for String { - fn from(token_text: TokenText<'_>) -> Self { - token_text.as_str().into() - } -} - -impl From> for SmolStr { - fn from(token_text: TokenText<'_>) -> Self { - SmolStr::new(token_text.as_str()) - } -} - -impl PartialEq<&'_ str> for TokenText<'_> { - fn eq(&self, other: &&str) -> bool { - self.as_str() == *other - } -} -impl PartialEq> for &'_ str { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq for TokenText<'_> { - fn eq(&self, other: &String) -> bool { - self.as_str() == other.as_str() - } -} -impl PartialEq> for String { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq for TokenText<'_> { - fn eq(&self, other: &TokenText<'_>) -> bool { - self.as_str() == other.as_str() - } -} -impl Eq for TokenText<'_> {} -impl Ord for TokenText<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.as_str().cmp(other.as_str()) - } -} -impl PartialOrd for TokenText<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl fmt::Display for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) - } -} -impl fmt::Debug for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) - } -} diff --git a/src/tools/rust-analyzer/crates/syntax/src/utils.rs b/src/tools/rust-analyzer/crates/syntax/src/utils.rs index d1f60f0b71bcc..9538ae2b90792 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/utils.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/utils.rs @@ -1,6 +1,25 @@ //! A set of utils methods to reuse on other abstraction levels use crate::SyntaxKind; +use rustc_lexer; + +#[inline] +/// Checks that the name is an identifier. +/// This also means that it is not a strict keyword. +/// But it may be a weak keyword. +pub fn is_identifier(name: &str, edition: parser::Edition) -> bool { + if rustc_lexer::is_ident(name) { + if let Some(syntax_kind) = SyntaxKind::from_keyword(name, edition) + && syntax_kind.is_strict_keyword(edition) + { + false + } else { + true + } + } else { + false + } +} #[inline] pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool { diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index d48b984407e65..8d2c65f79163a 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -211,7 +211,7 @@ impl Vfs { /// /// Returns `true` if the file was modified, and saves the [change](ChangedFile). /// - /// If the path does not currently exists in the `Vfs`, allocates a new + /// If the path does not currently exist in the `Vfs`, allocates a new /// [`FileId`] for it. pub fn set_file_contents(&mut self, path: VfsPath, contents: Option>) -> bool { let _p = span!(Level::INFO, "Vfs::set_file_contents").entered(); @@ -280,7 +280,7 @@ impl Vfs { true } - /// Drain and returns all the changes in the `Vfs`. + /// Drains and returns all the changes in the `Vfs`. pub fn take_changes(&mut self) -> IndexMap> { mem::take(&mut self.changes) } @@ -292,7 +292,7 @@ impl Vfs { /// Returns the id associated with `path` /// - /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a + /// - If `path` does not exist in the `Vfs`, allocates a new id for it, associated with a /// deleted file; /// - Else, returns `path`'s id. /// diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index c49e4c4322d43..97e46ae894294 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -60,7 +60,7 @@ pub enum Message { n_total: usize, /// The files that have been loaded successfully. n_done: LoadingProgress, - /// The dir being loaded, `None` if its for a file. + /// The dir being loaded, `None` if it's for a file. dir: Option, /// The [`Config`] version. config_version: u32, diff --git a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs index 225bfc7218b44..fbd1a624e3ce8 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs @@ -17,7 +17,7 @@ pub(crate) struct PathInterner { impl PathInterner { /// Get the id corresponding to `path`. /// - /// If `path` does not exists in `self`, returns [`None`]. + /// If `path` does not exist in `self`, returns [`None`]. pub(crate) fn get(&self, path: &VfsPath) -> Option { self.map.get_index_of(path).map(|i| FileId(i as u32)) } @@ -36,7 +36,7 @@ impl PathInterner { /// /// # Panics /// - /// Panics if `id` does not exists in `self`. + /// Panics if `id` does not exist in `self`. pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { self.map.get_index(id.0 as usize).unwrap() } diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs index 7e2c787afc738..eb55081c2038e 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs @@ -337,9 +337,9 @@ impl PartialEq for AbsPath { struct VirtualPath(String); impl VirtualPath { - /// Returns `true` if `other` is a prefix of `self` (as strings). + /// Returns `true` if `other` is a prefix of `self`. fn starts_with(&self, other: &VirtualPath) -> bool { - self.0.starts_with(&other.0) + <_ as AsRef>::as_ref(&self.0).starts_with(&other.0) } fn strip_prefix(&self, base: &VirtualPath) -> Option<&RelPath> { diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs index 2d89362ee0691..c21fabfbf0460 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs @@ -1,5 +1,13 @@ use super::*; +#[test] +fn virtual_path_starts_with_is_component_based() { + let path = |path: &str| VfsPath::new_virtual_path(path.to_owned()); + + assert!(!path("/foobar").starts_with(&path("/foo"))); + assert!(path("/foo/bar").starts_with(&path("/foo"))); +} + #[test] fn virtual_path_extensions() { assert_eq!(VirtualPath("/".to_owned()).name_and_extension(), None); diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index fd377616d9566..4df17d77edfbf 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1769,9 +1769,9 @@ will likely be useful: **Warning**: This format is provisional and subject to change. -The discover command should output JSON objects, one per -line (JSONL format). These objects should correspond to -this Rust data type: +The discover command should output JSON objects to stdout, +one per line (JSONL format). These objects should correspond +to this Rust data type: ```norun #[derive(Debug, Clone, Deserialize, Serialize)] @@ -1812,6 +1812,9 @@ Only the finished event is required, but the other variants are encouraged to give users more feedback about progress or errors. +Stderr is not parsed as JSONL. It is treated as command log +output and forwarded to rust-analyzer's own logs. + ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports} diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index 9cc3292444980..75e7fc900f193 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -237,8 +237,8 @@ There are four ways to feed `rust-project.json` to rust-analyzer: - Use [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig) to specify a workspace discovery command to generate project descriptions - on-the-fly. Please note that the command output is message-oriented and must - output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig). + on-the-fly. Please note that the command's stdout is message-oriented and + must output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig). - Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 61bc4cb29dfb7..d152cfb5861ea 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -3258,7 +3258,7 @@ "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects, one per\nline (JSONL format). These objects should correspond to\nthis Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUCK\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.", + "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects to stdout,\none per line (JSONL format). These objects should correspond\nto this Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUCK\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.\n\nStderr is not parsed as JSONL. It is treated as command log\noutput and forwarded to rust-analyzer's own logs.", "default": null, "anyOf": [ { diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index ca5b7e3ec7855..440f21cbe3c50 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -176,14 +176,24 @@ async function fileExists(uri: vscode.Uri) { ); } +// Captures the elements of a `components` array. They are matched with `[^\]]` rather than `.` +// so that the array may be spread over several lines, which is just as valid TOML as keeping it +// on one, while still stopping at the end of the array. +const COMPONENTS_RE = /components\s*=\s*\[(?[^\]]*)\]/; +// TOML strings come in both quote flavours. +const RA_COMPONENT_RE = /["']rust-analyzer["']/; + +function declaresRaComponent(toolchainFileContents: string): boolean { + const components = toolchainFileContents.match(COMPONENTS_RE)?.groups?.["components"]; + return components !== undefined && RA_COMPONENT_RE.test(components); +} + async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise { try { const toolchainFileContents = new TextDecoder().decode( await vscode.workspace.fs.readFile(uri), ); - return ( - toolchainFileContents.match(/components\s*=\s*\[.*"rust-analyzer".*\]/g)?.length === 1 - ); + return declaresRaComponent(toolchainFileContents); } catch (_) { return false; } @@ -296,6 +306,7 @@ async function patchelf(dest: vscode.Uri): Promise { } export const _private = { + declaresRaComponent, earliestToolchainPath, orderFromPath, }; diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index baabf4f89773b..259428da41dbf 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -93,4 +93,75 @@ export async function getTests(ctx: Context) { ); }); }); + + await ctx.suite("Bootstrap/Detect RA component in toolchain file", (suite) => { + suite.addTest("Single line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = ["cargo", "rust-analyzer", "rustfmt"] +`, + ), + ); + }); + + suite.addTest("Multi line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rust-analyzer", + "rustfmt", +] +profile = "default" +`, + ), + ); + }); + + suite.addTest("Components array with literal strings", async () => { + assert.ok(_private.declaresRaComponent(`components = ['cargo', 'rust-analyzer']`)); + }); + + suite.addTest("Components array without RA", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rustfmt", +] +`, + ), + ); + }); + + suite.addTest("No components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +`, + ), + ); + }); + + suite.addTest("RA mentioned outside the components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", +] +# add "rust-analyzer" here to use the matching server +`, + ), + ); + }); + }); }