From 940c67f5b82331d8a2c9962795c4928df719aad3 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sun, 29 Mar 2026 16:05:11 +0800 Subject: [PATCH 01/98] feat: exclude refs from deps and stdlib --- .../rust-analyzer/crates/ide-db/src/search.rs | 52 ++++++++++++++++++- .../crates/ide/src/annotations.rs | 8 ++- .../crates/ide/src/references.rs | 22 +++++++- .../crates/rust-analyzer/src/config.rs | 7 +++ .../rust-analyzer/src/handlers/request.rs | 19 ++++--- .../docs/book/src/configuration_generated.md | 7 +++ .../rust-analyzer/editors/code/package.json | 10 ++++ 7 files changed, 112 insertions(+), 13 deletions(-) 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 69459a4b72dac..a0655341fa36f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -449,6 +449,8 @@ impl Definition { scope: None, include_self_kw_refs: None, search_self_mod: false, + excluded_categories: ReferenceCategory::empty(), + exclude_library_files: false, } } } @@ -465,6 +467,10 @@ pub struct FindUsages<'a> { include_self_kw_refs: Option>, /// whether to search for the `self` module search_self_mod: bool, + /// categories to exclude while collecting usages + excluded_categories: ReferenceCategory, + /// whether to skip files from library source roots + exclude_library_files: bool, } impl<'a> FindUsages<'a> { @@ -495,6 +501,16 @@ impl<'a> FindUsages<'a> { self } + pub fn set_excluded_categories(mut self, categories: ReferenceCategory) -> Self { + self.excluded_categories = categories; + self + } + + pub fn set_exclude_library_files(mut self, exclude_library_files: bool) -> Self { + self.exclude_library_files = exclude_library_files; + self + } + pub fn at_least_one(&self) -> bool { let mut found = false; self.search(&mut |_, _| { @@ -599,7 +615,7 @@ impl<'a> FindUsages<'a> { search_scope: &SearchScope, name: &str, ) -> bool { - if self.scope.is_some() { + if self.scope.is_some() || self.exclude_library_files { return false; } @@ -922,7 +938,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("FindUsages:search").entered(); let sema = self.sema; - let search_scope = { + let mut search_scope = { // FIXME: Is the trait scope needed for trait impl assoc items? let base = as_trait_assoc_def(sema.db, self.def).unwrap_or(self.def).search_scope(sema.db); @@ -931,6 +947,14 @@ impl<'a> FindUsages<'a> { Some(scope) => base.intersection(scope), } }; + if self.exclude_library_files { + search_scope + .entries + .retain(|&file_id, _| !is_library_file(sema.db, file_id.file_id(sema.db))); + } + if search_scope.entries.is_empty() { + return; + } let name = match (self.rename, self.def) { (Some(rename), _) => { @@ -1118,6 +1142,10 @@ impl<'a> FindUsages<'a> { name_ref: &ast::NameRef, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { + if self.is_excluded_name_ref(name_ref) { + return false; + } + // See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845 let ty_eq = |ty: hir::Type<'_>| match (ty.as_adt(), self_ty.as_adt()) { (Some(ty), Some(self_ty)) => ty == self_ty, @@ -1146,6 +1174,10 @@ impl<'a> FindUsages<'a> { name_ref: &ast::NameRef, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { + if self.is_excluded_name_ref(name_ref) { + return false; + } + match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); @@ -1210,6 +1242,10 @@ impl<'a> FindUsages<'a> { name_ref: &ast::NameRef, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { + if self.is_excluded_name_ref(name_ref) { + return false; + } + match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(def, _)) if self.def == def @@ -1283,6 +1319,13 @@ impl<'a> FindUsages<'a> { } } + fn is_excluded_name_ref(&self, name_ref: &ast::NameRef) -> bool { + (self.excluded_categories.contains(ReferenceCategory::TEST) + && is_name_ref_in_test(self.sema, name_ref)) + || (self.excluded_categories.contains(ReferenceCategory::IMPORT) + && is_name_ref_in_import(name_ref)) + } + fn found_name( &self, name: &ast::Name, @@ -1409,3 +1452,8 @@ fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameR None => false, }) } + +fn is_library_file(db: &RootDatabase, file_id: span::FileId) -> bool { + let source_root = db.file_source_root(file_id).source_root_id(db); + db.source_root(source_root).source_root(db).is_library +} diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 21b2339c722c7..3d61b9d35fd14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -216,7 +216,13 @@ pub(crate) fn resolve_annotation( *data = find_all_refs( &Semantics::new(db), pos, - &FindAllRefsConfig { search_scope: None, ra_fixture: config.ra_fixture }, + &FindAllRefsConfig { + search_scope: None, + ra_fixture: config.ra_fixture, + exclude_imports: false, + exclude_tests: false, + exclude_library_refs: false, + }, ) .map(|result| { result diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 0288099bbcc2b..c35e3dc3aaf79 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -91,6 +91,9 @@ pub struct Declaration { pub struct FindAllRefsConfig<'a> { pub search_scope: Option, pub ra_fixture: RaFixtureConfig<'a>, + pub exclude_imports: bool, + pub exclude_tests: bool, + pub exclude_library_refs: bool, } /// Find all references to the item at the given position. @@ -127,8 +130,20 @@ pub(crate) fn find_all_refs( let syntax = sema.parse_guess_edition(position.file_id).syntax().clone(); let make_searcher = |literal_search: bool| { move |def: Definition| { - let mut usages = - def.usages(sema).set_scope(config.search_scope.as_ref()).include_self_refs().all(); + let mut excluded_categories = ReferenceCategory::empty(); + if config.exclude_imports { + excluded_categories |= ReferenceCategory::IMPORT; + } + if config.exclude_tests { + excluded_categories |= ReferenceCategory::TEST; + } + let mut usages = def + .usages(sema) + .set_scope(config.search_scope.as_ref()) + .set_excluded_categories(excluded_categories) + .set_exclude_library_files(config.exclude_library_refs) + .include_self_refs() + .all(); if literal_search { retain_adt_literal_usages(&mut usages, def, sema); } @@ -1568,6 +1583,9 @@ fn main() { let config = FindAllRefsConfig { search_scope: search_scope.map(|it| it(&analysis.db)), ra_fixture: RaFixtureConfig::default(), + exclude_imports: false, + exclude_tests: false, + exclude_library_refs: false, }; let refs = analysis.find_all_refs(pos, &config).unwrap().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 3a88a8fe84807..27546b50ca408 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -409,6 +409,9 @@ config_data! { /// Exclude imports from find-all-references. references_excludeImports: bool = false, + /// Exclude references from dependencies and stdlib in find-all-references. + references_excludeLibraries: bool = false, + /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, @@ -2652,6 +2655,10 @@ impl Config { *self.references_excludeImports() } + pub fn find_all_refs_exclude_libraries(&self) -> bool { + *self.references_excludeLibraries() + } + pub fn find_all_refs_exclude_tests(&self) -> bool { *self.references_excludeTests() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 86516b6079c72..c6485badd4cc9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -9,8 +9,8 @@ use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition, FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData, - InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, - SingleResolve, SourceChange, TextEdit, + InlayFieldsToResolve, Query, RangeInfo, Runnable, RunnableKind, SingleResolve, SourceChange, + TextEdit, }; use ide_db::{FxHashMap, SymbolKind}; use itertools::Itertools; @@ -1396,12 +1396,16 @@ pub(crate) fn handle_references( let exclude_imports = snap.config.find_all_refs_exclude_imports(); let exclude_tests = snap.config.find_all_refs_exclude_tests(); + let exclude_library_refs = snap.config.find_all_refs_exclude_libraries(); let Some(refs) = snap.analysis.find_all_refs( position, &FindAllRefsConfig { search_scope: None, ra_fixture: snap.config.ra_fixture(snap.minicore()), + exclude_imports, + exclude_library_refs, + exclude_tests, }, )? else { @@ -1423,12 +1427,7 @@ pub(crate) fn handle_references( refs.references .into_iter() .flat_map(|(file_id, refs)| { - refs.into_iter() - .filter(|&(_, category)| { - (!exclude_imports || !category.contains(ReferenceCategory::IMPORT)) - && (!exclude_tests || !category.contains(ReferenceCategory::TEST)) - }) - .map(move |(range, _)| FileRange { file_id, range }) + refs.into_iter().map(move |(range, _)| FileRange { file_id, range }) }) .chain(decl) }) @@ -2211,7 +2210,11 @@ fn show_ref_command_link( *position, &FindAllRefsConfig { search_scope: None, + ra_fixture: snap.config.ra_fixture(snap.minicore()), + exclude_imports: snap.config.find_all_refs_exclude_imports(), + exclude_tests: snap.config.find_all_refs_exclude_tests(), + exclude_library_refs: snap.config.find_all_refs_exclude_libraries(), }, ) .unwrap_or(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 da37fc1582342..245e77f088f23 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1376,6 +1376,13 @@ Default: `false` Exclude imports from find-all-references. +## rust-analyzer.references.excludeLibraries {#references.excludeLibraries} + +Default: `false` + +Exclude references from dependencies and stdlib in find-all-references. + + ## rust-analyzer.references.excludeTests {#references.excludeTests} Default: `false` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 29cbc8bd4fdab..2f1d75816a948 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2875,6 +2875,16 @@ } } }, + { + "title": "References", + "properties": { + "rust-analyzer.references.excludeLibraries": { + "markdownDescription": "Exclude references from dependencies and stdlib in find-all-references.", + "default": false, + "type": "boolean" + } + } + }, { "title": "References", "properties": { From f18e47485bfa4c603abc43c1d3ea79e25c73c120 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sun, 29 Mar 2026 16:13:25 +0800 Subject: [PATCH 02/98] fix: exclude declartion --- src/tools/rust-analyzer/crates/ide/src/references.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index c35e3dc3aaf79..199dea5e86c43 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -181,6 +181,9 @@ pub(crate) fn find_all_refs( is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)), nav, } + }) + .filter(|decl| { + !(config.exclude_library_refs && is_library_file(sema.db, decl.nav.file_id)) }); ReferenceSearchResult { declaration, references } } @@ -222,6 +225,11 @@ pub(crate) fn find_all_refs( } } +fn is_library_file(db: &RootDatabase, file_id: FileId) -> bool { + let source_root = db.file_source_root(file_id).source_root_id(db); + db.source_root(source_root).source_root(db).is_library +} + pub(crate) fn find_defs( sema: &Semantics<'_, RootDatabase>, syntax: &SyntaxNode, From 190557aac752352822e0c7f1ebe3159245123a74 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sun, 29 Mar 2026 16:31:57 +0800 Subject: [PATCH 03/98] test: add test case for exclude refs from deps and stdlib --- .../crates/ide/src/references.rs | 140 +++++++++++++++++- 1 file changed, 137 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 199dea5e86c43..2dd13ecbcea73 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -492,7 +492,7 @@ mod tests { #[test] fn exclude_tests() { - check( + check_with_filters( r#" fn test_func() {} @@ -505,6 +505,9 @@ fn test() { test_func(); } "#, + false, + false, + false, expect![[r#" test_func Function FileId(0) 0..17 3..12 @@ -513,7 +516,7 @@ fn test() { "#]], ); - check( + check_with_filters( r#" fn test_func() {} @@ -526,6 +529,9 @@ fn test() { test_func(); } "#, + false, + false, + false, expect![[r#" test_func Function FileId(0) 0..17 3..12 @@ -533,6 +539,103 @@ fn test() { FileId(0) 96..105 test "#]], ); + + check_with_filters( + r#" +fn test_func() {} + +fn func() { + test_func$0(); +} + +#[test] +fn test() { + test_func(); +} +"#, + false, + true, + false, + expect![[r#" + test_func Function FileId(0) 0..17 3..12 + + FileId(0) 35..44 + "#]], + ); + } + + #[test] + fn exclude_library_refs_filtering() { + // exclude refs in 3rd party lib + check_with_filters( + r#" +//- /main.rs crate:main deps:dep +use dep::foo; + +fn main() { + foo$0(); +} + +//- /dep/lib.rs crate:dep new_source_root:library +pub fn foo() {} + +pub fn also_calls_foo() { + foo(); +} +"#, + false, + false, + true, + expect![[r#" + FileId(0) 9..12 import + FileId(0) 31..34 + "#]], + ); + + // exclude refs in stdlib + check_with_filters( + r#" +//- minicore: option +fn main() { + let _ = core::option::Option::Some$0(0); +} +"#, + false, + false, + true, + expect![[r#" + FileId(0) 46..50 + "#]], + ); + + // keep refs in local lib + check_with_filters( + r#" +//- /main.rs crate:main deps:dep +use dep::foo; + +fn main() { + foo$0(); +} + +//- /dep/lib.rs crate:dep +pub fn foo() {} + +pub fn also_calls_foo() { + foo(); +} +"#, + false, + false, + true, + expect![[r#" + foo Function FileId(1) 0..15 7..10 + + FileId(0) 9..12 import + FileId(0) 31..34 + FileId(1) 47..50 + "#]], + ); } #[test] @@ -1579,18 +1682,49 @@ fn main() { } fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - check_with_scope(ra_fixture, None, expect) + check_with_filters(ra_fixture, false, false, false, expect) + } + + fn check_with_filters( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + exclude_imports: bool, + exclude_tests: bool, + exclude_library_refs: bool, + expect: Expect, + ) { + check_with_scope_and_filters( + ra_fixture, + None, + exclude_imports, + exclude_tests, + exclude_library_refs, + expect, + ) } fn check_with_scope( #[rust_analyzer::rust_fixture] ra_fixture: &str, search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>, expect: Expect, + ) { + check_with_scope_and_filters(ra_fixture, search_scope, false, false, false, expect) + } + + fn check_with_scope_and_filters( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>, + exclude_imports: bool, + exclude_tests: bool, + exclude_library_refs: bool, + expect: Expect, ) { let (analysis, pos) = fixture::position(ra_fixture); let config = FindAllRefsConfig { search_scope: search_scope.map(|it| it(&analysis.db)), ra_fixture: RaFixtureConfig::default(), + exclude_imports, + exclude_tests, + exclude_library_refs, exclude_imports: false, exclude_tests: false, exclude_library_refs: false, From 67fa3a112c85d5dcd7889ce51e7e91b5521d6cc4 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sat, 11 Apr 2026 13:28:08 +0800 Subject: [PATCH 04/98] internal: auto exclude --- .../rust-analyzer/crates/ide-db/src/search.rs | 8 ++- .../crates/ide/src/annotations.rs | 1 - .../crates/ide/src/references.rs | 70 ++++++++++--------- .../crates/rust-analyzer/src/config.rs | 7 -- .../rust-analyzer/src/handlers/request.rs | 4 -- .../docs/book/src/configuration_generated.md | 7 -- .../rust-analyzer/editors/code/package.json | 10 --- 7 files changed, 43 insertions(+), 64 deletions(-) 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 a0655341fa36f..8922eb65877b6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -615,7 +615,7 @@ impl<'a> FindUsages<'a> { search_scope: &SearchScope, name: &str, ) -> bool { - if self.scope.is_some() || self.exclude_library_files { + if self.scope.is_some() { return false; } @@ -897,12 +897,16 @@ impl<'a> FindUsages<'a> { let finder = Finder::new(name.as_bytes()); // The search for `Self` may return duplicate results with `ContainerName`, so deduplicate them. let mut self_positions = FxHashSet::default(); + let is_possibly_self = is_possibly_self.into_iter().filter(|position| { + !self.exclude_library_files + || !is_library_file(self.sema.db, position.file_id.file_id(self.sema.db)) + }); tracing::info_span!("Self_search").in_scope(|| { search( self, &finder, name, - is_possibly_self.into_iter().map(|position| { + is_possibly_self.map(|position| { (position.file_text(self.sema.db).clone(), position.file_id, position.range) }), |path, name_position| { diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 3d61b9d35fd14..f716f94d7141b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -221,7 +221,6 @@ pub(crate) fn resolve_annotation( ra_fixture: config.ra_fixture, exclude_imports: false, exclude_tests: false, - exclude_library_refs: false, }, ) .map(|result| { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 2dd13ecbcea73..ea32f0226783b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -20,6 +20,7 @@ use hir::{PathResolution, Semantics}; use ide_db::{ FileId, RootDatabase, + base_db::SourceDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, ra_fixture::{RaFixtureConfig, UpmapFromRaFixture}, @@ -93,7 +94,6 @@ pub struct FindAllRefsConfig<'a> { pub ra_fixture: RaFixtureConfig<'a>, pub exclude_imports: bool, pub exclude_tests: bool, - pub exclude_library_refs: bool, } /// Find all references to the item at the given position. @@ -128,6 +128,7 @@ pub(crate) fn find_all_refs( ) -> Option> { let _p = tracing::info_span!("find_all_refs").entered(); let syntax = sema.parse_guess_edition(position.file_id).syntax().clone(); + let exclude_library_refs = !is_library_file(sema.db, position.file_id); let make_searcher = |literal_search: bool| { move |def: Definition| { let mut excluded_categories = ReferenceCategory::empty(); @@ -141,7 +142,7 @@ pub(crate) fn find_all_refs( .usages(sema) .set_scope(config.search_scope.as_ref()) .set_excluded_categories(excluded_categories) - .set_exclude_library_files(config.exclude_library_refs) + .set_exclude_library_files(exclude_library_refs) .include_self_refs() .all(); if literal_search { @@ -182,9 +183,7 @@ pub(crate) fn find_all_refs( nav, } }) - .filter(|decl| { - !(config.exclude_library_refs && is_library_file(sema.db, decl.nav.file_id)) - }); + .filter(|decl| !(exclude_library_refs && is_library_file(sema.db, decl.nav.file_id))); ReferenceSearchResult { declaration, references } } }; @@ -505,7 +504,6 @@ fn test() { test_func(); } "#, - false, false, false, expect![[r#" @@ -529,7 +527,6 @@ fn test() { test_func(); } "#, - false, false, false, expect![[r#" @@ -555,7 +552,6 @@ fn test() { "#, false, true, - false, expect![[r#" test_func Function FileId(0) 0..17 3..12 @@ -585,7 +581,6 @@ pub fn also_calls_foo() { "#, false, false, - true, expect![[r#" FileId(0) 9..12 import FileId(0) 31..34 @@ -602,7 +597,6 @@ fn main() { "#, false, false, - true, expect![[r#" FileId(0) 46..50 "#]], @@ -627,7 +621,36 @@ pub fn also_calls_foo() { "#, false, false, - true, + expect![[r#" + foo Function FileId(1) 0..15 7..10 + + FileId(0) 9..12 import + FileId(0) 31..34 + FileId(1) 47..50 + "#]], + ); + } + + #[test] + fn find_refs_from_library_source_keeps_library_refs() { + check_with_filters( + r#" +//- /main.rs crate:main deps:dep +use dep::foo; + +fn main() { + foo(); +} + +//- /dep/lib.rs crate:dep new_source_root:library +pub fn foo$0() {} + +pub fn also_calls_foo() { + foo(); +} +"#, + false, + false, expect![[r#" foo Function FileId(1) 0..15 7..10 @@ -1682,24 +1705,16 @@ fn main() { } fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { - check_with_filters(ra_fixture, false, false, false, expect) + check_with_filters(ra_fixture, false, false, expect) } fn check_with_filters( #[rust_analyzer::rust_fixture] ra_fixture: &str, exclude_imports: bool, exclude_tests: bool, - exclude_library_refs: bool, expect: Expect, ) { - check_with_scope_and_filters( - ra_fixture, - None, - exclude_imports, - exclude_tests, - exclude_library_refs, - expect, - ) + check_with_scope_and_filters(ra_fixture, None, exclude_imports, exclude_tests, expect) } fn check_with_scope( @@ -1707,7 +1722,7 @@ fn main() { search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>, expect: Expect, ) { - check_with_scope_and_filters(ra_fixture, search_scope, false, false, false, expect) + check_with_scope_and_filters(ra_fixture, search_scope, false, false, expect) } fn check_with_scope_and_filters( @@ -1715,7 +1730,6 @@ fn main() { search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>, exclude_imports: bool, exclude_tests: bool, - exclude_library_refs: bool, expect: Expect, ) { let (analysis, pos) = fixture::position(ra_fixture); @@ -1724,10 +1738,6 @@ fn main() { ra_fixture: RaFixtureConfig::default(), exclude_imports, exclude_tests, - exclude_library_refs, - exclude_imports: false, - exclude_tests: false, - exclude_library_refs: false, }; let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap(); @@ -2234,8 +2244,6 @@ use proc_macros::identity; fn func() {} "#, expect![[r#" - identity Attribute FileId(1) 1..107 32..40 - FileId(0) 17..25 import FileId(0) 43..51 "#]], @@ -2265,8 +2273,6 @@ use proc_macros::mirror; mirror$0! {} "#, expect![[r#" - mirror ProcMacro FileId(1) 1..77 22..28 - FileId(0) 17..23 import FileId(0) 26..32 "#]], @@ -2285,8 +2291,6 @@ use proc_macros::DeriveIdentity; struct Foo; "#, expect![[r#" - derive_identity Derive FileId(2) 1..107 45..60 - FileId(0) 17..31 import FileId(0) 56..70 "#]], 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 27546b50ca408..3a88a8fe84807 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -409,9 +409,6 @@ config_data! { /// Exclude imports from find-all-references. references_excludeImports: bool = false, - /// Exclude references from dependencies and stdlib in find-all-references. - references_excludeLibraries: bool = false, - /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, @@ -2655,10 +2652,6 @@ impl Config { *self.references_excludeImports() } - pub fn find_all_refs_exclude_libraries(&self) -> bool { - *self.references_excludeLibraries() - } - pub fn find_all_refs_exclude_tests(&self) -> bool { *self.references_excludeTests() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index c6485badd4cc9..35c5df6e917a0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1396,15 +1396,12 @@ pub(crate) fn handle_references( let exclude_imports = snap.config.find_all_refs_exclude_imports(); let exclude_tests = snap.config.find_all_refs_exclude_tests(); - let exclude_library_refs = snap.config.find_all_refs_exclude_libraries(); - let Some(refs) = snap.analysis.find_all_refs( position, &FindAllRefsConfig { search_scope: None, ra_fixture: snap.config.ra_fixture(snap.minicore()), exclude_imports, - exclude_library_refs, exclude_tests, }, )? @@ -2214,7 +2211,6 @@ fn show_ref_command_link( ra_fixture: snap.config.ra_fixture(snap.minicore()), exclude_imports: snap.config.find_all_refs_exclude_imports(), exclude_tests: snap.config.find_all_refs_exclude_tests(), - exclude_library_refs: snap.config.find_all_refs_exclude_libraries(), }, ) .unwrap_or(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 245e77f088f23..da37fc1582342 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1376,13 +1376,6 @@ Default: `false` Exclude imports from find-all-references. -## rust-analyzer.references.excludeLibraries {#references.excludeLibraries} - -Default: `false` - -Exclude references from dependencies and stdlib in find-all-references. - - ## rust-analyzer.references.excludeTests {#references.excludeTests} Default: `false` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2f1d75816a948..29cbc8bd4fdab 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2875,16 +2875,6 @@ } } }, - { - "title": "References", - "properties": { - "rust-analyzer.references.excludeLibraries": { - "markdownDescription": "Exclude references from dependencies and stdlib in find-all-references.", - "default": false, - "type": "boolean" - } - } - }, { "title": "References", "properties": { From cb666a0e6875bf3219a700ae03692ce3e3352bdf Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 12 Apr 2026 01:46:35 +0800 Subject: [PATCH 05/98] fix: enable vscode suggest in strings Co-authored-by: Amit Singhmar --- src/tools/rust-analyzer/editors/code/package.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 29cbc8bd4fdab..67570cd06774b 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -3296,6 +3296,13 @@ } ], "configurationDefaults": { + "[rust]": { + "editor.quickSuggestions": { + "other": true, + "comments": false, + "strings": true + } + }, "explorer.fileNesting.patterns": { "Cargo.toml": "Cargo.lock" } From 5ff216c7db276ae3eff73f4e82702135255709a7 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:00:25 +0000 Subject: [PATCH 06/98] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index c25feceb4157d..be595ec0f608a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -1345,7 +1345,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "uninhabited_static", description: r##"uninhabited static"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -6927,6 +6927,22 @@ The tracking issue for this feature is: [#154181] [#154181]: https://github.com/rust-lang/rust/issues/154181 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "diagnostic_on_unknown", + description: r##"# `diagnostic_on_unknown` + +Allows giving unresolved imports a custom diagnostic message + +The tracking issue for this feature is: [#152900] + +[#152900]: https://github.com/rust-lang/rust/issues/152900 + ------------------------ "##, default_severity: Severity::Allow, @@ -12989,19 +13005,20 @@ only discuss a few of them. ------------------------ The `rustc_attrs` feature allows debugging rustc type layouts by using -`#[rustc_layout(...)]` to debug layout at compile time (it even works +`#[rustc_dump_layout(...)]` to debug layout at compile time (it even works with `cargo check`) as an alternative to `rustc -Z print-type-sizes` that is way more verbose. -Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`, -`abi`. Note that it only works on sized types without generics. +Options provided by `#[rustc_dump_layout(...)]` are `backend_repr`, `align`, +`debug`, `homogeneous_aggregate` and `size`. +Note that it only works on sized types without generics. ## Examples ```rust,compile_fail #![feature(rustc_attrs)] -#[rustc_layout(abi, size)] +#[rustc_dump_layout(backend_repr, size)] pub enum X { Y(u8, u8, u8), Z(isize), @@ -13011,7 +13028,7 @@ pub enum X { When that is compiled, the compiler will error with something like ```text -error: abi: Aggregate { sized: true } +error: backend_repr: Aggregate { sized: true } --> src/lib.rs:4:1 | 4 | / pub enum T { From 67ed22e34e96dac71343de8fe085cf4122d44c1c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 13 Apr 2026 04:02:52 +0800 Subject: [PATCH 07/98] fix: no deref index-expr for extract_function Example --- ```rust fn foo() { let mut arr = [1i32]; $0arr[0] = 3;$0 let _ = arr; } ``` **Before this PR** ```rust fn foo() { let mut arr = [1i32]; fun_name(&mut arr); let _ = arr; } fn $0fun_name(arr: &mut [i32; 1]) { *arr[0] = 3; } ``` **After this PR** ```rust fn foo() { let mut arr = [1i32]; fun_name(&mut arr); let _ = arr; } fn $0fun_name(arr: &mut [i32; 1]) { arr[0] = 3; } ``` --- .../src/handlers/extract_function.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) 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 fa5bb39c54ba8..4865aa71735fc 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 @@ -2088,7 +2088,11 @@ fn fix_param_usages( for (param, usages) in usages_for_param { for usage in usages { match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { - Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => { + Some( + ast::Expr::MethodCallExpr(_) + | ast::Expr::FieldExpr(_) + | ast::Expr::IndexExpr(_), + ) => { // do nothing } Some(ast::Expr::RefExpr(node)) @@ -3211,6 +3215,32 @@ fn $0fun_name(n: &mut i32) { ); } + #[test] + fn mut_index_from_outer_scope() { + check_assist( + extract_function, + r#" +//- minicore: index +fn foo() { + let mut arr = [1i32]; + $0arr[0] = 3;$0 + let _ = arr; +} +"#, + r#" +fn foo() { + let mut arr = [1i32]; + fun_name(&mut arr); + let _ = arr; +} + +fn $0fun_name(arr: &mut [i32; 1]) { + arr[0] = 3; +} +"#, + ); + } + #[test] fn mut_field_from_outer_scope() { check_assist( From 1a77f3d7dfc287129eb0563ce7fb5905c5c739f8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Apr 2026 13:14:29 +0300 Subject: [PATCH 08/98] Support `impl` and `mut` restrictions I chose the order of mut restrictions to be `unsafe mut(...)` for unsafe fields, but it's undecided yet. --- .../ide-completion/src/context/analysis.rs | 4 +- .../crates/parser/src/grammar.rs | 85 +++++----- .../crates/parser/src/grammar/items.rs | 19 +++ .../crates/parser/src/grammar/items/adt.rs | 37 ++++- .../parser/src/syntax_kind/generated.rs | 6 + .../parser/test_data/generated/runner.rs | 16 ++ .../err/crate_visibility_empty_recover.rast | 5 +- .../parser/inline/ok/crate_visibility.rast | 39 ++--- .../parser/inline/ok/crate_visibility_in.rast | 38 ++--- .../parser/inline/ok/impl_restrictions.rast | 80 ++++++++++ .../parser/inline/ok/impl_restrictions.rs | 4 + .../ok/record_mut_restrictions_after.rast | 35 +++++ .../ok/record_mut_restrictions_after.rs | 1 + .../ok/record_mut_restrictions_before.rast | 35 +++++ .../ok/record_mut_restrictions_before.rs | 1 + .../inline/ok/tuple_mut_restrictions.rast | 37 +++++ .../inline/ok/tuple_mut_restrictions.rs | 1 + .../test_data/parser/ok/0012_visibility.rast | 49 +++--- .../rust-analyzer/crates/syntax/rust.ungram | 13 +- .../crates/syntax/src/ast/generated/nodes.rs | 146 +++++++++++++++++- .../crates/syntax/src/ast/node_ext.rs | 9 ++ .../crates/syntax/src/validation.rs | 12 +- .../validation/0037_visibility_in_traits.rast | 26 ++-- 23 files changed, 581 insertions(+), 117 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs 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 2a293313f2c92..60f5dabefc62e 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 @@ -1592,7 +1592,7 @@ fn classify_name_ref<'db>( kind_macro_call(it)? }, ast::Meta(meta) => make_path_kind_attr(meta)?, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, ast::UseTree(_) => PathKind::Use, // completing inside a qualifier ast::Path(parent) => { @@ -1621,7 +1621,7 @@ fn classify_name_ref<'db>( kind_macro_call(it)? }, ast::Meta(meta) => make_path_kind_attr(meta)?, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, ast::UseTree(_) => PathKind::Use, ast::RecordExpr(it) => make_path_kind_expr(it.into()), _ => return None, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 1ff8a56b580f0..e8a9ddde69ea3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -228,48 +228,61 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { let m = p.start(); p.bump(T![pub]); - if p.at(T!['(']) { - match p.nth(1) { - // test crate_visibility - // pub(crate) struct S; - // pub(self) struct S; - // pub(super) struct S; - - // test_err crate_visibility_empty_recover - // pub() struct S; - - // test pub_parens_typepath - // struct B(pub (super::A)); - // struct B(pub (crate::A,)); - T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { - // If we are in a tuple struct, then the parens following `pub` - // might be an tuple field, not part of the visibility. So in that - // case we don't want to consume an identifier. - - // test pub_tuple_field - // struct MyStruct(pub (u32, u32)); - // struct MyStruct(pub (u32)); - // struct MyStruct(pub ()); - if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { - p.bump(T!['(']); - paths::vis_path(p); - p.expect(T![')']); - } - } - // test crate_visibility_in - // pub(in super::A) struct S; - // pub(in crate) struct S; - T![in] => { + opt_visibility_inner(p, in_tuple_field); + m.complete(p, VISIBILITY); + true +} + +fn opt_visibility_inner(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { + if !p.at(T!['(']) { + return false; + } + + match p.nth(1) { + // test crate_visibility + // pub(crate) struct S; + // pub(self) struct S; + // pub(super) struct S; + + // test_err crate_visibility_empty_recover + // pub() struct S; + + // test pub_parens_typepath + // struct B(pub (super::A)); + // struct B(pub (crate::A,)); + T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { + // If we are in a tuple struct, then the parens following `pub` + // might be an tuple field, not part of the visibility. So in that + // case we don't want to consume an identifier. + + // test pub_tuple_field + // struct MyStruct(pub (u32, u32)); + // struct MyStruct(pub (u32)); + // struct MyStruct(pub ()); + if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { + let m = p.start(); p.bump(T!['(']); - p.bump(T![in]); paths::vis_path(p); p.expect(T![')']); + m.complete(p, VISIBILITY_INNER); + return true; } - _ => {} } + // test crate_visibility_in + // pub(in super::A) struct S; + // pub(in crate) struct S; + T![in] => { + let m = p.start(); + p.bump(T!['(']); + p.bump(T![in]); + paths::vis_path(p); + p.expect(T![')']); + m.complete(p, VISIBILITY_INNER); + return true; + } + _ => {} } - m.complete(p, VISIBILITY); - true + false } fn opt_rename(p: &mut Parser<'_>) { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index c0acdde2a7240..6c46fac290762 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -167,6 +167,25 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res has_mods = true; } + if p.at(T![impl]) + && p.nth(1) == T!['('] + && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')']) + || p.nth(2) == T![in]) + { + // test impl_restrictions + // pub unsafe impl(crate) trait Foo {} + // impl(in super::bar) trait Bar {} + // impl () {} + // impl (i32) {} + let m = p.start(); + p.bump(T![impl]); + if !opt_visibility_inner(p, false) { + p.error("expected an impl restriction"); + } + m.complete(p, IMPL_RESTRICTION); + has_mods = true; + } + // test default_item // default impl T for Foo {} if p.at_contextual_kw(T![default]) { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index cfba4c3a77b2a..a030190ad34ba 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -133,7 +133,30 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { // struct S { #[attr] f: f32 } attributes::outer_attrs(p); opt_visibility(p, false); - p.eat(T![unsafe]); + + if p.at(T![mut]) && p.nth(1) == T!['('] { + // test record_mut_restrictions_before + // struct Foo { mut(super) unsafe i: i32 } + let m = p.start(); + p.bump(T![mut]); + if !opt_visibility_inner(p, false) { + p.error("expected a mut restriction"); + } + m.complete(p, MUT_RESTRICTION); + } + + // We accept mut restriction both after and before `unsafe`, as the order is undecided yet. + if p.eat(T![unsafe]) && p.at(T![mut]) && p.nth(1) == T!['('] { + // test record_mut_restrictions_after + // struct Foo { unsafe mut(super) i: i32 } + let m = p.start(); + p.bump(T![mut]); + if !opt_visibility_inner(p, false) { + p.error("expected a mut restriction"); + } + m.complete(p, MUT_RESTRICTION); + } + if p.at(IDENT) { name(p); p.expect(T![:]); @@ -175,6 +198,18 @@ fn tuple_field_list(p: &mut Parser<'_>) { // struct S (#[attr] f32); attributes::outer_attrs(p); let has_vis = opt_visibility(p, true); + + if p.at(T![mut]) && p.nth(1) == T!['('] { + // test tuple_mut_restrictions + // struct Foo(pub(crate) mut(super) i32); + let m = p.start(); + p.bump(T![mut]); + if !opt_visibility_inner(p, false) { + p.error("expected a mut restriction"); + } + m.complete(p, MUT_RESTRICTION); + } + if !p.at_ts(types::TYPE_FIRST) { p.error("expected a type"); if has_vis { diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 9cd48f2aa4f3e..59fa3ee773882 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -218,6 +218,7 @@ pub enum SyntaxKind { IDENT_PAT, IF_EXPR, IMPL, + IMPL_RESTRICTION, IMPL_TRAIT_TYPE, INDEX_EXPR, INFER_TYPE, @@ -247,6 +248,7 @@ pub enum SyntaxKind { MATCH_GUARD, METHOD_CALL_EXPR, MODULE, + MUT_RESTRICTION, NAME, NAME_REF, NEVER_TYPE, @@ -318,6 +320,7 @@ pub enum SyntaxKind { VARIANT, VARIANT_LIST, VISIBILITY, + VISIBILITY_INNER, WHERE_CLAUSE, WHERE_PRED, WHILE_EXPR, @@ -399,6 +402,7 @@ impl SyntaxKind { | IDENT_PAT | IF_EXPR | IMPL + | IMPL_RESTRICTION | IMPL_TRAIT_TYPE | INDEX_EXPR | INFER_TYPE @@ -428,6 +432,7 @@ impl SyntaxKind { | MATCH_GUARD | METHOD_CALL_EXPR | MODULE + | MUT_RESTRICTION | NAME | NAME_REF | NEVER_TYPE @@ -499,6 +504,7 @@ impl SyntaxKind { | VARIANT | VARIANT_LIST | VISIBILITY + | VISIBILITY_INNER | WHERE_CLAUSE | WHERE_PRED | WHILE_EXPR 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 71978390df6a8..10b1f85d39113 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 @@ -348,6 +348,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/impl_item_never_type.rs"); } #[test] + fn impl_restrictions() { + run_and_expect_no_errors("test_data/parser/inline/ok/impl_restrictions.rs"); + } + #[test] fn impl_trait_type() { run_and_expect_no_errors("test_data/parser/inline/ok/impl_trait_type.rs"); } @@ -556,6 +560,14 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_literal_field_with_attr.rs"); } #[test] + fn record_mut_restrictions_after() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_after.rs"); + } + #[test] + fn record_mut_restrictions_before() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_before.rs"); + } + #[test] fn record_pat_field() { run_and_expect_no_errors("test_data/parser/inline/ok/record_pat_field.rs"); } @@ -658,6 +670,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_field_attrs.rs"); } #[test] + fn tuple_mut_restrictions() { + run_and_expect_no_errors("test_data/parser/inline/ok/tuple_mut_restrictions.rs"); + } + #[test] fn tuple_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_pat.rs"); } #[test] fn tuple_pat_fields() { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast index 172bc099b58d0..37116ca895be6 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast @@ -2,8 +2,9 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast index 8738292a9f7fe..c946f19321e58 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast @@ -2,12 +2,13 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - CRATE_KW "crate" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + CRATE_KW "crate" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " @@ -18,12 +19,13 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - SELF_KW "self" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SELF_KW "self" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " @@ -34,12 +36,13 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - SUPER_KW "super" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast index ac45c56956790..1a551ea2212fe 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast @@ -2,19 +2,20 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - IN_KW "in" - WHITESPACE " " - PATH + VISIBILITY_INNER + L_PAREN "(" + IN_KW "in" + WHITESPACE " " PATH + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + COLON2 "::" PATH_SEGMENT NAME_REF - SUPER_KW "super" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "A" - R_PAREN ")" + IDENT "A" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " @@ -25,14 +26,15 @@ SOURCE_FILE STRUCT VISIBILITY PUB_KW "pub" - L_PAREN "(" - IN_KW "in" - WHITESPACE " " - PATH - PATH_SEGMENT - NAME_REF - CRATE_KW "crate" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + IN_KW "in" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + CRATE_KW "crate" + R_PAREN ")" WHITESPACE " " STRUCT_KW "struct" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast new file mode 100644 index 0000000000000..5f2680cbaa921 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast @@ -0,0 +1,80 @@ +SOURCE_FILE + TRAIT + VISIBILITY + PUB_KW "pub" + WHITESPACE " " + UNSAFE_KW "unsafe" + WHITESPACE " " + IMPL_RESTRICTION + IMPL_KW "impl" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + CRATE_KW "crate" + R_PAREN ")" + WHITESPACE " " + TRAIT_KW "trait" + WHITESPACE " " + NAME + IDENT "Foo" + WHITESPACE " " + ASSOC_ITEM_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + TRAIT + IMPL_RESTRICTION + IMPL_KW "impl" + VISIBILITY_INNER + L_PAREN "(" + IN_KW "in" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "bar" + R_PAREN ")" + WHITESPACE " " + TRAIT_KW "trait" + WHITESPACE " " + NAME + IDENT "Bar" + WHITESPACE " " + ASSOC_ITEM_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + IMPL + IMPL_KW "impl" + WHITESPACE " " + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + ASSOC_ITEM_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + IMPL + IMPL_KW "impl" + WHITESPACE " " + PAREN_TYPE + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + WHITESPACE " " + ASSOC_ITEM_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs new file mode 100644 index 0000000000000..0a46b158affc9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs @@ -0,0 +1,4 @@ +pub unsafe impl(crate) trait Foo {} +impl(in super::bar) trait Bar {} +impl () {} +impl (i32) {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast new file mode 100644 index 0000000000000..ebe3c81468322 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast @@ -0,0 +1,35 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "Foo" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_FIELD + UNSAFE_KW "unsafe" + WHITESPACE " " + MUT_RESTRICTION + MUT_KW "mut" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + R_PAREN ")" + WHITESPACE " " + NAME + IDENT "i" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs new file mode 100644 index 0000000000000..bf37f60f85442 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs @@ -0,0 +1 @@ +struct Foo { unsafe mut(super) i: i32 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast new file mode 100644 index 0000000000000..7f76e737f7eef --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast @@ -0,0 +1,35 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "Foo" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_FIELD + MUT_RESTRICTION + MUT_KW "mut" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + R_PAREN ")" + WHITESPACE " " + UNSAFE_KW "unsafe" + WHITESPACE " " + NAME + IDENT "i" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs new file mode 100644 index 0000000000000..9bbb80f205a06 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs @@ -0,0 +1 @@ +struct Foo { mut(super) unsafe i: i32 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast new file mode 100644 index 0000000000000..944133ff8551c --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast @@ -0,0 +1,37 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "Foo" + TUPLE_FIELD_LIST + L_PAREN "(" + TUPLE_FIELD + VISIBILITY + PUB_KW "pub" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + CRATE_KW "crate" + R_PAREN ")" + WHITESPACE " " + MUT_RESTRICTION + MUT_KW "mut" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + R_PAREN ")" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs new file mode 100644 index 0000000000000..42653b0043b65 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs @@ -0,0 +1 @@ +struct Foo(pub(crate) mut(super) i32); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0012_visibility.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0012_visibility.rast index 3d9322947b35a..348498daa990b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0012_visibility.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0012_visibility.rast @@ -52,12 +52,13 @@ SOURCE_FILE FN VISIBILITY PUB_KW "pub" - L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - CRATE_KW "crate" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + CRATE_KW "crate" + R_PAREN ")" WHITESPACE " " FN_KW "fn" WHITESPACE " " @@ -75,12 +76,13 @@ SOURCE_FILE FN VISIBILITY PUB_KW "pub" - L_PAREN "(" - PATH - PATH_SEGMENT - NAME_REF - SUPER_KW "super" - R_PAREN ")" + VISIBILITY_INNER + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + SUPER_KW "super" + R_PAREN ")" WHITESPACE " " FN_KW "fn" WHITESPACE " " @@ -98,24 +100,25 @@ SOURCE_FILE FN VISIBILITY PUB_KW "pub" - L_PAREN "(" - IN_KW "in" - WHITESPACE " " - PATH + VISIBILITY_INNER + L_PAREN "(" + IN_KW "in" + WHITESPACE " " PATH PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "foo" + IDENT "bar" COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "bar" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "baz" - R_PAREN ")" + IDENT "baz" + R_PAREN ")" WHITESPACE " " FN_KW "fn" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 324b2bbd58e1c..caf92aca87744 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -277,6 +277,7 @@ RecordFieldList = RecordField = Attr* Visibility? 'unsafe'? + MutRestriction? Name ':' Type ('=' default_val:ConstArg)? TupleFieldList = @@ -284,12 +285,16 @@ TupleFieldList = TupleField = Attr* Visibility? + MutRestriction? Type FieldList = RecordFieldList | TupleFieldList +MutRestriction = + 'mut' VisibilityInner + Enum = Attr* Visibility? 'enum' Name GenericParamList? WhereClause? @@ -336,10 +341,14 @@ Static = Trait = Attr* Visibility? 'unsafe'? 'auto'? + ImplRestriction? 'trait' Name GenericParamList? (((':' TypeBoundList?)? WhereClause? AssocItemList) | ('=' TypeBoundList? WhereClause? ';')) +ImplRestriction = + 'impl' VisibilityInner + AssocItemList = '{' Attr* AssocItem* '}' @@ -368,8 +377,10 @@ ExternItem = | TypeAlias Visibility = - 'pub' ('(' 'in'? Path ')')? + 'pub' VisibilityInner? +VisibilityInner = + '(' 'in'? Path ')' //****************************// // Statements and Expressions // diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index cd7f6a018ab2b..69fd703c0398e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -808,6 +808,15 @@ impl Impl { #[inline] pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } +pub struct ImplRestriction { + pub(crate) syntax: SyntaxNode, +} +impl ImplRestriction { + #[inline] + pub fn visibility_inner(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn impl_token(&self) -> Option { support::token(&self.syntax, T![impl]) } +} pub struct ImplTraitType { pub(crate) syntax: SyntaxNode, } @@ -1114,6 +1123,15 @@ impl Module { #[inline] pub fn mod_token(&self) -> Option { support::token(&self.syntax, T![mod]) } } +pub struct MutRestriction { + pub(crate) syntax: SyntaxNode, +} +impl MutRestriction { + #[inline] + pub fn visibility_inner(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn mut_token(&self) -> Option { support::token(&self.syntax, T![mut]) } +} pub struct Name { pub(crate) syntax: SyntaxNode, } @@ -1400,6 +1418,8 @@ impl RecordField { #[inline] pub fn default_val(&self) -> Option { support::child(&self.syntax) } #[inline] + pub fn mut_restriction(&self) -> Option { support::child(&self.syntax) } + #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) } @@ -1690,6 +1710,8 @@ impl Trait { #[inline] pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } #[inline] + pub fn impl_restriction(&self) -> Option { support::child(&self.syntax) } + #[inline] pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } #[inline] pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } @@ -1742,6 +1764,8 @@ impl ast::HasAttrs for TupleField {} impl ast::HasDocComments for TupleField {} impl ast::HasVisibility for TupleField {} impl TupleField { + #[inline] + pub fn mut_restriction(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } } @@ -2000,6 +2024,15 @@ pub struct Visibility { pub(crate) syntax: SyntaxNode, } impl Visibility { + #[inline] + pub fn visibility_inner(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pub_token(&self) -> Option { support::token(&self.syntax, T![pub]) } +} +pub struct VisibilityInner { + pub(crate) syntax: SyntaxNode, +} +impl VisibilityInner { #[inline] pub fn path(&self) -> Option { support::child(&self.syntax) } #[inline] @@ -2008,8 +2041,6 @@ impl Visibility { pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } #[inline] pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } - #[inline] - pub fn pub_token(&self) -> Option { support::token(&self.syntax, T![pub]) } } pub struct WhereClause { pub(crate) syntax: SyntaxNode, @@ -4192,6 +4223,38 @@ impl fmt::Debug for Impl { f.debug_struct("Impl").field("syntax", &self.syntax).finish() } } +impl AstNode for ImplRestriction { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IMPL_RESTRICTION + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_RESTRICTION } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for ImplRestriction { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for ImplRestriction {} +impl PartialEq for ImplRestriction { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for ImplRestriction { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for ImplRestriction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ImplRestriction").field("syntax", &self.syntax).finish() + } +} impl AstNode for ImplTraitType { #[inline] fn kind() -> SyntaxKind @@ -5120,6 +5183,38 @@ impl fmt::Debug for Module { f.debug_struct("Module").field("syntax", &self.syntax).finish() } } +impl AstNode for MutRestriction { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MUT_RESTRICTION + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == MUT_RESTRICTION } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for MutRestriction { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for MutRestriction {} +impl PartialEq for MutRestriction { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for MutRestriction { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for MutRestriction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MutRestriction").field("syntax", &self.syntax).finish() + } +} impl AstNode for Name { #[inline] fn kind() -> SyntaxKind @@ -7392,6 +7487,38 @@ impl fmt::Debug for Visibility { f.debug_struct("Visibility").field("syntax", &self.syntax).finish() } } +impl AstNode for VisibilityInner { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VISIBILITY_INNER + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY_INNER } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for VisibilityInner { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for VisibilityInner {} +impl PartialEq for VisibilityInner { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for VisibilityInner { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for VisibilityInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("VisibilityInner").field("syntax", &self.syntax).finish() + } +} impl AstNode for WhereClause { #[inline] fn kind() -> SyntaxKind @@ -10092,6 +10219,11 @@ impl std::fmt::Display for Impl { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ImplRestriction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ImplTraitType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10237,6 +10369,11 @@ impl std::fmt::Display for Module { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for MutRestriction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10592,6 +10729,11 @@ impl std::fmt::Display for Visibility { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for VisibilityInner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for WhereClause { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) 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 03118d01dc90a..751f8d7e1cbe1 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 @@ -947,6 +947,15 @@ pub enum VisibilityKind { } impl ast::Visibility { + pub fn kind(&self) -> VisibilityKind { + match self.visibility_inner() { + Some(inner) => inner.kind(), + None => VisibilityKind::Pub, + } + } +} + +impl ast::VisibilityInner { pub fn kind(&self) -> VisibilityKind { match self.path() { Some(path) => { diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index 485140be8f69c..4622590656e4c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -240,8 +240,16 @@ fn validate_numeric_name(name_ref: Option, errors: &mut Vec) { - let path_without_in_token = vis.in_token().is_none() - && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some(); + let path_without_in_token = if let Some(inner) = vis.visibility_inner() { + inner.in_token().is_none() + && inner + .path() + .and_then(|p| p.as_single_name_ref()) + .and_then(|n| n.ident_token()) + .is_some() + } else { + false + }; if path_without_in_token { errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range())); } diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast index 90c258cd1a6c4..2d6d4b2681354 100644 --- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast @@ -51,12 +51,13 @@ SOURCE_FILE@0..118 TYPE_ALIAS@56..81 VISIBILITY@56..66 PUB_KW@56..59 "pub" - L_PAREN@59..60 "(" - PATH@60..65 - PATH_SEGMENT@60..65 - NAME_REF@60..65 - CRATE_KW@60..65 "crate" - R_PAREN@65..66 ")" + VISIBILITY_INNER@59..66 + L_PAREN@59..60 "(" + PATH@60..65 + PATH_SEGMENT@60..65 + NAME_REF@60..65 + CRATE_KW@60..65 "crate" + R_PAREN@65..66 ")" WHITESPACE@66..67 " " TYPE_KW@67..71 "type" WHITESPACE@71..72 " " @@ -73,12 +74,13 @@ SOURCE_FILE@0..118 CONST@86..115 VISIBILITY@86..96 PUB_KW@86..89 "pub" - L_PAREN@89..90 "(" - PATH@90..95 - PATH_SEGMENT@90..95 - NAME_REF@90..95 - CRATE_KW@90..95 "crate" - R_PAREN@95..96 ")" + VISIBILITY_INNER@89..96 + L_PAREN@89..90 "(" + PATH@90..95 + PATH_SEGMENT@90..95 + NAME_REF@90..95 + CRATE_KW@90..95 "crate" + R_PAREN@95..96 ")" WHITESPACE@96..97 " " CONST_KW@97..102 "const" WHITESPACE@102..103 " " From 9cd6a392e3844c29fdf75c65e4263b5d0eac9ad9 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 13 Apr 2026 13:36:10 +0100 Subject: [PATCH 09/98] internal: Fix rustdoc quoting GitHub actions are currently failing with: error: unresolved link to `cfg_attr` --> crates/ide-completion/src/context.rs:414:61 | 414 | /// Set if we are inside the predicate of a #[cfg] or #[cfg_attr]. | ^^^^^^^^ no item named `cfg_attr` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: `-D rustdoc::broken-intra-doc-links` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rustdoc::broken_intra_doc_links)]` Documenting ide v0.0.0 (/home/runner/work/rust-analyzer/rust-analyzer/crates/ide) error: could not document `ide-completion` Use backticks to fix this. AI disclosure: written with Claude Opus because it was marginally faster. --- src/tools/rust-analyzer/crates/ide-completion/src/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 485e5f0cafd7f..a9f9f7997cc0b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -411,7 +411,7 @@ pub(crate) enum CompletionAnalysis<'db> { fake_attribute_under_caret: Option, extern_crate: Option, }, - /// Set if we are inside the predicate of a #[cfg] or #[cfg_attr]. + /// Set if we are inside the predicate of a `#[cfg]` or `#[cfg_attr]`. CfgPredicate, MacroSegment, } From 1ba2ec7fb1b528a084a2bd181916b0f697c67e93 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 13 Apr 2026 15:26:22 +0100 Subject: [PATCH 10/98] fix: MIR evaluation of sized &T with recursive const fn Previously, we didn't recurse in Evaluator::create_memory_map() or Evaluator::patch_addresses() when handling references with a known size. This caused problems for hover rendering on const values, which would build a memory map for evaluated allocations and then pretty-printed values by following references through that map. As a result, references in the map still pointed to the original addresses, meaning that the value pretty-printer would see the type parameters before substitution. This could cause stack overflows on well-formed Rust code using recursive const functions. Add a regression test for hovering on a recursive const fn that previously caused stack overflow. Fixes rust-lang/rust-analyzer#21503 AI disclosure: I used Codex with GPT 5.4 to investigate the issue and write the initial version of this commit. --- .../crates/hir-ty/src/mir/eval.rs | 25 ++++++--- .../crates/ide/src/hover/tests.rs | 52 +++++++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) 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 505db1776f280..88376f14d1ed3 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 @@ -2211,10 +2211,10 @@ impl<'db> Evaluator<'db> { match size { Some((size, _)) => { let addr_usize = from_bytes!(usize, bytes); - mm.insert( - addr_usize, - this.read_memory(Address::from_usize(addr_usize), size)?.into(), - ) + let bytes = + this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(); + mm.insert(addr_usize, bytes.clone().into()); + rec(this, &bytes, t, locals, mm, stack_depth_limit - 1)?; } None => { let mut check_inner = None; @@ -2379,9 +2379,20 @@ impl<'db> Evaluator<'db> { match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); - if let Some(it) = patch_map.get(¤t) { - self.write_memory(addr, &it.to_le_bytes())?; - } + let patched = match patch_map.get(¤t) { + Some(it) => { + self.write_memory(addr, &it.to_le_bytes())?; + *it + } + None => current, + }; + self.patch_addresses( + patch_map, + ty_of_bytes, + Address::from_usize(patched), + t, + locals, + )?; } None => { let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); 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 9c53b05539e27..6f6a4b3e0c65f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -11078,6 +11078,58 @@ impl PublicFlags for NoteDialects { ); } +#[test] +fn hover_recursive_const_fn() { + check( + r#" +//- minicore: option +enum Child { + Static { child: &'static MyEnum }, +} + +enum MyEnum { + Unit, + Array(Child), +} + +impl MyEnum { + pub const fn static_array(child: &'static MyEnum) -> Self { + MyEnum::Array(Child::Static { child }) + } +} + +pub trait MyTrait { + const MY_CONST: &'static MyEnum; +} + +impl MyTrait for Option where T: MyTrait { + const MY_CONST: &'static MyEnum = &MyEnum::static_array(T::MY_CONST); +} + +impl MyTrait for () { + const MY_CONST: &'static MyEnum = &MyEnum::Unit; +} + +pub struct Address; + +impl MyTrait for Address { + const MY_CONST$0: &'static MyEnum = ( as MyTrait>::MY_CONST); +} + "#, + expect![[r#" + *MY_CONST* + + ```rust + ra_test_fixture::Address + ``` + + ```rust + const MY_CONST: &'static MyEnum = &Array(Static { child: &Unit }) + ``` + "#]], + ); +} + #[test] fn bounds_from_container_do_not_panic() { check( From b198955c9012fa08d3d2a0cb1f59c76b6cd46fef Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 13 Apr 2026 15:09:42 +0100 Subject: [PATCH 11/98] internal: Ensure rustdoc GitHub action runs on PRs We want to complain on PRs that generate rustdoc warnings, but only deploy the generate HTML on the master branch. Also fix the existing rustdoc warning. AI disclosure: Some Claude Opus usage. --- src/tools/rust-analyzer/.github/workflows/rustdoc.yaml | 3 +++ src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml index 03fd083175017..c5588a29f6761 100644 --- a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml +++ b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml @@ -3,6 +3,8 @@ on: push: branches: - master + pull_request: + merge_group: env: CARGO_INCREMENTAL: 0 @@ -28,6 +30,7 @@ jobs: run: cargo doc --all --no-deps --document-private-items - name: Deploy Docs + if: github.event_name == 'push' && github.repository == 'rust-lang/rust-analyzer' && github.ref == 'refs/heads/master' uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 49baecb90cd50..d1f962f7ffd38 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -92,7 +92,7 @@ impl AstKeyValueMetaExt for ast::KeyValueMeta { } /// The callback is passed the attribute and the outermost `ast::Attr`. -/// Note that one node may map to multiple [`Meta`]s due to `cfg_attr`. +/// Note that one node may map to multiple [`ast::Meta`]s due to `cfg_attr`. /// /// `unsafe(attr)` are passed the inner attribute for now. #[inline] From 92da3d3479aa356412b7f69b269a8a5110eb89ab Mon Sep 17 00:00:00 2001 From: Amit Singhmar Date: Thu, 9 Apr 2026 15:56:14 +0000 Subject: [PATCH 12/98] fix: complete variants of hidden enums through public aliases Fixes rust-lang/rust-analyzer#21891. Previously, the completion engine would aggressively bail out if the parent enum was marked . This pushes the visibility check down to the individual variants so they can be properly completed when accessed via a public type alias. --- .../crates/ide-completion/src/completions.rs | 3 --- .../crates/ide-completion/src/tests/item.rs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 4a94383ff4cbd..8e2ef2b2ab7f9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -276,9 +276,6 @@ impl Completions { path_ctx: &PathCompletionCtx<'_>, e: hir::Enum, ) { - if !ctx.check_stability_and_hidden(e) { - return; - } e.variants(ctx.db) .into_iter() .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None)); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index 2f032c3f4ca56..45024ad21638c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -380,3 +380,23 @@ foo!(f$0); "#]], ); } + +#[test] +fn completes_variant_through_hidden_enum_alias() { + check( + r#" +//- /lib.rs crate:dep +#[doc(hidden)] +pub enum Foo { Variant } +pub type Bar = Foo; + +//- /main.rs crate:main deps:dep +fn main() { + let x = dep::Bar::V$0; +} +"#, + expect![[r#" + ev Variant Variant + "#]], + ); +} From 482a3597ca8f4a5c719e61f598e66e1da296a40f Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Tue, 14 Apr 2026 00:24:08 +0800 Subject: [PATCH 13/98] Added a field `has_local_inherent_impl` to `CompletionRelevance`. --- .../crates/ide-completion/src/item.rs | 7 ++ .../crates/ide-completion/src/render.rs | 78 ++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) 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 e6dd1d37d9335..62211a808ca6f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -184,6 +184,8 @@ pub struct CompletionRelevance { pub function: Option, /// true when there is an `await.method()` or `iter().method()` completion. pub is_skipping_completion: bool, + /// if inherent impl already exists in current module, user may not want to implement it again. + pub has_local_inherent_impl: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -275,6 +277,7 @@ impl CompletionRelevance { trait_, function, is_skipping_completion, + has_local_inherent_impl, } = self; // only applicable for completions within use items @@ -347,6 +350,10 @@ impl CompletionRelevance { score += fn_score; }; + if has_local_inherent_impl { + score -= 5; + } + score } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index b6da6fba638fc..3b9d75b15f062 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -10,7 +10,7 @@ pub(crate) mod type_alias; pub(crate) mod union_literal; pub(crate) mod variant; -use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; +use hir::{AsAssocItem, HasAttrs, HirDisplay, Impl, ModuleDef, ScopeDef, Type}; use ide_db::text_edit::TextEdit; use ide_db::{ RootDatabase, SnippetCap, SymbolKind, @@ -23,7 +23,9 @@ use syntax::{AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr, ast, format_smo use crate::{ CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, - context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext}, + context::{ + DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext, TypeLocation, + }, item::{Builder, CompletionRelevanceTypeMatch}, render::{ function::render_fn, @@ -422,6 +424,7 @@ fn render_resolution_path( } let completion = ctx.completion; + let module = completion.module; let cap = ctx.snippet_cap(); let db = completion.db; let config = completion.config; @@ -466,6 +469,7 @@ fn render_resolution_path( exact_name_match: compute_exact_name_match(completion, &name), is_local: matches!(resolution, ScopeDef::Local(_)), requires_import, + has_local_inherent_impl: compute_has_local_inherent_impl(db, path_ctx, &ty, module), ..CompletionRelevance::default() }); @@ -660,6 +664,18 @@ fn compute_type_match( match_types(ctx, expected_type, completion_ty) } +fn compute_has_local_inherent_impl( + db: &RootDatabase, + path_ctx: &PathCompletionCtx<'_>, + completion_ty: &hir::Type<'_>, + curr_module: hir::Module, +) -> bool { + matches!(path_ctx.kind, PathKind::Type { location: TypeLocation::ImplTarget }) + && Impl::all_for_type(db, completion_ty.clone()) + .iter() + .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module) +} + fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) } @@ -832,6 +848,7 @@ mod tests { ), (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), + (relevance.has_local_inherent_impl, "has_local_inherent_impl"), ] .into_iter() .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) @@ -1214,6 +1231,7 @@ fn main() { Foo::Fo$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1264,6 +1282,7 @@ fn main() { Foo::Fo$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1407,6 +1426,7 @@ fn main() { Foo::Fo$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1490,6 +1510,7 @@ fn main() { let _: m::Spam = S$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1526,6 +1547,7 @@ fn main() { let _: m::Spam = S$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1616,6 +1638,7 @@ fn foo() { A { the$0 } } postfix_match: None, function: None, is_skipping_completion: false, + has_local_inherent_impl: false, }, }, ] @@ -1675,6 +1698,7 @@ impl S { }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, }, CompletionItem { @@ -1766,6 +1790,7 @@ use self::E::*; }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, trigger_call_info: true, }, @@ -1836,6 +1861,7 @@ fn foo(s: S) { s.$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, }, ] @@ -2048,6 +2074,7 @@ fn f() -> i32 { postfix_match: None, function: None, is_skipping_completion: false, + has_local_inherent_impl: false, }, }, ] @@ -2193,6 +2220,48 @@ fn f() { ); } + #[test] + fn score_has_local_inherent_impl() { + check_relevance( + r#" +trait Foob {} +struct Fooa {} +impl Fooa {} + +impl Foo$0 +"#, + expect![[r#" + tt Foob [] + st Fooa Fooa [has_local_inherent_impl] + "#]], + ); + + // inherent impl in different modules, not trigger `has_local_inherent_impl` + check_relevance( + r#" +trait Foob {} +struct Fooa {} + +mod a { + use super::*; + impl Fooa {} +} + +mod b { + use super::*; + impl Foo$0 +} + +"#, + expect![[r#" + st Fooa Fooa [] + tt Foob [] + md a [] + md b [] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -2861,6 +2930,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, ref_match: "&@107", }, @@ -2948,6 +3018,7 @@ fn foo() { postfix_match: None, function: None, is_skipping_completion: false, + has_local_inherent_impl: false, }, }, ] @@ -3006,6 +3077,7 @@ fn main() { }, ), is_skipping_completion: false, + has_local_inherent_impl: false, }, ref_match: "&@92", }, @@ -3476,6 +3548,7 @@ fn main() { postfix_match: None, function: None, is_skipping_completion: false, + has_local_inherent_impl: false, }, }, CompletionItem { @@ -3510,6 +3583,7 @@ fn main() { postfix_match: None, function: None, is_skipping_completion: false, + has_local_inherent_impl: false, }, }, ] From 063763fb49b12d3d962f040fbdacb29e77d1a703 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 9 Apr 2026 20:41:44 +0800 Subject: [PATCH 14/98] fix: Fix ref_match position when keyword prefix This is a quick fix, please review if there is a regression Example --- ```rust fn foo(data: &i32) {} fn main() { let indent = 2i32; foo(in$0) } ``` **Before this PR** ```rust fn foo(data: &i32) {} fn main() { let indent = 2i32; foo(in&dent) } ``` ```rust source_range: 65..67, delete: 65..67, ref_match: "&@67", .. ``` **After this PR** ```rust fn foo(data: &i32) {} fn main() { let indent = 2i32; foo(&indent) } ``` ```rust source_range: 65..67, delete: 65..67, ref_match: "&@65", .. ``` --- .../crates/ide-completion/src/render.rs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 3b9d75b15f062..f14fd9221f454 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -733,7 +733,7 @@ fn path_ref_match( // FIXME: This might create inconsistent completions where we show a ref match in macro inputs // as long as nothing was typed yet if let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, completion.position.offset); + item.ref_match(ref_mode, completion.source_range().start()); } } } @@ -2142,6 +2142,53 @@ fn go(world: &WorldSnapshot) { go(w$0) } ); } + #[test] + fn complete_ref_match_after_keyword_prefix() { + // About https://github.com/rust-lang/rust-analyzer/issues/15139 + check_kinds( + r#" +fn foo(data: &i32) {} +fn main() { + let indent = 2i32; + foo(in$0) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Local)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 65..67, + delete: 65..67, + insert: "indent", + kind: SymbolKind( + Local, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: true, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + }, + ref_match: "&@65", + }, + ] + "#]], + ); + } + #[test] fn too_many_arguments() { cov_mark::check!(too_many_arguments); From 536ce324d06f815b143f68669e9853ccbd4ac444 Mon Sep 17 00:00:00 2001 From: mehmet-yalcinkaya_volvo Date: Wed, 18 Mar 2026 16:31:36 +0100 Subject: [PATCH 15/98] feat: add is_mutable_raw_ptr and as_raw_ptr to hir::Type --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 2829902035985..7a4085c4746c9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5818,6 +5818,18 @@ impl<'db> Type<'db> { matches!(self.ty.kind(), TyKind::RawPtr(..)) } + pub fn is_mutable_raw_ptr(&self) -> bool { + // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). + matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut)) + } + + pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> { + // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). + let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None }; + let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); + Some((self.derived(ty), m)) + } + pub fn remove_raw_ptr(&self) -> Option> { if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None } } From 2fc39617589f3dc2d3c84645d5427aaf11ab6a75 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 14 Apr 2026 02:40:51 +0300 Subject: [PATCH 16/98] Minor simplification --- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 e6b8329ca861a..5086be7330411 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -62,7 +62,7 @@ use std::{hash::Hash, ops::ControlFlow}; use hir_def::{ CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId, - TypeParamId, hir::generics::GenericParams, resolver::TypeNs, type_ref::Rawness, + TypeParamId, resolver::TypeNs, type_ref::Rawness, }; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; @@ -495,10 +495,7 @@ pub fn associated_type_shorthand_candidates( TypeNs::GenericParam(param) => (def, param), TypeNs::SelfType(impl_) => { let impl_trait = db.impl_trait(impl_)?.skip_binder().def_id.0; - let param = TypeParamId::from_unchecked(TypeOrConstParamId { - parent: impl_trait.into(), - local_id: GenericParams::SELF_PARAM_ID_IN_SELF, - }); + let param = TypeParamId::trait_self(impl_trait); (impl_trait.into(), param) } _ => return None, From 69716009f7dba68fcc5ca51d7722daa081c6b74b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 14 Apr 2026 06:37:50 +0300 Subject: [PATCH 17/98] Ignore a hangy test --- .../crates/rust-analyzer/tests/slow-tests/flycheck.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index c6f1f81139d28..e5d4d7c88e803 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -112,6 +112,7 @@ fn main() {} } #[test] +#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { if skip_slow_tests() { return; From 89abb12593c5afe3c8679db31ad188838fef9b1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 03:38:00 +0000 Subject: [PATCH 18/98] build(deps): bump rand from 0.9.2 to 0.9.3 Bumps [rand](https://github.com/rust-random/rand) from 0.9.2 to 0.9.3. - [Release notes](https://github.com/rust-random/rand/releases) - [Changelog](https://github.com/rust-random/rand/blob/0.9.3/CHANGELOG.md) - [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.2...0.9.3) --- updated-dependencies: - dependency-name: rand dependency-version: 0.9.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/lib/smol_str/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index da530b3a9304d..cfc0932a22e3a 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2174,9 +2174,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" dependencies = [ "rand_chacha", "rand_core", diff --git a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml index 22068fe8418b4..719462bc9a5d9 100644 --- a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml +++ b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml @@ -22,7 +22,7 @@ proptest = "1.5" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } criterion = "0.7" -rand = "0.9.2" +rand = "0.9.3" [features] default = ["std"] From 0f4f33b1d476cd40f66e27a79aa0eba2006b8cf0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 14 Apr 2026 02:25:11 +0300 Subject: [PATCH 19/98] Allow ambiguity in assoc type shorthand if they resolve to the same assoc type, between supertraits this time --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 12 +++++--- .../crates/hir-ty/src/tests/regression.rs | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) 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 71a7db6559a86..a7b159a5c5278 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1869,10 +1869,14 @@ fn resolve_type_param_assoc_type_shorthand( .skip_binder(); let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); - if let Some(this_trait_resolution) = this_trait_resolution { - return AssocTypeShorthandResolution::Ambiguous { - sub_trait_resolution: Some(this_trait_resolution), - }; + if let Some(this_trait_resolution) = &this_trait_resolution { + if *this_trait_resolution == current_result { + continue; + } else { + return AssocTypeShorthandResolution::Ambiguous { + sub_trait_resolution: Some(this_trait_resolution.clone()), + }; + } } else if let Some(prev_resolution) = &supertraits_resolution { if let AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: Some(prev_resolution), 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 d3dfc44c227f9..a559ad9b91d68 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 @@ -2856,3 +2856,31 @@ fn foo(v: T::T) {} "#, ); } + +#[test] +fn regression_() { + check_types( + r#" +//- minicore: fn +trait Super { + type Assoc; + fn foo(self) -> Self::Assoc + where + Self: Sub, + { loop {} } +} +trait Sub: Super {} + +struct Struct; +impl Super for Struct { + type Assoc = u8; +} +impl Sub for Struct {} + +fn foo() { + Struct.foo(); + // ^^^^^^^^^^^^ u8 +} + "#, + ); +} From d5146d345e25a459f50529a5f6ab8ff29724a754 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 13:31:41 +0530 Subject: [PATCH 20/98] remove set visibility method of HasVisibility --- .../crates/syntax/src/ast/edit_in_place.rs | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 7f59ae4213829..3ed8f2991caf7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -833,31 +833,6 @@ impl ast::IdentPat { } } -pub trait HasVisibilityEdit: ast::HasVisibility { - fn set_visibility(&self, visibility: Option) { - if let Some(visibility) = visibility { - match self.visibility() { - Some(current_visibility) => { - ted::replace(current_visibility.syntax(), visibility.syntax()) - } - None => { - let vis_before = self - .syntax() - .children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) - .unwrap_or_else(|| self.syntax().first_child_or_token().unwrap()); - - ted::insert(ted::Position::before(vis_before), visibility.syntax()); - } - } - } else if let Some(visibility) = self.visibility() { - ted::remove(visibility.syntax()); - } - } -} - -impl HasVisibilityEdit for T {} - pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) From 42c6a7b2694b2fffe55d23816e1471d9840ef241 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 14:11:53 +0530 Subject: [PATCH 21/98] add type_bound_text in constructor --- .../crates/syntax/src/ast/syntax_factory/constructors.rs | 4 ++++ 1 file changed, 4 insertions(+) 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 c66f096e8342c..c92f7ec6f3adb 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 @@ -67,6 +67,10 @@ impl SyntaxFactory { make::type_bound(bound).clone_for_update() } + pub fn type_bound_text(&self, bound: &str) -> ast::TypeBound { + make::type_bound_text(bound).clone_for_update() + } + pub fn type_bound_list( &self, bounds: impl IntoIterator, From 6c84b2b178e6da8a244c6de3700afade27619b62 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 14:13:01 +0530 Subject: [PATCH 22/98] migrate generate blanket trait impl from make to SyntaxFactory --- .../handlers/generate_blanket_trait_impl.rs | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) 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 fccc04770e897..7a097e1866ef6 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 @@ -13,7 +13,7 @@ use syntax::{ AstNode, ast::{ self, AssocItem, GenericParam, HasAttrs, HasGenericParams, HasName, HasTypeBounds, - HasVisibility, edit::AstNodeEdit, make, + HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory, }, syntax_editor::Position, }; @@ -73,24 +73,25 @@ pub(crate) fn generate_blanket_trait_impl( "Generate blanket trait implementation", name.syntax().text_range(), |builder| { - let mut edit = builder.make_editor(traitd.syntax()); - let namety = make::ty_path(make::path_from_text(&name.text())); + let mut editor = builder.make_editor(traitd.syntax()); + let make = SyntaxFactory::with_mappings(); + 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(exlucde_sized); + 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(&traitd); - let thisty = make::ty_path(make::path_from_text(&thisname.text())); + let thisname = this_name(&make, &traitd); + 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( + let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param( thisname.clone(), - apply_sized(has_sized(&traitd, &ctx.sema), bounds), + apply_sized(&make, has_sized(&traitd, &ctx.sema), bounds), ))]); let trait_gen_args = traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); - let impl_ = make::impl_trait( + let impl_ = make.impl_trait( cfg_attrs(&traitd), is_unsafe, traitd.generic_param_list(), @@ -98,20 +99,20 @@ pub(crate) fn generate_blanket_trait_impl( Some(gendecl), None, false, - namety, - thisty.clone(), + namety.into(), + thisty.into(), trait_where_clause, None, None, - ) - .clone_for_update(); + ); if let Some(trait_assoc_list) = traitd.assoc_item_list() { - let assoc_item_list = impl_.get_or_create_assoc_item_list(); + let assoc_item_list = + impl_.get_or_create_assoc_item_list_with_editor(&mut editor, &make); for item in trait_assoc_list.assoc_items() { let item = match item { ast::AssocItem::Fn(method) if method.body().is_none() => { - todo_fn(&method, ctx.config).into() + todo_fn(&make, &method, ctx.config).into() } ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, _ => continue, @@ -122,10 +123,10 @@ pub(crate) fn generate_blanket_trait_impl( let impl_ = impl_.indent(indent); - edit.insert_all( + editor.insert_all( Position::after(traitd.syntax()), vec![ - make::tokens::whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); @@ -136,7 +137,8 @@ pub(crate) fn generate_blanket_trait_impl( builder.add_tabstop_before(cap, self_ty); } - builder.add_file_edits(ctx.vfs_file_id(), edit); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); @@ -212,22 +214,26 @@ fn where_clause_sized(where_clause: Option) -> Option { }) } -fn apply_sized(has_sized: bool, bounds: Option) -> Option { +fn apply_sized( + make: &SyntaxFactory, + has_sized: bool, + bounds: Option, +) -> Option { if has_sized { return bounds; } let bounds = bounds .into_iter() .flat_map(|bounds| bounds.bounds()) - .chain([make::type_bound_text("?Sized")]); - make::type_bound_list(bounds) + .chain([make.type_bound_text("?Sized")]); + make.type_bound_list(bounds) } -fn exlucde_sized(bounds: ast::TypeBoundList) -> Option { - make::type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized"))) +fn exclude_sized(make: &SyntaxFactory, bounds: ast::TypeBoundList) -> Option { + make.type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized"))) } -fn this_name(traitd: &ast::Trait) -> ast::Name { +fn this_name(make: &SyntaxFactory, traitd: &ast::Trait) -> ast::Name { let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some(); let params = traitd @@ -245,7 +251,7 @@ fn this_name(traitd: &ast::Trait) -> ast::Name { let mut name_gen = suggest_name::NameGenerator::new_with_names(params.iter().map(String::as_str)); - make::name(&name_gen.suggest_name(if has_iter { "I" } else { "T" })) + make.name(&name_gen.suggest_name(if has_iter { "I" } else { "T" })) } fn find_bound(s: &str, bounds: Option) -> Option { @@ -260,16 +266,16 @@ fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool { .is_some_and(|name| name.text() == s)) } -fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { - let params = f.param_list().unwrap_or_else(|| make::param_list(None, None)); - make::fn_( +fn todo_fn(make: &SyntaxFactory, f: &ast::Fn, config: &AssistConfig) -> ast::Fn { + let params = f.param_list().unwrap_or_else(|| make.param_list(None, None)); + make.fn_( cfg_attrs(f), f.visibility(), - f.name().unwrap_or_else(|| make::name("unnamed")), + f.name().unwrap_or_else(|| make.name("unnamed")), f.generic_param_list(), f.where_clause(), params, - make::block_expr(None, Some(crate::utils::expr_fill_default(config))), + make.block_expr(None, Some(crate::utils::expr_fill_default(config))), f.ret_type(), f.async_token().is_some(), f.const_token().is_some(), From 4958d7703caa508a5428025b967f817f6ff1a8ef Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 14:56:56 +0530 Subject: [PATCH 23/98] remove generic params owner edit trait --- .../crates/syntax/src/ast/edit_in_place.rs | 239 +----------------- 1 file changed, 1 insertion(+), 238 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 7f59ae4213829..c6affec427262 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -9,221 +9,13 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, HasGenericParams, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, + ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::{Position, SyntaxEditor}, ted, }; use super::{GenericParam, HasName}; -pub trait GenericParamsOwnerEdit: ast::HasGenericParams { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; - fn get_or_create_where_clause(&self) -> ast::WhereClause; -} - -impl GenericParamsOwnerEdit for ast::Fn { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - ted::Position::after(name.syntax) - } else if let Some(fn_token) = self.fn_token() { - ted::Position::after(fn_token) - } else if let Some(param_list) = self.param_list() { - ted::Position::before(param_list.syntax) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = if let Some(ty) = self.ret_type() { - ted::Position::after(ty.syntax()) - } else if let Some(param_list) = self.param_list() { - ted::Position::after(param_list.syntax()) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::Impl { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = match self.impl_token() { - Some(imp_token) => ted::Position::after(imp_token), - None => ted::Position::last_child_of(self.syntax()), - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match self.assoc_item_list() { - Some(items) => ted::Position::before(items.syntax()), - None => ted::Position::last_child_of(self.syntax()), - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::Trait { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - ted::Position::after(name.syntax) - } else if let Some(trait_token) = self.trait_token() { - ted::Position::after(trait_token) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match (self.assoc_item_list(), self.semicolon_token()) { - (Some(items), _) => ted::Position::before(items.syntax()), - (_, Some(tok)) => ted::Position::before(tok), - (None, None) => ted::Position::last_child_of(self.syntax()), - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::TypeAlias { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - ted::Position::after(name.syntax) - } else if let Some(trait_token) = self.type_token() { - ted::Position::after(trait_token) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match self.eq_token() { - Some(tok) => ted::Position::before(tok), - None => match self.semicolon_token() { - Some(tok) => ted::Position::before(tok), - None => ted::Position::last_child_of(self.syntax()), - }, - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::Struct { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - ted::Position::after(name.syntax) - } else if let Some(struct_token) = self.struct_token() { - ted::Position::after(struct_token) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let tfl = self.field_list().and_then(|fl| match fl { - ast::FieldList::RecordFieldList(_) => None, - ast::FieldList::TupleFieldList(it) => Some(it), - }); - let position = if let Some(tfl) = tfl { - ted::Position::after(tfl.syntax()) - } else if let Some(gpl) = self.generic_param_list() { - ted::Position::after(gpl.syntax()) - } else if let Some(name) = self.name() { - ted::Position::after(name.syntax()) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::Enum { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - ted::Position::after(name.syntax) - } else if let Some(enum_token) = self.enum_token() { - ted::Position::after(enum_token) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = if let Some(gpl) = self.generic_param_list() { - ted::Position::after(gpl.syntax()) - } else if let Some(name) = self.name() { - ted::Position::after(name.syntax()) - } else { - ted::Position::last_child_of(self.syntax()) - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -fn create_where_clause(position: ted::Position) { - let where_clause = make::where_clause(empty()).clone_for_update(); - ted::insert(position, where_clause.syntax()); -} - -fn create_generic_param_list(position: ted::Position) -> ast::GenericParamList { - let gpl = make::generic_param_list(empty()).clone_for_update(); - ted::insert_raw(position, gpl.syntax()); - gpl -} - pub trait AttrsOwnerEdit: ast::HasAttrs { fn remove_attrs_and_docs(&self) { remove_attrs_and_docs(self.syntax()); @@ -879,8 +671,6 @@ impl Indent for N {} #[cfg(test)] mod tests { - use std::fmt; - use parser::Edition; use crate::SourceFile; @@ -892,33 +682,6 @@ mod tests { parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update() } - #[test] - fn test_create_generic_param_list() { - fn check_create_gpl(before: &str, after: &str) { - let gpl_owner = ast_mut_from_text::(before); - gpl_owner.get_or_create_generic_param_list(); - assert_eq!(gpl_owner.to_string(), after); - } - - check_create_gpl::("fn foo", "fn foo<>"); - check_create_gpl::("fn foo() {}", "fn foo<>() {}"); - - check_create_gpl::("impl", "impl<>"); - check_create_gpl::("impl Struct {}", "impl<> Struct {}"); - check_create_gpl::("impl Trait for Struct {}", "impl<> Trait for Struct {}"); - - check_create_gpl::("trait Trait<>", "trait Trait<>"); - check_create_gpl::("trait Trait<> {}", "trait Trait<> {}"); - - check_create_gpl::("struct A", "struct A<>"); - check_create_gpl::("struct A;", "struct A<>;"); - check_create_gpl::("struct A();", "struct A<>();"); - check_create_gpl::("struct A {}", "struct A<> {}"); - - check_create_gpl::("enum E", "enum E<>"); - check_create_gpl::("enum E {", "enum E<> {"); - } - #[test] fn test_increase_indent() { let arm_list = ast_mut_from_text::( From b1034b0361087017db10c7b945f69b936037274b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 14:57:34 +0530 Subject: [PATCH 24/98] update derive_macro --- .../hir-expand/src/builtin/derive_macro.rs | 83 +++++++++---------- 1 file changed, 40 insertions(+), 43 deletions(-) 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 f208203c931b7..bfd7dffb058aa 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 @@ -22,8 +22,9 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, edit_in_place::GenericParamsOwnerEdit, make, + HasName, HasTypeBounds, make, syntax_factory::SyntaxFactory, }, + syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, ted, }; @@ -1150,11 +1151,9 @@ fn coerce_pointee_expand( const ADDED_PARAM: &str = "__S"; - let where_clause = strukt.get_or_create_where_clause(); + let mut new_predicates: Vec = Vec::new(); { - let mut new_predicates = Vec::new(); - // # Rewrite generic parameter bounds // For each bound `U: ..` in `struct`, make a new bound with `__S` in place of `#[pointee]` // Example: @@ -1196,16 +1195,13 @@ fn coerce_pointee_expand( } else { make::name_ref(¶m_name.text()) }; - new_predicates.push( - make::where_pred( - Either::Right(make::ty_path(make::path_from_segments( - [make::path_segment(new_bounds_target)], - false, - ))), - new_bounds, - ) - .clone_for_update(), - ); + new_predicates.push(make::where_pred( + Either::Right(make::ty_path(make::path_from_segments( + [make::path_segment(new_bounds_target)], + false, + ))), + new_bounds, + )); } } @@ -1235,7 +1231,7 @@ fn coerce_pointee_expand( // // We should also write a few new `where` bounds from `#[pointee] T` to `__S` // as well as any bound that indirectly involves the `#[pointee] T` type. - for predicate in where_clause.predicates() { + for predicate in strukt.where_clause().into_iter().flat_map(|wc| wc.predicates()) { let predicate = predicate.clone_subtree().clone_for_update(); let Some(pred_target) = predicate.ty() else { continue }; @@ -1269,42 +1265,43 @@ fn coerce_pointee_expand( ); } } - - for new_predicate in new_predicates { - where_clause.add_predicate(new_predicate); - } } { // # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location // // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. - where_clause.add_predicate( - make::where_pred( - Either::Right(make::ty_path(make::path_from_segments( - [make::path_segment(make::name_ref(&pointee_param_name.text()))], - false, - ))), - [make::type_bound(make::ty_path(make::path_from_segments( - [ - make::path_segment(make::name_ref("core")), - make::path_segment(make::name_ref("marker")), - make::generic_ty_path_segment( - make::name_ref("Unsize"), - [make::type_arg(make::ty_path(make::path_from_segments( - [make::path_segment(make::name_ref(ADDED_PARAM))], - false, - ))) - .into()], - ), - ], - true, - )))], - ) - .clone_for_update(), - ); + new_predicates.push(make::where_pred( + Either::Right(make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(&pointee_param_name.text()))], + false, + ))), + [make::type_bound(make::ty_path(make::path_from_segments( + [ + make::path_segment(make::name_ref("core")), + make::path_segment(make::name_ref("marker")), + make::generic_ty_path_segment( + make::name_ref("Unsize"), + [make::type_arg(make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(ADDED_PARAM))], + false, + ))) + .into()], + ), + ], + true, + )))], + )); } + let (mut editor, strukt) = SyntaxEditor::with_ast_node(strukt); + let make = SyntaxFactory::with_mappings(); + strukt.get_or_create_where_clause(&mut editor, &make, new_predicates.into_iter()); + editor.add_mappings(make.finish_with_mappings()); + let edit = editor.finish(); + let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap(); + let adt = ast::Adt::Struct(strukt.clone()); + let self_for_traits = { // Replace the `#[pointee]` with `__S`. let mut type_param_idx = 0; From c6b54969efacbffc16ea4999fa13a92227f5b0de Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 18 Jan 2026 18:32:32 +0800 Subject: [PATCH 25/98] Support extract variable in macro call Implement based first and last token mappings Example --- ```rust macro_rules! foo { (= $($t:tt)*) => { $($t)* }; } fn main() { let x = foo!(= $02 + 3$0 + 4); } ``` **Before this PR** Assist not applicable **After this PR** ```rust macro_rules! foo { (= $($t:tt)*) => { $($t)* }; } fn main() { let $0var_name = 2+3; let x = foo!(= var_name + 4); } ``` --- .../src/handlers/extract_variable.rs | 244 ++++++++++++++++-- 1 file changed, 221 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 1556339d8df43..728af87bc6526 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -4,14 +4,14 @@ use ide_db::{ syntax_helpers::{LexedStr, suggest_name}, }; use syntax::{ - NodeOrToken, SyntaxKind, SyntaxNode, T, - algo::ancestors_at_offset, + Direction, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange, + algo::{ancestors_at_offset, skip_trivia_token}, ast::{ self, AstNode, edit::{AstNodeEdit, IndentLevel}, syntax_factory::SyntaxFactory, }, - syntax_editor::Position, + syntax_editor::{Element, Position}, }; use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; @@ -92,27 +92,54 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?; let range = node.text_range(); - let to_extract = node - .descendants() - .take_while(|it| range.contains_range(it.text_range())) - .find_map(valid_target_expr(ctx))?; + let (to_replace, analysis) = if node.kind() == SyntaxKind::TOKEN_TREE { + let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?; - let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted); + let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone()); + let last_descend = ctx.sema.descend_into_macros_single_exact(last.clone()); + let range = first_descend.text_range().cover(last_descend.text_range()); + + if first_descend.parent_ancestors().last() != last_descend.parent_ancestors().last() { + return None; + } + + let expr = first_descend + .parent_ancestors() + .skip_while(|it| !it.text_range().contains_range(range)) + .find_map(valid_target_expr(ctx))?; + let original_range = ctx.sema.original_range(expr.syntax()); + let (first, last) = extract_token_range_of(&node, original_range.range)?; + let to_extract = first.syntax_element()..=last.syntax_element(); + (to_extract, expr) + } else { + let expr = node + .descendants() + .take_while(|it| range.contains_range(it.text_range())) + .find_map(valid_target_expr(ctx))?; + let to_extract = expr.syntax().syntax_element(); + (to_extract.clone()..=to_extract, expr) + }; + let place = match to_replace.start() { + NodeOrToken::Node(node) => node.clone(), + NodeOrToken::Token(t) => t.parent()?, + }; + + let ty = ctx.sema.type_of_expr(&analysis).map(TypeInfo::adjusted); if matches!(&ty, Some(ty_info) if ty_info.is_unit()) { return None; } - let parent = to_extract.syntax().parent().and_then(ast::Expr::cast); + let parent = analysis.syntax().parent().and_then(ast::Expr::cast); // Any expression that autoderefs may need adjustment. let mut needs_adjust = parent.as_ref().is_some_and(|it| match it { ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::AwaitExpr(_) => true, - ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&to_extract) => true, + ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&analysis) => true, _ => false, }); - let mut to_extract_no_ref = peel_parens(to_extract.clone()); + let mut to_extract_no_ref = peel_parens(analysis.clone()); let needs_ref = needs_adjust && match &to_extract_no_ref { ast::Expr::FieldExpr(_) @@ -127,14 +154,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } _ => false, }; - let module = ctx.sema.scope(to_extract.syntax())?.module(); - let target = to_extract.syntax().text_range(); + let module = ctx.sema.scope(analysis.syntax())?.module(); + let target = to_replace.start().text_range().cover(to_replace.end().text_range()); let needs_mut = match &parent { Some(ast::Expr::RefExpr(expr)) => expr.mut_token().is_some(), _ => needs_adjust && !needs_ref && ty.as_ref().is_some_and(|ty| ty.is_mutable_reference()), }; for kind in ExtractionKind::ALL { - let Some(anchor) = Anchor::from(&to_extract, kind) else { + let Some(anchor) = Anchor::from(&place, kind) else { continue; }; @@ -169,10 +196,18 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op kind.label(), target, |edit| { - let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &to_extract); + let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &analysis); + + let to_replace = + if expr_replace.ancestors().last() == to_replace.start().ancestors().last() { + let element = expr_replace.clone().syntax_element(); + element.clone()..=element + } else { + to_replace.clone() + }; let make = SyntaxFactory::with_mappings(); - let mut editor = edit.make_editor(&expr_replace); + let mut editor = edit.make_editor(&place); let pat_name = make.name(&var_name); let name_expr = make.expr_path(make.ident_path(&var_name)); @@ -236,7 +271,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ], ); - editor.replace(expr_replace, name_expr.syntax()); + editor.replace_all(to_replace, vec![name_expr.syntax().syntax_element()]); } Anchor::Replace(stmt) => { cov_mark::hit!(test_extract_var_expr_stmt); @@ -252,7 +287,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op make.block_expr([new_stmt], Some(name_expr)) } else { // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`. - editor.replace(expr_replace, name_expr.syntax()); + editor + .replace_all(to_replace, vec![name_expr.syntax().syntax_element()]); make.block_expr([new_stmt], Some(to_wrap.clone())) } // fixup indentation of block @@ -272,6 +308,23 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op Some(()) } +fn extract_token_range_of( + node: &SyntaxNode, + range: TextRange, +) -> Option<(SyntaxToken, SyntaxToken)> { + let first = node.token_at_offset(range.start()).right_biased()?; + let last = node.token_at_offset(range.end()).left_biased()?; + + let first = skip_trivia_token(first, Direction::Next)?; + let last = skip_trivia_token(last, Direction::Next)?; + + if first.text_range().ordering(last.text_range()).is_gt() { + return None; + } + + Some((first, last)) +} + fn peel_parens(mut expr: ast::Expr) -> ast::Expr { while let ast::Expr::ParenExpr(parens) = &expr { let Some(expr_inside) = parens.expr() else { break }; @@ -401,9 +454,8 @@ enum Anchor { } impl Anchor { - fn from(to_extract: &ast::Expr, kind: &ExtractionKind) -> Option { - let result = to_extract - .syntax() + fn from(place: &SyntaxNode, kind: &ExtractionKind) -> Option { + let result = place .ancestors() .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind())) .find_map(|node| { @@ -435,7 +487,7 @@ impl Anchor { if let Some(stmt) = ast::Stmt::cast(node.clone()) { if let ast::Stmt::ExprStmt(stmt) = stmt - && stmt.expr().as_ref() == Some(to_extract) + && stmt.expr().is_some_and(|it| it.syntax() == place) { return Some(Anchor::Replace(stmt)); } @@ -446,7 +498,7 @@ impl Anchor { match kind { ExtractionKind::Constant | ExtractionKind::Static if result.is_none() => { - to_extract.syntax().ancestors().find_map(|node| { + place.ancestors().find_map(|node| { let item = ast::Item::cast(node.clone())?; let parent = item.syntax().parent()?; match parent.kind() { @@ -2771,6 +2823,152 @@ fn main() { let t2 = t; let x = s; } +"#, + "Extract into variable", + ); + } + + #[test] + fn extract_variable_in_token_tree() { + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= $02 + 3$0 + 4); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0var_name = 2+3; + let x = foo!(= var_name + 4); +} +"#, + "Extract into variable", + ); + + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= $02 +$0 3 + 4); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0var_name = 2+3; + let x = foo!(= var_name + 4); +} +"#, + "Extract into variable", + ); + + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= $02 + 3 + 4$0); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0var_name = 2+3+4; + let x = foo!(= var_name); +} +"#, + "Extract into variable", + ); + } + + #[test] + fn extract_variable_in_token_tree_record_expr() { + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= Foo { x: $02 + 3$0 }); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0x = 2+3; + let x = foo!(= Foo { x: x }); +} +"#, + "Extract into variable", + ); + + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= Foo { x: $02 + 3$0 + 4 }); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0var_name = 2+3; + let x = foo!(= Foo { x: var_name + 4 }); +} "#, "Extract into variable", ); From f74767b8d2615730b0052338dbee4f2d9b905a2b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 14 Apr 2026 18:34:49 +0800 Subject: [PATCH 26/98] Add some fixme comments --- .../src/handlers/extract_variable.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 728af87bc6526..732bab4ceca43 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -2830,6 +2830,7 @@ fn main() { #[test] fn extract_variable_in_token_tree() { + // FIXME: Keep the original trivia instead of extracting macro expanded? check_assist_by_label( extract_variable, r#" @@ -2910,6 +2911,39 @@ fn main() { let $0var_name = 2+3+4; let x = foo!(= var_name); } +"#, + "Extract into variable", + ); + + // FIXME: Extract to inside the macro instead of outside the macro + check_assist_by_label( + extract_variable, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let x = foo!(= { + $02 + 3 + 4$0 + }); +} +"#, + r#" +macro_rules! foo { + (= $($t:tt)*) => { + $($t)* + }; +} + +fn main() { + let $0var_name = 2+3+4; + let x = foo!(= { + var_name + }); +} "#, "Extract into variable", ); From 533f183e0f91d7af6a46473837c14c965aad01bb Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 12:40:00 +0530 Subject: [PATCH 27/98] remove set_pat from edit_in_place and move set_pat_with_editor in edit --- .../crates/syntax/src/ast/edit.rs | 68 +++++++++- .../crates/syntax/src/ast/edit_in_place.rs | 128 +----------------- 2 files changed, 66 insertions(+), 130 deletions(-) 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 23a0411eadbd6..c84da3202cb9e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -1,12 +1,15 @@ //! This module contains functions for editing syntax trees. As the trees are //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. +use parser::T; use std::{fmt, iter, ops}; use crate::{ - AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, - ast::{self, AstNode, make}, - syntax_editor::{SyntaxEditor, SyntaxMappingBuilder}, + AstToken, NodeOrToken, SyntaxElement, + SyntaxKind::WHITESPACE, + SyntaxNode, SyntaxToken, + ast::{self, AstNode, HasName, make}, + syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder}, ted, }; @@ -194,6 +197,65 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl AstNodeEdit for N {} +impl ast::IdentPat { + pub fn set_pat_with_editor( + &self, + pat: Option, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, + ) -> ast::IdentPat { + match pat { + None => { + if let Some(at_token) = self.at_token() { + // Remove `@ Pat` + let start = at_token.clone().into(); + let end = self + .pat() + .map(|it| it.syntax().clone().into()) + .unwrap_or_else(|| at_token.into()); + syntax_editor.delete_all(start..=end); + + // Remove any trailing ws + if let Some(last) = + self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + } + Some(pat) => { + if let Some(old_pat) = self.pat() { + // Replace existing pattern + syntax_editor.replace(old_pat.syntax(), pat.syntax()) + } else if let Some(at_token) = self.at_token() { + // Have an `@` token but not a pattern yet + syntax_editor.insert(Position::after(at_token), pat.syntax()); + } else { + // Don't have an `@`, should have a name + let name = self.name().unwrap(); + let elements = vec![ + syntax_factory.whitespace(" ").into(), + syntax_factory.token(T![@]).into(), + syntax_factory.whitespace(" ").into(), + pat.syntax().clone().into(), + ]; + + if self.syntax().parent().is_none() { + let (mut local, local_self) = SyntaxEditor::with_ast_node(self); + let local_name = local_self.name().unwrap(); + local.insert_all(Position::after(local_name.syntax()), elements); + let edit = local.finish(); + return ast::IdentPat::cast(edit.new_root().clone()).unwrap(); + } else { + syntax_editor.insert_all(Position::after(name.syntax()), elements); + } + } + } + } + self.clone() + } +} + #[test] fn test_increase_indent() { let arm_list = { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 9171987f85b53..157285d1b6917 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -9,8 +9,7 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, - syntax_editor::{Position, SyntaxEditor}, + ast::{self, edit::IndentLevel, make}, ted, }; @@ -528,103 +527,6 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } -impl ast::IdentPat { - pub fn set_pat(&self, pat: Option) { - match pat { - None => { - if let Some(at_token) = self.at_token() { - // Remove `@ Pat` - let start = at_token.clone().into(); - let end = self - .pat() - .map(|it| it.syntax().clone().into()) - .unwrap_or_else(|| at_token.into()); - - ted::remove_all(start..=end); - - // Remove any trailing ws - if let Some(last) = - self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) - { - last.detach(); - } - } - } - Some(pat) => { - if let Some(old_pat) = self.pat() { - // Replace existing pattern - ted::replace(old_pat.syntax(), pat.syntax()) - } else if let Some(at_token) = self.at_token() { - // Have an `@` token but not a pattern yet - ted::insert(ted::Position::after(at_token), pat.syntax()); - } else { - // Don't have an `@`, should have a name - let name = self.name().unwrap(); - - ted::insert_all( - ted::Position::after(name.syntax()), - vec![ - make::token(T![@]).into(), - make::tokens::single_space().into(), - pat.syntax().clone().into(), - ], - ) - } - } - } - } - - pub fn set_pat_with_editor( - &self, - pat: Option, - syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, - ) { - match pat { - None => { - if let Some(at_token) = self.at_token() { - // Remove `@ Pat` - let start = at_token.clone().into(); - let end = self - .pat() - .map(|it| it.syntax().clone().into()) - .unwrap_or_else(|| at_token.into()); - syntax_editor.delete_all(start..=end); - - // Remove any trailing ws - if let Some(last) = - self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) - { - last.detach(); - } - } - } - Some(pat) => { - if let Some(old_pat) = self.pat() { - // Replace existing pattern - syntax_editor.replace(old_pat.syntax(), pat.syntax()) - } else if let Some(at_token) = self.at_token() { - // Have an `@` token but not a pattern yet - syntax_editor.insert(Position::after(at_token), pat.syntax()); - } else { - // Don't have an `@`, should have a name - let name = self.name().unwrap(); - - syntax_editor.insert_all( - Position::after(name.syntax()), - vec![ - syntax_factory.whitespace(" ").into(), - syntax_factory.token(T![@]).into(), - syntax_factory.whitespace(" ").into(), - pat.syntax().clone().into(), - ], - ) - } - } - } - } -} - pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) @@ -674,32 +576,4 @@ mod tests { }", ); } - - #[test] - fn test_ident_pat_set_pat() { - #[track_caller] - fn check(before: &str, expected: &str, pat: Option) { - let pat = pat.map(|it| it.clone_for_update()); - - let ident_pat = ast_mut_from_text::(&format!("fn f() {{ {before} }}")); - ident_pat.set_pat(pat); - - let after = ast_mut_from_text::(&format!("fn f() {{ {expected} }}")); - assert_eq!(ident_pat.to_string(), after.to_string()); - } - - // replacing - check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into())); - - // note: no trailing semicolon is added for the below tests since it - // seems to be picked up by the ident pat during error recovery? - - // adding - check("let a ", "let a @ ()", Some(make::tuple_pat([]).into())); - check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into())); - - // removing - check("let a @ ()", "let a", None); - check("let a @ ", "let a", None); - } } From 9919b159884e0b94e44bafb42c62d6424c4147da Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 12:40:37 +0530 Subject: [PATCH 28/98] migrate destructure_tuple_binding and convert_let_else_to_match to use set_pat_with_editor --- .../src/handlers/convert_let_else_to_match.rs | 57 +++++++++++-------- .../src/handlers/destructure_tuple_binding.rs | 2 +- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 9a9808e270fa2..f32f9c633e615 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -3,6 +3,7 @@ use syntax::ast::RangeItem; use syntax::ast::edit::AstNodeEdit; use syntax::ast::syntax_factory::SyntaxFactory; use syntax::ast::{self, AstNode, HasName, LetStmt, Pat}; +use syntax::syntax_editor::SyntaxEditor; use crate::{AssistContext, AssistId, Assists}; @@ -25,12 +26,15 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let root = ctx.source_file().syntax().clone(); + let (mut editor, _) = SyntaxEditor::new(root); // Should focus on the `else` token to trigger let let_stmt = ctx .find_token_syntax_at_offset(T![else]) .and_then(|it| it.parent()?.parent()) .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?; let let_stmt = LetStmt::cast(let_stmt)?; + let make = SyntaxFactory::with_mappings(); let else_block = let_stmt.let_else()?.block_expr()?; let else_expr = if else_block.statements().next().is_none() && let Some(tail_expr) = else_block.tail_expr() @@ -45,10 +49,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' return None; } let pat = let_stmt.pat()?; - - let make = SyntaxFactory::with_mappings(); let mut idents = Vec::default(); - let pat_without_mut = remove_mut_and_collect_idents(&make, &pat, &mut idents)?; + let pat_without_mut = remove_mut_and_collect_idents(&make, &mut editor, &pat, &mut idents)?; let bindings = idents .into_iter() .filter_map(|ref pat| { @@ -70,7 +72,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' }, let_stmt.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(let_stmt.syntax()); + // let mut editor = builder.make_editor(let_stmt.syntax()); let binding_paths = bindings .iter() @@ -124,6 +126,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' fn remove_mut_and_collect_idents( make: &SyntaxFactory, + editor: &mut SyntaxEditor, pat: &ast::Pat, acc: &mut Vec, ) -> Option { @@ -135,34 +138,40 @@ fn remove_mut_and_collect_idents( p.ref_token().is_some() && p.mut_token().is_some(), p.name()?, ); - if let Some(inner) = p.pat() { - non_mut_pat.set_pat(remove_mut_and_collect_idents(make, &inner, acc)); - } + let non_mut_pat = if let Some(inner) = p.pat() { + non_mut_pat.set_pat_with_editor( + remove_mut_and_collect_idents(make, editor, &inner, acc), + editor, + make, + ) + } else { + non_mut_pat + }; non_mut_pat.into() } ast::Pat::BoxPat(p) => { - make.box_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into() + make.box_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() } ast::Pat::OrPat(p) => make .or_pat( p.pats() - .map(|pat| remove_mut_and_collect_idents(make, &pat, acc)) + .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) .collect::>>()?, p.leading_pipe().is_some(), ) .into(), ast::Pat::ParenPat(p) => { - make.paren_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into() + make.paren_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() } ast::Pat::RangePat(p) => make .range_pat( if let Some(start) = p.start() { - Some(remove_mut_and_collect_idents(make, &start, acc)?) + Some(remove_mut_and_collect_idents(make, editor, &start, acc)?) } else { None }, if let Some(end) = p.end() { - Some(remove_mut_and_collect_idents(make, &end, acc)?) + Some(remove_mut_and_collect_idents(make, editor, &end, acc)?) } else { None }, @@ -175,13 +184,15 @@ fn remove_mut_and_collect_idents( p.record_pat_field_list()? .fields() .map(|field| { - remove_mut_and_collect_idents(make, &field.pat()?, acc).map(|pat| { - if let Some(name_ref) = field.name_ref() { - make.record_pat_field(name_ref, pat) - } else { - make.record_pat_field_shorthand(pat) - } - }) + remove_mut_and_collect_idents(make, editor, &field.pat()?, acc).map( + |pat| { + if let Some(name_ref) = field.name_ref() { + make.record_pat_field(name_ref, pat) + } else { + make.record_pat_field_shorthand(pat) + } + }, + ) }) .collect::>>()?, p.record_pat_field_list()?.rest_pat(), @@ -194,20 +205,20 @@ fn remove_mut_and_collect_idents( acc.push(ident); p.clone().into() } else { - make.ref_pat(remove_mut_and_collect_idents(make, &inner, acc)?).into() + make.ref_pat(remove_mut_and_collect_idents(make, editor, &inner, acc)?).into() } } ast::Pat::SlicePat(p) => make .slice_pat( p.pats() - .map(|pat| remove_mut_and_collect_idents(make, &pat, acc)) + .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) .collect::>>()?, ) .into(), ast::Pat::TuplePat(p) => make .tuple_pat( p.fields() - .map(|field| remove_mut_and_collect_idents(make, &field, acc)) + .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) .collect::>>()?, ) .into(), @@ -215,7 +226,7 @@ fn remove_mut_and_collect_idents( .tuple_struct_pat( p.path()?, p.fields() - .map(|field| remove_mut_and_collect_idents(make, &field, acc)) + .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) .collect::>>()?, ) .into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 23c11b258c1a7..7e5df838d2144 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -230,7 +230,7 @@ impl AssignmentEdit { Some(self.tuple_pat.into()), syntax_editor, syntax_mapping, - ) + ); } else if self.is_shorthand_field { syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); syntax_editor From e32773b39c5cb6bbdbe3d3da7122bab457ce57a7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 12:41:53 +0530 Subject: [PATCH 29/98] rename set_path_with_editor to set_path --- .../ide-assists/src/handlers/convert_let_else_to_match.rs | 2 +- .../ide-assists/src/handlers/destructure_tuple_binding.rs | 6 +----- src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index f32f9c633e615..994fb44279b6a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -139,7 +139,7 @@ fn remove_mut_and_collect_idents( p.name()?, ); let non_mut_pat = if let Some(inner) = p.pat() { - non_mut_pat.set_pat_with_editor( + non_mut_pat.set_pat( remove_mut_and_collect_idents(make, editor, &inner, acc), editor, make, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 7e5df838d2144..05fa00f4e8c11 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -226,11 +226,7 @@ impl AssignmentEdit { fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) { // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { - self.ident_pat.set_pat_with_editor( - Some(self.tuple_pat.into()), - syntax_editor, - syntax_mapping, - ); + self.ident_pat.set_pat(Some(self.tuple_pat.into()), syntax_editor, syntax_mapping); } else if self.is_shorthand_field { syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); syntax_editor 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 c84da3202cb9e..567bd09025648 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -198,7 +198,7 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl AstNodeEdit for N {} impl ast::IdentPat { - pub fn set_pat_with_editor( + pub fn set_pat( &self, pat: Option, syntax_editor: &mut SyntaxEditor, From 862febff7b7c46b4c1f5b61340d3ba4fc98bb8d2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 18:52:42 +0530 Subject: [PATCH 30/98] add use tree list method to syntaxFactory constructor --- .../src/ast/syntax_factory/constructors.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 c92f7ec6f3adb..ad67abfbed7a8 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 @@ -71,6 +71,22 @@ impl SyntaxFactory { make::type_bound_text(bound).clone_for_update() } + pub fn use_tree_list( + &self, + 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(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.use_trees().map(|b| b.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn type_bound_list( &self, bounds: impl IntoIterator, From 7a56ebd96164d17a7b494480834efeaf8ebd6b48 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 18:53:16 +0530 Subject: [PATCH 31/98] replace make with syntax_factory definitions --- .../src/handlers/expand_glob_import.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 6c5c21bfc90f3..79b9f5d69afc1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -8,7 +8,7 @@ use ide_db::{ use stdx::never; use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, - ast::{self, Use, UseTree, VisibilityKind, make}, + ast::{self, Use, UseTree, VisibilityKind, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -148,6 +148,8 @@ fn build_expanded_import( current_module: Module, reexport_public_items: bool, ) { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(use_tree.syntax()); let (must_be_pub, visible_from) = if !reexport_public_items { (false, current_module) } else { @@ -167,15 +169,13 @@ fn build_expanded_import( if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; let names_to_import = find_names_to_import(filtered_defs, imported_defs); - let expanded = make::use_tree_list(names_to_import.iter().map(|n| { - let path = make::ext::ident_path( + let expanded = make.use_tree_list(names_to_import.iter().map(|n| { + let path = make.ident_path( &n.display(ctx.db(), current_module.krate(ctx.db()).edition(ctx.db())).to_string(), ); - make::use_tree(path, None, None, false) - })) - .clone_for_update(); + make.use_tree(path, None, None, false) + })); - let mut editor = builder.make_editor(use_tree.syntax()); match use_tree.star_token() { Some(star) => { let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1; @@ -192,6 +192,7 @@ fn build_expanded_import( } None => never!(), } + editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); } From 3347cb8b4c0cfa7983fcac8fea11e03163389a14 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 14 Apr 2026 17:23:42 +0300 Subject: [PATCH 32/98] Do not complete unstable items that use an internal feature Unless the feature is enabled (and we're on nightly). We can choose to not complete any unstable item if its feature is not enabled. However people may first type the thing then insert the feature. Only doing that for internal features is a good middle ground: normal people don't want their completion list polluted by `std::intrinsics` (even if they use nightly), and std people still get their completion if the feature is enabled (and realistically, it will be). --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 35 ++++++ .../rust-analyzer/crates/hir/src/attrs.rs | 13 +++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 + .../crates/ide-completion/src/context.rs | 54 ++++++++- .../ide-completion/src/tests/expression.rs | 108 +++++++++++++++++- .../crates/intern/src/symbol/symbols.rs | 29 ++++- 6 files changed, 238 insertions(+), 5 deletions(-) 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 b560d08492ff9..9da5b98d8361b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -1076,6 +1076,41 @@ impl AttrFlags { }) } } + + pub fn unstable_feature(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option { + if !self.contains(AttrFlags::IS_UNSTABLE) { + return None; + } + + return unstable_feature(db, owner); + + #[salsa::tracked] + fn unstable_feature(db: &dyn DefDatabase, owner: AttrDefId) -> Option { + collect_attrs(db, owner, |attr| { + if let ast::Meta::TokenTreeMeta(attr) = attr + && let path = attr.path() + && path.is1("unstable") + && let Some(tt) = attr.token_tree() + { + let mut tt = TokenTreeChildren::new(&tt); + // Technically the `feature = "..."` always comes first, but it's not a requirement. + while let Some(token) = tt.next() { + if let NodeOrToken::Token(token) = token + && token.text() == "feature" + && let Some(NodeOrToken::Token(eq)) = tt.next() + && eq.kind() == T![=] + && let Some(NodeOrToken::Token(feature)) = tt.next() + && let Some(feature) = ast::String::cast(feature) + && let Ok(feature) = feature.value() + { + return ControlFlow::Break(Symbol::intern(&feature)); + } + } + } + ControlFlow::Continue(()) + }) + } + } } fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 27e7985146107..bec91032b9a9e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -85,6 +85,19 @@ impl AttrsWithOwner { self.attrs.contains(AttrFlags::IS_UNSTABLE) } + /// Currently, it could be that `is_unstable() == true` but `unstable_feature == None` + /// (due to unstable features not being retrieved for fields etc.). + #[inline] + pub fn unstable_feature(&self, db: &dyn HirDatabase) -> Option { + match self.owner { + AttrsOwner::AttrDef(owner) => self.attrs.unstable_feature(db, owner), + AttrsOwner::Field(_) + | AttrsOwner::LifetimeParam(_) + | AttrsOwner::TypeOrConstParam(_) + | AttrsOwner::Dummy => None, + } + } + #[inline] pub fn is_macro_export(&self) -> bool { self.attrs.contains(AttrFlags::IS_MACRO_EXPORT) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 7a4085c4746c9..7664d07f0c55e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -342,6 +342,10 @@ impl Crate { }) .map(Crate::from) } + + pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool { + crate_def_map(db, self.id).is_unstable_feature_enabled(feature) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index a9f9f7997cc0b..b9520e9132143 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -4,12 +4,12 @@ mod analysis; #[cfg(test)] mod tests; -use std::iter; +use std::{iter, sync::LazyLock}; use base_db::toolchain_channel; use hir::{ DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution, - ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, + ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, sym, }; use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs, @@ -601,7 +601,18 @@ impl CompletionContext<'_> { let Some(attrs) = attrs else { return true; }; - !attrs.is_unstable() || self.is_nightly + if !attrs.is_unstable() { + return true; + } + if !self.is_nightly { + return false; + } + // Unstable on nightly, but we still don't want to suggest internal features, unless the feature flag is enabled. + let Some(unstable_feature) = attrs.unstable_feature(self.db) else { + return true; + }; + !INTERNAL_FEATURES.contains(&unstable_feature) + || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } pub(crate) fn check_stability_and_hidden(&self, item: I) -> bool @@ -924,3 +935,40 @@ const OP_TRAIT_LANG: &[hir::LangItem] = &[ hir::LangItem::Shr, hir::LangItem::Sub, ]; + +// FIXME: Find a way to keep this up to date somehow? +const INTERNAL_FEATURES_LIST: &[Symbol] = &[ + sym::abi_unadjusted, + sym::allocator_internals, + sym::allow_internal_unsafe, + sym::allow_internal_unstable, + sym::cfg_emscripten_wasm_eh, + sym::cfg_target_has_reliable_f16_f128, + sym::compiler_builtins, + sym::custom_mir, + sym::eii_internals, + sym::field_representing_type_raw, + sym::intrinsics, + sym::lang_items, + sym::link_cfg, + sym::more_maybe_bounds, + sym::negative_bounds, + sym::pattern_complexity_limit, + sym::prelude_import, + sym::profiler_runtime, + sym::rustc_attrs, + sym::staged_api, + sym::test_unstable_lint, + sym::builtin_syntax, + sym::link_llvm_intrinsics, + sym::needs_panic_runtime, + sym::panic_runtime, + sym::pattern_types, + sym::rustdoc_internals, + sym::contracts_internals, + sym::freeze_impls, + sym::unsized_fn_params, +]; + +static INTERNAL_FEATURES: LazyLock> = + LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 4a5983097a12c..294434297eccb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2273,7 +2273,7 @@ fn main() { $0 } //- /std.rs crate:std -#[unstable] +#[unstable(feature = "some_non_internal_feature")] pub struct UnstableButWeAreOnNightlyAnyway; "#, expect![[r#" @@ -2317,6 +2317,112 @@ pub struct UnstableButWeAreOnNightlyAnyway; ); } +#[test] +fn expr_unstable_item_internal_feature() { + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable(feature = "intrinsics")] +pub mod intrinsics {} + "#, + expect![[r#" + fn main() fn() + md std + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +#![feature(intrinsics)] +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable(feature = "intrinsics")] +pub mod intrinsics {} + "#, + expect![[r#" + fn main() fn() + md intrinsics + md std + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn inside_format_args_completions_work() { check( 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 cc09a1aae7a6d..4be1f79fb5a8e 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -123,7 +123,6 @@ define_symbols! { all, alloc_layout, alloc, - allow_internal_unsafe, allow, any, as_str, @@ -541,4 +540,32 @@ define_symbols! { DispatchFromDyn, define_opaque, marker, + abi_unadjusted, + allocator_internals, + allow_internal_unsafe, + allow_internal_unstable, + cfg_emscripten_wasm_eh, + cfg_target_has_reliable_f16_f128, + compiler_builtins, + custom_mir, + eii_internals, + field_representing_type_raw, + intrinsics, + link_cfg, + more_maybe_bounds, + negative_bounds, + pattern_complexity_limit, + profiler_runtime, + rustc_attrs, + staged_api, + test_unstable_lint, + builtin_syntax, + link_llvm_intrinsics, + needs_panic_runtime, + panic_runtime, + pattern_types, + rustdoc_internals, + contracts_internals, + freeze_impls, + unsized_fn_params, } From e95ac9eb1f7fd847d6fd8e0c63abc842a067f41a Mon Sep 17 00:00:00 2001 From: ypp Date: Wed, 15 Apr 2026 00:12:53 +0800 Subject: [PATCH 33/98] parse `type const` items Adds parser support for the unstable `type const` syntax from the `min_generic_const_args` feature, e.g. `type const FOO: i32 = 2;`. Closes rust-lang/rust-analyzer#22038. Assisted-by: Claude Opus 4.6 (1M context) --- .../crates/parser/src/grammar/items.rs | 4 ++++ .../crates/parser/src/grammar/items/consts.rs | 1 + .../parser/test_data/generated/runner.rs | 2 ++ .../parser/inline/ok/type_const.rast | 22 +++++++++++++++++++ .../test_data/parser/inline/ok/type_const.rs | 1 + .../rust-analyzer/crates/syntax/rust.ungram | 1 + .../crates/syntax/src/ast/generated/nodes.rs | 2 ++ 7 files changed, 33 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index 6c46fac290762..c5c6e04dd49aa 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -235,6 +235,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res T![trait] => traits::trait_(p, m), T![impl] => traits::impl_(p, m), + T![type] if p.nth(1) == T![const] => consts::konst(p, m), T![type] => type_alias(p, m), // test extern_block @@ -266,6 +267,9 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke T![use] => use_item::use_(p, m), T![mod] => mod_item(p, m), + // test type_const + // type const FOO: i32 = 2; + T![type] if la == T![const] => consts::konst(p, m), T![type] => type_alias(p, m), T![struct] => adt::strukt(p, m), T![enum] => adt::enum_(p, m), diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs index e6a8aca5861a6..cc5bb73bdcabc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs @@ -3,6 +3,7 @@ use super::*; // test const_item // const C: u32 = 92; pub(super) fn konst(p: &mut Parser<'_>, m: Marker) { + p.eat(T![type]); p.bump(T![const]); const_or_static(p, m, true); } 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 10b1f85d39113..6dfb78b12878b 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 @@ -688,6 +688,8 @@ mod ok { #[test] fn type_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/type_alias.rs"); } #[test] + fn type_const() { run_and_expect_no_errors("test_data/parser/inline/ok/type_const.rs"); } + #[test] fn type_item_type_params() { run_and_expect_no_errors("test_data/parser/inline/ok/type_item_type_params.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rast new file mode 100644 index 0000000000000..9ceae9e44b3a8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rast @@ -0,0 +1,22 @@ +SOURCE_FILE + CONST + TYPE_KW "type" + WHITESPACE " " + CONST_KW "const" + WHITESPACE " " + NAME + IDENT "FOO" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "2" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rs new file mode 100644 index 0000000000000..8e2c4259227a0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_const.rs @@ -0,0 +1 @@ +type const FOO: i32 = 2; diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index caf92aca87744..768cf2013d650 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -328,6 +328,7 @@ VariantDef = Const = Attr* Visibility? 'default'? + 'type'? 'const' (Name | '_') GenericParamList? ':' Type ('=' body:Expr)? WhereClause? ';' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69fd703c0398e..9a2bba9ebf0dd 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -484,6 +484,8 @@ impl Const { pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) } #[inline] pub fn default_token(&self) -> Option { support::token(&self.syntax, T![default]) } + #[inline] + pub fn type_token(&self) -> Option { support::token(&self.syntax, T![type]) } } pub struct ConstArg { pub(crate) syntax: SyntaxNode, From 6615cd9679acfadbcf18f149ae5c22efd97c025b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 15 Apr 2026 12:12:31 +0530 Subject: [PATCH 34/98] move SyntaxFactory inside SyntaxEditor --- .../crates/syntax/src/ast/edit.rs | 7 +- .../crates/syntax/src/ast/syntax_factory.rs | 10 +-- .../crates/syntax/src/syntax_editor.rs | 88 +++++++++---------- .../syntax/src/syntax_editor/edit_algo.rs | 3 +- .../crates/syntax/src/syntax_editor/edits.rs | 34 +++---- 5 files changed, 61 insertions(+), 81 deletions(-) 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 567bd09025648..ae1293fb2f74a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -202,7 +202,6 @@ impl ast::IdentPat { &self, pat: Option, syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, ) -> ast::IdentPat { match pat { None => { @@ -234,9 +233,9 @@ impl ast::IdentPat { // Don't have an `@`, should have a name let name = self.name().unwrap(); let elements = vec![ - syntax_factory.whitespace(" ").into(), - syntax_factory.token(T![@]).into(), - syntax_factory.whitespace(" ").into(), + syntax_editor.make().whitespace(" ").into(), + syntax_editor.make().token(T![@]).into(), + syntax_editor.make().whitespace(" ").into(), pat.syntax().clone().into(), ]; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs index f3ae7544cc37f..9369a4e700cbd 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs @@ -12,6 +12,7 @@ use std::cell::{RefCell, RefMut}; use crate::syntax_editor::SyntaxMapping; +#[derive(Debug)] pub struct SyntaxFactory { // Stored in a refcell so that the factory methods can be &self mappings: Option>, @@ -19,7 +20,7 @@ pub struct SyntaxFactory { impl SyntaxFactory { /// Creates a new [`SyntaxFactory`], generating mappings between input nodes and generated nodes. - pub fn with_mappings() -> Self { + pub(crate) fn with_mappings() -> Self { Self { mappings: Some(RefCell::new(SyntaxMapping::default())) } } @@ -28,13 +29,8 @@ impl SyntaxFactory { Self { mappings: None } } - /// Gets all of the tracked syntax mappings, if any. - pub fn finish_with_mappings(self) -> SyntaxMapping { - self.mappings.unwrap_or_default().into_inner() - } - /// Take all of the tracked syntax mappings, leaving `SyntaxMapping::default()` in its place, if any. - pub fn take(&self) -> SyntaxMapping { + pub(crate) fn take(&self) -> SyntaxMapping { self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default() } 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 8e4dc75d22194..4c20dc7a15663 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -30,8 +30,8 @@ pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; pub struct SyntaxEditor { root: SyntaxNode, changes: Vec, - mappings: SyntaxMapping, annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + make: SyntaxFactory, } impl SyntaxEditor { @@ -51,8 +51,8 @@ impl SyntaxEditor { let editor = Self { root: root.clone(), changes: Vec::new(), - mappings: SyntaxMapping::default(), annotations: Vec::new(), + make: SyntaxFactory::with_mappings(), }; (editor, root) @@ -68,6 +68,10 @@ impl SyntaxEditor { (editor, T::cast(root).unwrap()) } + pub fn make(&self) -> &SyntaxFactory { + &self.make + } + pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { self.annotations.push((element.syntax_element(), annotation)) } @@ -90,7 +94,9 @@ impl SyntaxEditor { ); self.changes.append(&mut other.changes); - self.mappings.merge(other.mappings); + if let Some(mut m) = self.make.mappings() { + m.merge(other.make.take()); + } self.annotations.append(&mut other.annotations); } @@ -104,28 +110,22 @@ impl SyntaxEditor { self.changes.push(Change::InsertAll(position, elements)) } - pub fn insert_with_whitespace( - &mut self, - position: Position, - element: impl Element, - factory: &SyntaxFactory, - ) { - self.insert_all_with_whitespace(position, vec![element.syntax_element()], factory) + pub fn insert_with_whitespace(&mut self, position: Position, element: impl Element) { + self.insert_all_with_whitespace(position, vec![element.syntax_element()]) } pub fn insert_all_with_whitespace( &mut self, position: Position, mut elements: Vec, - factory: &SyntaxFactory, ) { if let Some(first) = elements.first() - && let Some(ws) = ws_before(&position, first, factory) + && let Some(ws) = ws_before(&position, first, &self.make) { elements.insert(0, ws.into()); } if let Some(last) = elements.last() - && let Some(ws) = ws_after(&position, last, factory) + && let Some(ws) = ws_after(&position, last, &self.make) { elements.push(ws.into()); } @@ -181,10 +181,6 @@ impl SyntaxEditor { pub fn finish(self) -> SyntaxEdit { edit_algo::apply_edits(self) } - - pub fn add_mappings(&mut self, other: SyntaxMapping) { - self.mappings.merge(other); - } } /// Represents a completed [`SyntaxEditor`] operation. @@ -538,7 +534,7 @@ mod tests { use crate::{ AstNode, - ast::{self, make, syntax_factory::SyntaxFactory}, + ast::{self, make}, }; use super::*; @@ -564,8 +560,6 @@ mod tests { let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); - let make = SyntaxFactory::with_mappings(); - let name = make::name("var_name"); let name_ref = make::name_ref("var_name").clone_for_update(); @@ -573,10 +567,11 @@ mod tests { editor.add_annotation(name.syntax(), placeholder_snippet); editor.add_annotation(name_ref.syntax(), placeholder_snippet); - let new_block = make.block_expr( - [make + let new_block = editor.make().block_expr( + [editor + .make() .let_stmt( - make.ident_pat(false, false, name.clone()).into(), + editor.make().ident_pat(false, false, name.clone()).into(), None, Some(to_replace.clone().into()), ) @@ -586,7 +581,6 @@ mod tests { editor.replace(to_replace.syntax(), name_ref.syntax()); editor.replace(to_wrap.syntax(), new_block.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); @@ -620,26 +614,29 @@ mod tests { let (mut editor, root) = SyntaxEditor::with_ast_node(&root); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let make = SyntaxFactory::without_mappings(); editor.insert( Position::first_child_of(root.stmt_list().unwrap().syntax()), - make.let_stmt( - make::ext::simple_ident_pat(make::name("first")).into(), - None, - Some(make::expr_literal("1").into()), - ) - .syntax(), + editor + .make() + .let_stmt( + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), ); editor.insert( Position::after(second_let.syntax()), - make.let_stmt( - make::ext::simple_ident_pat(make::name("third")).into(), - None, - Some(make::expr_literal("3").into()), - ) - .syntax(), + editor + .make() + .let_stmt( + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), ); let edit = editor.finish(); @@ -675,17 +672,16 @@ mod tests { root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let make = SyntaxFactory::with_mappings(); - - let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = + editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = make.let_stmt( + let first_let = editor.make().let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), ); - let third_let = make.let_stmt( + let third_let = editor.make().let_stmt( make::ext::simple_ident_pat(make::name("third")).into(), None, Some(make::expr_literal("3").into()), @@ -697,7 +693,6 @@ mod tests { ); editor.insert(Position::after(second_let.syntax()), third_let.syntax()); editor.replace(inner_block.syntax(), new_block_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); @@ -727,11 +722,11 @@ mod tests { let (mut editor, root) = SyntaxEditor::with_ast_node(&root); let inner_block = root; - let make = SyntaxFactory::with_mappings(); - let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = + editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = make.let_stmt( + let first_let = editor.make().let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), @@ -742,7 +737,6 @@ mod tests { first_let.syntax(), ); editor.replace(inner_block.syntax(), new_block_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); 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 78e7083f97e4c..4e08daba7bcf0 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 @@ -35,7 +35,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) // - Propagate annotations - let SyntaxEditor { root, mut changes, mappings, annotations } = editor; + let SyntaxEditor { root, mut changes, annotations, make } = editor; + let mappings = make.take(); let mut node_depths = FxHashMap::::default(); let mut get_node_depth = |node: SyntaxNode| { diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index d741adb6e3449..1718f1a51c740 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -3,10 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{ - self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make, - syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make}, syntax_editor::{Position, SyntaxEditor}, }; @@ -16,20 +13,19 @@ pub trait GetOrCreateWhereClause: ast::HasGenericParams { fn get_or_create_where_clause( &self, editor: &mut SyntaxEditor, - make: &SyntaxFactory, new_preds: impl Iterator, ) { let existing = self.where_clause(); let all_preds: Vec<_> = existing.iter().flat_map(|wc| wc.predicates()).chain(new_preds).collect(); - let new_where = make.where_clause(all_preds); + let new_where = editor.make().where_clause(all_preds); if let Some(existing) = &existing { editor.replace(existing.syntax(), new_where.syntax()); } else if let Some(pos) = self.where_clause_position() { editor.insert_all( pos, - vec![make.whitespace(" ").into(), new_where.syntax().clone().into()], + vec![editor.make().whitespace(" ").into(), new_where.syntax().clone().into()], ); } } @@ -178,7 +174,6 @@ impl SyntaxEditor { } fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { - let make = SyntaxFactory::without_mappings(); match syntax .siblings_with_tokens(Direction::Next) .filter_map(|it| it.into_token()) @@ -186,7 +181,7 @@ fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> { Some(it) => it, None => { - let comma = make.token(T![,]); + let comma = editor.make().token(T![,]); editor.insert(Position::after(syntax), &comma); comma } @@ -233,15 +228,14 @@ impl ast::Impl { pub fn get_or_create_assoc_item_list_with_editor( &self, editor: &mut SyntaxEditor, - make: &SyntaxFactory, ) -> ast::AssocItemList { if let Some(list) = self.assoc_item_list() { list } else { - let list = make.assoc_item_list_empty(); + let list = editor.make().assoc_item_list_empty(); editor.insert_all( Position::last_child_of(self.syntax()), - vec![make.whitespace(" ").into(), list.syntax().clone().into()], + vec![editor.make().whitespace(" ").into(), list.syntax().clone().into()], ); list } @@ -250,7 +244,6 @@ impl ast::Impl { impl ast::VariantList { pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { - let make = SyntaxFactory::without_mappings(); let (indent, position) = match self.variants().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -265,9 +258,9 @@ impl ast::VariantList { }, }; let elements: Vec = vec![ - make.whitespace(&format!("{}{indent}", "\n")).into(), + editor.make().whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), - make.token(T![,]).into(), + editor.make().token(T![,]).into(), ]; editor.insert_all(position, elements); } @@ -291,7 +284,6 @@ impl ast::Fn { } fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> { - let make = SyntaxFactory::without_mappings(); let l = node .children_with_tokens() .filter_map(|it| it.into_token()) @@ -306,11 +298,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> match l.next_sibling_or_token() { Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { if ws.next_sibling_or_token()?.into_token()? == r { - editor.replace(ws, make.whitespace(&format!("\n{indent}"))); + editor.replace(ws, editor.make().whitespace(&format!("\n{indent}"))); } } Some(ws) if ws.kind() == T!['}'] => { - editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}"))); + editor.insert(Position::after(l), editor.make().whitespace(&format!("\n{indent}"))); } _ => (), } @@ -332,8 +324,6 @@ impl Removable for ast::TypeBoundList { impl Removable for ast::Use { fn remove(&self, editor: &mut SyntaxEditor) { - let make = SyntaxFactory::without_mappings(); - let next_ws = self .syntax() .next_sibling_or_token() @@ -345,7 +335,7 @@ impl Removable for ast::Use { if rest.is_empty() { editor.delete(next_ws.syntax()); } else { - editor.replace(next_ws.syntax(), make.whitespace(rest)); + editor.replace(next_ws.syntax(), editor.make().whitespace(rest)); } } } @@ -379,7 +369,7 @@ mod tests { use stdx::trim_indent; use test_utils::assert_eq_text; - use crate::SourceFile; + use crate::{SourceFile, ast::syntax_factory::SyntaxFactory}; use super::*; From a6f0e7872a7424cc8773530bb5e5e6d6e6a28aa0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 15 Apr 2026 12:13:45 +0530 Subject: [PATCH 35/98] fix constructor mapping which were incorrect --- .../src/ast/syntax_factory/constructors.rs | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) 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 ad67abfbed7a8..421d13f0dc011 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 @@ -96,7 +96,9 @@ impl SyntaxFactory { if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); - builder.map_children(input, ast.bounds().map(|b| b.syntax().clone())); + for (input_node, output_bound) in input.into_iter().zip(ast.bounds()) { + builder.map_node(input_node, output_bound.syntax().clone()); + } builder.finish(&mut mapping); } @@ -209,7 +211,7 @@ impl SyntaxFactory { } builder.map_children( params_input, - ast.param_list().unwrap().params().map(|p| p.syntax().clone()), + ast.syntax().children().filter(|c| ast::Param::can_cast(c.kind())), ); if let Some(ret_type) = ret_type { builder @@ -242,13 +244,17 @@ impl SyntaxFactory { builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); } } + builder.finish(&mut mapping); + if let Some(type_bound_list) = ast.type_bound_list() { - builder.map_children( + let mut bounds_builder = + SyntaxMappingBuilder::new(type_bound_list.syntax().clone()); + bounds_builder.map_children( bounds_input, type_bound_list.bounds().map(|b| b.syntax().clone()), ); + bounds_builder.finish(&mut mapping); } - builder.finish(&mut mapping); } ast @@ -484,11 +490,13 @@ impl SyntaxFactory { if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); - builder.map_children( - input, - ast.generic_arg_list().unwrap().generic_args().map(|a| a.syntax().clone()), - ); builder.finish(&mut mapping); + + let generic_arg_list = ast.generic_arg_list().unwrap(); + let mut arg_builder = SyntaxMappingBuilder::new(generic_arg_list.syntax().clone()); + arg_builder + .map_children(input, generic_arg_list.generic_args().map(|a| a.syntax().clone())); + arg_builder.finish(&mut mapping); } ast @@ -629,9 +637,16 @@ impl SyntaxFactory { let ast = make::path_from_segments(segments, is_abs).clone_for_update(); if let Some(mut mapping) = self.mappings() { - let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); - builder.map_children(input, ast.segments().map(|it| it.syntax().clone())); - builder.finish(&mut mapping); + let mut current_path = Some(ast.clone()); + for input_segment in input.iter().rev() { + let Some(path) = current_path else { break }; + if let Some(segment) = path.segment() { + let mut builder = SyntaxMappingBuilder::new(path.syntax().clone()); + builder.map_node(input_segment.clone(), segment.syntax().clone()); + builder.finish(&mut mapping); + } + current_path = path.qualifier(); + } } ast From 15b3b408fd4066feb700c0e1201dcca7f8c2b100 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 15 Apr 2026 12:16:30 +0530 Subject: [PATCH 36/98] Remove all occurance of explicit SyntaxFactory::with_mapping, and update all the occurances to use make via SyntaxEditor --- .../hir-expand/src/builtin/derive_macro.rs | 6 +- .../ide-assists/src/handlers/add_braces.rs | 7 +- .../src/handlers/add_explicit_dot_deref.rs | 11 +- .../src/handlers/add_label_to_loop.rs | 17 +- .../src/handlers/add_missing_impl_members.rs | 12 +- .../src/handlers/add_missing_match_arms.rs | 3 +- .../src/handlers/add_turbo_fish.rs | 15 +- .../src/handlers/apply_demorgan.rs | 46 ++--- .../src/handlers/convert_bool_then.rs | 22 +-- .../src/handlers/convert_for_to_while_let.rs | 44 ++--- .../src/handlers/convert_from_to_tryfrom.rs | 31 +-- .../handlers/convert_iter_for_each_to_for.rs | 14 +- .../src/handlers/convert_let_else_to_match.rs | 181 +++++++++--------- .../src/handlers/convert_match_to_let_else.rs | 8 +- .../convert_named_struct_to_tuple_struct.rs | 57 +++--- .../handlers/convert_range_for_to_while.rs | 48 ++--- .../src/handlers/convert_to_guarded_return.rs | 17 +- .../convert_tuple_return_type_to_struct.rs | 44 ++--- .../convert_tuple_struct_to_named_struct.rs | 31 ++- .../src/handlers/convert_while_to_loop.rs | 41 ++-- .../handlers/destructure_struct_binding.rs | 59 +++--- .../src/handlers/destructure_tuple_binding.rs | 36 ++-- .../src/handlers/desugar_try_expr.rs | 74 ++++--- .../src/handlers/expand_glob_import.rs | 10 +- .../src/handlers/expand_rest_pattern.rs | 55 +++--- .../extract_expressions_from_format_string.rs | 19 +- .../extract_struct_from_enum_variant.rs | 47 ++--- .../src/handlers/extract_type_alias.rs | 36 ++-- .../src/handlers/extract_variable.rs | 40 ++-- .../ide-assists/src/handlers/flip_binexpr.rs | 6 +- .../ide-assists/src/handlers/flip_comma.rs | 11 +- .../handlers/generate_blanket_trait_impl.rs | 31 ++- .../src/handlers/generate_default_from_new.rs | 7 +- .../src/handlers/generate_delegate_methods.rs | 43 +++-- .../src/handlers/generate_deref.rs | 68 ++++--- .../src/handlers/generate_derive.rs | 16 +- .../src/handlers/generate_enum_variant.rs | 6 +- .../src/handlers/generate_fn_type_alias.rs | 30 +-- .../handlers/generate_from_impl_for_enum.rs | 8 +- .../src/handlers/generate_getter_or_setter.rs | 45 +++-- .../ide-assists/src/handlers/generate_impl.rs | 51 ++--- .../src/handlers/generate_mut_trait_impl.rs | 24 ++- .../ide-assists/src/handlers/generate_new.rs | 88 +++++---- .../generate_single_field_struct_from.rs | 45 ++--- .../src/handlers/generate_trait_from_impl.rs | 10 +- .../src/handlers/inline_local_variable.rs | 10 +- .../src/handlers/inline_type_alias.rs | 3 +- .../src/handlers/introduce_named_lifetime.rs | 41 ++-- .../introduce_named_type_parameter.rs | 10 +- .../ide-assists/src/handlers/merge_imports.rs | 8 +- .../ide-assists/src/handlers/move_bounds.rs | 15 +- .../ide-assists/src/handlers/move_guard.rs | 29 +-- .../src/handlers/promote_local_to_const.rs | 18 +- .../src/handlers/pull_assignment_up.rs | 13 +- .../src/handlers/qualify_method_call.rs | 5 +- .../ide-assists/src/handlers/qualify_path.rs | 31 ++- .../src/handlers/remove_parentheses.rs | 10 +- .../src/handlers/replace_arith_op.rs | 21 +- .../replace_derive_with_manual_impl.rs | 46 +++-- .../src/handlers/replace_if_let_with_match.rs | 55 +++--- .../replace_is_method_with_if_let_method.rs | 25 +-- .../src/handlers/replace_let_with_if_let.rs | 24 +-- .../replace_named_generic_with_impl.rs | 6 +- .../src/handlers/toggle_macro_delimiter.rs | 21 +- .../src/handlers/unmerge_imports.rs | 19 +- .../src/handlers/unmerge_match_arm.rs | 12 +- .../src/handlers/unqualify_method_call.rs | 14 +- .../src/handlers/unwrap_return_type.rs | 17 +- .../src/handlers/wrap_return_type.rs | 23 +-- .../src/handlers/wrap_unwrap_cfg_attr.rs | 39 ++-- .../crates/ide-assists/src/utils.rs | 11 +- .../crates/ide-db/src/imports/insert_use.rs | 28 ++- .../src/handlers/type_mismatch.rs | 10 +- 73 files changed, 996 insertions(+), 1088 deletions(-) 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 bfd7dffb058aa..11e1f9dd0cadd 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 @@ -22,7 +22,7 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, make, syntax_factory::SyntaxFactory, + HasName, HasTypeBounds, make, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, ted, @@ -1295,9 +1295,7 @@ fn coerce_pointee_expand( } let (mut editor, strukt) = SyntaxEditor::with_ast_node(strukt); - let make = SyntaxFactory::with_mappings(); - strukt.get_or_create_where_clause(&mut editor, &make, new_predicates.into_iter()); - editor.add_mappings(make.finish_with_mappings()); + strukt.get_or_create_where_clause(&mut editor, new_predicates.into_iter()); let edit = editor.finish(); let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap(); let adt = ast::Adt::Struct(strukt.clone()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index da1322de4b641..1370fe3226190 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs @@ -1,7 +1,7 @@ use either::Either; use syntax::{ AstNode, T, - ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, + ast::{self, edit::AstNodeEdit}, match_ast, }; @@ -56,15 +56,12 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }, expr.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(expr.syntax()); let new_expr = expr.reset_indent().indent(1.into()); - let block_expr = make.block_expr(None, Some(new_expr)); + let block_expr = editor.make().block_expr(None, Some(new_expr)); editor.replace(expr.syntax(), block_expr.indent(expr.indent_level()).syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs index d27a6b4ce7709..5251860ba0527 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs @@ -53,18 +53,17 @@ pub(crate) fn add_explicit_method_call_deref( "Insert explicit method call derefs", dot_token.text_range(), |builder| { - let mut edit = builder.make_editor(method_call_expr.syntax()); - let make = SyntaxFactory::without_mappings(); + let mut editor = builder.make_editor(method_call_expr.syntax()); let mut expr = receiver.clone(); for adjust_kind in adjustments { - expr = adjust_kind.wrap_expr(expr, &make); + expr = adjust_kind.wrap_expr(expr, editor.make()); } - expr = make.expr_paren(expr).into(); - edit.replace(receiver.syntax(), expr.syntax()); + expr = editor.make().expr_paren(expr).into(); + editor.replace(receiver.syntax(), expr.syntax()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 6a408e5254fd6..7e3b55b673e4f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -3,7 +3,7 @@ use ide_db::{ }; use syntax::{ SyntaxToken, T, - ast::{self, AstNode, HasLoopBody, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, HasLoopBody}, syntax_editor::{Position, SyntaxEditor}, }; @@ -42,14 +42,13 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O "Add Label", loop_expr.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(loop_expr.syntax()); - let label = make.lifetime("'l"); + let label = editor.make().lifetime("'l"); let elements = vec![ label.syntax().clone().into(), - make.token(T![:]).into(), - make.whitespace(" ").into(), + editor.make().token(T![:]).into(), + editor.make().whitespace(" ").into(), ]; editor.insert_all(Position::before(&loop_kw), elements); @@ -65,11 +64,10 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O _ => return, }; if let Some(token) = token { - insert_label_after_token(&mut editor, &make, &token, ctx, builder); + insert_label_after_token(&mut editor, &token, ctx, builder); } }); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); builder.rename(); }, @@ -86,13 +84,12 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { fn insert_label_after_token( editor: &mut SyntaxEditor, - make: &SyntaxFactory, token: &SyntaxToken, ctx: &AssistContext<'_>, builder: &mut SourceChangeBuilder, ) { - let label = make.lifetime("'l"); - let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; + let label = editor.make().lifetime("'l"); + let elements = vec![editor.make().whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); if let Some(cap) = ctx.config.snippet_cap { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 44b367059eca3..71a0758773696 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -148,9 +148,9 @@ fn add_missing_impl_members_inner( let target = impl_def.syntax().text_range(); acc.add(AssistId::quick_fix(assist_id), label, target, |edit| { - let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(impl_def.syntax()); let new_item = add_trait_assoc_items_to_impl( - &make, + editor.make(), &ctx.sema, ctx.config, &missing_items, @@ -166,7 +166,7 @@ fn add_missing_impl_members_inner( let mut first_new_item = if let DefaultMethods::No = mode && let ast::AssocItem::Fn(func) = &first_new_item && let Some(body) = try_gen_trait_body( - &make, + editor.make(), ctx, func, trait_ref, @@ -188,14 +188,13 @@ fn add_missing_impl_members_inner( .chain(other_items.iter().cloned()) .collect::>(); - let mut editor = edit.make_editor(impl_def.syntax()); if let Some(assoc_item_list) = impl_def.assoc_item_list() { assoc_item_list.add_items(&mut editor, new_assoc_items); } else { - let assoc_item_list = make.assoc_item_list(new_assoc_items); + let assoc_item_list = editor.make().assoc_item_list(new_assoc_items); editor.insert_all( Position::after(impl_def.syntax()), - vec![make.whitespace(" ").into(), assoc_item_list.syntax().clone().into()], + vec![editor.make().whitespace(" ").into(), assoc_item_list.syntax().clone().into()], ); first_new_item = assoc_item_list.assoc_items().next(); } @@ -218,7 +217,6 @@ fn add_missing_impl_members_inner( editor.add_annotation(first_new_item.syntax(), tabstop); }; }; - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b7510bb82676a..588e65900cb4a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -74,7 +74,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) .collect(); - let make = SyntaxFactory::with_mappings(); + let make = SyntaxFactory::without_mappings(); let scope = ctx.sema.scope(expr.syntax())?; let module = scope.module(); @@ -297,7 +297,6 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } } - editor.add_mappings(make.take()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index c5e722d87e1ae..45d559152bf71 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -94,21 +94,20 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ident.text_range(), |builder| { let mut editor = builder.make_editor(let_stmt.syntax()); - let make = SyntaxFactory::without_mappings(); if let_stmt.semicolon_token().is_none() { editor.insert( Position::last_child_of(let_stmt.syntax()), - make.token(syntax::SyntaxKind::SEMICOLON), + editor.make().token(syntax::SyntaxKind::SEMICOLON), ); } - let placeholder_ty = make.ty_placeholder(); + let placeholder_ty = editor.make().ty_placeholder(); if let Some(pat) = let_stmt.pat() { let elements = vec![ - make.token(syntax::SyntaxKind::COLON).into(), - make.whitespace(" ").into(), + editor.make().token(syntax::SyntaxKind::COLON).into(), + editor.make().whitespace(" ").into(), placeholder_ty.syntax().clone().into(), ]; editor.insert_all(Position::after(pat.syntax()), elements); @@ -141,14 +140,12 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ident.text_range(), |builder| { builder.trigger_parameter_hints(); - - let make = SyntaxFactory::with_mappings(); let mut editor = match &turbofish_target { Either::Left(it) => builder.make_editor(it.syntax()), Either::Right(it) => builder.make_editor(it.syntax()), }; - let fish_head = get_fish_head(&make, number_of_arguments); + let fish_head = get_fish_head(editor.make(), number_of_arguments); match turbofish_target { Either::Left(path_segment) => { @@ -180,8 +177,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti editor.add_annotation(arg.syntax(), builder.make_placeholder_snippet(cap)); } } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) 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 2ea0d76b01617..a3a0c252f6a46 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 @@ -80,10 +80,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti _ => return None, }; - let make = SyntaxFactory::with_mappings(); - let (mut editor, demorganed) = SyntaxEditor::with_ast_node(&bin_expr); - editor.replace(demorganed.op_token()?, make.token(inv_token)); + editor.replace(demorganed.op_token()?, editor.make().token(inv_token)); let mut exprs = VecDeque::from([ (bin_expr.lhs()?, demorganed.lhs()?, prec), @@ -94,13 +92,13 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti if let BinExpr(bin_expr) = &expr { if let BinExpr(cbin_expr) = &demorganed { if op == bin_expr.op_kind()? { - editor.replace(cbin_expr.op_token()?, make.token(inv_token)); + editor.replace(cbin_expr.op_token()?, editor.make().token(inv_token)); exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec)); exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec)); } else { - let mut inv = invert_boolean_expression(&make, expr); + let mut inv = invert_boolean_expression(editor.make(), expr); if precedence(&inv).needs_parentheses_in(prec) { - inv = make.expr_paren(inv).into(); + inv = editor.make().expr_paren(inv).into(); } editor.replace(demorganed.syntax(), inv.syntax()); } @@ -108,15 +106,14 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti return None; } } else { - let mut inv = invert_boolean_expression(&make, demorganed.clone()); + let mut inv = invert_boolean_expression(editor.make(), demorganed.clone()); if precedence(&inv).needs_parentheses_in(prec) { - inv = make.expr_paren(inv).into(); + inv = editor.make().expr_paren(inv).into(); } editor.replace(demorganed.syntax(), inv.syntax()); } } - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); let demorganed = ast::Expr::cast(edit.new_root().clone())?; @@ -126,7 +123,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti "Apply De Morgan's law", op_range, |builder| { - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(bin_expr.syntax()); + let (target_node, result_expr) = if let Some(neg_expr) = bin_expr .syntax() .parent() @@ -141,9 +139,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti bin_expr.syntax().parent().and_then(ast::ParenExpr::cast) { cov_mark::hit!(demorgan_double_parens); - (paren_expr.syntax().clone(), add_bang_paren(&make, demorganed)) + (paren_expr.syntax().clone(), add_bang_paren(editor.make(), demorganed)) } else { - (bin_expr.syntax().clone(), add_bang_paren(&make, demorganed)) + (bin_expr.syntax().clone(), add_bang_paren(editor.make(), demorganed)) }; let final_expr = if target_node @@ -151,14 +149,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti .is_some_and(|p| result_expr.needs_parens_in_place_of(&p, &target_node)) { cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); - make.expr_paren(result_expr).into() + editor.make().expr_paren(result_expr).into() } else { result_expr }; - let mut editor = builder.make_editor(&target_node); editor.replace(&target_node, final_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -206,18 +202,17 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> label, op_range, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(method_call.syntax()); // replace the method name let new_name = match name.text().as_str() { - "all" => make.name_ref("any"), - "any" => make.name_ref("all"), + "all" => editor.make().name_ref("any"), + "any" => editor.make().name_ref("all"), _ => unreachable!(), }; editor.replace(name.syntax(), new_name.syntax()); // negate all tail expressions in the closure body - let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e); + let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, e); walk_expr(&closure_body, &mut |expr| { if let ast::Expr::ReturnExpr(ret_expr) = expr && let Some(ret_expr_arg) = &ret_expr.expr() @@ -238,10 +233,11 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> prefix_expr.op_token().expect("prefix expression always has an operator"), ); } else { - editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG)); + editor.insert( + Position::before(method_call.syntax()), + editor.make().token(SyntaxKind::BANG), + ); } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -269,18 +265,18 @@ fn validate_method_call_expr( it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr)) } -fn tail_cb_impl(editor: &mut SyntaxEditor, make: &SyntaxFactory, e: &ast::Expr) { +fn tail_cb_impl(editor: &mut SyntaxEditor, e: &ast::Expr) { match e { ast::Expr::BreakExpr(break_expr) => { if let Some(break_expr_arg) = break_expr.expr() { - for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, make, e)) + for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, e)) } } ast::Expr::ReturnExpr(_) => { // all return expressions have already been handled by the walk loop } e => { - let inverted_body = invert_boolean_expression(make, e.clone()); + let inverted_body = invert_boolean_expression(editor.make(), e.clone()); editor.replace(e.syntax(), inverted_body.syntax()); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index c36c79ee998b4..8b0b7d3b0713f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -96,12 +96,12 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> let closure_body = ast::Expr::cast(edit.new_root().clone()).unwrap(); let mut editor = builder.make_editor(expr.syntax()); - let make = SyntaxFactory::with_mappings(); let closure_body = match closure_body { ast::Expr::BlockExpr(block) => unwrap_trivial_block(block), e => e, }; - let cond = if invert_cond { invert_boolean_expression(&make, cond) } else { cond }; + let cond = + if invert_cond { invert_boolean_expression(editor.make(), cond) } else { cond }; let parenthesize = matches!( cond, @@ -124,12 +124,12 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::YieldExpr(_) ); - let cond = if parenthesize { make.expr_paren(cond).into() } else { cond }; - let arg_list = make.arg_list(Some(make.expr_closure(None, closure_body).into())); - let mcall = make.expr_method_call(cond, make.name_ref("then"), arg_list); + let cond = if parenthesize { editor.make().expr_paren(cond).into() } else { cond }; + let arg_list = + editor.make().arg_list(Some(editor.make().expr_closure(None, closure_body).into())); + let mcall = + editor.make().expr_method_call(cond, editor.make().name_ref("then"), arg_list); editor.replace(expr.syntax(), mcall.syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -211,22 +211,20 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> let closure_body = ast::BlockExpr::cast(edit.new_root().clone()).unwrap(); let mut editor = builder.make_editor(mcall.syntax()); - let make = SyntaxFactory::with_mappings(); let cond = match &receiver { ast::Expr::ParenExpr(expr) => expr.expr().unwrap_or(receiver), _ => receiver, }; - let if_expr = make + let if_expr = editor + .make() .expr_if( cond, closure_body, - Some(ast::ElseBranch::Block(make.block_expr(None, Some(none_path)))), + Some(ast::ElseBranch::Block(editor.make().block_expr(None, Some(none_path)))), ) .indent(mcall.indent_level()); editor.replace(mcall.syntax().clone(), if_expr.syntax().clone()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index a5c29a45a51f2..07ebe981b4f1c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -2,7 +2,7 @@ use hir::{Name, sym}; use ide_db::{famous_defs::FamousDefs, syntax_helpers::suggest_name}; use syntax::{ AstNode, - ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel, syntax_factory::SyntaxFactory}, + ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel}, syntax_editor::Position, }; @@ -48,22 +48,24 @@ pub(crate) fn convert_for_loop_to_while_let( "Replace this for loop with `while let`", for_loop.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(for_loop.syntax()); let (iterable, method) = if impls_core_iter(&ctx.sema, &iterable) { (iterable, None) } else if let Some((expr, method)) = is_ref_and_impls_iter_method(&ctx.sema, &iterable) { - (expr, Some(make.name_ref(method.as_str()))) + (expr, Some(editor.make().name_ref(method.as_str()))) } else if let ast::Expr::RefExpr(_) = iterable { - (make.expr_paren(iterable).into(), Some(make.name_ref("into_iter"))) + ( + editor.make().expr_paren(iterable).into(), + Some(editor.make().name_ref("into_iter")), + ) } else { - (iterable, Some(make.name_ref("into_iter"))) + (iterable, Some(editor.make().name_ref("into_iter"))) }; let iterable = if let Some(method) = method { - make.expr_method_call(iterable, method, make.arg_list([])).into() + editor.make().expr_method_call(iterable, method, editor.make().arg_list([])).into() } else { iterable }; @@ -73,8 +75,8 @@ pub(crate) fn convert_for_loop_to_while_let( ); let tmp_var = new_name.suggest_name("iter"); - let mut_expr = make.let_stmt( - make.ident_pat(false, true, make.name(&tmp_var)).into(), + let mut_expr = editor.make().let_stmt( + editor.make().ident_pat(false, true, editor.make().name(&tmp_var)).into(), None, Some(iterable), ); @@ -82,35 +84,29 @@ pub(crate) fn convert_for_loop_to_while_let( if let Some(label) = for_loop.label() { let label = label.syntax(); - editor.insert(Position::before(for_loop.syntax()), make.whitespace(" ")); + editor.insert(Position::before(for_loop.syntax()), editor.make().whitespace(" ")); editor.insert(Position::before(for_loop.syntax()), label); } - crate::utils::insert_attributes( - for_loop.syntax(), - &mut editor, - for_loop.attrs(), - &make, - ); + crate::utils::insert_attributes(for_loop.syntax(), &mut editor, for_loop.attrs()); editor.insert( Position::before(for_loop.syntax()), - make.whitespace(format!("\n{indent}").as_str()), + editor.make().whitespace(format!("\n{indent}").as_str()), ); editor.insert(Position::before(for_loop.syntax()), mut_expr.syntax()); - let opt_pat = make.tuple_struct_pat(make.ident_path("Some"), [pat]); - let iter_next_expr = make.expr_method_call( - make.expr_path(make.ident_path(&tmp_var)), - make.name_ref("next"), - make.arg_list([]), + let opt_pat = editor.make().tuple_struct_pat(editor.make().ident_path("Some"), [pat]); + let iter_next_expr = editor.make().expr_method_call( + editor.make().expr_path(editor.make().ident_path(&tmp_var)), + editor.make().name_ref("next"), + editor.make().arg_list([]), ); - let cond = make.expr_let(opt_pat.into(), iter_next_expr.into()); + let cond = editor.make().expr_let(opt_pat.into(), iter_next_expr.into()); - let while_loop = make.expr_while_loop(cond.into(), body); + let while_loop = editor.make().expr_while_loop(cond.into(), body); editor.replace(for_loop.syntax(), while_loop.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 66ccd2a4e4093..dc977b3447419 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -74,24 +74,32 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> "Convert From to TryFrom", impl_.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(impl_.syntax()); - editor.replace(trait_ty.syntax(), make.ty(&format!("TryFrom<{from_type}>")).syntax()); + editor.replace( + trait_ty.syntax(), + editor.make().ty(&format!("TryFrom<{from_type}>")).syntax(), + ); editor.replace( from_fn_return_type.syntax(), - make.ty("Result").syntax(), + editor.make().ty("Result").syntax(), ); - editor.replace(from_fn_name.syntax(), make.name("try_from").syntax()); - editor.replace(tail_expr.syntax(), wrap_ok(&make, tail_expr.clone()).syntax()); + editor.replace(from_fn_name.syntax(), editor.make().name("try_from").syntax()); + editor.replace(tail_expr.syntax(), wrap_ok(editor.make(), tail_expr.clone()).syntax()); for r in return_exprs { - let t = r.expr().unwrap_or_else(|| make.expr_unit()); - editor.replace(t.syntax(), wrap_ok(&make, t.clone()).syntax()); + let t = r.expr().unwrap_or_else(|| editor.make().expr_unit()); + editor.replace(t.syntax(), wrap_ok(editor.make(), t.clone()).syntax()); } - let error_type_alias = - make.ty_alias(None, "Error", None, None, None, Some((make.ty("()"), None))); + let error_type_alias = editor.make().ty_alias( + None, + "Error", + None, + None, + None, + Some((editor.make().ty("()"), None)), + ); let error_type = ast::AssocItem::TypeAlias(error_type_alias); if let Some(cap) = ctx.config.snippet_cap @@ -106,12 +114,11 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> editor.insert_all( Position::after(associated_l_curly), vec![ - make.whitespace(&format!("\n{indent}")).syntax_element(), + editor.make().whitespace(&format!("\n{indent}")).syntax_element(), error_type.syntax().syntax_element(), - make.whitespace("\n").syntax_element(), + editor.make().whitespace("\n").syntax_element(), ], ); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 63b1a0193bd6d..a0dcf62e25d46 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -3,7 +3,7 @@ use ide_db::famous_defs::FamousDefs; use stdx::format_to; use syntax::{ AstNode, - ast::{self, HasArgList, HasLoopBody, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, + ast::{self, HasArgList, HasLoopBody, edit::AstNodeEdit}, }; use crate::{AssistContext, AssistId, Assists}; @@ -57,20 +57,20 @@ pub(crate) fn convert_iter_for_each_to_for( "Replace this `Iterator::for_each` with a for loop", range, |builder| { - let make = SyntaxFactory::with_mappings(); + let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax); + let mut editor = builder.make_editor(target_node); let indent = stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level); let block = match body { ast::Expr::BlockExpr(block) => block.reset_indent(), - _ => make.block_expr(Vec::new(), Some(body.reset_indent().indent(1.into()))), + _ => { + editor.make().block_expr(Vec::new(), Some(body.reset_indent().indent(1.into()))) + } } .indent(indent); - let expr_for_loop = make.expr_for_loop(param, receiver, block); - - let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax); - let mut editor = builder.make_editor(target_node); + let expr_for_loop = editor.make().expr_for_loop(param, receiver, block); editor.replace(target_node, expr_for_loop.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 994fb44279b6a..75d7d0a836e3f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -1,7 +1,6 @@ use syntax::T; use syntax::ast::RangeItem; use syntax::ast::edit::AstNodeEdit; -use syntax::ast::syntax_factory::SyntaxFactory; use syntax::ast::{self, AstNode, HasName, LetStmt, Pat}; use syntax::syntax_editor::SyntaxEditor; @@ -26,15 +25,13 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let root = ctx.source_file().syntax().clone(); - let (mut editor, _) = SyntaxEditor::new(root); + let (mut editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); // Should focus on the `else` token to trigger let let_stmt = ctx .find_token_syntax_at_offset(T![else]) .and_then(|it| it.parent()?.parent()) .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?; let let_stmt = LetStmt::cast(let_stmt)?; - let make = SyntaxFactory::with_mappings(); let else_block = let_stmt.let_else()?.block_expr()?; let else_expr = if else_block.statements().next().is_none() && let Some(tail_expr) = else_block.tail_expr() @@ -50,7 +47,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' } let pat = let_stmt.pat()?; let mut idents = Vec::default(); - let pat_without_mut = remove_mut_and_collect_idents(&make, &mut editor, &pat, &mut idents)?; + let pat_without_mut = remove_mut_and_collect_idents(&mut editor, &pat, &mut idents)?; let bindings = idents .into_iter() .filter_map(|ref pat| { @@ -72,14 +69,14 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' }, let_stmt.syntax().text_range(), |builder| { - // let mut editor = builder.make_editor(let_stmt.syntax()); - let binding_paths = bindings .iter() - .map(|(name, _)| make.expr_path(make.ident_path(&name.to_string()))) + .map(|(name, _)| { + editor.make().expr_path(editor.make().ident_path(&name.to_string())) + }) .collect::>(); - let binding_arm = make.match_arm( + let binding_arm = editor.make().match_arm( pat_without_mut, None, // There are three possible cases: @@ -88,15 +85,16 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' // - Single binding: `Some(it) => it` // - Multiple bindings: `Foo::Bar { a, b, .. } => (a, b)` match binding_paths.len() { - 0 => make.expr_empty_block().into(), + 0 => editor.make().expr_empty_block().into(), 1 => binding_paths[0].clone(), - _ => make.expr_tuple(binding_paths).into(), + _ => editor.make().expr_tuple(binding_paths).into(), }, ); - let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr); + let else_arm = + editor.make().match_arm(editor.make().wildcard_pat().into(), None, else_expr); let arms = [binding_arm, else_arm].map(|arm| arm.indent(1.into())); - let match_ = make.expr_match(init, make.match_arm_list(arms)); + let match_ = editor.make().expr_match(init, editor.make().match_arm_list(arms)); let match_ = match_.indent(let_stmt.indent_level()); if bindings.is_empty() { @@ -104,28 +102,25 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' } else { let ident_pats = bindings .into_iter() - .map(|(name, is_mut)| make.ident_pat(false, is_mut, name).into()) + .map(|(name, is_mut)| editor.make().ident_pat(false, is_mut, name).into()) .collect::>(); - let new_let_stmt = make.let_stmt( + let new_let_stmt = editor.make().let_stmt( if ident_pats.len() == 1 { ident_pats[0].clone() } else { - make.tuple_pat(ident_pats).into() + editor.make().tuple_pat(ident_pats).into() }, None, Some(match_.into()), ); editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } fn remove_mut_and_collect_idents( - make: &SyntaxFactory, editor: &mut SyntaxEditor, pat: &ast::Pat, acc: &mut Vec, @@ -133,103 +128,101 @@ fn remove_mut_and_collect_idents( Some(match pat { ast::Pat::IdentPat(p) => { acc.push(p.clone()); - let non_mut_pat = make.ident_pat( + let non_mut_pat = editor.make().ident_pat( p.ref_token().is_some(), p.ref_token().is_some() && p.mut_token().is_some(), p.name()?, ); let non_mut_pat = if let Some(inner) = p.pat() { - non_mut_pat.set_pat( - remove_mut_and_collect_idents(make, editor, &inner, acc), - editor, - make, - ) + non_mut_pat.set_pat(remove_mut_and_collect_idents(editor, &inner, acc), editor) } else { non_mut_pat }; non_mut_pat.into() } ast::Pat::BoxPat(p) => { - make.box_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() + let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; + editor.make().box_pat(pat).into() + } + ast::Pat::OrPat(p) => { + let pats = p + .pats() + .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc)) + .collect::>>()?; + editor.make().or_pat(pats, p.leading_pipe().is_some()).into() } - ast::Pat::OrPat(p) => make - .or_pat( - p.pats() - .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) - .collect::>>()?, - p.leading_pipe().is_some(), - ) - .into(), ast::Pat::ParenPat(p) => { - make.paren_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() + let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; + editor.make().paren_pat(pat).into() + } + ast::Pat::RangePat(p) => { + let start = if let Some(start) = p.start() { + Some(remove_mut_and_collect_idents(editor, &start, acc)?) + } else { + None + }; + let end = if let Some(end) = p.end() { + Some(remove_mut_and_collect_idents(editor, &end, acc)?) + } else { + None + }; + editor.make().range_pat(start, end).into() + } + ast::Pat::RecordPat(p) => { + let fields = p + .record_pat_field_list()? + .fields() + .map(|field| { + remove_mut_and_collect_idents(editor, &field.pat()?, acc).map(|pat| { + if let Some(name_ref) = field.name_ref() { + editor.make().record_pat_field(name_ref, pat) + } else { + editor.make().record_pat_field_shorthand(pat) + } + }) + }) + .collect::>>()?; + editor + .make() + .record_pat_with_fields( + p.path()?, + editor + .make() + .record_pat_field_list(fields, p.record_pat_field_list()?.rest_pat()), + ) + .into() } - ast::Pat::RangePat(p) => make - .range_pat( - if let Some(start) = p.start() { - Some(remove_mut_and_collect_idents(make, editor, &start, acc)?) - } else { - None - }, - if let Some(end) = p.end() { - Some(remove_mut_and_collect_idents(make, editor, &end, acc)?) - } else { - None - }, - ) - .into(), - ast::Pat::RecordPat(p) => make - .record_pat_with_fields( - p.path()?, - make.record_pat_field_list( - p.record_pat_field_list()? - .fields() - .map(|field| { - remove_mut_and_collect_idents(make, editor, &field.pat()?, acc).map( - |pat| { - if let Some(name_ref) = field.name_ref() { - make.record_pat_field(name_ref, pat) - } else { - make.record_pat_field_shorthand(pat) - } - }, - ) - }) - .collect::>>()?, - p.record_pat_field_list()?.rest_pat(), - ), - ) - .into(), ast::Pat::RefPat(p) => { let inner = p.pat()?; if let ast::Pat::IdentPat(ident) = inner { acc.push(ident); p.clone().into() } else { - make.ref_pat(remove_mut_and_collect_idents(make, editor, &inner, acc)?).into() + let pat = remove_mut_and_collect_idents(editor, &inner, acc)?; + editor.make().ref_pat(pat).into() } } - ast::Pat::SlicePat(p) => make - .slice_pat( - p.pats() - .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) - .collect::>>()?, - ) - .into(), - ast::Pat::TuplePat(p) => make - .tuple_pat( - p.fields() - .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) - .collect::>>()?, - ) - .into(), - ast::Pat::TupleStructPat(p) => make - .tuple_struct_pat( - p.path()?, - p.fields() - .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) - .collect::>>()?, - ) - .into(), + ast::Pat::SlicePat(p) => { + let pats = p + .pats() + .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc)) + .collect::>>()?; + editor.make().slice_pat(pats).into() + } + ast::Pat::TuplePat(p) => { + let pats = p + .fields() + .map(|field| remove_mut_and_collect_idents(editor, &field, acc)) + .collect::>>()?; + editor.make().tuple_pat(pats).into() + } + ast::Pat::TupleStructPat(p) => { + let fields = p + .fields() + .map(|field| remove_mut_and_collect_idents(editor, &field, acc)) + .collect::>>()?; + editor.make().tuple_struct_pat(p.path()?, fields).into() + } ast::Pat::RestPat(_) | ast::Pat::LiteralPat(_) | ast::Pat::PathPat(_) 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 4b132d68ee3a5..54784983d7bdf 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 @@ -1,7 +1,7 @@ use ide_db::defs::{Definition, NameRefClass}; use syntax::{ AstNode, SyntaxNode, - ast::{self, HasName, Name, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, + ast::{self, HasName, Name, edit::AstNodeEdit}, syntax_editor::SyntaxEditor, }; @@ -122,7 +122,6 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti // Rename `extracted` with `binding` in `pat`. fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode { let (mut editor, syntax) = SyntaxEditor::new(pat.syntax().clone()); - let make = SyntaxFactory::with_mappings(); let extracted = extracted .iter() .map(|e| e.syntax().text_range() - pat.syntax().text_range().start()) @@ -137,7 +136,9 @@ 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()) + editor + .make() + .record_pat_field(editor.make().name_ref(&name_ref.text()), binding.clone()) .syntax(), ); } @@ -145,7 +146,6 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn editor.replace(extracted_syntax, binding.syntax()); } } - editor.add_mappings(make.finish_with_mappings()); let new_node = editor.finish().new_root().clone(); if let Some(pat) = ast::Pat::cast(new_node.clone()) { pat.dedent(1.into()).syntax().clone() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 4ea56e3e613fb..42f310c35eb75 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -3,9 +3,7 @@ use ide_db::{defs::Definition, search::FileReference}; use syntax::{ NodeOrToken, SyntaxKind, SyntaxNode, T, algo::next_non_trivia_token, - ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility}, match_ast, syntax_editor::{Element, Position, SyntaxEditor}, }; @@ -113,21 +111,20 @@ fn edit_struct_def( Some(field) }); - let make = SyntaxFactory::without_mappings(); - let mut edit = builder.make_editor(strukt.syntax()); + let mut editor = builder.make_editor(strukt.syntax()); - let tuple_fields = make.tuple_field_list(tuple_fields); + let tuple_fields = editor.make().tuple_field_list(tuple_fields); let mut elements = vec![tuple_fields.syntax().clone().into()]; if let Either::Left(strukt) = strukt { if let Some(w) = strukt.where_clause() { - edit.delete(w.syntax()); + editor.delete(w.syntax()); elements.extend([ - make.whitespace("\n").into(), + editor.make().whitespace("\n").into(), remove_trailing_comma(w).into(), - make.token(T![;]).into(), - make.whitespace("\n").into(), + editor.make().token(T![;]).into(), + editor.make().whitespace("\n").into(), ]); if let Some(tok) = strukt @@ -136,23 +133,23 @@ fn edit_struct_def( .and_then(|tok| tok.next_token()) .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) { - edit.delete(tok); + editor.delete(tok); } } else { - elements.push(make.token(T![;]).into()); + elements.push(editor.make().token(T![;]).into()); } } - edit.replace_with_many(record_fields.syntax(), elements); + editor.replace_with_many(record_fields.syntax(), elements); if let Some(tok) = record_fields .l_curly_token() .and_then(|tok| tok.prev_token()) .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) { - edit.delete(tok) + editor.delete(tok) } - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); } fn edit_struct_references( @@ -232,7 +229,7 @@ fn process_struct_name_reference( fn record_to_tuple_struct_like( ctx: &AssistContext<'_>, source: &ast::SourceFile, - edit: &mut SyntaxEditor, + editor: &mut SyntaxEditor, field_list: T, fields: impl FnOnce(&T) -> I, ) -> Option<()> @@ -240,7 +237,6 @@ where T: AstNode, I: IntoIterator, { - let make = SyntaxFactory::without_mappings(); let orig = ctx.sema.original_range_opt(field_list.syntax())?; let list_range = cover_edit_range(source.syntax(), orig.range); @@ -254,13 +250,13 @@ where }; if l_curly.kind() == T!['{'] { - delete_whitespace(edit, l_curly.prev_token()); - delete_whitespace(edit, l_curly.next_token()); - edit.replace(l_curly, make.token(T!['('])); + delete_whitespace(editor, l_curly.prev_token()); + delete_whitespace(editor, l_curly.next_token()); + editor.replace(l_curly, editor.make().token(T!['('])); } if r_curly.kind() == T!['}'] { - delete_whitespace(edit, r_curly.prev_token()); - edit.replace(r_curly, make.token(T![')'])); + delete_whitespace(editor, r_curly.prev_token()); + editor.replace(r_curly, editor.make().token(T![')'])); } for name_ref in fields(&field_list) { @@ -270,14 +266,14 @@ where if let Some(colon) = next_non_trivia_token(name_range.end().clone()) && colon.kind() == T![:] { - edit.delete(&colon); - edit.delete_all(name_range); + editor.delete(&colon); + editor.delete_all(name_range); if let Some(next) = next_non_trivia_token(colon.clone()) && next.kind() != T!['}'] { // Avoid overlapping delete whitespace on `{ field: }` - delete_whitespace(edit, colon.next_token()); + delete_whitespace(editor, colon.next_token()); } } } @@ -289,7 +285,6 @@ fn edit_field_references( builder: &mut SourceChangeBuilder, fields: impl Iterator, ) { - let make = SyntaxFactory::without_mappings(); for (index, field) in fields.enumerate() { let field = match ctx.sema.to_def(&field) { Some(it) => it, @@ -299,21 +294,23 @@ fn edit_field_references( let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let mut edit = builder.make_editor(source.syntax()); + let mut editor = builder.make_editor(source.syntax()); for r in refs { if let Some(name_ref) = r.name.as_name_ref() { // Only edit the field reference if it's part of a `.field` access if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { - edit.replace_all( + editor.replace_all( cover_edit_range(source.syntax(), r.range), - vec![make.name_ref(&index.to_string()).syntax().clone().into()], + vec![ + editor.make().name_ref(&index.to_string()).syntax().clone().into(), + ], ); } } } - builder.add_file_edits(file_id.file_id(ctx.db()), edit); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); } } } 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 61393950767f8..b0b9695f0f3a8 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 @@ -5,10 +5,7 @@ use syntax::{ SyntaxKind::WHITESPACE, T, algo::previous_non_trivia_token, - ast::{ - self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make, - syntax_factory::SyntaxFactory, - }, + ast::{self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make}, syntax_editor::{Element, Position, SyntaxEditor}, }; @@ -55,56 +52,49 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext< description, for_.syntax().text_range(), |builder| { - let mut edit = builder.make_editor(for_.syntax()); - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(for_.syntax()); let indent = for_.indent_level(); - let pat = make.ident_pat(pat.ref_token().is_some(), true, name.clone()); - let let_stmt = make.let_stmt(pat.into(), None, Some(start)); - edit.insert_all( + let pat = editor.make().ident_pat(pat.ref_token().is_some(), true, name.clone()); + let let_stmt = editor.make().let_stmt(pat.into(), None, Some(start)); + editor.insert_all( Position::before(for_.syntax()), vec![ let_stmt.syntax().syntax_element(), - make.whitespace(&format!("\n{}", indent)).syntax_element(), + editor.make().whitespace(&format!("\n{}", indent)).syntax_element(), ], ); let mut elements = vec![]; - let var_expr = make.expr_path(make.ident_path(&name.text())); + let var_expr = editor.make().expr_path(editor.make().ident_path(&name.text())); let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: !inclusive, }); if let Some(end) = end { elements.extend([ - make.token(T![while]).syntax_element(), - make.whitespace(" ").syntax_element(), - make.expr_bin(var_expr.clone(), op, end).syntax().syntax_element(), + editor.make().token(T![while]).syntax_element(), + editor.make().whitespace(" ").syntax_element(), + editor.make().expr_bin(var_expr.clone(), op, end).syntax().syntax_element(), ]); } else { - elements.push(make.token(T![loop]).syntax_element()); + elements.push(editor.make().token(T![loop]).syntax_element()); } - edit.replace_all( + editor.replace_all( for_kw.syntax_element()..=iterable.syntax().syntax_element(), elements, ); let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) }; - process_loop_body( - body, - label, - &mut edit, - vec![ - make.whitespace(&format!("\n{}", indent + 1)).syntax_element(), - make.expr_bin(var_expr, op, step).syntax().syntax_element(), - make.token(T![;]).syntax_element(), - ], - ); - - edit.add_mappings(make.finish_with_mappings()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + let incrementer = vec![ + editor.make().whitespace(&format!("\n{}", indent + 1)).syntax_element(), + editor.make().expr_bin(var_expr, op, step).syntax().syntax_element(), + editor.make().token(T![;]).syntax_element(), + ]; + process_loop_body(body, label, &mut editor, incrementer); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 004d09acac6e2..f62cf462b1df3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -130,7 +130,7 @@ fn if_expr_to_guarded_return( "Convert to guarded return", target, |edit| { - let make = SyntaxFactory::without_mappings(); + let mut editor = edit.make_editor(if_expr.syntax()); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); let early_expression = else_block.make_early_block(&ctx.sema, &make); let replacement = let_chains.into_iter().map(|expr| { @@ -170,7 +170,6 @@ fn if_expr_to_guarded_return( .take_while(|i| *i != end_of_then), ) .collect(); - let mut editor = edit.make_editor(if_expr.syntax()); editor.replace_with_many(if_expr.syntax(), then_statements); edit.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -209,22 +208,20 @@ fn let_stmt_to_guarded_return( "Convert to guarded return", target, |edit| { + let mut editor = edit.make_editor(let_stmt.syntax()); let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); - let make = SyntaxFactory::without_mappings(); let replacement = { - let let_else_stmt = make.let_else_stmt( + let let_else_stmt = editor.make().let_else_stmt( happy_pattern, let_stmt.ty(), expr.reset_indent(), - else_block.make_early_block(&ctx.sema, &make), + else_block.make_early_block(&ctx.sema, editor.make()), ); let let_else_stmt = let_else_stmt.indent(let_indent_level); let_else_stmt.syntax().clone() }; - let mut editor = edit.make_editor(let_stmt.syntax()); editor.replace(let_stmt.syntax(), replacement); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -277,12 +274,10 @@ impl<'db> ElseBlock<'db> { }; let whitespace = last_element.prev_sibling_or_token().filter(|it| it.kind() == WHITESPACE); - let make = SyntaxFactory::without_mappings(); - if let Some(tail_expr) = block_expr.tail_expr() && !self.kind.is_unit() { - let early_expr = self.kind.make_early_expr(sema, &make, Some(tail_expr.clone())); + let early_expr = self.kind.make_early_expr(sema, make, Some(tail_expr.clone())); edit.replace(tail_expr.syntax(), early_expr.syntax()); } else { let last_stmt = match block_expr.tail_expr() { @@ -291,7 +286,7 @@ impl<'db> ElseBlock<'db> { }; let whitespace = make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string())); - let early_expr = self.kind.make_early_expr(sema, &make, None).syntax().clone().into(); + let early_expr = self.kind.make_early_expr(sema, make, None).syntax().clone().into(); edit.replace_with_many( last_element, vec![last_stmt.into(), whitespace.into(), early_expr], diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 1740cd024a89c..3d03ccc484770 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -72,15 +72,14 @@ pub(crate) fn convert_tuple_return_type_to_struct( "Convert tuple return type to tuple struct", target, move |edit| { - let mut syntax_editor = edit.make_editor(ret_type.syntax()); - let syntax_factory = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(ret_type.syntax()); let usages = Definition::Function(fn_def).usages(&ctx.sema).all(); let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string())); let parent = fn_.syntax().ancestors().find_map(>::cast); add_tuple_struct_def( edit, - &syntax_factory, + editor.make(), ctx, &usages, parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()), @@ -89,22 +88,19 @@ pub(crate) fn convert_tuple_return_type_to_struct( &target_module, ); - syntax_editor.replace( + editor.replace( ret_type.syntax(), - syntax_factory.ret_type(syntax_factory.ty(&struct_name)).syntax(), + editor.make().ret_type(editor.make().ty(&struct_name)).syntax(), ); if let Some(fn_body) = fn_.body() { replace_body_return_values( - &mut syntax_editor, - &syntax_factory, + &mut editor, ast::Expr::BlockExpr(fn_body), &struct_name, ); } - - syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); - edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); + edit.add_file_edits(ctx.vfs_file_id(), editor); replace_usages(edit, ctx, &usages, &struct_name, &target_module); }, @@ -123,10 +119,9 @@ fn replace_usages( let Some(first_ref) = references.first() else { continue }; let mut editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap()); - let syntax_factory = SyntaxFactory::with_mappings(); let refs_with_imports = augment_references_with_imports( - &syntax_factory, + editor.make(), ctx, references, struct_name, @@ -140,14 +135,13 @@ fn replace_usages( if let Some(ret_type) = fn_.ret_type() { editor.replace( ret_type.syntax(), - syntax_factory.ret_type(syntax_factory.ty(struct_name)).syntax(), + editor.make().ret_type(editor.make().ty(struct_name)).syntax(), ); } if let Some(fn_body) = fn_.body() { replace_body_return_values( &mut editor, - &syntax_factory, ast::Expr::BlockExpr(fn_body), struct_name, ); @@ -172,9 +166,10 @@ fn replace_usages( for tuple_pat in tuple_pats { editor.replace( tuple_pat.syntax(), - syntax_factory + editor + .make() .tuple_struct_pat( - syntax_factory.path_from_text(struct_name), + editor.make().path_from_text(struct_name), tuple_pat.fields(), ) .syntax(), @@ -182,17 +177,9 @@ fn replace_usages( } } if let Some((import_scope, path)) = import_data { - insert_use_with_editor( - &import_scope, - path, - &ctx.config.insert_use, - &mut editor, - &syntax_factory, - ); + insert_use_with_editor(&import_scope, path, &ctx.config.insert_use, &mut editor); } }); - - editor.add_mappings(syntax_factory.finish_with_mappings()); edit.add_file_edits(file_id.file_id(ctx.db()), editor); } } @@ -298,7 +285,6 @@ fn add_tuple_struct_def( /// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`. fn replace_body_return_values( syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, body: ast::Expr, struct_name: &str, ) { @@ -316,9 +302,9 @@ fn replace_body_return_values( for ret_expr in exprs_to_wrap { if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr { - let struct_constructor = syntax_factory.expr_call( - syntax_factory.expr_path(syntax_factory.ident_path(struct_name)), - syntax_factory.arg_list(tuple_expr.fields()), + let struct_constructor = syntax_editor.make().expr_call( + syntax_editor.make().expr_path(syntax_editor.make().ident_path(struct_name)), + syntax_editor.make().arg_list(tuple_expr.fields()), ); syntax_editor.replace(ret_expr.syntax(), struct_constructor.syntax()); } 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 4ce7a9d866a9f..5a57e12253731 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 @@ -86,9 +86,9 @@ pub(crate) fn convert_tuple_struct_to_named_struct( "Convert to named struct", target, |edit| { - let names = generate_names(tuple_fields.fields()); - edit_field_references(ctx, edit, tuple_fields.fields(), &names); let mut editor = edit.make_editor(syntax); + let names = generate_names(tuple_fields.fields(), editor.make()); + edit_field_references(ctx, edit, tuple_fields.fields(), &names); edit_struct_references(ctx, edit, strukt_def, &names); edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names); edit.add_file_edits(ctx.vfs_file_id(), editor); @@ -111,8 +111,7 @@ fn edit_struct_def( ); ast::RecordField::cast(field_editor.finish().new_root().clone()) }); - let make = SyntaxFactory::without_mappings(); - let record_fields = make.record_field_list(record_fields); + let record_fields = editor.make().record_field_list(record_fields); let tuple_fields_before = Position::before(tuple_fields.syntax()); if let Either::Left(strukt) = strukt { @@ -171,7 +170,6 @@ fn process_struct_name_reference( strukt_def: &Definition, names: &[ast::Name], ) -> Option<()> { - let make = SyntaxFactory::without_mappings(); let name_ref = r.name.as_name_ref()?; let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; let full_path = path_segment.syntax().parent().and_then(ast::Path::cast)?.top_path(); @@ -187,7 +185,7 @@ fn process_struct_name_reference( match parent { ast::TupleStructPat(tuple_struct_pat) => { let range = ctx.sema.original_range_opt(tuple_struct_pat.syntax())?.range; - let new = make.record_pat_with_fields( + let new = editor.make().record_pat_with_fields( full_path, generate_record_pat_list(&tuple_struct_pat, names), ); @@ -209,9 +207,9 @@ 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.token(T![:]).into(), - make.whitespace(" ").into(), + editor.make().name_ref(&name.text()).syntax().clone().into(), + editor.make().token(T![:]).into(), + editor.make().whitespace(" ").into(), ]; if first_insert.is_empty() { // XXX: SyntaxEditor cannot insert after deleted element @@ -247,14 +245,13 @@ fn process_delimiter( syntax::NodeOrToken::Token(t) => Some(t.clone()), }; - let make = SyntaxFactory::without_mappings(); if let Some(l_paren) = l_paren && l_paren.kind() == T!['('] { let mut open_delim = vec![ - make.whitespace(" ").into(), - make.token(T!['{']).into(), - make.whitespace(" ").into(), + editor.make().whitespace(" ").into(), + editor.make().token(T!['{']).into(), + editor.make().whitespace(" ").into(), ]; open_delim.extend(first_insert); editor.replace_with_many(l_paren, open_delim); @@ -264,7 +261,7 @@ fn process_delimiter( { editor.replace_with_many( r_paren, - vec![make.whitespace(" ").into(), make.token(T!['}']).into()], + vec![editor.make().whitespace(" ").into(), editor.make().token(T!['}']).into()], ); } } @@ -300,8 +297,10 @@ fn edit_field_references( } } -fn generate_names(fields: impl Iterator) -> Vec { - let make = SyntaxFactory::without_mappings(); +fn generate_names( + fields: impl Iterator, + make: &SyntaxFactory, +) -> Vec { fields .enumerate() .map(|(i, _)| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index f8215d6723d3f..f758a4dab6108 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -6,7 +6,6 @@ use syntax::{ ast::{ self, HasLoopBody, edit::{AstNodeEdit, IndentLevel}, - syntax_factory::SyntaxFactory, }, syntax_editor::{Element, Position}, }; @@ -52,48 +51,50 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) "Convert while to loop", target, |builder| { - let make = SyntaxFactory::without_mappings(); - let mut edit = builder.make_editor(while_expr.syntax()); + let mut editor = builder.make_editor(while_expr.syntax()); let while_indent_level = IndentLevel::from_node(while_expr.syntax()); - let break_block = make + let break_block = editor + .make() .block_expr( - iter::once(make.expr_stmt(make.expr_break(None, None).into()).into()), + iter::once( + editor.make().expr_stmt(editor.make().expr_break(None, None).into()).into(), + ), None, ) .indent(IndentLevel(1)); - edit.replace_all( + editor.replace_all( while_kw.syntax_element()..=while_cond.syntax().syntax_element(), - vec![make.token(T![loop]).syntax_element()], + vec![editor.make().token(T![loop]).syntax_element()], ); if is_pattern_cond(while_cond.clone()) { let then_branch = while_body.reset_indent().indent(IndentLevel(1)); - let if_expr = make.expr_if(while_cond, then_branch, Some(break_block.into())); - let stmts = iter::once(make.expr_stmt(if_expr.into()).into()); - let block_expr = make.block_expr(stmts, None); - edit.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax()); + let if_expr = + editor.make().expr_if(while_cond, then_branch, Some(break_block.into())); + let stmts = iter::once(editor.make().expr_stmt(if_expr.into()).into()); + let block_expr = editor.make().block_expr(stmts, None); + editor.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax()); } else { - let if_cond = invert_boolean_expression(&make, while_cond); - let if_expr = make.expr_if(if_cond, break_block, None).indent(while_indent_level); + let if_cond = invert_boolean_expression(editor.make(), while_cond); + let if_expr = + editor.make().expr_if(if_cond, break_block, None).indent(while_indent_level); if !while_body.syntax().text().contains_char('\n') { - edit.insert( + editor.insert( Position::after(&l_curly), - make.whitespace(&format!("\n{while_indent_level}")), + editor.make().whitespace(&format!("\n{while_indent_level}")), ); } - edit.insert_all( + editor.insert_all( Position::after(&l_curly), vec![ - make.whitespace(&format!("\n{}", while_indent_level + 1)).into(), + editor.make().whitespace(&format!("\n{}", while_indent_level + 1)).into(), if_expr.syntax().syntax_element(), ], ); }; - - edit.add_mappings(make.finish_with_mappings()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index ec4a83b642c01..1ccbd80e60ccd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -145,19 +145,15 @@ struct StructEditData { } impl StructEditData { - fn apply_to_destruct( - &self, - new_pat: ast::Pat, - editor: &mut SyntaxEditor, - make: &SyntaxFactory, - ) { + fn apply_to_destruct(&self, new_pat: ast::Pat, editor: &mut SyntaxEditor) { match &self.target { Target::IdentPat(pat) => { // If the binding is nested inside a record, we need to wrap the new // destructured pattern in a non-shorthand record field if self.need_record_field_name { - let new_pat = - make.record_pat_field(make.name_ref(&self.name.to_string()), new_pat); + let new_pat = editor + .make() + .record_pat_field(editor.make().name_ref(&self.name.to_string()), new_pat); editor.replace(pat.syntax(), new_pat.syntax()) } else { editor.replace(pat.syntax(), new_pat.syntax()) @@ -165,9 +161,9 @@ impl StructEditData { } Target::SelfParam { insert_after, .. } => { let indent = IndentLevel::from_token(insert_after) + 1; - let newline = make.whitespace(&format!("\n{indent}")); - let initializer = make.expr_path(make.ident_path("self")); - let let_stmt = make.let_stmt(new_pat, None, Some(initializer)); + let newline = editor.make().whitespace(&format!("\n{indent}")); + let initializer = editor.make().expr_path(editor.make().ident_path("self")); + let let_stmt = editor.make().let_stmt(new_pat, None, Some(initializer)); editor.insert_all( Position::after(insert_after), vec![newline.into(), let_stmt.syntax().clone().into()], @@ -283,39 +279,46 @@ fn destructure_pat( let is_ref = data.target.is_ref(); let is_mut = data.target.is_mut(); - let make = SyntaxFactory::with_mappings(); let new_pat = match data.kind { hir::StructKind::Tuple => { let ident_pats = field_names.iter().map(|(_, new_name)| { - let name = make.name(new_name); - ast::Pat::from(make.ident_pat(is_ref, is_mut, name)) + let name = editor.make().name(new_name); + ast::Pat::from(editor.make().ident_pat(is_ref, is_mut, name)) }); - ast::Pat::TupleStructPat(make.tuple_struct_pat(struct_path, ident_pats)) + ast::Pat::TupleStructPat(editor.make().tuple_struct_pat(struct_path, ident_pats)) } hir::StructKind::Record => { let fields = field_names.iter().map(|(old_name, new_name)| { // Use shorthand syntax if possible if old_name == new_name { - make.record_pat_field_shorthand( - make.ident_pat(is_ref, is_mut, make.name(old_name)).into(), + editor.make().record_pat_field_shorthand( + editor + .make() + .ident_pat(is_ref, is_mut, editor.make().name(old_name)) + .into(), ) } else { - make.record_pat_field( - make.name_ref(old_name), - ast::Pat::IdentPat(make.ident_pat(is_ref, is_mut, make.name(new_name))), + editor.make().record_pat_field( + editor.make().name_ref(old_name), + ast::Pat::IdentPat(editor.make().ident_pat( + is_ref, + is_mut, + editor.make().name(new_name), + )), ) } }); - let field_list = make - .record_pat_field_list(fields, data.has_private_members.then_some(make.rest_pat())); + let field_list = editor.make().record_pat_field_list( + fields, + data.has_private_members.then_some(editor.make().rest_pat()), + ); - ast::Pat::RecordPat(make.record_pat_with_fields(struct_path, field_list)) + ast::Pat::RecordPat(editor.make().record_pat_with_fields(struct_path, field_list)) } - hir::StructKind::Unit => make.path_pat(struct_path), + hir::StructKind::Unit => editor.make().path_pat(struct_path), }; - data.apply_to_destruct(new_pat, editor, &make); - editor.add_mappings(make.finish_with_mappings()); + data.apply_to_destruct(new_pat, editor); } fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> { @@ -359,13 +362,11 @@ fn update_usages( field_names: &FxHashMap, ) { let source = ctx.source_file().syntax(); - let make = SyntaxFactory::with_mappings(); let edits = data .usages .iter() - .filter_map(|r| build_usage_edit(ctx, &make, data, r, field_names)) + .filter_map(|r| build_usage_edit(ctx, editor.make(), data, r, field_names)) .collect_vec(); - editor.add_mappings(make.finish_with_mappings()); for (old, new) in edits { if let Some(range) = ctx.sema.original_range_opt(&old) { editor.replace_all(cover_edit_range(source, range.range), vec![new.into()]); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 05fa00f4e8c11..e3acbf22f3074 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -90,20 +90,18 @@ fn destructure_tuple_edit_impl( in_sub_pattern: bool, ) { let mut syntax_editor = edit.make_editor(data.ident_pat.syntax()); - let syntax_factory = SyntaxFactory::with_mappings(); let assignment_edit = - edit_tuple_assignment(ctx, edit, &mut syntax_editor, &syntax_factory, data, in_sub_pattern); - let current_file_usages_edit = edit_tuple_usages(data, ctx, &syntax_factory, in_sub_pattern); + edit_tuple_assignment(ctx, edit, &mut syntax_editor, data, in_sub_pattern); + let current_file_usages_edit = + edit_tuple_usages(data, ctx, syntax_editor.make(), in_sub_pattern); - assignment_edit.apply(&mut syntax_editor, &syntax_factory); + assignment_edit.apply(&mut syntax_editor); if let Some(usages_edit) = current_file_usages_edit { usages_edit .into_iter() .for_each(|usage_edit| usage_edit.apply(ctx, edit, &mut syntax_editor)) } - - syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); } @@ -176,7 +174,6 @@ fn edit_tuple_assignment( ctx: &AssistContext<'_>, edit: &mut SourceChangeBuilder, editor: &mut SyntaxEditor, - make: &SyntaxFactory, data: &TupleData, in_sub_pattern: bool, ) -> AssignmentEdit { @@ -184,11 +181,10 @@ fn edit_tuple_assignment( let original = &data.ident_pat; let is_ref = original.ref_token().is_some(); let is_mut = original.mut_token().is_some(); - let fields = data - .field_names - .iter() - .map(|name| ast::Pat::from(make.ident_pat(is_ref, is_mut, make.name(name)))); - make.tuple_pat(fields) + let fields = data.field_names.iter().map(|name| { + ast::Pat::from(editor.make().ident_pat(is_ref, is_mut, editor.make().name(name))) + }); + editor.make().tuple_pat(fields) }; let is_shorthand_field = data .ident_pat @@ -223,16 +219,20 @@ struct AssignmentEdit { } impl AssignmentEdit { - fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) { + fn apply(self, syntax_editor: &mut SyntaxEditor) { // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { - self.ident_pat.set_pat(Some(self.tuple_pat.into()), syntax_editor, syntax_mapping); + self.ident_pat.set_pat(Some(self.tuple_pat.into()), syntax_editor); } else if self.is_shorthand_field { syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); - syntax_editor - .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.whitespace(" ")); - syntax_editor - .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.token(T![:])); + syntax_editor.insert( + Position::after(self.ident_pat.syntax()), + syntax_editor.make().whitespace(" "), + ); + syntax_editor.insert( + Position::after(self.ident_pat.syntax()), + syntax_editor.make().token(T![:]), + ); } else { syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index 865dc862215f1..c8753574cde49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -65,37 +65,40 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Replace try expression with match", target, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(try_expr.syntax()); let sad_pat = match try_enum { - TryEnum::Option => make.path_pat(make.ident_path("None")), - TryEnum::Result => make + TryEnum::Option => editor.make().path_pat(editor.make().ident_path("None")), + TryEnum::Result => editor + .make() .tuple_struct_pat( - make.ident_path("Err"), - iter::once(make.path_pat(make.ident_path("err"))), + editor.make().ident_path("Err"), + iter::once(editor.make().path_pat(editor.make().ident_path("err"))), ) .into(), }; - let sad_expr = make.expr_return(Some(sad_expr(try_enum, &make, || { - make.expr_path(make.ident_path("err")) - }))); + let sad_expr = + editor.make().expr_return(Some(sad_expr(try_enum, editor.make(), || { + editor.make().expr_path(editor.make().ident_path("err")) + }))); - let happy_arm = make.match_arm( - try_enum.happy_pattern(make.ident_pat(false, false, make.name("it")).into()), + let happy_arm = editor.make().match_arm( + try_enum.happy_pattern( + editor.make().ident_pat(false, false, editor.make().name("it")).into(), + ), None, - make.expr_path(make.ident_path("it")), + editor.make().expr_path(editor.make().ident_path("it")), ); - let sad_arm = make.match_arm(sad_pat, None, sad_expr.into()); + let sad_arm = editor.make().match_arm(sad_pat, None, sad_expr.into()); - let match_arm_list = make.match_arm_list([happy_arm, sad_arm]); + let match_arm_list = editor.make().match_arm_list([happy_arm, sad_arm]); - let expr_match = make + let expr_match = editor + .make() .expr_match(expr.clone(), match_arm_list) .indent(IndentLevel::from_node(try_expr.syntax())); editor.replace(try_expr.syntax(), expr_match.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); @@ -109,31 +112,42 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Replace try expression with let else", target, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(let_stmt.syntax()); let indent_level = IndentLevel::from_node(let_stmt.syntax()); let fill_expr = || crate::utils::expr_fill_default(ctx.config); - let new_let_stmt = make.let_else_stmt( + let new_let_stmt = editor.make().let_else_stmt( try_enum.happy_pattern(pat), let_stmt.ty().map(|ty| match try_enum { - TryEnum::Option => make.ty_option(ty).into(), - TryEnum::Result => make.ty_result(ty, make.ty_infer().into()).into(), + TryEnum::Option => editor.make().ty_option(ty).into(), + TryEnum::Result => { + editor.make().ty_result(ty, editor.make().ty_infer().into()).into() + } }), expr, - make.block_expr( - iter::once( - make.expr_stmt( - make.expr_return(Some(sad_expr(try_enum, &make, fill_expr))).into(), - ) - .into(), - ), - None, - ) - .indent(indent_level), + editor + .make() + .block_expr( + iter::once( + editor + .make() + .expr_stmt( + editor + .make() + .expr_return(Some(sad_expr( + try_enum, + editor.make(), + fill_expr, + ))) + .into(), + ) + .into(), + ), + None, + ) + .indent(indent_level), ); editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 79b9f5d69afc1..da6900b99b1de 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -8,7 +8,7 @@ use ide_db::{ use stdx::never; use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, - ast::{self, Use, UseTree, VisibilityKind, syntax_factory::SyntaxFactory}, + ast::{self, Use, UseTree, VisibilityKind}, }; use crate::{ @@ -148,7 +148,6 @@ fn build_expanded_import( current_module: Module, reexport_public_items: bool, ) { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(use_tree.syntax()); let (must_be_pub, visible_from) = if !reexport_public_items { (false, current_module) @@ -169,11 +168,11 @@ fn build_expanded_import( if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; let names_to_import = find_names_to_import(filtered_defs, imported_defs); - let expanded = make.use_tree_list(names_to_import.iter().map(|n| { - let path = make.ident_path( + let expanded = editor.make().use_tree_list(names_to_import.iter().map(|n| { + let path = editor.make().ident_path( &n.display(ctx.db(), current_module.krate(ctx.db()).edition(ctx.db())).to_string(), ); - make.use_tree(path, None, None, false) + editor.make().use_tree(path, None, None, false) })); match use_tree.star_token() { @@ -192,7 +191,6 @@ fn build_expanded_import( } None => never!(), } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index a7e78dfc9c940..5ca6d4ca2c3b2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -51,23 +51,24 @@ fn expand_record_rest_pattern( "Fill struct fields", rest_pat.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| { - make.record_pat_field_shorthand( - make.ident_pat( - false, - false, - make.name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()), - ) - .into(), + editor.make().record_pat_field_shorthand( + editor + .make() + .ident_pat( + false, + false, + editor + .make() + .name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()), + ) + .into(), ) })); - let new_field_list = make.record_pat_field_list(new_fields, None); + let new_field_list = editor.make().record_pat_field_list(new_fields, None); editor.replace(old_field_list.syntax(), new_field_list.syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -130,18 +131,17 @@ fn expand_tuple_struct_rest_pattern( "Fill tuple struct fields", rest_pat.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = make.tuple_struct_pat( + let new_pat = editor.make().tuple_struct_pat( path, pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { gen_unnamed_pat( ctx, - &make, + editor.make(), &mut name_gen, &f.ty(ctx.db()).to_type(ctx.sema.db), f.index(), @@ -151,8 +151,6 @@ fn expand_tuple_struct_rest_pattern( ); editor.replace(pat.syntax(), new_pat.syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -200,24 +198,27 @@ fn expand_tuple_rest_pattern( "Fill tuple fields", rest_pat.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = make.tuple_pat( + let new_pat = editor.make().tuple_pat( pat.fields() .take(prefix_count) .chain(fields[prefix_count..len - suffix_count].iter().enumerate().map( |(index, ty)| { - gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index) + gen_unnamed_pat( + ctx, + editor.make(), + &mut name_gen, + ty, + prefix_count + index, + ) }, )) .chain(pat.fields().skip(prefix_count + 1)), ); editor.replace(pat.syntax(), new_pat.syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -264,23 +265,19 @@ fn expand_slice_rest_pattern( "Fill slice fields", rest_pat.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = make.slice_pat( + let new_pat = editor.make().slice_pat( pat.pats() .take(prefix_count) - .chain( - (prefix_count..len - suffix_count) - .map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)), - ) + .chain((prefix_count..len - suffix_count).map(|index| { + gen_unnamed_pat(ctx, editor.make(), &mut name_gen, &ty, index) + })) .chain(pat.pats().skip(prefix_count + 1)), ); editor.replace(pat.syntax(), new_pat.syntax()); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 35e8baa18aca7..0b96acaee3a9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -8,7 +8,7 @@ use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, SyntaxToken, T, - ast::{self, TokenTree, syntax_factory::SyntaxFactory}, + ast::{self, TokenTree}, }; // Assist: extract_expressions_from_format_string @@ -57,7 +57,7 @@ pub(crate) fn extract_expressions_from_format_string( "Extract format expressions", tt.syntax().text_range(), |edit| { - let make = SyntaxFactory::without_mappings(); + let mut editor = edit.make_editor(tt.syntax()); // Extract existing arguments in macro let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); let format_string_index = format_str_index(&raw_tokens, &fmt_string); @@ -95,14 +95,15 @@ pub(crate) fn extract_expressions_from_format_string( let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; - new_tt_bits.push(NodeOrToken::Token(make.expr_literal(&new_fmt).token().clone())); + new_tt_bits + .push(NodeOrToken::Token(editor.make().expr_literal(&new_fmt).token().clone())); for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg new_tt_bits.extend_from_slice(&[ - NodeOrToken::Token(make.token(T![,])), - NodeOrToken::Token(make.whitespace(" ")), + NodeOrToken::Token(editor.make().token(T![,])), + NodeOrToken::Token(editor.make().whitespace(" ")), ]); } @@ -110,7 +111,7 @@ pub(crate) fn extract_expressions_from_format_string( Arg::Expr(s) => { // insert arg let expr = ast::Expr::parse(&s, ctx.edition()).syntax_node(); - let mut expr_tt = utils::tt_from_syntax(expr, &make); + let mut expr_tt = utils::tt_from_syntax(expr, editor.make()); new_tt_bits.append(&mut expr_tt); } Arg::Placeholder => { @@ -121,7 +122,7 @@ pub(crate) fn extract_expressions_from_format_string( } None => { placeholder_indexes.push(new_tt_bits.len()); - new_tt_bits.push(NodeOrToken::Token(make.token(T![_]))); + new_tt_bits.push(NodeOrToken::Token(editor.make().token(T![_]))); } } } @@ -130,8 +131,7 @@ pub(crate) fn extract_expressions_from_format_string( } // Insert new args - let new_tt = make.token_tree(tt_delimiter, new_tt_bits); - let mut editor = edit.make_editor(tt.syntax()); + let new_tt = editor.make().token_tree(tt_delimiter, new_tt_bits); editor.replace(tt.syntax(), new_tt.syntax()); if let Some(cap) = ctx.config.snippet_cap { @@ -158,7 +158,6 @@ pub(crate) fn extract_expressions_from_format_string( editor.add_annotation(literal, annotation); } } - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ); 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 3bbf9a0ad3a25..6975789b982bc 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 @@ -60,7 +60,6 @@ pub(crate) fn extract_struct_from_enum_variant( "Extract struct from enum variant", target, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(variant.syntax()); let edition = enum_hir.krate(ctx.db()).edition(ctx.db()); let variant_hir_name = variant_hir.name(ctx.db()); @@ -96,10 +95,8 @@ pub(crate) fn extract_struct_from_enum_variant( import, edition, &mut file_editor, - &make, ) }); - file_editor.add_mappings(make.take()); builder.add_file_edits(file_id.file_id(ctx.db()), file_editor); } @@ -119,13 +116,12 @@ pub(crate) fn extract_struct_from_enum_variant( import, edition, &mut editor, - &make, ) }); } let generic_params = enum_ast.generic_param_list().and_then(|known_generics| { - extract_generic_params(&make, &known_generics, &field_list) + extract_generic_params(editor.make(), &known_generics, &field_list) }); // resolve GenericArg in field_list to actual type @@ -148,13 +144,13 @@ pub(crate) fn extract_struct_from_enum_variant( }; let (comments_for_struct, comments_to_delete) = - collect_variant_comments(&make, variant.syntax()); + collect_variant_comments(editor.make(), variant.syntax()); for element in &comments_to_delete { editor.delete(element.clone()); } let def = create_struct_def( - &make, + editor.make(), variant_name.clone(), &field_list, generic_params.clone(), @@ -168,20 +164,15 @@ pub(crate) fn extract_struct_from_enum_variant( let mut insert_items: Vec = Vec::new(); for attr in enum_ast.attrs() { insert_items.push(attr.syntax().clone().into()); - insert_items.push(make.whitespace("\n").into()); + insert_items.push(editor.make().whitespace("\n").into()); } insert_items.extend(comments_for_struct); insert_items.push(def.syntax().clone().into()); - insert_items.push(make.whitespace(&format!("\n\n{indent}")).into()); - editor.insert_all_with_whitespace( - Position::before(enum_ast.syntax()), - insert_items, - &make, - ); + insert_items.push(editor.make().whitespace(&format!("\n\n{indent}")).into()); + editor.insert_all_with_whitespace(Position::before(enum_ast.syntax()), insert_items); - update_variant(&make, &mut editor, &variant, generic_params); + update_variant(&mut editor, &variant, generic_params); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -340,7 +331,6 @@ fn create_struct_def( } fn update_variant( - make: &SyntaxFactory, editor: &mut SyntaxEditor, variant: &ast::Variant, generics: Option, @@ -351,13 +341,13 @@ fn update_variant( .map(|generics| generics.to_generic_args()); // 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()), + Some(generic_args) => editor.make().ty(&format!("{name}{generic_args}")), + None => editor.make().ty(&name.text()), }; // change from a record to a tuple field list - let tuple_field = make.tuple_field(None, ty); - let field_list = make.tuple_field_list(iter::once(tuple_field)); + let tuple_field = editor.make().tuple_field(None, ty); + let field_list = editor.make().tuple_field_list(iter::once(tuple_field)); editor.replace(variant.field_list()?.syntax(), field_list.syntax()); // remove any ws after the name @@ -408,22 +398,15 @@ fn apply_references( import: Option<(ImportScope, hir::ModPath)>, edition: Edition, editor: &mut SyntaxEditor, - make: &SyntaxFactory, ) { if let Some((scope, path)) = import { - insert_use_with_editor( - &scope, - mod_path_to_ast(&path, edition), - &insert_use_cfg, - editor, - make, - ); + insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor); } // deep clone to prevent cycle - let path = make.path_from_segments(iter::once(segment.clone()), false); - editor.insert(Position::before(segment.syntax()), make.token(T!['('])); + let path = editor.make().path_from_segments(iter::once(segment.clone()), false); + editor.insert(Position::before(segment.syntax()), editor.make().token(T!['('])); editor.insert(Position::before(segment.syntax()), path.syntax()); - editor.insert(Position::after(&node), make.token(T![')'])); + editor.insert(Position::after(&node), editor.make().token(T![')'])); } fn process_references( 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 e4fdac27f47ff..f077260fa3de4 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 @@ -2,10 +2,7 @@ use either::Either; use hir::HirDisplay; use ide_db::syntax_helpers::node_ext::walk_ty; use syntax::{ - ast::{ - self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel, - syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor, }; @@ -56,10 +53,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Extract type as type alias", target, |builder| { - let mut edit = builder.make_editor(node); - let make = SyntaxFactory::without_mappings(); + let mut editor = builder.make_editor(node); - let resolved_ty = make.ty(&resolved_ty); + let resolved_ty = editor.make().ty(&resolved_ty); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -73,37 +69,43 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> } let generics = collect_used_generics(&ty, &known_generics); let generic_params = - generics.map(|it| make.generic_param_list(it.into_iter().cloned())); + generics.map(|it| editor.make().generic_param_list(it.into_iter().cloned())); // Replace original type with the alias let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); let new_ty = if let Some(ty_args) = ty_args { - make.generic_ty_path_segment(make.name_ref("Type"), ty_args) + editor.make().generic_ty_path_segment(editor.make().name_ref("Type"), ty_args) } else { - make.path_segment(make.name_ref("Type")) + editor.make().path_segment(editor.make().name_ref("Type")) }; - edit.replace(ty.syntax(), new_ty.syntax()); + editor.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let ty_alias = - make.ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None))); + let ty_alias = editor.make().ty_alias( + None, + "Type", + generic_params, + None, + None, + Some((resolved_ty, None)), + ); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() { - edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); + editor.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); } let indent = IndentLevel::from_node(node); - edit.insert_all( + editor.insert_all( syntax_editor::Position::before(node), vec![ ty_alias.syntax().clone().into(), - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), ], ); - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 732bab4ceca43..edf12cde5bdf6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -9,7 +9,6 @@ use syntax::{ ast::{ self, AstNode, edit::{AstNodeEdit, IndentLevel}, - syntax_factory::SyntaxFactory, }, syntax_editor::{Element, Position}, }; @@ -206,11 +205,10 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op to_replace.clone() }; - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(&place); - let pat_name = make.name(&var_name); - let name_expr = make.expr_path(make.ident_path(&var_name)); + let pat_name = editor.make().name(&var_name); + let name_expr = editor.make().expr_path(editor.make().ident_path(&var_name)); if let Some(cap) = ctx.config.snippet_cap { let tabstop = edit.make_tabstop_before(cap); @@ -219,27 +217,33 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let initializer = match ty.as_ref().filter(|_| needs_ref) { Some(receiver_type) if receiver_type.is_mutable_reference() => { - make.expr_ref(to_extract_no_ref.clone(), true) + editor.make().expr_ref(to_extract_no_ref.clone(), true) } Some(receiver_type) if receiver_type.is_reference() => { - make.expr_ref(to_extract_no_ref.clone(), false) + editor.make().expr_ref(to_extract_no_ref.clone(), false) } _ => to_extract_no_ref.clone(), }; let new_stmt: ast::Stmt = match kind { ExtractionKind::Variable => { - let ident_pat = make.ident_pat(false, needs_mut, pat_name); - make.let_stmt(ident_pat.into(), None, Some(initializer)).into() + let ident_pat = editor.make().ident_pat(false, needs_mut, pat_name); + editor.make().let_stmt(ident_pat.into(), None, Some(initializer)).into() } ExtractionKind::Constant => { - let ast_ty = make.ty(&ty_string); - ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer)) - .into() + let ast_ty = editor.make().ty(&ty_string); + ast::Item::Const(editor.make().item_const( + None, + None, + pat_name, + ast_ty, + initializer, + )) + .into() } ExtractionKind::Static => { - let ast_ty = make.ty(&ty_string); - ast::Item::Static(make.item_static( + let ast_ty = editor.make().ty(&ty_string); + ast::Item::Static(editor.make().item_static( None, false, false, @@ -267,7 +271,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op Position::before(place), vec![ new_stmt.syntax().clone().into(), - make.whitespace(&trailing_ws).into(), + editor.make().whitespace(&trailing_ws).into(), ], ); @@ -284,21 +288,19 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let block = if to_wrap.syntax() == &expr_replace { // Since `expr_replace` is the same that needs to be wrapped in a block, // we can just directly replace it with a block - make.block_expr([new_stmt], Some(name_expr)) + editor.make().block_expr([new_stmt], Some(name_expr)) } else { // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`. editor .replace_all(to_replace, vec![name_expr.syntax().syntax_element()]); - make.block_expr([new_stmt], Some(to_wrap.clone())) + editor.make().block_expr([new_stmt], Some(to_wrap.clone())) } // fixup indentation of block - .indent_with_mapping(indent_to, &make); + .indent_with_mapping(indent_to, editor.make()); editor.replace(to_wrap.syntax(), block.syntax()); } } - - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); edit.rename(); }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs index 922a61bf3a854..679369b9b1418 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs @@ -1,6 +1,6 @@ use syntax::{ SyntaxKind, T, - ast::{self, AstNode, BinExpr, RangeItem, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, BinExpr, RangeItem}, syntax_editor::Position, }; @@ -49,13 +49,11 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option op_token.text_range(), |builder| { let mut editor = builder.make_editor(&expr.syntax().parent().unwrap()); - let make = SyntaxFactory::with_mappings(); if let FlipAction::FlipAndReplaceOp(binary_op) = action { - editor.replace(op_token, make.token(binary_op)) + editor.replace(op_token, editor.make().token(binary_op)) }; editor.replace(lhs.syntax(), rhs.syntax()); editor.replace(rhs.syntax(), lhs.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index 1e95d4772349e..722a6c1457411 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -2,7 +2,6 @@ use syntax::{ AstNode, Direction, NodeOrToken, SyntaxKind, SyntaxToken, T, algo::non_trivia_sibling, ast::{self, syntax_factory::SyntaxFactory}, - syntax_editor::SyntaxMapping, }; use crate::{AssistContext, AssistId, Assists}; @@ -47,9 +46,8 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( if let Some(parent) = ast::TokenTree::cast(parent) { // An attribute. It often contains a path followed by a // token tree (e.g. `align(2)`), so we have to be smarter. - let (new_tree, mapping) = flip_tree(parent.clone(), comma); + let new_tree = flip_tree(parent.clone(), comma, editor.make()); editor.replace(parent.syntax(), new_tree.syntax()); - editor.add_mappings(mapping); } else { editor.replace(prev.clone(), next.clone()); editor.replace(next.clone(), prev.clone()); @@ -59,7 +57,7 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }) } -fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) { +fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken, make: &SyntaxFactory) -> ast::TokenTree { let mut tree_iter = tree.token_trees_and_tokens(); let before: Vec<_> = tree_iter.by_ref().take_while(|it| it.as_token() != Some(&comma)).collect(); @@ -100,10 +98,7 @@ fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, Synta &after[next_end..after.len() - 1], ] .concat(); - - let make = SyntaxFactory::with_mappings(); - let new_token_tree = make.token_tree(tree.left_delimiter_token().unwrap().kind(), result); - (new_token_tree, make.finish_with_mappings()) + make.token_tree(tree.left_delimiter_token().unwrap().kind(), result) } #[cfg(test)] 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 7a097e1866ef6..ee98e1f7d49b7 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 @@ -74,24 +74,26 @@ pub(crate) fn generate_blanket_trait_impl( name.syntax().text_range(), |builder| { let mut editor = builder.make_editor(traitd.syntax()); - let make = SyntaxFactory::with_mappings(); - let namety = make.ty_path(make.path_from_text(&name.text())); + let namety = editor.make().ty_path(editor.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 bounds = + traitd.type_bound_list().and_then(|list| exclude_sized(editor.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 thisname = this_name(editor.make(), &traitd); + let thisty = editor.make().ty_path(editor.make().path_from_text(&thisname.text())); let indent = traitd.indent_level(); - let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param( - thisname.clone(), - apply_sized(&make, has_sized(&traitd, &ctx.sema), bounds), - ))]); + let gendecl = editor.make().generic_param_list([GenericParam::TypeParam( + editor.make().type_param( + thisname.clone(), + apply_sized(editor.make(), has_sized(&traitd, &ctx.sema), bounds), + ), + )]); let trait_gen_args = traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); - let impl_ = make.impl_trait( + let impl_ = editor.make().impl_trait( cfg_attrs(&traitd), is_unsafe, traitd.generic_param_list(), @@ -107,12 +109,11 @@ pub(crate) fn generate_blanket_trait_impl( ); if let Some(trait_assoc_list) = traitd.assoc_item_list() { - let assoc_item_list = - impl_.get_or_create_assoc_item_list_with_editor(&mut editor, &make); + let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&mut editor); for item in trait_assoc_list.assoc_items() { let item = match item { ast::AssocItem::Fn(method) if method.body().is_none() => { - todo_fn(&make, &method, ctx.config).into() + todo_fn(editor.make(), &method, ctx.config).into() } ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, _ => continue, @@ -126,7 +127,7 @@ pub(crate) fn generate_blanket_trait_impl( editor.insert_all( Position::after(traitd.syntax()), vec![ - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); @@ -136,8 +137,6 @@ pub(crate) fn generate_blanket_trait_impl( { builder.add_tabstop_before(cap, self_ty); } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs index 2d92bf5146227..d4a570b8ba5fb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs @@ -73,16 +73,15 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<' "Generate a Default impl from a new fn", target, move |builder| { - let make = SyntaxFactory::without_mappings(); - let default_impl = generate_default_impl(&make, &impl_, self_ty); + let mut editor = builder.make_editor(impl_.syntax()); + let default_impl = generate_default_impl(editor.make(), &impl_, self_ty); let indent = IndentLevel::from_node(impl_.syntax()); let default_impl = default_impl.indent(indent); - let mut editor = builder.make_editor(impl_.syntax()); editor.insert_all( Position::after(impl_.syntax()), vec![ - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), default_impl.syntax().clone().into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 63033c7d5e398..09baea2694049 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -4,7 +4,6 @@ use syntax::{ ast::{ self, AstNode, HasGenericParams, HasName, HasVisibility as _, edit::{AstNodeEdit, IndentLevel}, - syntax_factory::SyntaxFactory, }, syntax_editor::Position, }; @@ -107,8 +106,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' format!("Generate delegate for `{field_name}.{name}()`",), target, |edit| { - let make = SyntaxFactory::without_mappings(); - let field = make + let mut editor = edit.make_editor(strukt.syntax()); + let field = editor + .make() .field_from_idents(["self", &field_name]) .expect("always be a valid expression"); // Create the function @@ -135,27 +135,32 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let is_unsafe = method_source.unsafe_token().is_some(); let is_gen = method_source.gen_token().is_some(); - let fn_name = make.name(&name); + let fn_name = editor.make().name(&name); let type_params = method_source.generic_param_list(); let where_clause = method_source.where_clause(); - let params = - method_source.param_list().unwrap_or_else(|| make.param_list(None, [])); + let params = method_source + .param_list() + .unwrap_or_else(|| editor.make().param_list(None, [])); // compute the `body` let arg_list = method_source .param_list() - .map(|v| convert_param_list_to_arg_list(v, &make)) - .unwrap_or_else(|| make.arg_list([])); + .map(|v| convert_param_list_to_arg_list(v, editor.make())) + .unwrap_or_else(|| editor.make().arg_list([])); - let tail_expr = make.expr_method_call(field, make.name_ref(&name), arg_list).into(); + let tail_expr = editor + .make() + .expr_method_call(field, editor.make().name_ref(&name), arg_list) + .into(); let tail_expr_finished = - if is_async { make.expr_await(tail_expr).into() } else { tail_expr }; - let body = make.block_expr([], Some(tail_expr_finished)); + if is_async { editor.make().expr_await(tail_expr).into() } else { tail_expr }; + let body = editor.make().block_expr([], Some(tail_expr_finished)); let ret_type = method_source.ret_type(); - let f = make + let f = editor + .make() .fn_( None, vis, @@ -173,7 +178,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' .indent(IndentLevel(1)); let item = ast::AssocItem::Fn(f.clone()); - let mut editor = edit.make_editor(strukt.syntax()); let fn_: Option = match impl_def { Some(impl_def) => match impl_def.assoc_item_list() { Some(assoc_item_list) => { @@ -182,7 +186,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' Some(item) } None => { - let assoc_item_list = make.assoc_item_list(vec![item]); + let assoc_item_list = editor.make().assoc_item_list(vec![item]); editor.insert( Position::last_child_of(impl_def.syntax()), assoc_item_list.syntax(), @@ -195,13 +199,15 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ty_params = strukt.generic_param_list(); let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); let where_clause = strukt.where_clause(); - let assoc_item_list = make.assoc_item_list(vec![item]); + let assoc_item_list = editor.make().assoc_item_list(vec![item]); - let impl_def = make.impl_( + let impl_def = editor.make().impl_( None, ty_params, ty_args, - syntax::ast::Type::PathType(make.ty_path(make.ident_path(name))), + syntax::ast::Type::PathType( + editor.make().ty_path(editor.make().ident_path(name)), + ), where_clause, Some(assoc_item_list), ); @@ -215,7 +221,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' editor.insert_all( Position::after(strukt.syntax()), vec![ - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), impl_def.syntax().clone().into(), ], ); @@ -229,7 +235,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let tabstop = edit.make_tabstop_before(cap); editor.add_annotation(fn_.syntax(), tabstop); } - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, )?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 5534dc1cd304f..5a514fca09d0f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -2,7 +2,7 @@ use hir::{ModPath, ModuleDef}; use ide_db::{FileId, RootDatabase, famous_defs::FamousDefs}; use syntax::{ Edition, - ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, HasName, edit::AstNodeEdit}, syntax_editor::Position, }; @@ -138,28 +138,35 @@ fn generate_edit( trait_path: ModPath, edition: Edition, ) { - let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(strukt.syntax()); let strukt_adt = ast::Adt::Struct(strukt.clone()); - let trait_ty = make.ty(&trait_path.display(db, edition).to_string()); + let trait_ty = editor.make().ty(&trait_path.display(db, edition).to_string()); let assoc_items: Vec = match deref_type { DerefType::Deref => { let target_alias = - make.ty_alias([], "Target", None, None, None, Some((field_type, None))); - let ret_ty = - make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), false); - let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); - let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), false))); - let fn_ = make + editor.make().ty_alias([], "Target", None, None, None, Some((field_type, None))); + let ret_ty = editor.make().ty_ref( + editor.make().ty_path(editor.make().path_from_text("Self::Target")).into(), + false, + ); + let field_expr = editor + .make() + .expr_field(editor.make().expr_path(editor.make().ident_path("self")), field_name); + let body = editor + .make() + .block_expr([], Some(editor.make().expr_ref(field_expr.into(), false))); + let fn_ = editor + .make() .fn_( [], None, - make.name("deref"), + editor.make().name("deref"), None, None, - make.param_list(Some(make.self_param()), []), + editor.make().param_list(Some(editor.make().self_param()), []), body, - Some(make.ret_type(ret_ty)), + Some(editor.make().ret_type(ret_ty)), false, false, false, @@ -169,20 +176,26 @@ fn generate_edit( vec![ast::AssocItem::TypeAlias(target_alias), ast::AssocItem::Fn(fn_)] } DerefType::DerefMut => { - let ret_ty = - make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), true); - let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); - let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), true))); - let fn_ = make + let ret_ty = editor.make().ty_ref( + editor.make().ty_path(editor.make().path_from_text("Self::Target")).into(), + true, + ); + let field_expr = editor + .make() + .expr_field(editor.make().expr_path(editor.make().ident_path("self")), field_name); + let body = + editor.make().block_expr([], Some(editor.make().expr_ref(field_expr.into(), true))); + let fn_ = editor + .make() .fn_( [], None, - make.name("deref_mut"), + editor.make().name("deref_mut"), None, None, - make.param_list(Some(make.mut_self_param()), []), + editor.make().param_list(Some(editor.make().mut_self_param()), []), body, - Some(make.ret_type(ret_ty)), + Some(editor.make().ret_type(ret_ty)), false, false, false, @@ -193,17 +206,18 @@ fn generate_edit( } }; - let body = make.assoc_item_list(assoc_items); + let body = editor.make().assoc_item_list(assoc_items); let indent = strukt.indent_level(); - let impl_ = generate_trait_impl_intransitive_with_item(&make, &strukt_adt, trait_ty, body) - .indent(indent); - - let mut editor = edit.make_editor(strukt.syntax()); + let impl_ = + generate_trait_impl_intransitive_with_item(editor.make(), &strukt_adt, trait_ty, body) + .indent(indent); editor.insert_all( Position::after(strukt.syntax()), - vec![make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into()], + vec![ + editor.make().whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], ); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(file_id, editor); } 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 7aeb5e3396969..7631d13aa8aa8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,7 +1,7 @@ use syntax::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, T, - ast::{self, AstNode, HasAttrs, edit::IndentLevel, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, HasAttrs, edit::IndentLevel}, syntax_editor::{Element, Position}, }; @@ -42,17 +42,13 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt }; acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| { - let make = SyntaxFactory::without_mappings(); - match derive_attr { None => { - let derive = - make.attr_outer(make.meta_token_tree( - make.ident_path("derive"), - make.token_tree(T!['('], vec![]), - )); - let mut editor = edit.make_editor(nominal.syntax()); + let derive = editor.make().attr_outer(editor.make().meta_token_tree( + editor.make().ident_path("derive"), + editor.make().token_tree(T!['('], vec![]), + )); let indent = IndentLevel::from_node(nominal.syntax()); let after_attrs_and_comments = nominal .syntax() @@ -64,7 +60,7 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt after_attrs_and_comments, vec![ derive.syntax().syntax_element(), - make.whitespace(&format!("\n{indent}")).syntax_element(), + editor.make().whitespace(&format!("\n{indent}")).syntax_element(), ], ); 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 3514ebb811ee2..096c445d183b8 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 @@ -60,9 +60,9 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) acc.add(AssistId::generate("generate_enum_variant"), "Generate variant", target, |builder| { let mut editor = builder.make_editor(enum_node.syntax()); - let make = SyntaxFactory::with_mappings(); - let field_list = parent.make_field_list(ctx, &make); - let variant = make.variant(None, make.name(&name_ref.text()), field_list, None); + let field_list = parent.make_field_list(ctx, editor.make()); + let variant = + editor.make().variant(None, editor.make().name(&name_ref.text()), field_list, None); if let Some(it) = enum_node.variant_list() { it.add_variant(&mut editor, &variant); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 6bcbd9b0ccc2a..8d4f014389c5a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -2,7 +2,7 @@ use either::Either; use ide_db::assists::{AssistId, GroupLabel}; use syntax::{ AstNode, - ast::{self, HasGenericParams, HasName, edit::IndentLevel, syntax_factory::SyntaxFactory}, + ast::{self, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor, }; @@ -55,8 +55,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) style.label(), func_node.syntax().text_range(), |builder| { - let mut edit = builder.make_editor(func); - let make = SyntaxFactory::without_mappings(); + let mut editor = builder.make_editor(func); let alias_name = format!("{}Fn", stdx::to_camel_case(&name.to_string())); @@ -69,24 +68,27 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) let is_mut = self_ty.is_mutable_reference(); if let Some(adt) = self_ty.strip_references().as_adt() { - let inner_type = make.ty(adt.name(ctx.db()).as_str()); + let inner_type = editor.make().ty(adt.name(ctx.db()).as_str()); - let ast_self_ty = - if is_ref { make.ty_ref(inner_type, is_mut) } else { inner_type }; + let ast_self_ty = if is_ref { + editor.make().ty_ref(inner_type, is_mut) + } else { + inner_type + }; - fn_params_vec.push(make.unnamed_param(ast_self_ty)); + fn_params_vec.push(editor.make().unnamed_param(ast_self_ty)); } } fn_params_vec.extend(param_list.params().filter_map(|p| match style { ParamStyle::Named => Some(p), - ParamStyle::Unnamed => p.ty().map(|ty| make.unnamed_param(ty)), + ParamStyle::Unnamed => p.ty().map(|ty| editor.make().unnamed_param(ty)), })); let generic_params = func_node.generic_param_list(); let is_unsafe = func_node.unsafe_token().is_some(); - let ty = make.ty_fn_ptr( + let ty = editor.make().ty_fn_ptr( is_unsafe, func_node.abi(), fn_params_vec.into_iter(), @@ -94,7 +96,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ); // Insert new alias - let ty_alias = make.ty_alias( + let ty_alias = editor.make().ty_alias( None, &alias_name, generic_params, @@ -104,21 +106,21 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ); let indent = IndentLevel::from_node(insertion_node); - edit.insert_all( + editor.insert_all( syntax_editor::Position::before(insertion_node), vec![ ty_alias.syntax().clone().into(), - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), ], ); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() { - edit.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap)); + editor.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap)); } - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 1adb3f4fe49a1..744ba2ba78b7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -40,19 +40,17 @@ pub(crate) fn generate_from_impl_for_enum( "Generate `From` impl for this enum variant(s)", target, |edit| { - let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(adt.syntax()); let indent = adt.indent_level(); let mut elements = Vec::new(); for variant_info in variants { - let impl_ = build_from_impl(&make, &adt, variant_info).indent(indent); - elements.push(make.whitespace(&format!("\n\n{indent}")).into()); + let impl_ = build_from_impl(editor.make(), &adt, variant_info).indent(indent); + elements.push(editor.make().whitespace(&format!("\n\n{indent}")).into()); elements.push(impl_.syntax().clone().into()); } - let mut editor = edit.make_editor(adt.syntax()); editor.insert_all(Position::after(adt.syntax()), elements); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(file_id, editor); }, ) 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 4cd018d02d029..f6cbecf81a0a8 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 @@ -410,34 +410,36 @@ fn parse_record_field( Some(RecordFieldInfo { field_name, field_ty, fn_name, target }) } -fn build_source_change( - builder: &mut SourceChangeBuilder, +fn items( ctx: &AssistContext<'_>, info_of_record_fields: Vec, - assist_info: AssistInfo, -) { - let syntax_factory = SyntaxFactory::without_mappings(); - - let items: Vec = info_of_record_fields + assist_info: &AssistInfo, + make: &SyntaxFactory, +) -> Vec { + info_of_record_fields .iter() .map(|record_field_info| { let method = match assist_info.assist_type { - AssistType::Set => { - generate_setter_from_info(&assist_info, record_field_info, &syntax_factory) - } - _ => { - generate_getter_from_info(ctx, &assist_info, record_field_info, &syntax_factory) - } + AssistType::Set => generate_setter_from_info(assist_info, record_field_info, make), + _ => generate_getter_from_info(ctx, assist_info, record_field_info, make), }; let new_fn = method; let new_fn = new_fn.indent(1.into()); new_fn.into() }) - .collect(); + .collect() +} +fn build_source_change( + builder: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + info_of_record_fields: Vec, + assist_info: AssistInfo, +) { if let Some(impl_def) = &assist_info.impl_def { // We have an existing impl to add to let mut editor = builder.make_editor(impl_def.syntax()); + let items = items(ctx, info_of_record_fields, &assist_info, editor.make()); impl_def.assoc_item_list().unwrap().add_items(&mut editor, items.clone()); if let Some(cap) = ctx.config.snippet_cap @@ -451,22 +453,25 @@ fn build_source_change( builder.add_file_edits(ctx.vfs_file_id(), editor); return; } + + let mut editor = builder.make_editor(assist_info.strukt.syntax()); + let items = items(ctx, info_of_record_fields, &assist_info, editor.make()); let ty_params = assist_info.strukt.generic_param_list(); let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); - let impl_def = syntax_factory.impl_( + let impl_def = editor.make().impl_( None, ty_params, ty_args, - syntax_factory - .ty_path(syntax_factory.ident_path(&assist_info.strukt.name().unwrap().to_string())) + editor + .make() + .ty_path(editor.make().ident_path(&assist_info.strukt.name().unwrap().to_string())) .into(), None, - Some(syntax_factory.assoc_item_list(items)), + Some(editor.make().assoc_item_list(items)), ); - let mut editor = builder.make_editor(assist_info.strukt.syntax()); editor.insert_all( Position::after(assist_info.strukt.syntax()), - vec![syntax_factory.whitespace("\n\n").into(), impl_def.syntax().clone().into()], + vec![editor.make().whitespace("\n\n").into(), impl_def.syntax().clone().into()], ); if let Some(cap) = ctx.config.snippet_cap 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 af123eeaa0ce8..835675f1b6a34 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 @@ -1,7 +1,5 @@ use syntax::{ - ast::{ - self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit}, syntax_editor::{Position, SyntaxEditor}, }; @@ -15,7 +13,6 @@ use crate::{ fn insert_impl( editor: &mut SyntaxEditor, - make: &SyntaxFactory, impl_: &ast::Impl, nominal: &impl AstNodeEdit, ) -> ast::Impl { @@ -26,7 +23,7 @@ fn insert_impl( Position::after(nominal.syntax()), vec![ // Add a blank line after the ADT, and indentation for the impl to match the ADT - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); @@ -65,13 +62,10 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio format!("Generate impl for `{name}`"), target, |edit| { - let make = SyntaxFactory::with_mappings(); - // Generate the impl - let impl_ = generate_impl_with_factory(&make, &nominal); - let mut editor = edit.make_editor(nominal.syntax()); + let impl_ = generate_impl_with_factory(editor.make(), &nominal); - let impl_ = insert_impl(&mut editor, &make, &impl_, &nominal); + let impl_ = insert_impl(&mut editor, &impl_, &nominal); // Add a tabstop after the left curly brace if let Some(cap) = ctx.config.snippet_cap && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) @@ -79,8 +73,6 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio let tabstop = edit.make_tabstop_after(cap); editor.add_annotation(l_curly, tabstop); } - - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -117,13 +109,13 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Generate trait impl for `{name}`"), target, |edit| { - let make = SyntaxFactory::with_mappings(); - // Generate the impl - let impl_ = generate_trait_impl_intransitive(&make, &nominal, make.ty_placeholder()); - let mut editor = edit.make_editor(nominal.syntax()); - - let impl_ = insert_impl(&mut editor, &make, &impl_, &nominal); + let impl_ = generate_trait_impl_intransitive( + editor.make(), + &nominal, + editor.make().ty_placeholder(), + ); + let impl_ = insert_impl(&mut editor, &impl_, &nominal); // Make the trait type a placeholder snippet if let Some(cap) = ctx.config.snippet_cap { if let Some(trait_) = impl_.trait_() { @@ -136,8 +128,6 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> editor.add_annotation(l_curly, tabstop); } } - - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -176,10 +166,10 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Generate `{name}` impl for type"), target, |edit| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(trait_.syntax()); - let holder_arg = ast::GenericArg::TypeArg(make.type_arg(make.ty_placeholder())); + let holder_arg = + ast::GenericArg::TypeArg(editor.make().type_arg(editor.make().ty_placeholder())); let missing_items = utils::filter_assoc_items( &ctx.sema, &hir_trait.items(ctx.db()), @@ -188,11 +178,13 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> ); let trait_gen_args = trait_.generic_param_list().map(|list| { - make.generic_arg_list(list.generic_params().map(|_| holder_arg.clone()), false) + editor + .make() + .generic_arg_list(list.generic_params().map(|_| holder_arg.clone()), false) }); let make_impl_ = |body| { - make.impl_trait( + editor.make().impl_trait( None, trait_.unsafe_token().is_some(), None, @@ -200,8 +192,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> None, None, false, - make.ty(&name.text()), - make.ty_placeholder(), + editor.make().ty(&name.text()), + editor.make().ty_placeholder(), None, None, body, @@ -213,7 +205,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> } else { let impl_ = make_impl_(None); let assoc_items = add_trait_assoc_items_to_impl( - &make, + editor.make(), &ctx.sema, ctx.config, &missing_items, @@ -221,12 +213,11 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> &impl_, &target_scope, ); - let assoc_item_list = make.assoc_item_list(assoc_items); + let assoc_item_list = editor.make().assoc_item_list(assoc_items); make_impl_(Some(assoc_item_list)) }; - let impl_ = insert_impl(&mut editor, &make, &impl_, &trait_); - editor.add_mappings(make.finish_with_mappings()); + let impl_ = insert_impl(&mut editor, &impl_, &trait_); if let Some(cap) = ctx.config.snippet_cap { if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { 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 31e49c8ce48e7..f63b907d86884 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 @@ -68,9 +68,8 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> target, |edit| { let (mut editor, impl_clone) = SyntaxEditor::with_ast_node(&impl_def.reset_indent()); - let factory = SyntaxFactory::without_mappings(); - apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new); + apply_generate_mut_impl(&mut editor, &impl_clone, trait_new); let new_root = editor.finish(); let new_root = new_root.new_root(); @@ -84,7 +83,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> Position::before(impl_def.syntax()), vec![ new_impl.syntax().syntax_element(), - factory.whitespace(&format!("\n\n{indent}")).syntax_element(), + editor.make().whitespace(&format!("\n\n{indent}")).syntax_element(), ], ); @@ -113,7 +112,6 @@ fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { fn apply_generate_mut_impl( editor: &mut SyntaxEditor, - factory: &SyntaxFactory, impl_def: &ast::Impl, trait_new: &str, ) -> Option<()> { @@ -122,13 +120,13 @@ fn apply_generate_mut_impl( let seg = path.segment()?; let name_ref = seg.name_ref()?; - let new_name_ref = factory.name_ref(trait_new); + let new_name_ref = editor.make().name_ref(trait_new); editor.replace(name_ref.syntax(), new_name_ref.syntax()); if let Some((name, new_name)) = impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name) { - let new_name_node = factory.name(new_name); + let new_name_node = editor.make().name(new_name); editor.replace(name.syntax(), new_name_node.syntax()); } @@ -137,14 +135,14 @@ fn apply_generate_mut_impl( } if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) { - let mut_self = factory.mut_self_param(); + let mut_self = editor.make().mut_self_param(); editor.replace(self_param.syntax(), mut_self.syntax()); } if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast) - && let Some(new_ty) = process_ret_type(factory, &ret_type) + && let Some(new_ty) = process_ret_type(editor.make(), &ret_type) { - let new_ret = factory.ret_type(new_ty); + let new_ret = editor.make().ret_type(new_ty); editor.replace(ret_type.syntax(), new_ret.syntax()) } @@ -154,13 +152,13 @@ fn apply_generate_mut_impl( _ => None, }) }) { - process_ref_mut(editor, factory, &fn_); + process_ref_mut(editor, &fn_); } Some(()) } -fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast::Fn) { +fn process_ref_mut(editor: &mut SyntaxEditor, fn_: &ast::Fn) { let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return }; let ast::Expr::RefExpr(ref_expr) = expr else { return }; @@ -171,8 +169,8 @@ fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast let Some(amp) = ref_expr.amp_token() else { return }; - let mut_kw = factory.token(T![mut]); - let space = factory.whitespace(" "); + let mut_kw = editor.make().token(T![mut]); + let space = editor.make().whitespace(" "); editor.insert(Position::after(amp.clone()), space.syntax_element()); editor.insert(Position::after(amp), mut_kw.syntax_element()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 301d13c095842..7a90428cad692 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -3,10 +3,7 @@ use ide_db::{ use_trivial_constructor::use_trivial_constructor, }; use syntax::{ - ast::{ - self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit, - syntax_factory::SyntaxFactory, - }, + ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit}, syntax_editor::Position, }; @@ -38,11 +35,9 @@ use crate::{ // ``` pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; - - let make = SyntaxFactory::without_mappings(); - let field_list = match strukt.kind() { + let field_list: Vec<(String, ast::Type)> = match strukt.kind() { StructKind::Record(named) => { - named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::>() + named.fields().filter_map(|f| Some((f.name()?.to_string(), f.ty()?))).collect() } StructKind::Tuple(tuple) => { let mut name_generator = NameGenerator::default(); @@ -56,12 +51,12 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ctx.db(), ctx.edition(), ) { - Some(name) => name, - None => name_generator.suggest_name(&format!("_{i}")), + Some(name) => name.to_string(), + None => name_generator.suggest_name(&format!("_{i}")).to_string(), }; - Some((make.name(name.as_str()), f.ty()?)) + Some((name, ty)) }) - .collect::>() + .collect() } StructKind::Unit => return None, }; @@ -74,7 +69,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let target = strukt.syntax().text_range(); acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| { - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(strukt.syntax()); + let trivial_constructors = field_list .iter() .map(|(name, ty)| { @@ -100,52 +96,64 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option edition, )?; - Some((make.name_ref(&name.text()), Some(expr))) + Some((editor.make().name_ref(name), Some(expr))) }) .collect::>(); let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| { if trivial_constructors[i].is_none() { - Some(make.param(make.ident_pat(false, false, name.clone()).into(), ty.clone())) + Some(editor.make().param( + editor.make().ident_pat(false, false, editor.make().name(name)).into(), + ty.clone(), + )) } else { None } }); - let params = make.param_list(None, params); + let params = editor.make().param_list(None, params); let fields = field_list.iter().enumerate().map(|(i, (name, _))| { if let Some(constructor) = trivial_constructors[i].clone() { constructor } else { - (make.name_ref(&name.text()), None) + (editor.make().name_ref(name), None) } }); let tail_expr: ast::Expr = match strukt.kind() { StructKind::Record(_) => { - let fields = fields.map(|(name, expr)| make.record_expr_field(name, expr)); - let fields = make.record_expr_field_list(fields); - make.record_expr(make.ident_path("Self"), fields).into() + let fields = fields.map(|(name, expr)| editor.make().record_expr_field(name, expr)); + let fields = editor.make().record_expr_field_list(fields); + editor.make().record_expr(editor.make().ident_path("Self"), fields).into() } StructKind::Tuple(_) => { let args = fields.map(|(arg, expr)| { - let arg = || make.expr_path(make.path_unqualified(make.path_segment(arg))); + let arg = || { + editor.make().expr_path( + editor.make().path_unqualified(editor.make().path_segment(arg)), + ) + }; expr.unwrap_or_else(arg) }); - let arg_list = make.arg_list(args); - make.expr_call(make.expr_path(make.ident_path("Self")), arg_list).into() + let arg_list = editor.make().arg_list(args); + editor + .make() + .expr_call(editor.make().expr_path(editor.make().ident_path("Self")), arg_list) + .into() } StructKind::Unit => unreachable!(), }; - let body = make.block_expr(None, tail_expr.into()); + let body = editor.make().block_expr(None, tail_expr.into()); - let ret_type = make.ret_type(make.ty_path(make.ident_path("Self")).into()); + let ret_type = + editor.make().ret_type(editor.make().ty_path(editor.make().ident_path("Self")).into()); - let fn_ = make + let fn_ = editor + .make() .fn_( [], strukt.visibility(), - make.name("new"), + editor.make().name("new"), None, None, params, @@ -158,8 +166,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ) .indent(1.into()); - let mut editor = builder.make_editor(strukt.syntax()); - // Get the node for set annotation let contain_fn = if let Some(impl_def) = impl_def { let fn_ = fn_.indent(impl_def.indent_level()); @@ -169,30 +175,36 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option editor.insert_all( Position::after(l_curly), vec![ - make.whitespace(&format!("\n{}", impl_def.indent_level() + 1)).into(), + editor + .make() + .whitespace(&format!("\n{}", impl_def.indent_level() + 1)) + .into(), fn_.syntax().clone().into(), - make.whitespace("\n").into(), + editor.make().whitespace("\n").into(), ], ); fn_.syntax().clone() } else { - let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]); + let list = editor.make().assoc_item_list([ast::AssocItem::Fn(fn_)]); editor.insert(Position::after(impl_def.syntax()), list.syntax()); list.syntax().clone() } } else { // Generate a new impl to add the method to let indent_level = strukt.indent_level(); - let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]); - let impl_def = - generate_impl_with_item(&make, &ast::Adt::Struct(strukt.clone()), Some(list)) - .indent(strukt.indent_level()); + let list = editor.make().assoc_item_list([ast::AssocItem::Fn(fn_)]); + let impl_def = generate_impl_with_item( + editor.make(), + &ast::Adt::Struct(strukt.clone()), + Some(list), + ) + .indent(strukt.indent_level()); // Insert it after the adt editor.insert_all( Position::after(strukt.syntax()), vec![ - make.whitespace(&format!("\n\n{indent_level}")).into(), + editor.make().whitespace(&format!("\n\n{indent_level}")).into(), impl_def.syntax().clone().into(), ], ); @@ -235,8 +247,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option editor.add_annotation(name.syntax(), tabstop_before); } } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } 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 7746cdc068a1f..d46514dce19e9 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 @@ -80,51 +80,55 @@ pub(crate) fn generate_single_field_struct_from( "Generate single field `From`", strukt.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(strukt.syntax()); let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); - let trait_gen_args = Some(make.generic_arg_list( - [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))], + let trait_gen_args = Some(editor.make().generic_arg_list( + [ast::GenericArg::TypeArg(editor.make().type_arg(main_field_ty.clone()))], false, )); - let ty = make.ty(&strukt_name.text()); + let ty = editor.make().ty(&strukt_name.text()); - let constructor = - make_adt_constructor(names.as_deref(), constructors, &main_field_name, &make); - let body = make.block_expr([], Some(constructor)); + let constructor = make_adt_constructor( + names.as_deref(), + constructors, + &main_field_name, + editor.make(), + ); + let body = editor.make().block_expr([], Some(constructor)); - let fn_ = make + let fn_ = editor + .make() .fn_( [], None, - make.name("from"), + editor.make().name("from"), None, None, - make.param_list( + editor.make().param_list( None, - [make.param( - make.path_pat(make.path_from_text(&main_field_name)), + [editor.make().param( + editor.make().path_pat(editor.make().path_from_text(&main_field_name)), main_field_ty, )], ), body, - Some(make.ret_type(make.ty("Self"))), + Some(editor.make().ret_type(editor.make().ty("Self"))), false, false, false, false, ) - .indent_with_mapping(1.into(), &make); + .indent_with_mapping(1.into(), editor.make()); let cfg_attrs = strukt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); - let impl_ = make.impl_trait( + let impl_ = editor.make().impl_trait( cfg_attrs, false, None, @@ -132,7 +136,7 @@ pub(crate) fn generate_single_field_struct_from( type_gen_params, type_gen_args, false, - make.ty("From"), + editor.make().ty("From"), ty.clone(), None, ty_where_clause.map(|wc| wc.reset_indent()), @@ -140,22 +144,19 @@ pub(crate) fn generate_single_field_struct_from( ); let (mut impl_editor, impl_root) = SyntaxEditor::with_ast_node(&impl_); - let assoc_list = - impl_root.get_or_create_assoc_item_list_with_editor(&mut impl_editor, &make); + let assoc_list = impl_root.get_or_create_assoc_item_list_with_editor(&mut impl_editor); assoc_list.add_items(&mut impl_editor, vec![fn_.into()]); let impl_ = ast::Impl::cast(impl_editor.finish().new_root().clone()) .unwrap() - .indent_with_mapping(indent, &make); + .indent_with_mapping(indent, editor.make()); editor.insert_all( Position::after(strukt.syntax()), vec![ - make.whitespace(&format!("\n\n{indent}")).into(), + editor.make().whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) 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 2d3d05849b0ba..2cbdcaa9da1f0 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 @@ -4,7 +4,6 @@ use syntax::{ AstNode, AstToken, SyntaxKind, T, ast::{ self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make, - syntax_factory::SyntaxFactory, }, syntax_editor::{Position, SyntaxEditor}, }; @@ -109,8 +108,8 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap() }; - let factory = SyntaxFactory::with_mappings(); - let trait_ast = factory.trait_( + let mut editor = builder.make_editor(impl_ast.syntax()); + let trait_ast = editor.make().trait_( false, &trait_name(&impl_assoc_items).text(), impl_ast.generic_param_list(), @@ -119,7 +118,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ ); let trait_name = trait_ast.name().expect("new trait should have a name"); - let trait_name_ref = factory.name_ref(&trait_name.to_string()); + let trait_name_ref = editor.make().name_ref(&trait_name.to_string()); // Change `impl Foo` to `impl NewTrait for Foo` let mut elements = vec![ @@ -134,7 +133,6 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ elements.insert(1, gen_args.syntax().clone().into()); } - let mut editor = builder.make_editor(impl_ast.syntax()); impl_assoc_items.assoc_items().for_each(|item| { remove_items_visibility(&mut editor, &item); remove_doc_comments(&mut editor, &item); @@ -157,8 +155,6 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ editor.add_annotation(trait_name.syntax(), placeholder); editor.add_annotation(trait_name_ref.syntax(), placeholder); } - - editor.add_mappings(factory.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index f55ef4229e587..5bf7e290e2dac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -7,7 +7,7 @@ use ide_db::{ }; use syntax::{ Direction, TextRange, - ast::{self, AstNode, AstToken, HasName, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, AstToken, HasName}, syntax_editor::{Element, SyntaxEditor}, }; @@ -98,25 +98,21 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let make = SyntaxFactory::with_mappings(); - for (name, should_wrap) in wrap_in_parens { let replacement = if should_wrap { - make.expr_paren(initializer_expr.clone()).into() + editor.make().expr_paren(initializer_expr.clone()).into() } else { initializer_expr.clone() }; if let Some(record_field) = ast::RecordExprField::for_field_name(&name) { cov_mark::hit!(inline_field_shorthand); - let replacement = make.record_expr_field(name, Some(replacement)); + let replacement = editor.make().record_expr_field(name, Some(replacement)); editor.replace(record_field.syntax(), replacement.syntax()); } else { editor.replace(name.syntax(), replacement.syntax()); } } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) 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 4b60f0ac1e3cf..13c455faad1b6 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 @@ -368,8 +368,7 @@ fn create_replacement( }; let new_string = replacement_syntax.to_string(); let new = if new_string == "_" { - let make = SyntaxFactory::without_mappings(); - make.wildcard_pat().syntax().clone() + editor.make().wildcard_pat().syntax().clone() } else { replacement_syntax.clone() }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 5e8ea7daff90d..7b6b84f7e8a65 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -1,7 +1,7 @@ use ide_db::{FileId, FxHashSet}; use syntax::{ AstNode, SmolStr, T, TextRange, ToSmolStr, - ast::{self, HasGenericParams, HasName, syntax_factory::SyntaxFactory}, + ast::{self, HasGenericParams, HasName}, format_smolstr, syntax_editor::{Element, Position, SyntaxEditor}, }; @@ -98,22 +98,21 @@ fn generate_fn_def_assist( acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { let mut editor = edit.make_editor(fn_def.syntax()); - let factory = SyntaxFactory::with_mappings(); if let Some(generic_list) = fn_def.generic_param_list() { - insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); + insert_lifetime_param(&mut editor, &generic_list, &new_lifetime_name); } else { - insert_new_generic_param_list_fn(&mut editor, &factory, &fn_def, &new_lifetime_name); + insert_new_generic_param_list_fn(&mut editor, &fn_def, &new_lifetime_name); } - editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); + editor.replace(lifetime.syntax(), editor.make().lifetime(&new_lifetime_name).syntax()); if let Some(pos) = loc_needing_lifetime.and_then(|l| l.to_position()) { editor.insert_all( pos, vec![ - factory.lifetime(&new_lifetime_name).syntax().clone().into(), - factory.whitespace(" ").into(), + editor.make().lifetime(&new_lifetime_name).syntax().clone().into(), + editor.make().whitespace(" ").into(), ], ); } @@ -124,7 +123,6 @@ fn generate_fn_def_assist( fn insert_new_generic_param_list_fn( editor: &mut SyntaxEditor, - factory: &SyntaxFactory, fn_def: &ast::Fn, lifetime_name: &str, ) -> Option<()> { @@ -133,9 +131,9 @@ fn insert_new_generic_param_list_fn( editor.insert_all( Position::after(name.syntax()), vec![ - factory.token(T![<]).syntax_element(), - factory.lifetime(lifetime_name).syntax().syntax_element(), - factory.token(T![>]).syntax_element(), + editor.make().token(T![<]).syntax_element(), + editor.make().lifetime(lifetime_name).syntax().syntax_element(), + editor.make().token(T![>]).syntax_element(), ], ); @@ -167,15 +165,14 @@ fn generate_impl_def_assist( acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { let mut editor = edit.make_editor(impl_def.syntax()); - let factory = SyntaxFactory::without_mappings(); if let Some(generic_list) = impl_def.generic_param_list() { - insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); + insert_lifetime_param(&mut editor, &generic_list, &new_lifetime_name); } else { - insert_new_generic_param_list_imp(&mut editor, &factory, &impl_def, &new_lifetime_name); + insert_new_generic_param_list_imp(&mut editor, &impl_def, &new_lifetime_name); } - editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); + editor.replace(lifetime.syntax(), editor.make().lifetime(&new_lifetime_name).syntax()); edit.add_file_edits(file_id, editor); }) @@ -183,7 +180,6 @@ fn generate_impl_def_assist( fn insert_new_generic_param_list_imp( editor: &mut SyntaxEditor, - factory: &SyntaxFactory, impl_: &ast::Impl, lifetime_name: &str, ) -> Option<()> { @@ -192,9 +188,9 @@ fn insert_new_generic_param_list_imp( editor.insert_all( Position::after(impl_kw), vec![ - factory.token(T![<]).syntax_element(), - factory.lifetime(lifetime_name).syntax().syntax_element(), - factory.token(T![>]).syntax_element(), + editor.make().token(T![<]).syntax_element(), + editor.make().lifetime(lifetime_name).syntax().syntax_element(), + editor.make().token(T![>]).syntax_element(), ], ); @@ -203,7 +199,6 @@ fn insert_new_generic_param_list_imp( fn insert_lifetime_param( editor: &mut SyntaxEditor, - factory: &SyntaxFactory, generic_list: &ast::GenericParamList, lifetime_name: &str, ) -> Option<()> { @@ -213,11 +208,11 @@ fn insert_lifetime_param( let mut elements = Vec::new(); if needs_comma { - elements.push(factory.token(T![,]).syntax_element()); - elements.push(factory.whitespace(" ").syntax_element()); + elements.push(editor.make().token(T![,]).syntax_element()); + elements.push(editor.make().whitespace(" ").syntax_element()); } - let lifetime = factory.lifetime(lifetime_name); + let lifetime = editor.make().lifetime(lifetime_name); elements.push(lifetime.syntax().clone().into()); editor.insert_all(Position::before(r_angle), elements); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index db51070a6430b..5987d8d9f5e41 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -1,6 +1,6 @@ use ide_db::syntax_helpers::suggest_name; use itertools::Itertools; -use syntax::ast::{self, AstNode, HasGenericParams, HasName, syntax_factory::SyntaxFactory}; +use syntax::ast::{self, AstNode, HasGenericParams, HasName}; use crate::{AssistContext, AssistId, Assists}; @@ -24,7 +24,6 @@ pub(crate) fn introduce_named_type_parameter( let fn_ = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; let type_bound_list = impl_trait_type.type_bound_list()?; - let make = SyntaxFactory::with_mappings(); let target = fn_.syntax().text_range(); acc.add( AssistId::refactor_rewrite("introduce_named_type_parameter"), @@ -48,8 +47,10 @@ pub(crate) fn introduce_named_type_parameter( ) .for_impl_trait_as_generic(&impl_trait_type); - let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list)); - let new_ty = make.ty(&type_param_name); + let type_param = editor + .make() + .type_param(editor.make().name(&type_param_name), Some(type_bound_list)); + let new_ty = editor.make().ty(&type_param_name); editor.replace(impl_trait_type.syntax(), new_ty.syntax()); editor.add_generic_param(&fn_, type_param.clone().into()); @@ -58,7 +59,6 @@ pub(crate) fn introduce_named_type_parameter( editor.add_annotation(type_param.syntax(), builder.make_tabstop_before(cap)); } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index 42bc05811fd14..d866da50b173c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -4,11 +4,7 @@ use ide_db::imports::{ merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees}, }; use syntax::{ - AstNode, SyntaxElement, SyntaxNode, - algo::neighbor, - ast::{self, syntax_factory::SyntaxFactory}, - match_ast, - syntax_editor::Removable, + AstNode, SyntaxElement, SyntaxNode, algo::neighbor, ast, match_ast, syntax_editor::Removable, }; use crate::{ @@ -76,7 +72,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio }; acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(&parent_node); for edit in edits { @@ -94,7 +89,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } } } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 79b8bd5d3d489..25008179b1200 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -46,21 +46,20 @@ pub(crate) fn move_bounds_to_where_clause( target, |builder| { let mut edit = builder.make_editor(&parent); - let make = SyntaxFactory::without_mappings(); let new_preds: Vec = type_param_list .generic_params() - .filter_map(|param| build_predicate(param, &make)) + .filter_map(|param| build_predicate(param, edit.make())) .collect(); match_ast! { match (&parent) { - ast::Fn(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), - ast::Trait(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), - ast::Impl(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), - ast::Enum(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), - ast::Struct(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), - ast::TypeAlias(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::Fn(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::Trait(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::Impl(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::Enum(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::Struct(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::TypeAlias(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), _ => return, } }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 80587372e5165..ec4acdafa9045 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -156,7 +156,7 @@ pub(crate) fn move_arm_cond_to_match_guard( "Move condition to match guard", replace_node.text_range(), |builder| { - let make = SyntaxFactory::without_mappings(); + let mut editor = builder.make_editor(match_arm.syntax()); let mut replace_arms = vec![]; // Dedent if if_expr is in a BlockExpr @@ -175,17 +175,17 @@ pub(crate) fn move_arm_cond_to_match_guard( (Some(lhs), Some(rhs)) => { let op_expr = |expr: Expr| { if expr.precedence().needs_parentheses_in(ExprPrecedence::LAnd) { - make.expr_paren(expr).into() + editor.make().expr_paren(expr).into() } else { expr } }; let op = syntax::ast::BinaryOp::LogicOp(syntax::ast::LogicOp::And); - let expr_bin = make.expr_bin(op_expr(lhs), op, op_expr(rhs)); + let expr_bin = editor.make().expr_bin(op_expr(lhs), op, op_expr(rhs)); expr_bin.into() } }; - Some(make.match_guard(condition)) + Some(editor.make().match_guard(condition)) }; for (cond, block) in conds_blocks { @@ -194,7 +194,8 @@ pub(crate) fn move_arm_cond_to_match_guard( Some(then_expr) if only_expr => then_expr, _ => block.dedent(dedent.into()).into(), }; - let new_arm = make.match_arm(match_pat.clone(), make_guard(Some(cond)), expr); + let new_arm = + editor.make().match_arm(match_pat.clone(), make_guard(Some(cond)), expr); replace_arms.push(new_arm); } if let Some(block) = tail { @@ -207,7 +208,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => block.dedent(dedent.into()).into(), }; - let new_arm = make.match_arm(match_pat, make_guard(None), expr); + let new_arm = editor.make().match_arm(match_pat, make_guard(None), expr); replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match @@ -221,20 +222,22 @@ pub(crate) fn move_arm_cond_to_match_guard( cov_mark::hit!(move_guard_ifelse_has_wildcard); } _ => { - let block_expr = make.expr_empty_block().into(); - replace_arms.push(make.match_arm(match_pat, make_guard(None), block_expr)); + let block_expr = editor.make().expr_empty_block().into(); + replace_arms.push(editor.make().match_arm( + match_pat, + make_guard(None), + block_expr, + )); } } } - let mut edit = builder.make_editor(match_arm.syntax()); - - let newline = make.whitespace(&format!("\n{indent_level}")); + let newline = editor.make().whitespace(&format!("\n{indent_level}")); let replace_arms = replace_arms.iter().map(|it| it.syntax().syntax_element()); let replace_arms = Itertools::intersperse(replace_arms, newline.syntax_element()); - edit.replace_with_many(match_arm.syntax(), replace_arms.collect()); + editor.replace_with_many(match_arm.syntax(), replace_arms.collect()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 483c90d1032e5..5b6251e3dbe21 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -3,7 +3,7 @@ use ide_db::{assists::AssistId, defs::Definition}; use stdx::to_upper_snake_case; use syntax::{ AstNode, - ast::{self, HasName, syntax_factory::SyntaxFactory}, + ast::{self, HasName}, }; use crate::{ @@ -68,18 +68,17 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) "Promote local to constant", let_stmt.syntax().text_range(), |edit| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(let_stmt.syntax()); let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { - let name_ref = make.name_ref(&name); + let name_ref = editor.make().name_ref(&name); for usage in usages { let Some(usage_name) = usage.name.as_name_ref().cloned() else { continue }; if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage_name) { - let path = make.ident_path(&name); - let name_expr = make.expr_path(path); + let path = editor.make().ident_path(&name); + let name_expr = editor.make().expr_path(path); utils::replace_record_field_expr(ctx, edit, record_field, name_expr); } else { let usage_range = usage.range; @@ -88,7 +87,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer); + let item = editor.make().item_const( + None, + None, + editor.make().name(&name), + editor.make().ty(&ty), + initializer, + ); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { let tabstop = edit.make_tabstop_before(cap); @@ -97,7 +102,6 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) editor.replace(let_stmt.syntax(), item.syntax()); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 74ed2e14fa239..5d89defd217fb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -1,10 +1,5 @@ use either::Either; -use syntax::{ - AstNode, - algo::find_node_at_range, - ast::{self, syntax_factory::SyntaxFactory}, - syntax_editor::SyntaxEditor, -}; +use syntax::{AstNode, algo::find_node_at_range, ast, syntax_editor::SyntaxEditor}; use crate::{ AssistId, @@ -110,13 +105,11 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Pull assignment up", target, move |edit| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(tgt.syntax()); - let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone()); - let assign_stmt = make.expr_stmt(assign_expr.into()); + let assign_expr = editor.make().expr_assignment(collector.common_lhs, new_tgt.clone()); + let assign_stmt = editor.make().expr_stmt(assign_expr.into()); editor.replace(tgt.syntax(), assign_stmt.syntax()); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 8b9e6570e917b..653e3e3f8776b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -1,6 +1,6 @@ use hir::{AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef, db::HirDatabase}; use ide_db::assists::AssistId; -use syntax::{AstNode, ast, ast::syntax_factory::SyntaxFactory}; +use syntax::{AstNode, ast}; use crate::{ assist_context::{AssistContext, Assists}, @@ -59,17 +59,14 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Qualify `{ident}` method call"), range, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(call.syntax()); qualify_candidate.qualify( |_| {}, &mut editor, - &make, &receiver_path, item_in_ns, current_edition, ); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index c059f758c47e2..123269e0ae6e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -9,11 +9,7 @@ use ide_db::{ }; use syntax::Edition; use syntax::ast::HasGenericArgs; -use syntax::{ - AstNode, ast, - ast::{HasArgList, syntax_factory::SyntaxFactory}, - syntax_editor::SyntaxEditor, -}; +use syntax::{AstNode, ast, ast::HasArgList, syntax_editor::SyntaxEditor}; use crate::{ AssistId, GroupLabel, @@ -102,17 +98,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option label(ctx.db(), candidate, &import, current_edition), range, |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(&syntax_under_caret); qualify_candidate.qualify( |replace_with: String| builder.replace(range, replace_with), &mut editor, - &make, &import.import_path, import.item_to_import, current_edition, ); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); @@ -132,7 +125,6 @@ impl QualifyCandidate<'_> { &self, mut replacer: impl FnMut(String), editor: &mut SyntaxEditor, - make: &SyntaxFactory, import: &hir::ModPath, item: hir::ItemInNs, edition: Edition, @@ -151,10 +143,10 @@ impl QualifyCandidate<'_> { replacer(format!("<{qualifier} as {import}>::{segment}")); } QualifyCandidate::TraitMethod(db, mcall_expr) => { - Self::qualify_trait_method(db, mcall_expr, editor, make, import, item); + Self::qualify_trait_method(db, mcall_expr, editor, import, item); } QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => { - Self::qualify_fn_call(db, mcall_expr, editor, make, import, hir_fn); + Self::qualify_fn_call(db, mcall_expr, editor, import, hir_fn); } } } @@ -163,7 +155,6 @@ impl QualifyCandidate<'_> { db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, editor: &mut SyntaxEditor, - make: &SyntaxFactory, import: ast::Path, hir_fn: &hir::Function, ) -> Option<()> { @@ -175,16 +166,17 @@ impl QualifyCandidate<'_> { if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) { let receiver = match self_access { - hir::Access::Shared => make.expr_ref(receiver, false), - hir::Access::Exclusive => make.expr_ref(receiver, true), + hir::Access::Shared => editor.make().expr_ref(receiver, false), + hir::Access::Exclusive => editor.make().expr_ref(receiver, true), hir::Access::Owned => receiver, }; let arg_list = match arg_list { - Some(args) => make.arg_list(iter::once(receiver).chain(args)), - None => make.arg_list(iter::once(receiver)), + Some(args) => editor.make().arg_list(iter::once(receiver).chain(args)), + None => editor.make().arg_list(iter::once(receiver)), }; - let call_path = make.path_from_text(&format!("{import}::{method_name}{generics}")); - let call_expr = make.expr_call(make.expr_path(call_path), arg_list); + let call_path = + editor.make().path_from_text(&format!("{import}::{method_name}{generics}")); + let call_expr = editor.make().expr_call(editor.make().expr_path(call_path), arg_list); editor.replace(mcall_expr.syntax(), call_expr.syntax()); } Some(()) @@ -194,14 +186,13 @@ impl QualifyCandidate<'_> { db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, editor: &mut SyntaxEditor, - make: &SyntaxFactory, import: ast::Path, item: hir::ItemInNs, ) -> Option<()> { let trait_method_name = mcall_expr.name_ref()?; let trait_ = item_as_trait(db, item)?; let method = find_trait_method(db, trait_, &trait_method_name)?; - Self::qualify_fn_call(db, mcall_expr, editor, make, import, &method) + Self::qualify_fn_call(db, mcall_expr, editor, import, &method) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index f07da489e23ae..480b9d44e1f84 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -1,8 +1,4 @@ -use syntax::{ - AstNode, SyntaxKind, T, - ast::{self, syntax_factory::SyntaxFactory}, - syntax_editor::Position, -}; +use syntax::{AstNode, SyntaxKind, T, ast, syntax_editor::Position}; use crate::{AssistContext, AssistId, Assists}; @@ -54,9 +50,7 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> None => false, }; if need_to_add_ws { - let make = SyntaxFactory::with_mappings(); - editor.insert(Position::before(parens.syntax()), make.whitespace(" ")); - editor.add_mappings(make.finish_with_mappings()); + editor.insert(Position::before(parens.syntax()), editor.make().whitespace(" ")); } editor.replace(parens.syntax(), expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index b686dc056667c..657f19845a72f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -1,7 +1,7 @@ use ide_db::assists::{AssistId, GroupLabel}; use syntax::{ AstNode, - ast::{self, ArithOp, BinaryOp, syntax_factory::SyntaxFactory}, + ast::{self, ArithOp, BinaryOp}, }; use crate::assist_context::{AssistContext, Assists}; @@ -83,19 +83,20 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> kind.label(), op_expr.text_range(), |builder| { - let mut edit = builder.make_editor(rhs.syntax()); - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(rhs.syntax()); let method_name = kind.method_name(op); let needs_parentheses = lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix); - let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs }; - let arith_expr = - make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])); - edit.replace(op_expr, arith_expr.syntax()); - - edit.add_mappings(make.finish_with_mappings()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + let receiver = + if needs_parentheses { editor.make().expr_paren(lhs).into() } else { lhs }; + let arith_expr = editor.make().expr_method_call( + receiver, + editor.make().name_ref(&method_name), + editor.make().arg_list([rhs]), + ); + editor.replace(op_expr, arith_expr.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } 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 5e595218f6b1a..fc63c59799ed0 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 @@ -4,7 +4,7 @@ use itertools::Itertools; use syntax::{ SyntaxKind::WHITESPACE, T, - ast::{self, AstNode, HasName, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, HasName}, syntax_editor::{Position, SyntaxEditor}, }; @@ -128,10 +128,11 @@ fn add_assist( let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(attr.syntax()); let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); let impl_def = impl_def_from_trait( + &editor, &ctx.sema, ctx.config, adt, @@ -140,11 +141,9 @@ fn add_assist( replace_trait_path, impl_is_unsafe, ); + update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr); - let mut editor = builder.make_editor(attr.syntax()); - update_attribute(&make, &mut editor, old_derives, old_tree, old_trait_path, attr); - - let trait_path = make.ty_path(replace_trait_path.clone()).into(); + let trait_path = editor.make().ty_path(replace_trait_path.clone()).into(); let (impl_def, first_assoc_item) = if let Some(impl_def) = impl_def { ( @@ -152,7 +151,7 @@ fn add_assist( impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()), ) } else { - (generate_trait_impl(&make, impl_is_unsafe, adt, trait_path), None) + (generate_trait_impl(editor.make(), impl_is_unsafe, adt, trait_path), None) }; if let Some(cap) = ctx.config.snippet_cap { @@ -176,14 +175,14 @@ fn add_assist( editor.insert_all( insert_after, - vec![make.whitespace("\n\n").into(), impl_def.syntax().clone().into()], + vec![editor.make().whitespace("\n\n").into(), impl_def.syntax().clone().into()], ); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } fn impl_def_from_trait( + editor: &SyntaxEditor, sema: &hir::Semantics<'_, ide_db::RootDatabase>, config: &AssistConfig, adt: &ast::Adt, @@ -208,12 +207,11 @@ fn impl_def_from_trait( if trait_items.is_empty() { return None; } - let make = SyntaxFactory::without_mappings(); - let trait_ty: ast::Type = make.ty_path(trait_path.clone()).into(); - let impl_def = generate_trait_impl(&make, impl_is_unsafe, adt, trait_ty.clone()); + let trait_ty: ast::Type = editor.make().ty_path(trait_path.clone()).into(); + let impl_def = generate_trait_impl(editor.make(), impl_is_unsafe, adt, trait_ty.clone()); let assoc_items = add_trait_assoc_items_to_impl( - &make, + editor.make(), sema, config, &trait_items, @@ -223,7 +221,7 @@ fn impl_def_from_trait( ); let assoc_item_list = if let Some((first, other)) = assoc_items.split_first() { let first_item = if let ast::AssocItem::Fn(func) = first - && let Some(body) = gen_trait_fn_body(&make, func, trait_path, adt, None) + && let Some(body) = gen_trait_fn_body(editor.make(), func, trait_path, adt, None) && let Some(func_body) = func.body() { let (mut editor, _) = SyntaxEditor::new(first.syntax().clone()); @@ -234,16 +232,21 @@ fn impl_def_from_trait( }; let items: Vec = first_item.into_iter().chain(other.iter().cloned()).collect(); - make.assoc_item_list(items) + editor.make().assoc_item_list(items) } else { - make.assoc_item_list_empty() + editor.make().assoc_item_list_empty() }; - Some(generate_trait_impl_with_item(&make, impl_is_unsafe, adt, trait_ty, assoc_item_list)) + Some(generate_trait_impl_with_item( + editor.make(), + impl_is_unsafe, + adt, + trait_ty, + assoc_item_list, + )) } fn update_attribute( - make: &SyntaxFactory, editor: &mut SyntaxEditor, old_derives: &[ast::Path], old_tree: &ast::TokenTree, @@ -264,13 +267,16 @@ fn update_attribute( .collect::>() }); // ...which are interspersed with ", " - let tt = Itertools::intersperse(tt, vec![make.token(T![,]), make.whitespace(" ")]); + let tt = Itertools::intersperse( + tt, + vec![editor.make().token(T![,]), editor.make().whitespace(" ")], + ); // ...wrap them into the appropriate `NodeOrToken` variant let tt = tt.flatten().map(syntax::NodeOrToken::Token); // ...and make them into a flat list of tokens let tt = tt.collect::>(); - let new_tree = make.token_tree(T!['('], tt); + let new_tree = editor.make().token_tree(T!['('], tt); editor.replace(old_tree.syntax(), new_tree.syntax()); } else { // Remove the attr and any trailing whitespace 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 ada2fd9b217ad..d1619e020dd2f 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 @@ -111,27 +111,34 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' format!("Replace if{let_} with match"), available_range, move |builder| { - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(if_expr.syntax()); let match_expr: ast::Expr = { - let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies); + let else_arm = make_else_arm(ctx, editor.make(), else_block, &cond_bodies); let make_match_arm = |(pat, guard, body): (_, Option, ast::BlockExpr)| { // Dedent from original position, then indent for match arm let body = body.dedent(indent); let body = unwrap_trivial_block(body); - match (pat, guard.map(|it| make.match_guard(it))) { - (Some(pat), guard) => make.match_arm(pat, guard, body), - (None, _) if !pat_seen => { - make.match_arm(make.literal_pat("true").into(), None, body) - } - (None, guard) => { - make.match_arm(make.wildcard_pat().into(), guard, body) - } + match (pat, guard.map(|it| editor.make().match_guard(it))) { + (Some(pat), guard) => editor.make().match_arm(pat, guard, body), + (None, _) if !pat_seen => editor.make().match_arm( + editor.make().literal_pat("true").into(), + None, + body, + ), + (None, guard) => editor.make().match_arm( + editor.make().wildcard_pat().into(), + guard, + body, + ), } }; let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); let expr = scrutinee_to_be_expr.reset_indent(); - let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent); + let match_expr = editor + .make() + .expr_match(expr, editor.make().match_arm_list(arms)) + .indent(indent); match_expr.into() }; @@ -139,17 +146,15 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` - let block_expr = make + let block_expr = editor + .make() .block_expr([], Some(match_expr.dedent(indent).indent(IndentLevel(1)))) .indent(indent); block_expr.into() } else { match_expr }; - - let mut editor = builder.make_editor(if_expr.syntax()); editor.replace(if_expr.syntax(), expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -267,14 +272,14 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' format!("Replace match with if{let_}"), match_expr.syntax().text_range(), move |builder| { - let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(match_expr.syntax()); let make_block_expr = |expr: ast::Expr| { // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are // formatted without enclosing braces. If we encounter such block exprs, // wrap them in another BlockExpr. match expr { ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, - expr => make.block_expr([], Some(expr.indent(IndentLevel(1)))), + expr => editor.make().block_expr([], Some(expr.indent(IndentLevel(1)))), } }; @@ -287,13 +292,16 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { - make.expr_prefix(T![!], scrutinee).into() + editor.make().expr_prefix(T![!], scrutinee).into() } - _ => make.expr_let(if_let_pat, scrutinee).into(), + _ => editor.make().expr_let(if_let_pat, scrutinee).into(), }; let condition = if let Some(guard) = guard { - let guard = wrap_paren(guard, &make, ast::prec::ExprPrecedence::LAnd); - make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() + let guard = wrap_paren(guard, editor.make(), ast::prec::ExprPrecedence::LAnd); + editor + .make() + .expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard) + .into() } else { condition }; @@ -301,7 +309,8 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let else_expr = else_expr.reset_indent(); let then_block = make_block_expr(then_expr); let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; - let if_let_expr = make + let if_let_expr = editor + .make() .expr_if( condition, then_block, @@ -309,9 +318,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ) .indent(IndentLevel::from_node(match_expr.syntax())); - let mut editor = builder.make_editor(match_expr.syntax()); editor.replace(match_expr.syntax(), if_let_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 38d8c38ef23d6..35acd8c3eef45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,6 +1,9 @@ use either::Either; use ide_db::syntax_helpers::suggest_name; -use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory}; +use syntax::{ + ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory}, + syntax_editor::SyntaxEditor, +}; use crate::{ AssistContext, AssistId, Assists, @@ -41,9 +44,8 @@ pub(crate) fn replace_is_method_with_if_let_method( let method_kind = token.text().strip_suffix("_and").unwrap_or(token.text()); match method_kind { "is_some" | "is_ok" => { + let (mut editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let receiver = call_expr.receiver()?; - let make = SyntaxFactory::with_mappings(); - let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals( ctx.sema.scope(has_cond.syntax()), ); @@ -52,7 +54,7 @@ pub(crate) fn replace_is_method_with_if_let_method( } else { name_generator.for_variable(&receiver, &ctx.sema) }; - let (pat, predicate) = method_predicate(&call_expr, &var_name, &make); + let (pat, predicate) = method_predicate(&call_expr, &var_name, editor.make()); let (assist_id, message, text) = if method_kind == "is_some" { ("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some") @@ -65,10 +67,11 @@ pub(crate) fn replace_is_method_with_if_let_method( message, call_expr.syntax().text_range(), |edit| { - let mut editor = edit.make_editor(call_expr.syntax()); - - let pat = make.tuple_struct_pat(make.ident_path(text), [pat]).into(); - let let_expr = make.expr_let(pat, receiver); + let pat = editor + .make() + .tuple_struct_pat(editor.make().ident_path(text), [pat]) + .into(); + let let_expr = editor.make().expr_let(pat, receiver); if let Some(cap) = ctx.config.snippet_cap && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() @@ -81,14 +84,12 @@ pub(crate) fn replace_is_method_with_if_let_method( let new_expr = if let Some(predicate) = predicate { let op = ast::BinaryOp::LogicOp(ast::LogicOp::And); - let predicate = wrap_paren(predicate, &make, ExprPrecedence::LAnd); - make.expr_bin(let_expr.into(), op, predicate).into() + let predicate = wrap_paren(predicate, editor.make(), ExprPrecedence::LAnd); + editor.make().expr_bin(let_expr.into(), op, predicate).into() } else { ast::Expr::from(let_expr) }; editor.replace(call_expr.syntax(), new_expr.syntax()); - - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 6ff5f0bbd30cf..5f6909ea3e242 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -47,7 +47,6 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> target, |builder| { let mut editor = builder.make_editor(let_stmt.syntax()); - let make = SyntaxFactory::with_mappings(); let ty = ctx.sema.type_of_expr(&init); let pat = if let_stmt.let_else().is_some() { // Do not add the wrapper type that implements `Try`, @@ -59,27 +58,30 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> .map(|it| it.happy_case()); match happy_variant { None => original_pat, - Some(var_name) => { - make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into() - } + Some(var_name) => editor + .make() + .tuple_struct_pat(editor.make().ident_path(var_name), [original_pat]) + .into(), } }; - let init_expr = - if let_expr_needs_paren(&init) { make.expr_paren(init).into() } else { init }; + let init_expr = if let_expr_needs_paren(&init) { + editor.make().expr_paren(init).into() + } else { + init + }; - let block = make.block_expr([], None); + let block = editor.make().block_expr([], None); let block = block.indent(IndentLevel::from_node(let_stmt.syntax())); - let if_expr = make.expr_if( - make.expr_let(pat, init_expr).into(), + let if_expr = editor.make().expr_if( + editor.make().expr_let(pat, init_expr).into(), block, let_stmt .let_else() .and_then(|let_else| let_else.block_expr().map(ast::ElseBranch::from)), ); - let if_stmt = make.expr_stmt(if_expr.into()); + let if_stmt = editor.make().expr_stmt(if_expr.into()); editor.replace(let_stmt.syntax(), if_stmt.syntax()); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 018642a047232..6403a896bc716 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -5,7 +5,6 @@ use ide_db::{ defs::Definition, search::{SearchScope, UsageSearchResult}, }; -use syntax::ast::syntax_factory::SyntaxFactory; use syntax::{ AstNode, ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType}, @@ -73,7 +72,6 @@ pub(crate) fn replace_named_generic_with_impl( target, |edit| { let mut editor = edit.make_editor(type_param.syntax()); - let make = SyntaxFactory::without_mappings(); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { @@ -85,12 +83,12 @@ pub(crate) fn replace_named_generic_with_impl( if params.is_empty() { editor.delete(generic_params.syntax()); } else { - let new_generic_param_list = make.generic_param_list(params); + let new_generic_param_list = editor.make().generic_param_list(params); editor.replace(generic_params.syntax(), new_generic_param_list.syntax()); } } - let new_bounds = make.impl_trait_type(type_bound_list); + let new_bounds = editor.make().impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { editor.replace(path_type.syntax(), new_bounds.syntax()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 15143575e7d84..7222d25ad3e55 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -2,7 +2,7 @@ use ide_db::assists::AssistId; use syntax::{ AstNode, SyntaxKind, SyntaxToken, T, algo::{previous_non_trivia_token, skip_trivia_token}, - ast::{self, syntax_factory::SyntaxFactory}, + ast, }; use crate::{AssistContext, Assists}; @@ -73,34 +73,35 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) }, token_tree.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(token_tree.syntax()); match token { MacroDelims::LPar | MacroDelims::RPar => { - editor.replace(ltoken, make.token(T!['{'])); - editor.replace(rtoken, make.token(T!['}'])); + editor.replace(ltoken, editor.make().token(T!['{'])); + editor.replace(rtoken, editor.make().token(T!['}'])); if let Some(sc) = semicolon { editor.delete(sc); } } MacroDelims::LBra | MacroDelims::RBra => { - editor.replace(ltoken, make.token(T!['('])); - editor.replace(rtoken, make.token(T![')'])); + editor.replace(ltoken, editor.make().token(T!['('])); + editor.replace(rtoken, editor.make().token(T![')'])); } MacroDelims::LCur | MacroDelims::RCur => { - editor.replace(ltoken, make.token(T!['['])); + editor.replace(ltoken, editor.make().token(T!['['])); if semicolon.is_some() || !needs_semicolon(token_tree) { - editor.replace(rtoken, make.token(T![']'])); + editor.replace(rtoken, editor.make().token(T![']'])); } else { editor.replace_with_many( rtoken, - vec![make.token(T![']']).into(), make.token(T![;]).into()], + vec![ + editor.make().token(T![']']).into(), + editor.make().token(T![;]).into(), + ], ); } } } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index accb5c28d6ed3..115e8c36bca3a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -1,6 +1,6 @@ use syntax::{ AstNode, SyntaxKind, - ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, + ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make}, syntax_editor::{Element, Position, Removable}, }; @@ -41,26 +41,31 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let target = tree.syntax().text_range(); acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| { - let make = SyntaxFactory::with_mappings(); - let new_use = make.use_( + let mut editor = builder.make_editor(use_.syntax()); + let new_use = editor.make().use_( use_.attrs(), use_.visibility(), - make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), + editor.make().use_tree( + path, + tree.use_tree_list(), + tree.rename(), + tree.star_token().is_some(), + ), ); - let mut editor = builder.make_editor(use_.syntax()); // Remove the use tree from the current use item tree.remove(&mut editor); // Insert a newline and indentation, followed by the new use item editor.insert_all( Position::after(use_.syntax()), vec![ - make.whitespace(&format!("\n{}", IndentLevel::from_node(use_.syntax()))) + editor + .make() + .whitespace(&format!("\n{}", IndentLevel::from_node(use_.syntax()))) .syntax_element(), new_use.syntax().syntax_element(), ], ); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index c4c03d3e35f56..1fc9a6c41db95 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -1,6 +1,6 @@ use syntax::{ Direction, SyntaxKind, T, - ast::{self, AstNode, edit::IndentLevel, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, edit::IndentLevel}, syntax_editor::{Element, Position}, }; @@ -56,15 +56,14 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O "Unmerge match arm", pipe_token.text_range(), |edit| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(&new_parent); // It is guaranteed that `pats_after` has at least one element let new_pat = if pats_after.len() == 1 { pats_after[0].clone() } else { - make.or_pat(pats_after, or_pat.leading_pipe().is_some()).into() + editor.make().or_pat(pats_after, or_pat.leading_pipe().is_some()).into() }; - let new_match_arm = make.match_arm(new_pat, match_arm.guard(), match_arm_body); + let new_match_arm = editor.make().match_arm(new_pat, match_arm.guard(), match_arm_body); let mut pipe_index = pipe_token.index(); if pipe_token .prev_sibling_or_token() @@ -92,16 +91,15 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // we don't want to insert a comma at all. let has_comma_after = match_arm.comma_token().is_some(); if !has_comma_after && !match_arm.expr().unwrap().is_block_like() { - insert_after_old_arm.push(make.token(T![,]).into()); + insert_after_old_arm.push(editor.make().token(T![,]).into()); } let indent = IndentLevel::from_node(match_arm.syntax()); - insert_after_old_arm.push(make.whitespace(&format!("\n{indent}")).into()); + insert_after_old_arm.push(editor.make().whitespace(&format!("\n{indent}")).into()); insert_after_old_arm.push(new_match_arm.syntax().clone().into()); editor.insert_all(Position::after(match_arm.syntax()), insert_after_old_arm); - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index ef395791e2518..131e4f091cc1c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -1,5 +1,5 @@ use hir::AsAssocItem; -use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory}; +use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence}; use crate::{AssistContext, AssistId, Assists}; @@ -50,16 +50,15 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) "Unqualify method call", call.syntax().text_range(), |builder| { - let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(call.syntax()); - let new_arg_list = make.arg_list(args.args().skip(1)); + let new_arg_list = editor.make().arg_list(args.args().skip(1)); let receiver = if first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix) { - ast::Expr::from(make.expr_paren(first_arg.clone())) + ast::Expr::from(editor.make().expr_paren(first_arg.clone())) } else { first_arg.clone() }; - let method_call = make.expr_method_call(receiver, method_name, new_arg_list); + let method_call = editor.make().expr_method_call(receiver, method_name, new_arg_list); editor.replace(call.syntax(), method_call.syntax()); @@ -67,10 +66,9 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) && let Some(trait_) = fun.container_or_implemented_trait(ctx.db()) && !scope.can_use_trait_methods(trait_) { - add_import(qualifier, ctx, &make, &mut editor); + add_import(qualifier, ctx, &mut editor); } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -79,7 +77,6 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) fn add_import( qualifier: ast::Path, ctx: &AssistContext<'_>, - make: &SyntaxFactory, editor: &mut syntax::syntax_editor::SyntaxEditor, ) { if let Some(path_segment) = qualifier.segment() { @@ -112,7 +109,6 @@ fn add_import( import, &ctx.config.insert_use, editor, - make, ); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs index eea6c85e8df0a..a5d7240667cdd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -5,7 +5,7 @@ use ide_db::{ }; use syntax::{ AstNode, NodeOrToken, SyntaxKind, - ast::{self, HasArgList, HasGenericArgs, syntax_factory::SyntaxFactory}, + ast::{self, HasArgList, HasGenericArgs}, match_ast, }; @@ -67,7 +67,6 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| { let mut editor = builder.make_editor(&parent); - let make = SyntaxFactory::with_mappings(); let mut exprs_to_unwrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); @@ -119,14 +118,15 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> .and_then(Either::::cast) .unwrap(); match tail_parent { - Either::Left(ret_expr) => { - editor.replace(ret_expr.syntax(), make.expr_return(None).syntax()) - } + Either::Left(ret_expr) => editor.replace( + ret_expr.syntax(), + editor.make().expr_return(None).syntax(), + ), Either::Right(stmt_list) => { let new_block = if stmt_list.statements().next().is_none() { - make.expr_empty_block() + editor.make().expr_empty_block() } else { - make.block_expr(stmt_list.statements(), None) + editor.make().block_expr(stmt_list.statements(), None) }; editor.replace( stmt_list.syntax(), @@ -147,7 +147,7 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> continue; } - let new_tail_expr = make.expr_unit(); + let new_tail_expr = editor.make().expr_unit(); editor.replace(path_expr.syntax(), new_tail_expr.syntax()); if let Some(cap) = ctx.config.snippet_cap { editor.add_annotation( @@ -168,7 +168,6 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap)); } - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index 0f089c9b66eb0..001e216568380 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -78,15 +78,18 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op type_ref.syntax().text_range(), |builder| { let mut editor = builder.make_editor(&parent); - let make = SyntaxFactory::with_mappings(); - let alias = wrapper_alias(ctx, &make, core_wrapper, type_ref, &ty, kind.symbol()); + let alias = + wrapper_alias(ctx, editor.make(), core_wrapper, type_ref, &ty, kind.symbol()); let (ast_new_return_ty, semantic_new_return_ty) = alias.unwrap_or_else(|| { let (ast_ty, ty_constructor) = match kind { - WrapperKind::Option => { - (make.ty_option(type_ref.clone()), famous_defs.core_option_Option()) - } + WrapperKind::Option => ( + editor.make().ty_option(type_ref.clone()), + famous_defs.core_option_Option(), + ), WrapperKind::Result => ( - make.ty_result(type_ref.clone(), make.ty_infer().into()), + editor + .make() + .ty_result(type_ref.clone(), editor.make().ty_infer().into()), famous_defs.core_result_Result(), ), }; @@ -121,9 +124,9 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op continue; } - let happy_wrapped = make.expr_call( - make.expr_path(make.ident_path(kind.happy_ident())), - make.arg_list(iter::once(ret_expr_arg.clone())), + let happy_wrapped = editor.make().expr_call( + editor.make().expr_path(editor.make().ident_path(kind.happy_ident())), + editor.make().arg_list(iter::once(ret_expr_arg.clone())), ); editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); } @@ -156,8 +159,6 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ); } } - - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 3b8988db7aae7..90c621c85da86 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -2,7 +2,7 @@ use ide_db::source_change::SourceChangeBuilder; use itertools::Itertools; use syntax::{ NodeOrToken, SyntaxToken, T, TextRange, algo, - ast::{self, AstNode, edit::AstNodeEdit, make, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, edit::AstNodeEdit, make}, }; use crate::{AssistContext, AssistId, Assists}; @@ -192,25 +192,25 @@ fn wrap_derive( } } let handle_source_change = |edit: &mut SourceChangeBuilder| { - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(attr.syntax()); - let new_derive = make.attr_outer( - make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)), - ); - let meta = make.cfg_attr_meta( - make.cfg_flag("cfg"), - [make.meta_token_tree( - make.ident_path("derive"), - make.token_tree(T!['('], cfg_derive_tokens), + let new_derive = editor.make().attr_outer(editor.make().meta_token_tree( + editor.make().ident_path("derive"), + editor.make().token_tree(T!['('], new_derive), + )); + let meta = editor.make().cfg_attr_meta( + editor.make().cfg_flag("cfg"), + [editor.make().meta_token_tree( + editor.make().ident_path("derive"), + editor.make().token_tree(T!['('], cfg_derive_tokens), )], ); - let cfg_attr = make.attr_outer(meta.clone().into()); + let cfg_attr = editor.make().attr_outer(meta.clone().into()); editor.replace_with_many( attr.syntax(), vec![ new_derive.syntax().clone().into(), - make.whitespace("\n").into(), + editor.make().whitespace("\n").into(), cfg_attr.syntax().clone().into(), ], ); @@ -221,8 +221,6 @@ fn wrap_derive( let tabstop = edit.make_placeholder_snippet(snippet_cap); editor.add_annotation(cfg_predicate.syntax(), tabstop); } - - editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }; @@ -239,14 +237,15 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec, attrs: Vec, - make: &SyntaxFactory, ) { let mut attrs = attrs.into_iter().peekable(); if attrs.peek().is_none() { @@ -357,12 +356,10 @@ pub(crate) fn insert_attributes( let elem = before.syntax_element(); let indent = IndentLevel::from_element(&elem); let whitespace = format!("\n{indent}"); - edit.insert_all( - syntax::syntax_editor::Position::before(elem), - attrs - .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()]) - .collect(), - ); + let elements: Vec = attrs + .flat_map(|attr| [attr.syntax().clone().into(), edit.make().whitespace(&whitespace).into()]) + .collect(); + edit.insert_all(syntax::syntax_editor::Position::before(elem), elements); } pub(crate) fn next_prev() -> impl Iterator { 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 9318c3e132725..d793c2799e26f 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 @@ -9,7 +9,7 @@ use syntax::{ Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, ast::{ self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, - edit_in_place::Removable, make, syntax_factory::SyntaxFactory, + edit_in_place::Removable, make, }, syntax_editor::{Position, SyntaxEditor}, ted, @@ -176,9 +176,8 @@ pub fn insert_use_with_editor( path: ast::Path, cfg: &InsertUseConfig, syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, ) { - insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor, syntax_factory); + insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor); } pub fn insert_use_as_alias( @@ -270,7 +269,6 @@ fn insert_use_with_alias_option_with_editor( cfg: &InsertUseConfig, alias: Option, syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, ) { let _p = tracing::info_span!("insert_use_with_alias_option").entered(); let mut mb = match cfg.granularity { @@ -301,7 +299,7 @@ fn insert_use_with_alias_option_with_editor( }; } - let use_tree = syntax_factory.use_tree(path, None, alias, false); + let use_tree = syntax_editor.make().use_tree(path, None, alias, false); if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { use_tree.wrap_in_tree_list(); } @@ -324,7 +322,7 @@ fn insert_use_with_alias_option_with_editor( } // either we weren't allowed to merge or there is no import that fits the merge conditions // so look for the place we have to insert to - insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor, syntax_factory); + insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor); } pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option> { @@ -605,7 +603,6 @@ fn insert_use_with_editor_( use_item: ast::Use, group_imports: bool, syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, ) { let scope_syntax = scope.as_syntax_node(); let insert_use_tree = @@ -656,7 +653,7 @@ fn insert_use_with_editor_( cov_mark::hit!(insert_group_new_group); syntax_editor.insert(Position::before(&node), use_item.syntax()); if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); + syntax_editor.insert(Position::after(node), syntax_editor.make().whitespace("\n")); } return; } @@ -664,7 +661,7 @@ fn insert_use_with_editor_( if let Some(node) = last { cov_mark::hit!(insert_group_no_group); syntax_editor.insert(Position::after(&node), use_item.syntax()); - syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); + syntax_editor.insert(Position::after(node), syntax_editor.make().whitespace("\n")); return; } } else { @@ -703,23 +700,20 @@ fn insert_use_with_editor_( { cov_mark::hit!(insert_empty_inner_attr); syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); - syntax_editor.insert(Position::after(last_inner_element), syntax_factory.whitespace("\n")); + syntax_editor + .insert(Position::after(last_inner_element), syntax_editor.make().whitespace("\n")); } else { match l_curly { Some(b) => { cov_mark::hit!(insert_empty_module); - syntax_editor.insert(Position::after(&b), syntax_factory.whitespace("\n")); - syntax_editor.insert_with_whitespace( - Position::after(&b), - use_item.syntax(), - syntax_factory, - ); + syntax_editor.insert(Position::after(&b), syntax_editor.make().whitespace("\n")); + syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax()); } None => { cov_mark::hit!(insert_empty_file); syntax_editor.insert( Position::first_child_of(scope_syntax), - syntax_factory.whitespace("\n\n"), + syntax_editor.make().whitespace("\n\n"), ); syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); } 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 ff0e6a254b6ae..d99d3043daf55 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 @@ -10,7 +10,6 @@ use syntax::{ ast::{ self, BlockExpr, Expr, ExprStmt, HasArgList, edit::{AstNodeEdit, IndentLevel}, - syntax_factory::SyntaxFactory, }, }; @@ -245,24 +244,21 @@ fn remove_unnecessary_wrapper( .and_then(Either::::cast)?; editor = builder.make_editor(parent.syntax()); - let make = SyntaxFactory::with_mappings(); match parent { Either::Left(ret_expr) => { - editor.replace(ret_expr.syntax(), make.expr_return(None).syntax()); + editor.replace(ret_expr.syntax(), editor.make().expr_return(None).syntax()); } Either::Right(stmt_list) => { let new_block = if stmt_list.statements().next().is_none() { - make.expr_empty_block() + editor.make().expr_empty_block() } else { - make.block_expr(stmt_list.statements(), None) + editor.make().block_expr(stmt_list.statements(), None) }; editor.replace(stmt_list.syntax().parent()?, new_block.syntax()); } } - - editor.add_mappings(make.finish_with_mappings()); } _ => { editor = builder.make_editor(call_expr.syntax()); From 02fa156965c737696f3bb8f65a6d68753d5dae07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 11:10:18 +0000 Subject: [PATCH 37/98] Bump follow-redirects from 1.15.9 to 1.16.0 in /editors/code Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.9 to 1.16.0. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.9...v1.16.0) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 5755f0708f0fa..3037deb05676e 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -4137,9 +4137,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { From 6f703399c90577348881854b95e6d3f1184cf9fa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 15 Apr 2026 11:32:10 +0300 Subject: [PATCH 38/98] Represent lowered coroutines with closures This is how rustc does it, because they're similar in many ways, and most importantly: they share (or will share, not yet) capture analysis. Also: - Copy closure inference from rustc again, this time really precisely. - Have separate ID types for coroutine closures and normal closures, for better type safety. - Validate, when constructing interned closures/coroutines/coroutine closures and `cfg(debug_assertions)`, that the `ExprId` is of an expr of the correct kind. --- .../crates/hir-def/src/expr_store.rs | 15 +- .../crates/hir-def/src/expr_store/lower.rs | 84 ++++-- .../crates/hir-def/src/expr_store/pretty.rs | 57 ++-- .../crates/hir-def/src/expr_store/scope.rs | 2 +- .../hir-def/src/expr_store/tests/body.rs | 12 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 26 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 88 +++++- .../crates/hir-ty/src/diagnostics/expr.rs | 7 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 2 +- .../crates/hir-ty/src/display.rs | 32 +-- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../crates/hir-ty/src/infer/closure.rs | 266 ++++++++++++------ .../hir-ty/src/infer/closure/analysis.rs | 10 +- .../crates/hir-ty/src/infer/expr.rs | 73 +---- .../crates/hir-ty/src/infer/mutability.rs | 1 - .../rust-analyzer/crates/hir-ty/src/layout.rs | 2 +- .../crates/hir-ty/src/mir/borrowck.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/eval/shim.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 11 +- .../crates/hir-ty/src/next_solver/def_id.rs | 13 +- .../crates/hir-ty/src/next_solver/interner.rs | 52 ++-- .../crates/hir-ty/src/next_solver/ty.rs | 11 +- .../hir-ty/src/tests/closure_captures.rs | 3 +- .../crates/hir/src/has_source.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +- 26 files changed, 483 insertions(+), 308 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 62a17168b18eb..75278f778b88e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -642,9 +642,7 @@ impl ExpressionStore { self.walk_exprs_in_pat(*pat, &mut f); f(*expr); } - Expr::Block { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Async { statements, tail, .. } => { + Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { for stmt in statements.iter() { match stmt { Statement::Let { initializer, else_branch, pat, .. } => { @@ -777,9 +775,7 @@ impl ExpressionStore { Expr::Let { expr, .. } => { f(*expr); } - Expr::Block { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Async { statements, tail, .. } => { + Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { for stmt in statements.iter() { match stmt { Statement::Let { initializer, else_branch, .. } => { @@ -923,6 +919,13 @@ impl ExpressionStore { None => const { &Arena::new() }.iter(), } } + + /// The coroutine associated with a coroutine closure. + #[inline] + pub fn coroutine_for_closure(coroutine_closure: ExprId) -> ExprId { + // We keep the async closure exactly one expr before. + ExprId::from_raw(la_arena::RawIdx::from_u32(coroutine_closure.into_raw().into_u32() - 1)) + } } impl Index for ExpressionStore { 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 7fe91a3d02dba..8a23ea69b8f75 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 @@ -46,8 +46,9 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId, - RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams, + CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, + generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -978,11 +979,33 @@ impl<'db> ExprCollector<'db> { *param = pat_id; } - self.alloc_expr_desugared(Expr::Async { - id: None, - statements: statements.into_boxed_slice(), - tail: Some(body), - }) + let async_ = self.async_block( + CoroutineSource::Fn, + CaptureBy::Value, + None, + statements.into_boxed_slice(), + Some(body), + ); + self.alloc_expr_desugared(async_) + } + + fn async_block( + &mut self, + source: CoroutineSource, + capture_by: CaptureBy, + id: Option, + statements: Box<[Statement]>, + tail: Option, + ) -> Expr { + let block = self.alloc_expr_desugared(Expr::Block { label: None, id, statements, tail }); + Expr::Closure { + args: Box::default(), + arg_types: Box::default(), + ret_type: None, + body: block, + closure_kind: ClosureKind::AsyncBlock { source }, + capture_by, + } } fn collect( @@ -1126,7 +1149,7 @@ impl<'db> ExprCollector<'db> { self.desugar_try_block(e, result_type) } Some(ast::BlockModifier::Unsafe(_)) => { - self.collect_block_(e, |id, statements, tail| Expr::Unsafe { + self.collect_block_(e, |_, id, statements, tail| Expr::Unsafe { id, statements, tail, @@ -1136,7 +1159,7 @@ impl<'db> ExprCollector<'db> { let label_hygiene = self.hygiene_id_for(label.syntax().text_range()); let label_id = self.collect_label(label); self.with_labeled_rib(label_id, label_hygiene, |this| { - this.collect_block_(e, |id, statements, tail| Expr::Block { + this.collect_block_(e, |_, id, statements, tail| Expr::Block { id, statements, tail, @@ -1145,12 +1168,18 @@ impl<'db> ExprCollector<'db> { }) } Some(ast::BlockModifier::Async(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; self.with_label_rib(RibKind::Closure, |this| { this.with_awaitable_block(Awaitable::Yes, |this| { - this.collect_block_(e, |id, statements, tail| Expr::Async { - id, - statements, - tail, + this.collect_block_(e, |this, id, statements, tail| { + this.async_block( + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) }) }) }) @@ -1406,7 +1435,7 @@ impl<'db> ExprCollector<'db> { } else { Awaitable::No("non-async closure") }; - let body = this + let mut body = this .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); let closure_kind = if this.is_lowering_coroutine { @@ -1417,7 +1446,22 @@ impl<'db> ExprCollector<'db> { }; ClosureKind::Coroutine(movability) } else if e.async_token().is_some() { - ClosureKind::Async + // It's important that this expr is allocated immediately before the closure. + // We rely on it for `coroutine_for_closure()`. + body = this.alloc_expr_desugared(Expr::Closure { + args: Box::default(), + arg_types: Box::default(), + ret_type: None, + body, + closure_kind: ClosureKind::AsyncBlock { + source: CoroutineSource::Closure, + }, + // The block may need to capture by move, but we cannot know it now. + // It will be fixed in capture analysis. + capture_by: CaptureBy::Ref, + }); + + ClosureKind::AsyncClosure } else { ClosureKind::Closure }; @@ -1762,7 +1806,7 @@ impl<'db> ExprCollector<'db> { let ptr = AstPtr::new(&e).upcast(); let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| { let mut btail = None; - let block = this.collect_block_(e, |id, statements, tail| { + let block = this.collect_block_(e, |_, id, statements, tail| { btail = tail; Expr::Block { id, statements, tail, label: Some(label) } }); @@ -2220,7 +2264,7 @@ impl<'db> ExprCollector<'db> { } fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { - self.collect_block_(block, |id, statements, tail| Expr::Block { + self.collect_block_(block, |_, id, statements, tail| Expr::Block { id, statements, tail, @@ -2231,7 +2275,7 @@ impl<'db> ExprCollector<'db> { fn collect_block_( &mut self, block: ast::BlockExpr, - mk_block: impl FnOnce(Option, Box<[Statement]>, Option) -> Expr, + mk_block: impl FnOnce(&mut Self, Option, Box<[Statement]>, Option) -> Expr, ) -> ExprId { let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| { let ast_id = self.expander.in_file(file_local_id); @@ -2266,8 +2310,8 @@ impl<'db> ExprCollector<'db> { }); let syntax_node_ptr = AstPtr::new(&block.into()); - let expr_id = self - .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr); + let expr = mk_block(self, block_id, statements.into_boxed_slice(), tail); + let expr_id = self.alloc_expr(expr, syntax_node_ptr); self.def_map = prev_def_map; self.module = prev_local_module; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 9c9c4db3b208c..71d59c904d8d2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -9,6 +9,7 @@ use std::{ use hir_expand::{Lookup, mod_path::PathKind}; use itertools::Itertools; use span::Edition; +use stdx::never; use syntax::ast::{HasName, RangeOp}; use crate::{ @@ -760,14 +761,31 @@ impl Printer<'_> { w!(self, "]"); } Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => { + let mut body = *body; + let mut print_pipes = true; match closure_kind { ClosureKind::Coroutine(Movability::Static) => { w!(self, "static "); } - ClosureKind::Async => { + ClosureKind::AsyncClosure => { + if let Expr::Closure { + body: inner_body, + closure_kind: ClosureKind::AsyncBlock { .. }, + .. + } = self.store[body] + { + body = inner_body; + } else { + never!("async closure should always have an async block body"); + } + w!(self, "async "); } - _ => (), + ClosureKind::AsyncBlock { .. } => { + w!(self, "async "); + print_pipes = false; + } + ClosureKind::Closure | ClosureKind::Coroutine(Movability::Movable) => (), } match capture_by { CaptureBy::Value => { @@ -775,24 +793,26 @@ impl Printer<'_> { } CaptureBy::Ref => (), } - w!(self, "|"); - for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { - if i != 0 { - w!(self, ", "); + if print_pipes { + w!(self, "|"); + for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_pat(*pat); + if let Some(ty) = ty { + w!(self, ": "); + self.print_type_ref(*ty); + } } - self.print_pat(*pat); - if let Some(ty) = ty { - w!(self, ": "); - self.print_type_ref(*ty); + w!(self, "|"); + if let Some(ret_ty) = ret_type { + w!(self, " -> "); + self.print_type_ref(*ret_ty); } + self.whitespace(); } - w!(self, "|"); - if let Some(ret_ty) = ret_type { - w!(self, " -> "); - self.print_type_ref(*ret_ty); - } - self.whitespace(); - self.print_expr(*body); + self.print_expr(body); } Expr::Tuple { exprs } => { w!(self, "("); @@ -832,9 +852,6 @@ impl Printer<'_> { Expr::Unsafe { id: _, statements, tail } => { self.print_block(Some("unsafe "), statements, tail); } - Expr::Async { id: _, statements, tail } => { - self.print_block(Some("async "), statements, tail); - } Expr::Const(id) => { w!(self, "const {{ /* {id:?} */ }}"); } 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 9738ac5c44c99..c6ba0241b7140 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 @@ -324,7 +324,7 @@ fn compute_expr_scopes( let mut scope = scopes.root_scope(); compute_expr_scopes(scopes, *id, &mut scope); } - Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { + Expr::Unsafe { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // 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). diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index 4e5f2ca89327e..6e711e3a38dfe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -652,12 +652,12 @@ fn async_fn_weird_param_patterns() { async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {} "#, expect![[r#" - fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async { - let ref mut param2 = param2; - let _ = 0; - let 123 = 1; - {} - }"#]], + fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async move { + let ref mut param2 = param2; + let _ = 0; + let 123 = 1; + {} + }"#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 7781a8fe54ee0..4dd113d419476 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -214,11 +214,6 @@ pub enum Expr { tail: Option, label: Option, }, - Async { - id: Option, - statements: Box<[Statement]>, - tail: Option, - }, Const(ExprId), // FIXME: Fold this into Block with an unsafe flag? Unsafe { @@ -339,7 +334,6 @@ impl Expr { | Expr::Block { .. } | Expr::Unsafe { .. } | Expr::Const(_) - | Expr::Async { .. } | Expr::If { .. } | Expr::Literal(_) | Expr::Loop { .. } @@ -534,7 +528,25 @@ pub enum InlineAsmRegOrRegClass { pub enum ClosureKind { Closure, Coroutine(Movability), - Async, + AsyncBlock { source: CoroutineSource }, + AsyncClosure, +} + +/// In the case of a coroutine created as part of an async/gen construct, +/// which kind of async/gen construct caused it to be created? +/// +/// This helps error messages but is also used to drive coercions in +/// type-checking (see #60424). +#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] +pub enum CoroutineSource { + /// An explicit `async`/`gen` block written by the user. + Block, + + /// An explicit `async`/`gen` closure written by the user. + Closure, + + /// The `async`/`gen` block generated as the body of an async/gen function. + Fn, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] 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 a0fb75397a235..54c4b8d3acd4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -7,7 +7,7 @@ use hir_def::{ AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, - db::DefDatabase, hir::ExprId, layout::TargetDataLayout, + db::DefDatabase, expr_store::ExpressionStore, hir::ExprId, layout::TargetDataLayout, }; use la_arena::ArenaMap; use salsa::plumbing::AsId; @@ -200,12 +200,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::interned] fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; - #[salsa::interned] - fn intern_closure(&self, id: InternedClosure) -> InternedClosureId; - - #[salsa::interned] - fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId; - #[salsa::invoke(crate::variance::variances_of)] #[salsa::transparent] fn variances_of<'db>(&'db self, def: GenericDefId) -> VariancesOf<'db>; @@ -238,17 +232,87 @@ pub struct InternedOpaqueTyId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedClosure(pub ExpressionStoreOwnerId, pub ExprId); -#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedClosureId { pub loc: InternedClosure, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct InternedCoroutine(pub ExpressionStoreOwnerId, pub ExprId); +impl InternedClosureId { + #[inline] + pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { + if cfg!(debug_assertions) { + let store = ExpressionStore::of(db, loc.0); + let expr = &store[loc.1]; + assert!( + matches!( + expr, + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::Closure, + .. + } + ), + "expected a closure, found {expr:?}" + ); + } + + Self::new_impl(db, loc) + } +} -#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedCoroutineId { - pub loc: InternedCoroutine, + pub loc: InternedClosure, +} + +impl InternedCoroutineId { + #[inline] + pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { + if cfg!(debug_assertions) { + let store = ExpressionStore::of(db, loc.0); + let expr = &store[loc.1]; + assert!( + matches!( + expr, + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::Coroutine(_) + | hir_def::hir::ClosureKind::AsyncBlock { .. }, + .. + } + ), + "expected a coroutine, found {expr:?}" + ); + } + + Self::new_impl(db, loc) + } +} + +#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedCoroutineClosureId { + pub loc: InternedClosure, +} + +impl InternedCoroutineClosureId { + #[inline] + pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { + if cfg!(debug_assertions) { + let store = ExpressionStore::of(db, loc.0); + let expr = &store[loc.1]; + assert!( + matches!( + expr, + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::AsyncClosure, + .. + } + ), + "expected a coroutine closure, found {expr:?}" + ); + } + + Self::new_impl(db, loc) + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 33d9dd538dd38..6706e92fc16e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -146,7 +146,7 @@ impl<'db> ExprValidator<'db> { Expr::If { .. } => { self.check_for_unnecessary_else(id, expr); } - Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => { + Expr::Block { .. } | Expr::Unsafe { .. } => { self.validate_block(expr); } _ => {} @@ -325,10 +325,7 @@ impl<'db> ExprValidator<'db> { } fn validate_block(&mut self, expr: &Expr) { - let (Expr::Block { statements, .. } - | Expr::Async { statements, .. } - | Expr::Unsafe { statements, .. }) = expr - else { + let (Expr::Block { statements, .. } | Expr::Unsafe { statements, .. }) = expr else { return; }; let pattern_arena = Arena::new(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 09c648139c458..ee33f7d1585e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -406,7 +406,7 @@ impl<'db> UnsafeVisitor<'db> { }); return; } - Expr::Block { statements, .. } | Expr::Async { statements, .. } => { + Expr::Block { statements, .. } => { self.walk_pats_top( statements.iter().filter_map(|statement| match statement { &Statement::Let { pat, .. } => Some(pat), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0c4e34db7db0e..7f1b1ecbd29e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -39,8 +39,7 @@ use rustc_apfloat::{ use rustc_ast_ir::FloatTy; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, CoroutineClosureArgsParts, RegionKind, - Upcast, + AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast, inherent::{AdtDef, GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _}, }; use smallvec::SmallVec; @@ -49,7 +48,7 @@ use stdx::never; use crate::{ CallableDefId, FnAbi, ImplTraitId, InferenceResult, MemoryMap, ParamEnvAndCrate, consteval, - db::{HirDatabase, InternedClosure, InternedCoroutine}, + db::{HirDatabase, InternedClosure}, generics::generics, layout::Layout, lower::GenericPredicates, @@ -1349,7 +1348,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe); let sig = sig.skip_binder(); - let InternedClosure(owner, _) = db.lookup_intern_closure(id); + let InternedClosure(owner, _) = id.loc(db); let infer = InferenceResult::of(db, owner); let (_, kind) = infer.closure_info(id); match f.closure_style { @@ -1403,26 +1402,16 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } _ => (), } - let CoroutineClosureArgsParts { closure_kind_ty, signature_parts_ty, .. } = - args.split_coroutine_closure_args(); - let kind = closure_kind_ty.to_opt_closure_kind().unwrap(); + let kind = args.as_coroutine_closure().kind(); let kind = match kind { rustc_type_ir::ClosureKind::Fn => "AsyncFn", rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut", rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce", }; - let TyKind::FnPtr(coroutine_sig, _) = signature_parts_ty.kind() else { - unreachable!("invalid coroutine closure signature"); - }; + let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig(); let coroutine_sig = coroutine_sig.skip_binder(); - let coroutine_inputs = coroutine_sig.inputs(); - let TyKind::Tuple(coroutine_inputs) = coroutine_inputs[1].kind() else { - unreachable!("invalid coroutine closure signature"); - }; - let TyKind::Tuple(coroutine_output) = coroutine_sig.output().kind() else { - unreachable!("invalid coroutine closure signature"); - }; - let coroutine_output = coroutine_output.as_slice()[1]; + let coroutine_inputs = coroutine_sig.tupled_inputs_ty.tuple_fields(); + let coroutine_output = coroutine_sig.return_ty; match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind}(")?, ClosureStyle::RANotation => write!(f, "async |")?, @@ -1536,17 +1525,16 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(coroutine_id, subst) => { - let InternedCoroutine(owner, expr_id) = coroutine_id.0.loc(db); + let InternedClosure(owner, expr_id) = coroutine_id.0.loc(db); let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = subst.split_coroutine_args(); let body = ExpressionStore::of(db, owner); let expr = &body[expr_id]; match expr { hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Async, + closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, .. - } - | hir_def::hir::Expr::Async { .. } => { + } => { let future_trait = f.lang_items().Future; let output = future_trait.and_then(|t| { t.trait_items(db) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index ddc4e4ce85ef7..d41a06c167468 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -133,7 +133,7 @@ fn has_drop_glue_impl<'db>( } TyKind::Slice(ty) => has_drop_glue_impl(infcx, ty, env, visited), TyKind::Closure(closure_id, subst) => { - let owner = db.lookup_intern_closure(closure_id.0).0; + let owner = closure_id.0.loc(db).0; let infer = InferenceResult::of(db, owner); let (captures, _) = infer.closure_info(closure_id.0); let env = db.trait_environment(owner); 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 ce99016470c14..2cb936fec3a7a 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 @@ -6,7 +6,7 @@ use std::{iter, mem, ops::ControlFlow}; use hir_def::{ TraitId, - hir::{ClosureKind, ExprId, PatId}, + hir::{ClosureKind, CoroutineSource, ExprId, PatId}, type_ref::TypeRefId, }; use rustc_type_ir::{ @@ -19,11 +19,11 @@ use tracing::debug; use crate::{ FnAbi, - db::{InternedClosure, InternedCoroutine}, + db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, infer::{BreakableKind, Diverges, coerce::CoerceMany}, next_solver::{ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, - PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, Tys, abi::Safety, infer::{ BoundRegionConversionTime, InferOk, InferResult, @@ -54,52 +54,47 @@ impl<'db> InferenceContext<'_, 'db> { ret_type: Option, arg_types: &[Option], closure_kind: ClosureKind, - tgt_expr: ExprId, + closure_expr: ExprId, expected: &Expectation<'db>, ) -> Ty<'db> { assert_eq!(args.len(), arg_types.len()); let interner = self.interner(); + // It's always helpful for inference if we know the kind of + // closure sooner rather than later, so first examine the expected + // type, and see if can glean a closure kind from there. let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { - Some(expected_ty) => self.deduce_closure_signature(expected_ty, closure_kind), + Some(ty) => { + let ty = self.table.try_structurally_resolve_type(ty); + self.deduce_closure_signature(ty, closure_kind) + } None => (None, None), }; - let ClosureSignatures { bound_sig, liberated_sig } = + let ClosureSignatures { bound_sig, mut liberated_sig } = self.sig_of_closure(arg_types, ret_type, expected_sig); - let body_ret_ty = bound_sig.output().skip_binder(); - let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); - // FIXME: Make this an infer var and infer it later. - let tupled_upvars_ty = self.types.types.unit; - let (id, ty, resume_yield_tys) = match closure_kind { - ClosureKind::Coroutine(_) => { - let yield_ty = self.table.next_ty_var(); - let resume_ty = - liberated_sig.inputs().first().copied().unwrap_or(self.types.types.unit); + debug!(?bound_sig, ?liberated_sig); - // FIXME: Infer the upvars later. - let parts = CoroutineArgsParts { - parent_args: parent_args.as_slice(), - kind_ty: self.types.types.unit, - resume_ty, - yield_ty, - return_ty: body_ret_ty, - tupled_upvars_ty, - }; + let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); - let coroutine_id = - self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into(); - let coroutine_ty = Ty::new_coroutine( - interner, - coroutine_id, - CoroutineArgs::new(interner, parts).args, - ); + // FIXME: Do this when we infer closures correctly: + // let tupled_upvars_ty = self.table.next_ty_var(); + let tupled_upvars_ty = self.types.types.unit; - (None, coroutine_ty, Some((resume_ty, yield_ty))) - } + let mut current_closure_id = None; + // FIXME: We could probably actually just unify this further -- + // instead of having a `FnSig` and a `Option`, + // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`, + // similar to how `ty::GenSig` is a distinct data structure. + let (closure_ty, resume_yield_tys) = match closure_kind { ClosureKind::Closure => { - let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr)); + let closure_id = + InternedClosureId::new(self.db, InternedClosure(self.owner, closure_expr)); + current_closure_id = Some(closure_id); + self.deferred_closures.entry(closure_id).or_default(); + self.add_current_closure_dependency(closure_id); + match expected_kind { Some(kind) => { self.result.closure_info.insert( @@ -116,6 +111,9 @@ impl<'db> InferenceContext<'_, 'db> { } None => {} }; + + // Tuple up the arguments and insert the resulting function type into + // the `closures` table. let sig = bound_sig.map_bound(|sig| { interner.mk_fn_sig( [Ty::new_tup(interner, sig.inputs())], @@ -125,52 +123,101 @@ impl<'db> InferenceContext<'_, 'db> { sig.abi, ) }); - let sig_ty = Ty::new_fn_ptr(interner, sig); - // FIXME: Infer the kind later if needed. - let parts = ClosureArgsParts { - parent_args: parent_args.as_slice(), - closure_kind_ty: Ty::from_closure_kind( - interner, - expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn), - ), - closure_sig_as_fn_ptr_ty: sig_ty, - tupled_upvars_ty, + + debug!(?sig, ?expected_kind); + + let closure_kind_ty = match expected_kind { + Some(kind) => Ty::from_closure_kind(interner, kind), + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + // FIXME: This too should be the next line: + // None => self.table.next_ty_var(), + None => self.types.types.i8, }; - let closure_ty = Ty::new_closure( + + let closure_args = ClosureArgs::new( interner, - closure_id.into(), - ClosureArgs::new(interner, parts).args, + ClosureArgsParts { + parent_args: parent_args.as_slice(), + closure_kind_ty, + closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(interner, sig), + tupled_upvars_ty, + }, ); - self.deferred_closures.entry(closure_id).or_default(); - self.add_current_closure_dependency(closure_id); - (Some(closure_id), closure_ty, None) + + (Ty::new_closure(interner, closure_id.into(), closure_args.args), None) } - ClosureKind::Async => { - // async closures always return the type ascribed after the `->` (if present), - // and yield `()`. - let bound_return_ty = bound_sig.skip_binder().output(); - let bound_yield_ty = self.types.types.unit; - // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems. - let resume_ty = self.types.types.unit; + ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => { + let yield_ty = match closure_kind { + ClosureKind::Coroutine(_) => self.table.next_ty_var(), + ClosureKind::AsyncBlock { .. } => self.types.types.unit, + _ => unreachable!(), + }; + + // Resume type defaults to `()` if the coroutine has no argument. + let resume_ty = + liberated_sig.inputs().first().copied().unwrap_or(self.types.types.unit); - // FIXME: Infer the kind later if needed. - let closure_kind_ty = Ty::from_closure_kind( + // Coroutines that come from coroutine closures have not yet determined + // their kind ty, so make a fresh infer var which will be constrained + // later during upvar analysis. Regular coroutines always have the kind + // ty of `().` + let kind_ty = match closure_kind { + ClosureKind::AsyncBlock { source: CoroutineSource::Closure } => { + self.table.next_ty_var() + } + _ => self.types.types.unit, + }; + + let coroutine_args = CoroutineArgs::new( interner, - expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn), + CoroutineArgsParts { + parent_args: parent_args.as_slice(), + kind_ty, + resume_ty, + yield_ty, + return_ty: liberated_sig.output(), + tupled_upvars_ty, + }, ); - // FIXME: Infer captures later. - // `for<'env> fn() -> ()`, for no captures. + let coroutine_id = + InternedCoroutineId::new(self.db, InternedClosure(self.owner, closure_expr)); + + ( + Ty::new_coroutine(interner, coroutine_id.into(), coroutine_args.args), + Some((resume_ty, yield_ty)), + ) + } + ClosureKind::AsyncClosure => { + // async closures always return the type ascribed after the `->` (if present), + // and yield `()`. + let (bound_return_ty, bound_yield_ty) = + (bound_sig.skip_binder().output(), self.types.types.unit); + // Compute all of the variables that will be used to populate the coroutine. + let resume_ty = self.table.next_ty_var(); + + let closure_kind_ty = match expected_kind { + Some(kind) => Ty::from_closure_kind(interner, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + // FIXME: Here again the next line should be active. + // None => self.table.next_ty_var(), + None => self.types.types.i8, + }; + + // FIXME: Another line that should be enabled. + // let coroutine_captures_by_ref_ty = self.table.next_ty_var(); let coroutine_captures_by_ref_ty = Ty::new_fn_ptr( interner, Binder::bind_with_vars( - interner.mk_fn_sig( - [], - self.types.types.unit, - false, - Safety::Safe, - FnAbi::Rust, - ), + FnSig { + inputs_and_output: Tys::new_from_slice(&[self.types.types.unit]), + c_variadic: false, + safety: Safety::Safe, + abi: FnAbi::Rust, + }, self.types.coroutine_captures_by_ref_bound_var_kinds, ), ); @@ -183,7 +230,13 @@ impl<'db> InferenceContext<'_, 'db> { interner, bound_sig.map_bound(|sig| { interner.mk_fn_sig( - [resume_ty, Ty::new_tup(interner, sig.inputs())], + [ + resume_ty, + Ty::new_tup_from_iter( + interner, + sig.inputs().iter().copied(), + ), + ], Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]), sig.c_variadic, sig.safety, @@ -196,9 +249,57 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let coroutine_id = - self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into(); - (None, Ty::new_coroutine_closure(interner, coroutine_id, closure_args.args), None) + let coroutine_kind_ty = match expected_kind { + Some(kind) => Ty::from_coroutine_closure_kind(interner, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + // FIXME: And here again. + // None => self.table.next_ty_var(), + None => self.types.types.i16, + }; + + let coroutine_upvars_ty = self.table.next_ty_var(); + + let coroutine_closure_id = InternedCoroutineClosureId::new( + self.db, + InternedClosure(self.owner, closure_expr), + ); + + // We need to turn the liberated signature that we got from HIR, which + // looks something like `|Args...| -> T`, into a signature that is suitable + // for type checking the inner body of the closure, which always returns a + // coroutine. To do so, we use the `CoroutineClosureSignature` to compute + // the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer + // vars which will get constrained during upvar analysis. + let coroutine_output_ty = closure_args + .coroutine_closure_sig() + .map_bound(|sig| { + sig.to_coroutine( + interner, + parent_args.as_slice(), + coroutine_kind_ty, + interner.coroutine_for_closure(coroutine_closure_id.into()), + coroutine_upvars_ty, + ) + }) + .skip_binder(); + liberated_sig = interner.mk_fn_sig( + liberated_sig.inputs().iter().copied(), + coroutine_output_ty, + liberated_sig.c_variadic, + liberated_sig.safety, + liberated_sig.abi, + ); + + ( + Ty::new_coroutine_closure( + interner, + coroutine_closure_id.into(), + closure_args.args, + ), + None, + ) } }; @@ -209,9 +310,10 @@ impl<'db> InferenceContext<'_, 'db> { // FIXME: lift these out into a struct let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_closure = mem::replace(&mut self.current_closure, id); - let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty); - let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty)); + let prev_closure = mem::replace(&mut self.current_closure, current_closure_id); + let prev_ret_ty = mem::replace(&mut self.return_ty, liberated_sig.output()); + let prev_ret_coercion = + self.return_coercion.replace(CoerceMany::new(liberated_sig.output())); let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys); self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { @@ -224,7 +326,7 @@ impl<'db> InferenceContext<'_, 'db> { self.current_closure = prev_closure; self.resume_yield_tys = prev_resume_yield_tys; - ty + closure_ty } fn fn_trait_kind_from_def_id(&self, trait_id: TraitId) -> Option { @@ -293,7 +395,9 @@ impl<'db> InferenceContext<'_, 'db> { let expected_sig = sig_tys.with(hdr); (Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn)) } - ClosureKind::Coroutine(_) | ClosureKind::Async => (None, None), + ClosureKind::Coroutine(_) + | ClosureKind::AsyncClosure + | ClosureKind::AsyncBlock { .. } => (None, None), }, _ => (None, None), } @@ -406,7 +510,7 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id), - ClosureKind::Async => self + ClosureKind::AsyncClosure => self .async_fn_trait_kind_from_def_id(trait_def_id) .or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)), _ => None, @@ -452,13 +556,13 @@ impl<'db> InferenceContext<'_, 'db> { ClosureKind::Closure if Some(def_id) == self.lang_items.FnOnceOutput => { self.extract_sig_from_projection(projection) } - ClosureKind::Async if Some(def_id) == self.lang_items.AsyncFnOnceOutput => { + ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.AsyncFnOnceOutput => { self.extract_sig_from_projection(projection) } // It's possible we've passed the closure to a (somewhat out-of-fashion) // `F: FnOnce() -> Fut, Fut: Future` style bound. Let's still // guide inference here, since it's beneficial for the user. - ClosureKind::Async if Some(def_id) == self.lang_items.FnOnceOutput => { + ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => { self.extract_sig_from_projection_and_future_bound(projection) } _ => None, 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 ce0ccfe82f27c..2d999b596b4ce 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 @@ -609,9 +609,7 @@ impl<'db> InferenceContext<'_, 'db> { self.consume_expr(expr); } } - Expr::Async { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Block { statements, tail, .. } => { + Expr::Unsafe { statements, tail, .. } | Expr::Block { statements, tail, .. } => { for s in statements.iter() { match s { Statement::Let { pat, type_ref: _, initializer, else_branch } => { @@ -755,7 +753,7 @@ impl<'db> InferenceContext<'_, 'db> { Expr::Closure { .. } => { let ty = self.expr_ty(tgt_expr); let TyKind::Closure(id, _) = ty.kind() else { - never!("closure type is always closure"); + // A coroutine or a coroutine closure. return; }; let (captures, _) = @@ -876,7 +874,7 @@ impl<'db> InferenceContext<'_, 'db> { fn is_upvar(&self, place: &HirPlace) -> bool { if let Some(c) = self.current_closure { - let InternedClosure(_, root) = self.db.lookup_intern_closure(c); + let InternedClosure(_, root) = c.loc(self.db); return self.store.is_binding_upvar(place.local, root); } false @@ -1139,7 +1137,7 @@ impl<'db> InferenceContext<'_, 'db> { } fn analyze_closure(&mut self, closure: InternedClosureId) -> FnTrait { - let InternedClosure(_, root) = self.db.lookup_intern_closure(closure); + let InternedClosure(_, root) = closure.loc(self.db); self.current_closure = Some(closure); let Expr::Closure { body, capture_by, .. } = &self.store[root] else { unreachable!("Closure expression id is always closure"); 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 ee34a30ebaaf0..06615cb691ab0 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 @@ -17,7 +17,7 @@ use hir_def::{FunctionId, hir::ClosureKind}; use hir_expand::name::Name; use rustc_ast_ir::Mutability; use rustc_type_ir::{ - CoroutineArgs, CoroutineArgsParts, InferTy, Interner, + InferTy, Interner, inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _}, }; use syntax::ast::RangeOp; @@ -27,7 +27,6 @@ use crate::{ Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, autoderef::InferenceContextAutoderef, consteval, - db::InternedCoroutine, generics::generics, infer::{ AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, @@ -244,7 +243,6 @@ impl<'db> InferenceContext<'_, 'db> { | Expr::Assignment { .. } | Expr::Yield { .. } | Expr::Cast { .. } - | Expr::Async { .. } | Expr::Unsafe { .. } | Expr::Await { .. } | Expr::Ref { .. } @@ -390,9 +388,6 @@ impl<'db> InferenceContext<'_, 'db> { }) .1 } - Expr::Async { id: _, statements, tail } => { - self.infer_async_block(tgt_expr, statements, tail) - } &Expr::Loop { body, label } => { // FIXME: should be: // let ty = expected.coercion_target_type(&mut self.table); @@ -1185,72 +1180,6 @@ impl<'db> InferenceContext<'_, 'db> { oprnd_t } - fn infer_async_block( - &mut self, - tgt_expr: ExprId, - statements: &[Statement], - tail: &Option, - ) -> Ty<'db> { - let ret_ty = self.table.next_ty_var(); - let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty); - let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty)); - - // FIXME: We should handle async blocks like we handle closures - let expected = &Expectation::has_type(ret_ty); - let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - let ty = this.infer_block(tgt_expr, statements, *tail, None, expected); - if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) { - Ok(res) => res, - Err(_) => { - this.result.type_mismatches.get_or_insert_default().insert( - tgt_expr.into(), - TypeMismatch { expected: target.store(), actual: ty.store() }, - ); - target - } - } - } else { - ty - } - }); - - self.diverges = prev_diverges; - self.return_ty = prev_ret_ty; - self.return_coercion = prev_ret_coercion; - - self.lower_async_block_type_impl_trait(inner_ty, tgt_expr) - } - - pub(crate) fn lower_async_block_type_impl_trait( - &mut self, - inner_ty: Ty<'db>, - tgt_expr: ExprId, - ) -> Ty<'db> { - let coroutine_id = InternedCoroutine(self.owner, tgt_expr); - let coroutine_id = self.db.intern_coroutine(coroutine_id).into(); - let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into()); - Ty::new_coroutine( - self.interner(), - coroutine_id, - CoroutineArgs::new( - self.interner(), - CoroutineArgsParts { - parent_args: parent_args.as_slice(), - kind_ty: self.types.types.unit, - // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems. - resume_ty: self.types.types.unit, - yield_ty: self.types.types.unit, - return_ty: inner_ty, - // FIXME: Infer upvars. - tupled_upvars_ty: self.types.types.unit, - }, - ) - .args, - ) - } - pub(crate) fn write_fn_trait_method_resolution( &mut self, fn_x: FnTrait, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index bfe43fc92827d..5aba123435a9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -86,7 +86,6 @@ impl<'db> InferenceContext<'_, 'db> { } Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), Expr::Block { id: _, statements, tail, label: _ } - | Expr::Async { id: _, statements, tail } | Expr::Unsafe { id: _, statements, tail } => { for st in statements.iter() { match st { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 54332122d0e40..4ba39b1b4571c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -332,7 +332,7 @@ pub fn layout_of_ty_query( Layout::scalar(dl, ptr) } TyKind::Closure(id, args) => { - let def = db.lookup_intern_closure(id.0); + let def = id.0.loc(db); let infer = InferenceResult::of(db, def.0); let (captures, _) = infer.closure_info(id.0); let fields = captures diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 3ff2db15aaf56..d843359dcbe87 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -121,7 +121,7 @@ fn make_fetch_closure_field<'db>( db: &'db dyn HirDatabase, ) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> { |c: InternedClosureId, subst: GenericArgs<'db>, f: usize| { - let InternedClosure(owner, _) = db.lookup_intern_closure(c); + let InternedClosure(owner, _) = c.loc(db); let interner = DbInterner::new_no_crate(db); let infer = InferenceResult::of(db, owner); let (captures, _) = infer.closure_info(c); 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 88376f14d1ed3..79b1c5cb7cc89 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 @@ -736,7 +736,7 @@ impl<'db> Evaluator<'db> { self.param_env.param_env, ty, |c, subst, f| { - let InternedClosure(owner, _) = self.db.lookup_intern_closure(c); + let InternedClosure(owner, _) = c.loc(self.db); let infer = InferenceResult::of(self.db, owner); let (captures, _) = infer.closure_info(c); let parent_subst = subst.as_closure().parent_args(); 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 2aed76ec90913..1f3cee2a03629 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 @@ -152,7 +152,7 @@ impl<'db> Evaluator<'db> { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let InternedClosure(owner, _) = self.db.lookup_intern_closure(id.0); + let InternedClosure(owner, _) = id.0.loc(self.db); let infer = InferenceResult::of(self.db, owner); let (captures, _) = infer.closure_info(id.0); let layout = self.layout(self_ty)?; 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 44785d948a49a..d044019629ca4 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 @@ -8,8 +8,9 @@ use hir_def::{ HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{ - ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, - Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, generics::GenericParams, + ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, LabelId, + Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + generics::GenericParams, }, item_tree::FieldsShape, lang_item::LangItems, @@ -956,7 +957,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Await { .. } => not_supported!("await"), Expr::Yeet { .. } => not_supported!("yeet"), - Expr::Async { .. } => not_supported!("async block"), &Expr::Const(_) => { // let subst = self.placeholder_subst(); // self.lower_const( @@ -1245,7 +1245,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ); Ok(Some(current)) } - Expr::Closure { .. } => { + Expr::Closure { closure_kind: ClosureKind::Closure, .. } => { let ty = self.expr_ty_without_adjust(expr_id); let TyKind::Closure(id, _) = ty.kind() else { not_supported!("closure with non closure type"); @@ -1304,6 +1304,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ); Ok(Some(current)) } + Expr::Closure { closure_kind, .. } => not_supported!("{closure_kind:?} closure"), Expr::Tuple { exprs } => { let Some(values) = exprs .iter() @@ -2110,7 +2111,7 @@ pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, ) -> Result<'db, Arc> { - let InternedClosure(owner, expr) = db.lookup_intern_closure(closure); + let InternedClosure(owner, expr) = closure.loc(db); let body_owner = owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); let body = Body::of(db, body_owner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 00161d6d08250..542eca3ded243 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -12,7 +12,9 @@ use hir_def::{ use rustc_type_ir::inherent; use stdx::impl_from; -use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}; +use crate::db::{ + InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId, InternedOpaqueTyId, +}; use super::DbInterner; @@ -35,6 +37,7 @@ pub enum SolverDefId { TypeAliasId(TypeAliasId), InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), + InternedCoroutineClosureId(InternedCoroutineClosureId), InternedOpaqueTyId(InternedOpaqueTyId), EnumVariantId(EnumVariantId), Ctor(Ctor), @@ -80,6 +83,9 @@ impl std::fmt::Debug for SolverDefId { SolverDefId::InternedCoroutineId(id) => { f.debug_tuple("InternedCoroutineId").field(&id).finish() } + SolverDefId::InternedCoroutineClosureId(id) => { + f.debug_tuple("InternedCoroutineClosureId").field(&id).finish() + } SolverDefId::InternedOpaqueTyId(id) => { f.debug_tuple("InternedOpaqueTyId").field(&id).finish() } @@ -123,6 +129,7 @@ impl_from!( TypeAliasId, InternedClosureId, InternedCoroutineId, + InternedCoroutineClosureId, InternedOpaqueTyId, EnumVariantId, Ctor @@ -206,6 +213,7 @@ impl TryFrom for AttrDefId { SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::AnonConstId(_) => Err(()), } @@ -229,6 +237,7 @@ impl TryFrom for DefWithBodyId { | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::Ctor(Ctor::Struct(_)) | SolverDefId::AnonConstId(_) | SolverDefId::AdtId(_) => return Err(()), @@ -251,6 +260,7 @@ impl TryFrom for GenericDefId { SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::EnumVariantId(_) | SolverDefId::BuiltinDeriveImplId(_) @@ -348,6 +358,7 @@ declare_id_wrapper!(TraitIdWrapper, TraitId); declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId); declare_id_wrapper!(ClosureIdWrapper, InternedClosureId); declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId); +declare_id_wrapper!(CoroutineClosureIdWrapper, InternedCoroutineClosureId); declare_id_wrapper!(AdtIdWrapper, AdtId); #[derive(Clone, Copy, PartialEq, Eq, Hash)] 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 622648bc8d52b..4f30fc7a89bc3 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 @@ -38,14 +38,14 @@ use rustc_type_ir::{ use crate::{ FnAbi, - db::{HirDatabase, InternedCoroutine, InternedCoroutineId}, + db::{HirDatabase, InternedClosure, InternedCoroutineId}, lower::GenericPredicates, method_resolution::TraitImpls, next_solver::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey, - RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, - TypeAliasIdWrapper, UnevaluatedConst, + CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, + GeneralConstIdWrapper, OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, + SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -1022,7 +1022,7 @@ impl<'db> Interner for DbInterner<'db> { type ForeignId = TypeAliasIdWrapper; type FunctionId = CallableIdWrapper; type ClosureId = ClosureIdWrapper; - type CoroutineClosureId = CoroutineIdWrapper; + type CoroutineClosureId = CoroutineClosureIdWrapper; type CoroutineId = CoroutineIdWrapper; type AdtId = AdtIdWrapper; type ImplId = AnyImplId; @@ -1198,6 +1198,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::AnonConstId(_) => { return VariancesOf::empty(self); } @@ -1315,10 +1316,13 @@ impl<'db> Interner for DbInterner<'db> { SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, SolverDefId::ConstId(it) => it.lookup(self.db()).container, SolverDefId::InternedClosureId(it) => { - return self.db().lookup_intern_closure(it).0.generic_def(self.db()).into(); + return it.loc(self.db).0.generic_def(self.db()).into(); } SolverDefId::InternedCoroutineId(it) => { - return self.db().lookup_intern_coroutine(it).0.generic_def(self.db()).into(); + return it.loc(self.db).0.generic_def(self.db()).into(); + } + SolverDefId::InternedCoroutineClosureId(it) => { + return it.loc(self.db).0.generic_def(self.db()).into(); } SolverDefId::StaticId(_) | SolverDefId::AdtId(_) @@ -1356,7 +1360,7 @@ impl<'db> Interner for DbInterner<'db> { fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); let expr = &store[expr_id]; match *expr { @@ -1365,16 +1369,17 @@ impl<'db> Interner for DbInterner<'db> { hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, }, - hir_def::hir::ClosureKind::Async => rustc_ast_ir::Movability::Static, + hir_def::hir::ClosureKind::AsyncBlock { .. } => rustc_ast_ir::Movability::Static, _ => panic!("unexpected expression for a coroutine: {expr:?}"), }, - hir_def::hir::Expr::Async { .. } => rustc_ast_ir::Movability::Static, _ => panic!("unexpected expression for a coroutine: {expr:?}"), } } fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId { - def_id + let InternedClosure(owner, coroutine_closure_expr) = def_id.0.loc(self.db); + let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr); + InternedCoroutineId::new(self.db, InternedClosure(owner, coroutine_expr)).into() } fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { @@ -1763,6 +1768,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::StaticId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::EnumVariantId(_) | SolverDefId::AnonConstId(_) @@ -1976,7 +1982,7 @@ impl<'db> Interner for DbInterner<'db> { fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); matches!( store[expr_id], @@ -1990,12 +1996,14 @@ impl<'db> Interner for DbInterner<'db> { fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); matches!( store[expr_id], - hir_def::hir::Expr::Closure { closure_kind: hir_def::hir::ClosureKind::Async, .. } - | hir_def::hir::Expr::Async { .. } + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, + .. + } ) } @@ -2118,16 +2126,15 @@ impl<'db> Interner for DbInterner<'db> { body.exprs().for_each(|(expr_id, expr)| { if matches!( expr, - hir_def::hir::Expr::Async { .. } - | hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Async - | hir_def::hir::ClosureKind::Coroutine(_), - .. - } + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. } + | hir_def::hir::ClosureKind::Coroutine(_), + .. + } ) { let coroutine = InternedCoroutineId::new( self.db, - InternedCoroutine(ExpressionStoreOwnerId::Body(def_id), expr_id), + InternedClosure(ExpressionStoreOwnerId::Body(def_id), expr_id), ); result.push(coroutine.into()); } @@ -2414,6 +2421,7 @@ TrivialTypeTraversalImpls! { CallableIdWrapper, ClosureIdWrapper, CoroutineIdWrapper, + CoroutineClosureIdWrapper, AdtIdWrapper, AnyImplId, GeneralConstIdWrapper, 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 8e892b65ea383..dccb8c7936319 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 @@ -27,11 +27,12 @@ use rustc_type_ir::{ use crate::{ FnAbi, - db::{HirDatabase, InternedCoroutine}, + db::{HirDatabase, InternedClosure}, lower::GenericPredicates, next_solver::{ AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const, - CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper, + CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region, + TraitRef, TypeAliasIdWrapper, abi::Safety, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, util::{CoroutineArgsExt, IntegerTypeExt}, @@ -527,7 +528,7 @@ impl<'db> Ty<'db> { let unit_ty = Ty::new_unit(interner); let return_ty = Ty::new_coroutine( interner, - coroutine_id, + interner.coroutine_for_closure(coroutine_id), CoroutineArgs::new( interner, CoroutineArgsParts { @@ -713,7 +714,7 @@ impl<'db> Ty<'db> { } } TyKind::Coroutine(coroutine_id, _args) => { - let InternedCoroutine(owner, _) = coroutine_id.0.loc(db); + let InternedClosure(owner, _) = coroutine_id.0.loc(db); let krate = owner.krate(db); if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future { // This is only used by type walking. @@ -1107,7 +1108,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_closure( interner: DbInterner<'db>, - def_id: CoroutineIdWrapper, + def_id: CoroutineClosureIdWrapper, args: as Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 9e687568216d9..9d1a1fbd116a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -11,7 +11,6 @@ use test_fixture::WithFixture; use crate::{ InferenceResult, - db::HirDatabase, display::{DisplayTarget, HirDisplay}, mir::MirSpan, test_db::TestDB, @@ -42,7 +41,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec let db = &db; captures_info.extend(infer.closure_info.iter().flat_map( |(closure_id, (captures, _))| { - let closure = db.lookup_intern_closure(*closure_id); + let closure = closure_id.loc(db); let body_owner = closure.0; let source_map = ExpressionStore::with_source_map(db, body_owner).1; let closure_text_range = source_map diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index f9badc0b79016..45c9811cc0158 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -293,7 +293,7 @@ impl HasSource for Param<'_> { .map(|value| InFile { file_id, value }) } Callee::Closure(closure, _) => { - let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure); + let InternedClosure(owner, expr_id) = closure.loc(db); let (_, source_map) = ExpressionStore::with_source_map(db, owner); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; let root = db.parse_or_expand(file_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 7a4085c4746c9..53240259e0922 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -85,7 +85,7 @@ use hir_ty::{ GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules, consteval::try_const_usize, - db::{InternedClosureId, InternedCoroutineId}, + db::{InternedClosureId, InternedCoroutineClosureId}, diagnostics::BodyValidationDiagnostic, direct_super_traits, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, @@ -2950,7 +2950,7 @@ impl<'db> Param<'db> { } } Callee::Closure(closure, _) => { - let c = db.lookup_intern_closure(closure); + let c = closure.loc(db); let body_owner = c.0; let store = ExpressionStore::of(db, c.0); @@ -5092,7 +5092,7 @@ impl<'db> TraitRef<'db> { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] enum AnyClosureId { ClosureId(InternedClosureId), - CoroutineClosureId(InternedCoroutineId), + CoroutineClosureId(InternedCoroutineClosureId), } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -5131,7 +5131,7 @@ impl<'db> Closure<'db> { // FIXME: Infer coroutine closures' captures. return Vec::new(); }; - let owner = db.lookup_intern_closure(id).0; + let owner = id.loc(db).0; let infer = InferenceResult::of(db, owner); let info = infer.closure_info(id); info.0 @@ -5151,7 +5151,7 @@ impl<'db> Closure<'db> { // FIXME: Infer coroutine closures' captures. return Vec::new(); }; - let owner = db.lookup_intern_closure(id).0; + let owner = id.loc(db).0; let Some(body_owner) = owner.as_def_with_body() else { return Vec::new(); }; @@ -5164,7 +5164,7 @@ impl<'db> Closure<'db> { pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait { match self.id { AnyClosureId::ClosureId(id) => { - let owner = db.lookup_intern_closure(id).0; + let owner = id.loc(db).0; let Some(body_owner) = owner.as_def_with_body() else { return FnTrait::FnOnce; }; @@ -6532,7 +6532,7 @@ pub struct Callable<'db> { enum Callee<'db> { Def(CallableDefId), Closure(InternedClosureId, GenericArgs<'db>), - CoroutineClosure(InternedCoroutineId, GenericArgs<'db>), + CoroutineClosure(InternedCoroutineClosureId, GenericArgs<'db>), FnPtr, FnImpl(traits::FnTrait), BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId }, From ddc5e117703c8c81d75ce914f77da75690b69e8d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 04:52:45 +0300 Subject: [PATCH 39/98] Upgrade rustc crates --- src/tools/rust-analyzer/Cargo.lock | 71 ++++++++++++++---------------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- 2 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index da530b3a9304d..4c6e0e5ed8407 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", @@ -2048,9 +2048,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "ra-ap-rustc_abi" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49dbe5d570793b3c3227972a6ac85fc3e830f09b32c3cb3b68cfceebad3b0a" +checksum = "1ada9f3d87f035c2234b48ceb43c07f04daa40348bffda88e91e5bc452fca147" dependencies = [ "bitflags 2.9.4", "ra-ap-rustc_hashes", @@ -2060,33 +2060,33 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0956db62c264a899d15667993cbbd2e8f0b02108712217e2579c61ac30b94b" +checksum = "113acd7f51ff383eb86ebc8fb12df8a44d418bed0a990bcea363388f737a329a" [[package]] name = "ra-ap-rustc_hashes" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df512084c24f4c96c8cc9a59cbd264301efbc8913d3759b065398024af316c9" +checksum = "2002d37e435be69d87d307bd4d9b494a3ac08b30d383c7e4b1b6b3f976fb2675" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca3a49a928d38ba7927605e5909b6abe77d09ff359e4695c070c3f91d69cc8a" +checksum = "32e8921cba582b0c93d8e4443cca24aec07f25e87b1483e4fa62113d2d9cfea1" dependencies = [ "ra-ap-rustc_index_macros", ] [[package]] name = "ra-ap-rustc_index_macros" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4463e908a62c64c2a65c1966c2f4995d0e1f8b7dfc85a8b8de2562edf3d89070" +checksum = "49574ecf35748cebf67fec53db7e81968b599cc50becceba01b895d1ad40e9ea" dependencies = [ "proc-macro2", "quote", @@ -2095,20 +2095,20 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228e01e1b237adb4bd8793487e1c37019c1e526a8f93716d99602301be267056" +checksum = "33aabb4e87bc6c0dbb721728ad71126fb17fb9262cb9cd215314a10d64c92985" dependencies = [ "memchr", + "unicode-ident", "unicode-properties", - "unicode-xid", ] [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d6f91143011d474bb844d268b0784c6a4c6db57743558b83f5ad34511627f1" +checksum = "b7559a4b05b358ec59838d8d2a9a6b6e3c4f7aaf3805c0878b82456d63ad4d05" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -2119,19 +2119,19 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fa8effbc436c0ddd9d7b1421aa3cccf8b94566c841c4e4aa3e09063b8f423f" +checksum = "a5ec9bf15c3fed1ba4e0f9a0ceba36f8fa740e2f606609a68fc42034e0e6b28d" dependencies = [ "ra-ap-rustc_lexer", - "rustc-literal-escaper 0.0.5", + "rustc-literal-escaper 0.0.7", ] [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883c843fc27847ad03b8e772dd4a2d2728af4333a6d6821a22dfcfe7136dff3e" +checksum = "a9088a430b80729958eda4c428acf731644d3b78caa4285ed659ff586b772743" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -2142,15 +2142,16 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86e33c46b2b261a173b23f207461a514812a8b2d2d7935bbc685f733eacce10" +checksum = "a0f59b0708ec44c375dad7dc3818a3bd8eb3497a54d5968f5c945ac11c029854" dependencies = [ "arrayvec", "bitflags 2.9.4", "derive-where", "ena", "indexmap", + "ra-ap-rustc_abi", "ra-ap-rustc_ast_ir", "ra-ap-rustc_index", "ra-ap-rustc_type_ir_macros", @@ -2162,9 +2163,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.143.0" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15034c2fcaa5cf302aea6db20eda0f71fffeb0b372d6073cc50f940e974a2a47" +checksum = "f4b09efb7749eb0a3fb8fac1ce26618eef8ee5ca0aa36fe01228b853d28113a2" dependencies = [ "proc-macro2", "quote", @@ -2385,9 +2386,9 @@ checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b" [[package]] name = "rustc-literal-escaper" -version = "0.0.5" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" +checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" [[package]] name = "rustc-stable-hash" @@ -3139,21 +3140,15 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-xid" -version = "0.2.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "url" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 3b3929df0dfbd..40fcb9aed607c 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.143", default-features = false } -ra-ap-rustc_parse_format = { version = "0.143", default-features = false } -ra-ap-rustc_index = { version = "0.143", default-features = false } -ra-ap-rustc_abi = { version = "0.143", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.143", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.143", default-features = false } -ra-ap-rustc_type_ir = { version = "0.143", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.143", default-features = false } +ra-ap-rustc_lexer = { version = "0.159", default-features = false } +ra-ap-rustc_parse_format = { version = "0.159", default-features = false } +ra-ap-rustc_index = { version = "0.159", default-features = false } +ra-ap-rustc_abi = { version = "0.159", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.159", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.159", default-features = false } +ra-ap-rustc_type_ir = { version = "0.159", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.159", default-features = false } # local crates that aren't published to crates.io. These should not have versions. From c9440c593d320d8ea96f43e93bc5947b61ae13fc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 05:03:49 +0300 Subject: [PATCH 40/98] Add support for scalable vectors repr As it is now supported by rustc_abi. --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) 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 b560d08492ff9..a8892dee5c9a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -159,7 +159,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< None => match &*first_segment { "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), "doc" => extract_doc_tt_attr(attr_flags, tt), - "repr" => attr_flags.insert(AttrFlags::HAS_REPR), + "repr" | "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR), "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE), "proc_macro_derive" | "rustc_builtin_macro" => { attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) @@ -217,6 +217,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "rustc_allow_incoherent_impl" => { attr_flags.insert(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) } + "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR), "fundamental" => attr_flags.insert(AttrFlags::FUNDAMENTAL), "no_std" => attr_flags.insert(AttrFlags::IS_NO_STD), "may_dangle" => attr_flags.insert(AttrFlags::MAY_DANGLE), @@ -724,14 +725,40 @@ impl AttrFlags { fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option { let mut result = None; collect_attrs::(db, owner.into(), |attr| { - if let ast::Meta::TokenTreeMeta(attr) = attr - && attr.path().is1("repr") + let mut current = None; + if let ast::Meta::TokenTreeMeta(attr) = &attr + && let Some(path) = attr.path() && let Some(tt) = attr.token_tree() - && let Some(repr) = parse_repr_tt(&tt) { + if path.is1("repr") + && let Some(repr) = parse_repr_tt(&tt) + { + current = Some(repr); + } else if path.is1("rustc_scalable_vector") + && let mut tt = TokenTreeChildren::new(&tt) + && let Some(NodeOrToken::Token(scalable)) = tt.next() + && let Some(scalable) = ast::IntNumber::cast(scalable) + && let Ok(scalable) = scalable.value() + && let Ok(scalable) = scalable.try_into() + { + current = Some(ReprOptions { + scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)), + ..ReprOptions::default() + }); + } + } else if let ast::Meta::PathMeta(attr) = &attr + && attr.path().is1("rustc_scalable_vector") + { + current = Some(ReprOptions { + scalable: Some(rustc_abi::ScalableElt::Container), + ..ReprOptions::default() + }); + } + + if let Some(current) = current { match &mut result { - Some(existing) => merge_repr(existing, repr), - None => result = Some(repr), + Some(existing) => merge_repr(existing, current), + None => result = Some(current), } } ControlFlow::Continue(()) @@ -1079,7 +1106,7 @@ impl AttrFlags { } fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { - let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this; + let ReprOptions { int, align, pack, flags, scalable, field_shuffle_seed: _ } = this; flags.insert(other.flags); *align = (*align).max(other.align); *pack = match (*pack, other.pack) { @@ -1089,6 +1116,9 @@ fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { if other.int.is_some() { *int = other.int; } + if other.scalable.is_some() { + *scalable = other.scalable; + } } fn parse_repr_tt(tt: &ast::TokenTree) -> Option { From 982643abd973139ede4f0bc88bd883fe37a18fd9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 13:15:11 +0300 Subject: [PATCH 41/98] Update solver types as required by the upgrade --- src/tools/rust-analyzer/Cargo.lock | 44 +- src/tools/rust-analyzer/Cargo.toml | 16 +- .../crates/hir-ty/src/builtin_derive.rs | 22 +- .../crates/hir-ty/src/display.rs | 16 +- .../crates/hir-ty/src/dyn_compatibility.rs | 57 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 10 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 7 +- .../crates/hir-ty/src/layout/target.rs | 31 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 13 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 10 +- .../crates/hir-ty/src/lower/path.rs | 6 +- .../crates/hir-ty/src/mir/eval.rs | 6 +- .../crates/hir-ty/src/next_solver.rs | 14 +- .../crates/hir-ty/src/next_solver/consts.rs | 81 +- .../crates/hir-ty/src/next_solver/fold.rs | 26 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 2 - .../hir-ty/src/next_solver/fulfill/errors.rs | 1173 ----------------- .../infer/canonical/canonicalizer.rs | 38 +- .../infer/canonical/instantiate.rs | 14 +- .../src/next_solver/infer/canonical/mod.rs | 18 +- .../hir-ty/src/next_solver/infer/mod.rs | 18 +- .../infer/region_constraints/mod.rs | 4 +- .../next_solver/infer/relate/generalize.rs | 2 +- .../next_solver/infer/relate/higher_ranked.rs | 23 +- .../src/next_solver/infer/relate/lattice.rs | 4 +- .../crates/hir-ty/src/next_solver/inspect.rs | 6 - .../crates/hir-ty/src/next_solver/interner.rs | 213 ++- .../crates/hir-ty/src/next_solver/ir_print.rs | 2 +- .../hir-ty/src/next_solver/normalize.rs | 1 + .../crates/hir-ty/src/next_solver/region.rs | 136 +- .../crates/hir-ty/src/next_solver/solver.rs | 9 +- .../crates/hir-ty/src/next_solver/ty.rs | 97 +- .../crates/hir-ty/src/next_solver/util.rs | 21 +- .../crates/hir-ty/src/variance.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 28 +- .../crates/hir/src/source_analyzer.rs | 12 +- 38 files changed, 416 insertions(+), 1770 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 4c6e0e5ed8407..94e9507c021f4 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2048,9 +2048,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "ra-ap-rustc_abi" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ada9f3d87f035c2234b48ceb43c07f04daa40348bffda88e91e5bc452fca147" +checksum = "4b917ab47d7036977be4c984321af3e0de089229404d68ea9a286f50aa464697" dependencies = [ "bitflags 2.9.4", "ra-ap-rustc_hashes", @@ -2060,33 +2060,33 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113acd7f51ff383eb86ebc8fb12df8a44d418bed0a990bcea363388f737a329a" +checksum = "021d80bea67458b8c90cc25bfdca6f911ea818a41905e370c1f310cced1dd07e" [[package]] name = "ra-ap-rustc_hashes" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2002d37e435be69d87d307bd4d9b494a3ac08b30d383c7e4b1b6b3f976fb2675" +checksum = "8bb89395306ecfc980d252f77a4038d8b8bb578a25c856b545cbeeb3fde8358e" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e8921cba582b0c93d8e4443cca24aec07f25e87b1483e4fa62113d2d9cfea1" +checksum = "84219d028a1954c4340ddde11adffe93eb83e476e942718fe926f4d99637cbbe" dependencies = [ "ra-ap-rustc_index_macros", ] [[package]] name = "ra-ap-rustc_index_macros" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49574ecf35748cebf67fec53db7e81968b599cc50becceba01b895d1ad40e9ea" +checksum = "3908fdfa258c663d8ee407e6b4a205b0880e323b533c0df7edceafbd54a02fb6" dependencies = [ "proc-macro2", "quote", @@ -2095,9 +2095,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33aabb4e87bc6c0dbb721728ad71126fb17fb9262cb9cd215314a10d64c92985" +checksum = "34b50f19d5856b8e2b36150e89b53a6102ab096e8044e1f55fd6fef977b10d85" dependencies = [ "memchr", "unicode-ident", @@ -2106,9 +2106,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7559a4b05b358ec59838d8d2a9a6b6e3c4f7aaf3805c0878b82456d63ad4d05" +checksum = "76f83dcc451bcee8a99e284a583d5b3d82db5a200107a256a40ef132c4988f1b" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -2119,9 +2119,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ec9bf15c3fed1ba4e0f9a0ceba36f8fa740e2f606609a68fc42034e0e6b28d" +checksum = "f31236bdc6cbcae8af42d0b2db2fa8d812a8715b90a2ba5afb1132b37a4d0bbc" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.7", @@ -2129,9 +2129,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9088a430b80729958eda4c428acf731644d3b78caa4285ed659ff586b772743" +checksum = "3fc4edac740e896fba4b3b4d9c423083e3eac49947732561ddfb2377e1f57829" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -2142,9 +2142,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f59b0708ec44c375dad7dc3818a3bd8eb3497a54d5968f5c945ac11c029854" +checksum = "8efa119afc1bcadd821b27aa94332abf79c48ac0a972cb78932f63004ba4cdd9" dependencies = [ "arrayvec", "bitflags 2.9.4", @@ -2163,9 +2163,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.159.0" +version = "0.160.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4b09efb7749eb0a3fb8fac1ce26618eef8ee5ca0aa36fe01228b853d28113a2" +checksum = "e6b1dc03abfabc7179393c282892c73a3f0e4bbd5f0b6c87ff42c2b142e68f57" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 40fcb9aed607c..4372afa0f5066 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.159", default-features = false } -ra-ap-rustc_parse_format = { version = "0.159", default-features = false } -ra-ap-rustc_index = { version = "0.159", default-features = false } -ra-ap-rustc_abi = { version = "0.159", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.159", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.159", default-features = false } -ra-ap-rustc_type_ir = { version = "0.159", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.159", default-features = false } +ra-ap-rustc_lexer = { version = "0.160", default-features = false } +ra-ap-rustc_parse_format = { version = "0.160", default-features = false } +ra-ap-rustc_index = { version = "0.160", default-features = false } +ra-ap-rustc_abi = { version = "0.160", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.160", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.160", default-features = false } +ra-ap-rustc_type_ir = { version = "0.160", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.160", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index eb3922f4b6233..6a9b1671e7be1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -20,8 +20,8 @@ use crate::{ GenericPredicates, db::HirDatabase, next_solver::{ - Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder, - StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics, + AliasTy, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, + StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics, }, }; @@ -342,7 +342,7 @@ fn extend_assoc_type_bounds<'db>( type Result = (); fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { - if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() { + if let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) = t.kind() { self.assoc_type_bounds.push( TraitRef::new_from_args( self.interner, @@ -546,49 +546,49 @@ struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N], T::Assoc); Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Debug, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Debug, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Clone, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Clone, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Copy, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Copy, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: PartialEq<[#1]>, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialEq<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): PartialEq<[Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. })]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Eq, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Eq, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: PartialOrd<[#1]>, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialOrd<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): PartialOrd<[Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. })]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Ord, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Ord, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Hash, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Hash, polarity:Positive), bound_vars: [] }) "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 7f1b1ecbd29e7..1e4c2985cf5c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -646,7 +646,7 @@ fn write_projection<'db>( ClauseKind::TypeOutlives(t) => t.0, _ => return false, }; - let TyKind::Alias(AliasTyKind::Projection, a) = ty.kind() else { + let TyKind::Alias(a) = ty.kind() else { return false; }; a == *alias @@ -657,7 +657,7 @@ fn write_projection<'db>( write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)), + Either::Left(Ty::new_alias(f.interner, *alias)), &bounds, SizedByDefault::NotSized, needs_parens_if_multi, @@ -673,7 +673,7 @@ fn write_projection<'db>( write!( f, ">::{}", - TypeAliasSignature::of(f.db, alias.def_id.expect_type_alias()) + TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias()) .name .display(f.db, f.edition()) )?; @@ -1005,7 +1005,7 @@ fn render_const_scalar_inner<'db>( TyKind::Pat(_, _) => f.write_str(""), TyKind::Error(..) | TyKind::Placeholder(_) - | TyKind::Alias(_, _) + | TyKind::Alias(..) | TyKind::Param(_) | TyKind::Bound(_, _) | TyKind::Infer(_) => f.write_str(""), @@ -1280,7 +1280,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?; } - TyKind::Alias(AliasTyKind::Projection, alias_ty) => { + TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { write_projection(f, &alias_ty, trait_bounds_need_parens)? } TyKind::Foreign(alias) => { @@ -1289,8 +1289,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; f.end_location_link(); } - TyKind::Alias(AliasTyKind::Opaque, alias_ty) => { - let opaque_ty_id = match alias_ty.def_id { + TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Opaque { def_id }, .. }) => { + let opaque_ty_id = match def_id { SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; @@ -1585,7 +1585,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, TyKind::Pat(_, _) => write!(f, "{{pat}}")?, TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?, - TyKind::Alias(_, _) => write!(f, "{{alias}}")?, + TyKind::Alias(..) => write!(f, "{{alias}}")?, } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index e70918f8e1125..ba63343d49351 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -21,8 +21,9 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower::{GenericPredicates, associated_ty_item_bounds}, next_solver::{ - Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, ParamTy, - SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, mk_param, + AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, + ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, + mk_param, }, traits::next_trait_solve_in_ctxt, }; @@ -239,30 +240,30 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable ControlFlow::Break(()), rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()), - rustc_type_ir::TyKind::Alias(AliasTyKind::Projection, proj) => { - match self.allow_self_projection { - AllowSelfProjection::Yes => { - let trait_ = proj.trait_def_id(interner); - let trait_ = match trait_ { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - if self.super_traits.is_none() { - self.super_traits = Some( - elaborate::supertrait_def_ids(interner, self.trait_.into()) - .map(|super_trait| super_trait.0) - .collect(), - ) - } - if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { - ControlFlow::Continue(()) - } else { - ty.super_visit_with(self) - } + rustc_type_ir::TyKind::Alias( + proj @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }, + ) => match self.allow_self_projection { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_def_id(interner); + let trait_ = match trait_ { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + if self.super_traits.is_none() { + self.super_traits = Some( + elaborate::supertrait_def_ids(interner, self.trait_.into()) + .map(|super_trait| super_trait.0) + .collect(), + ) + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) } - AllowSelfProjection::No => ty.super_visit_with(self), } - } + AllowSelfProjection::No => ty.super_visit_with(self), + }, _ => ty.super_visit_with(self), } } @@ -503,8 +504,12 @@ fn contains_illegal_impl_trait_in_trait<'db>( &mut self, ty: as rustc_type_ir::Interner>::Ty, ) -> Self::Result { - if let rustc_type_ir::TyKind::Alias(AliasTyKind::Opaque, op) = ty.kind() { - let id = match op.def_id { + if let rustc_type_ir::TyKind::Alias(AliasTy { + kind: AliasTyKind::Opaque { def_id }, + .. + }) = ty.kind() + { + let id = match def_id { SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; 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 bd897113bf0e5..70083dbe3e794 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1674,10 +1674,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Some(res_assoc_ty) => { let alias = Ty::new_alias( self.interner(), - AliasTyKind::Projection, AliasTy::new( self.interner(), - res_assoc_ty.into(), + AliasTyKind::Projection { def_id: res_assoc_ty.into() }, iter::once(inner_ty.into()).chain(params.iter().copied()), ), ); @@ -1728,8 +1727,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args); let alias = Ty::new_alias( self.interner(), - AliasTyKind::Projection, - AliasTy::new_from_args(self.interner(), assoc_type.into(), args), + AliasTy::new_from_args( + self.interner(), + AliasTyKind::Projection { def_id: assoc_type.into() }, + args, + ), ); ty = self.table.try_structurally_resolve_type(alias); segments = segments.skip(1); 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 2cb936fec3a7a..bebf39b6947ee 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 @@ -364,7 +364,7 @@ impl<'db> InferenceContext<'_, 'db> { closure_kind: ClosureKind, ) -> (Option>, Option) { match expected_ty.kind() { - TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id, args, .. }) => self + TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id }, args, .. }) => self .deduce_closure_signature_from_predicates( expected_ty, closure_kind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 47a70492487e1..f92503003b397 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1663,7 +1663,7 @@ fn coerce<'db>( Const::new_bound( self.interner, self.debruijn, - BoundConst { var: BoundVar::from_usize(i) }, + BoundConst::new(BoundVar::from_usize(i)), ) }, ) 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 d093412b42107..35b41f0bbdd14 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 @@ -538,8 +538,11 @@ impl<'db> InferenceTable<'db> { let proj_args = self.infer_ctxt.fill_rest_fresh_args(output_assoc_type.into(), args); let projection = Ty::new_alias( self.interner(), - rustc_type_ir::AliasTyKind::Projection, - AliasTy::new_from_args(self.interner(), output_assoc_type.into(), proj_args), + AliasTy::new_from_args( + self.interner(), + rustc_type_ir::Projection { def_id: output_assoc_type.into() }, + proj_args, + ), ); let pred = Predicate::upcast_from(trait_ref, self.interner()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index b0986c423b116..1752b56b0f6d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -2,7 +2,7 @@ use base_db::{Crate, target::TargetLoadError}; use hir_def::layout::TargetDataLayout; -use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors}; +use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError}; use triomphe::Arc; use crate::db::HirDatabase; @@ -16,30 +16,29 @@ pub fn target_data_layout_query( Ok(it) => Ok(Arc::new(it)), Err(e) => { Err(match e { - TargetDataLayoutErrors::InvalidAddressSpace { addr_space, cause, err } => { + TargetDataLayoutError::InvalidAddressSpace { addr_space, cause, err } => { format!( r#"invalid address space `{addr_space}` for `{cause}` in "data-layout": {err}"# ) } - TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => format!(r#"invalid {kind} `{bit}` for `{cause}` in "data-layout": {err}"#), - TargetDataLayoutErrors::MissingAlignment { cause } => format!(r#"missing alignment for `{cause}` in "data-layout""#), - TargetDataLayoutErrors::InvalidAlignment { cause, err } => format!( - r#"invalid alignment for `{cause}` in "data-layout": `{align}` is {err_kind}"#, - align = err.align(), - err_kind = match err { - AlignFromBytesError::NotPowerOfTwo(_) => "not a power of two", - AlignFromBytesError::TooLarge(_) => "too large", - } - ), - TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { + TargetDataLayoutError::InvalidBits { kind, bit, cause, err } => format!(r#"invalid {kind} `{bit}` for `{cause}` in "data-layout": {err}"#), + TargetDataLayoutError::MissingAlignment { cause } => format!(r#"missing alignment for `{cause}` in "data-layout""#), + TargetDataLayoutError::InvalidAlignment { cause, err } => { + let (align, err_kind) = match err { + AlignFromBytesError::NotPowerOfTwo(align) => (align, "not a power of two"), + AlignFromBytesError::TooLarge(align) => (align, "too large"), + }; + format!(r#"invalid alignment for `{cause}` in "data-layout": `{align}` is {err_kind}"#) + }, + TargetDataLayoutError::InconsistentTargetArchitecture { dl, target } => { format!(r#"inconsistent target specification: "data-layout" claims architecture is {dl}-endian, while "target-endian" is `{target}`"#) } - TargetDataLayoutErrors::InconsistentTargetPointerWidth { + TargetDataLayoutError::InconsistentTargetPointerWidth { pointer_size, target, } => format!(r#"inconsistent target specification: "data-layout" claims pointers are {pointer_size}-bit, while "target-pointer-width" is `{target}`"#), - TargetDataLayoutErrors::InvalidBitsSize { err } => err, - TargetDataLayoutErrors::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifier in "data-layout": {err}"#), + TargetDataLayoutError::InvalidBitsSize { err } => err, + TargetDataLayoutError::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifier in "data-layout": {err}"#), }.into()) } }, 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 5086be7330411..9198e8b424908 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -84,7 +84,7 @@ use crate::{ lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVars, ClauseKind, Const, ConstKind, DbInterner, FnSig, + CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig, GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, }, }; @@ -435,7 +435,7 @@ where ConstKind::Error(_) => { let var = rustc_type_ir::BoundVar::from_usize(self.vars.len()); self.vars.push(CanonicalVarKind::Const(rustc_type_ir::UniverseIndex::ZERO)); - Ok(Const::new_bound(self.interner, self.binder, BoundConst { var })) + Ok(Const::new_bound(self.interner, self.binder, BoundConst::new(var))) } ConstKind::Infer(_) => error(), ConstKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => { @@ -479,7 +479,7 @@ where Canonical { value, max_universe: rustc_type_ir::UniverseIndex::ZERO, - variables: CanonicalVars::new_from_slice(&error_replacer.vars), + var_kinds: CanonicalVarKinds::new_from_slice(&error_replacer.vars), } } @@ -551,8 +551,11 @@ pub fn callable_sig_from_fn_trait<'db>( let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args); let projection = Ty::new_alias( table.interner(), - rustc_type_ir::AliasTyKind::Projection, - AliasTy::new_from_args(table.interner(), output_assoc_type.into(), args), + AliasTy::new_from_args( + table.interner(), + rustc_type_ir::Projection { def_id: output_assoc_type.into() }, + args, + ), ); let pred = Predicate::upcast_from(trait_ref, table.interner()); 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 71a7db6559a86..5a6e14a1e5eb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -537,8 +537,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); Ty::new_alias( self.interner, - AliasTyKind::Opaque, - AliasTy::new_from_args(self.interner, opaque_ty_id, args), + AliasTy::new_from_args( + self.interner, + AliasTyKind::Opaque { def_id: opaque_ty_id }, + args, + ), ) } ImplTraitLoweringMode::Disallowed => { @@ -1039,8 +1042,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let args = GenericArgs::identity_for_item(interner, def_id); let self_ty = Ty::new_alias( self.interner, - rustc_type_ir::AliasTyKind::Opaque, - AliasTy::new_from_args(interner, def_id, args), + AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id }, args), ); let (predicates, assoc_ty_bounds_start) = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { 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 889f0792d347b..4f707321782a2 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 @@ -214,10 +214,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ); Ty::new_alias( self.ctx.interner, - AliasTyKind::Projection, AliasTy::new_from_args( self.ctx.interner, - associated_ty.into(), + AliasTyKind::Projection { def_id: associated_ty.into() }, args, ), ) @@ -949,10 +948,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { bound, Ty::new_alias( self.ctx.interner, - AliasTyKind::Projection, AliasTy::new_from_args( self.ctx.interner, - associated_ty.into(), + AliasTyKind::Projection { def_id: associated_ty.into() }, args, ), ), 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 79b1c5cb7cc89..17af4ad961da0 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 @@ -46,7 +46,7 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, + AliasTy, Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredConst, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, @@ -2340,7 +2340,7 @@ impl<'db> Evaluator<'db> { } AdtId::UnionId(_) => (), }, - TyKind::Alias(AliasTyKind::Projection, _) => { + TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { let mut ocx = ObligationCtxt::new(&this.infcx); let ty = ocx .structurally_normalize_ty( @@ -2483,7 +2483,7 @@ impl<'db> Evaluator<'db> { | TyKind::Error(_) | TyKind::Placeholder(_) | TyKind::Dynamic(_, _) - | TyKind::Alias(_, _) + | TyKind::Alias(..) | TyKind::Bound(_, _) | TyKind::Infer(_) | TyKind::Pat(_, _) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 605e31404c575..354ad16ffec98 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -101,7 +101,7 @@ pub struct DefaultEmpty<'db> { pub tys: Tys<'db>, pub generic_args: GenericArgs<'db>, pub bound_var_kinds: BoundVarKinds<'db>, - pub canonical_vars: CanonicalVars<'db>, + pub canonical_vars: CanonicalVarKinds<'db>, pub variances: VariancesOf<'db>, pub pat_list: PatList<'db>, pub predefined_opaques: PredefinedOpaques<'db>, @@ -109,6 +109,7 @@ pub struct DefaultEmpty<'db> { pub bound_existential_predicates: BoundExistentialPredicates<'db>, pub clauses: Clauses<'db>, pub region_assumptions: RegionAssumptions<'db>, + pub consts: Consts<'db>, } pub struct DefaultAny<'db> { @@ -167,7 +168,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { ty.as_ref() }; let create_canonical_vars = |slice| { - let ty = CanonicalVars::new_from_slice(slice); + let ty = CanonicalVarKinds::new_from_slice(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() @@ -220,6 +221,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; + let create_consts = |slice| { + let ty = Consts::new_from_slice(slice); + // We need to increase the refcount (forever), so that the types won't be freed. + let ty = ManuallyDrop::new(ty.store()); + ty.as_ref() + }; let str = create_ty(TyKind::Str); let statik = create_region(RegionKind::ReStatic); @@ -270,11 +277,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { bound_existential_predicates: create_bound_existential_predicates(&[]), clauses: create_clauses(&[]), region_assumptions: create_region_assumptions(&[]), + consts: create_consts(&[]), }, one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]), one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]), coroutine_captures_by_ref_bound_var_kinds: create_bound_var_kinds(&[ - BoundVarKind::Region(BoundRegionKind::ClosureEnv), + BoundVariableKind::Region(BoundRegionKind::ClosureEnv), ]), } }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 9643f1ba4c3a3..8ed515b452695 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -10,16 +10,19 @@ use rustc_type_ir::{ BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, - inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike}, + inherent::{IntoKind, ParamEnv as _, SliceLike}, relate::Relate, }; use crate::{ MemoryMap, - next_solver::{ClauseKind, ParamEnv, impl_stored_interned}, + next_solver::{ + ClauseKind, ParamEnv, impl_foldable_for_interned_slice, impl_stored_interned, + interned_slice, + }, }; -use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; +use super::{DbInterner, ErrorGuaranteed, GenericArgs, Ty}; pub type ConstKind<'db> = rustc_type_ir::ConstKind>; pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst>; @@ -71,11 +74,15 @@ impl<'db> Const<'db> { Const::new(interner, ConstKind::Param(param)) } - pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self { + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst<'db>) -> Self { Const::new(interner, ConstKind::Placeholder(placeholder)) } - pub fn new_bound(interner: DbInterner<'db>, index: DebruijnIndex, bound: BoundConst) -> Self { + pub fn new_bound( + interner: DbInterner<'db>, + index: DebruijnIndex, + bound: BoundConst<'db>, + ) -> Self { Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound)) } @@ -120,7 +127,7 @@ impl<'db> std::fmt::Debug for Const<'db> { } } -pub type PlaceholderConst = Placeholder; +pub type PlaceholderConst<'db> = rustc_type_ir::PlaceholderConst>; #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct ParamConst { @@ -169,6 +176,8 @@ impl ParamConst { } } +pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind>; + /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. @@ -235,6 +244,14 @@ const _: () = { is_copy::>(); }; +impl<'db> IntoKind for Valtree<'db> { + type Kind = ValTreeKind<'db>; + + fn kind(self) -> Self::Kind { + todo!() + } +} + impl<'db> Valtree<'db> { #[inline] pub fn new(bytes: ConstBytes<'db>) -> Self { @@ -388,25 +405,22 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { Const::new(interner, ConstKind::Infer(InferConst::Var(var))) } - fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst) -> Self { + fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst<'db>) -> Self { Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var)) } fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self { Const::new( interner, - ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst { var }), + ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst::new(var)), ) } fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self { - Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst { var })) + Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst::new(var))) } - fn new_placeholder( - interner: DbInterner<'db>, - param: as rustc_type_ir::Interner>::PlaceholderConst, - ) -> Self { + fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderConst<'db>) -> Self { Const::new(interner, ConstKind::Placeholder(param)) } @@ -426,43 +440,7 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct BoundConst { - pub var: BoundVar, -} - -impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundConst { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: BoundVarKind) { - var.expect_const() - } -} - -impl<'db> PlaceholderLike> for PlaceholderConst { - type Bound = BoundConst; - - fn universe(self) -> rustc_type_ir::UniverseIndex { - self.universe - } - - fn var(self) -> rustc_type_ir::BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { - Placeholder { universe: ui, bound: self.bound } - } - - fn new(ui: rustc_type_ir::UniverseIndex, var: BoundConst) -> Self { - Placeholder { universe: ui, bound: var } - } - fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { - Placeholder { universe: ui, bound: BoundConst { var } } - } -} +pub type BoundConst<'db> = rustc_type_ir::BoundConst>; impl<'db> Relate> for ExprConst { fn relate>>( @@ -483,3 +461,6 @@ impl<'db> rustc_type_ir::inherent::ExprConst> for ExprConst { GenericArgs::default() } } + +interned_slice!(ConstsStorage, Consts, StoredConsts, consts, Const<'db>, Const<'static>); +impl_foldable_for_interned_slice!(Consts); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index 7836419e8b751..af823aa005d08 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -17,28 +17,28 @@ use super::{ /// gets mapped to the same result. `BoundVarReplacer` caches by using /// a `DelayedMap` which does not cache the first few types it encounters. pub trait BoundVarReplacerDelegate<'db> { - fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; - fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; - fn replace_const(&mut self, bv: BoundConst) -> Const<'db>; + fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db>; + fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db>; + fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db>; } /// A simple delegate taking 3 mutable functions. The used functions must /// always return the same result for each bound variable, no matter how /// frequently they are called. pub struct FnMutDelegate<'db, 'a> { - pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), - pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), - pub consts: &'a mut (dyn FnMut(BoundConst) -> Const<'db> + 'a), + pub regions: &'a mut (dyn FnMut(BoundRegion<'db>) -> Region<'db> + 'a), + pub types: &'a mut (dyn FnMut(BoundTy<'db>) -> Ty<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundConst<'db>) -> Const<'db> + 'a), } impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { - fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> { (self.regions)(br) } - fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> { (self.types)(bt) } - fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> { (self.consts)(bv) } } @@ -177,13 +177,13 @@ impl<'db> DbInterner<'db> { self, value: Binder<'db, T>, mut fld_r: F, - ) -> (T, FxIndexMap>) + ) -> (T, FxIndexMap, Region<'db>>) where - F: FnMut(BoundRegion) -> Region<'db>, + F: FnMut(BoundRegion<'db>) -> Region<'db>, T: TypeFoldable>, { let mut region_map = FxIndexMap::default(); - let real_fld_r = |br: BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); + let real_fld_r = |br: BoundRegion<'db>| *region_map.entry(br).or_insert_with(|| fld_r(br)); let value = self.instantiate_bound_regions_uncached(value, real_fld_r); (value, region_map) } @@ -194,7 +194,7 @@ impl<'db> DbInterner<'db> { mut replace_regions: F, ) -> T where - F: FnMut(BoundRegion) -> Region<'db>, + F: FnMut(BoundRegion<'db>) -> Region<'db>, T: TypeFoldable>, { let value = value.skip_binder(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index a8bff44a02583..6739795a0060d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -1,7 +1,5 @@ //! Fulfill loop for next-solver. -mod errors; - use std::ops::ControlFlow; use rustc_hash::FxHashSet; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs deleted file mode 100644 index 0e8218b33aaa2..0000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ /dev/null @@ -1,1173 +0,0 @@ -//! Trait solving error diagnosis and reporting. -//! -//! This code isn't used by rust-analyzer (it should, but then it'll probably be better to re-port it from rustc). -//! It's only there because without it, debugging trait solver errors is a nightmare. - -use std::{fmt::Debug, ops::ControlFlow}; - -use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; -use rustc_type_ir::{ - AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity, - error::ExpectedFound, - inherent::{IntoKind, Span as _}, - lang_items::SolverTraitLangItem, - solve::{Certainty, GoalSource, MaybeCause, NoSolution}, -}; -use tracing::{instrument, trace}; - -use crate::next_solver::{ - AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind, - SolverContext, Span, Term, TraitPredicate, Ty, TyKind, TypeError, - fulfill::NextSolverError, - infer::{ - InferCtxt, - select::SelectionError, - traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, - }, - inspect::{self, ProofTreeVisitor}, - normalize::deeply_normalize_for_diagnostics, -}; - -#[derive(Debug)] -pub struct FulfillmentError<'db> { - pub obligation: PredicateObligation<'db>, - pub code: FulfillmentErrorCode<'db>, - /// Diagnostics only: the 'root' obligation which resulted in - /// the failure to process `obligation`. This is the obligation - /// that was initially passed to `register_predicate_obligation` - pub root_obligation: PredicateObligation<'db>, -} - -impl<'db> FulfillmentError<'db> { - pub fn new( - obligation: PredicateObligation<'db>, - code: FulfillmentErrorCode<'db>, - root_obligation: PredicateObligation<'db>, - ) -> FulfillmentError<'db> { - FulfillmentError { obligation, code, root_obligation } - } - - pub fn is_true_error(&self) -> bool { - match self.code { - FulfillmentErrorCode::Select(_) - | FulfillmentErrorCode::Project(_) - | FulfillmentErrorCode::Subtype(_, _) - | FulfillmentErrorCode::ConstEquate(_, _) => true, - FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { - false - } - } - } -} - -#[derive(Debug, Clone)] -pub enum FulfillmentErrorCode<'db> { - /// Inherently impossible to fulfill; this trait is implemented if and only - /// if it is already implemented. - Cycle(PredicateObligations<'db>), - Select(SelectionError<'db>), - Project(MismatchedProjectionTypes<'db>), - Subtype(ExpectedFound>, TypeError<'db>), // always comes from a SubtypePredicate - ConstEquate(ExpectedFound>, TypeError<'db>), - Ambiguity { - /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation - /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by - /// emitting a fatal error instead. - overflow: Option, - }, -} - -#[derive(Debug, Clone)] -pub struct MismatchedProjectionTypes<'db> { - pub err: TypeError<'db>, -} - -pub(super) fn fulfillment_error_for_no_solution<'db>( - infcx: &InferCtxt<'db>, - root_obligation: PredicateObligation<'db>, -) -> FulfillmentError<'db> { - let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); - - let code = match obligation.predicate.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::Projection(_)) => { - FulfillmentErrorCode::Project( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => { - let ct_ty = match ct.kind() { - ConstKind::Unevaluated(uv) => { - infcx.interner.type_of(uv.def.into()).instantiate(infcx.interner, uv.args) - } - ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env), - ConstKind::Value(cv) => cv.ty, - kind => panic!( - "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" - ), - }; - FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { - ct, - ct_ty, - expected_ty, - }) - } - PredicateKind::NormalizesTo(..) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - PredicateKind::Subtype(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(a, b); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - PredicateKind::Coerce(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(b, a); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => { - FulfillmentErrorCode::Select(SelectionError::Unimplemented) - } - PredicateKind::ConstEquate(..) => { - panic!("unexpected goal: {obligation:?}") - } - }; - - FulfillmentError { obligation, code, root_obligation } -} - -pub(super) fn fulfillment_error_for_stalled<'db>( - infcx: &InferCtxt<'db>, - root_obligation: PredicateObligation<'db>, -) -> FulfillmentError<'db> { - let (code, refine_obligation) = infcx.probe(|_| { - match <&SolverContext<'db>>::from(infcx).evaluate_root_goal( - root_obligation.as_goal(), - Span::dummy(), - None, - ) { - Ok(GoalEvaluation { - certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, - .. - }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), - Ok(GoalEvaluation { - certainty: - Certainty::Maybe { - cause: - MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, - .. - }, - .. - }) => ( - FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, - // Don't look into overflows because we treat overflows weirdly anyways. - // We discard the inference constraints from overflowing goals, so - // recomputing the goal again during `find_best_leaf_obligation` may apply - // inference guidance that makes other goals go from ambig -> pass, for example. - // - // FIXME: We should probably just look into overflows here. - false, - ), - Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { - panic!( - "did not expect successful goal when collecting ambiguity errors for `{:?}`", - infcx.resolve_vars_if_possible(root_obligation.predicate), - ) - } - Err(_) => { - panic!( - "did not expect selection error when collecting ambiguity errors for `{:?}`", - infcx.resolve_vars_if_possible(root_obligation.predicate), - ) - } - } - }); - - FulfillmentError { - obligation: if refine_obligation { - find_best_leaf_obligation(infcx, &root_obligation, true) - } else { - root_obligation.clone() - }, - code, - root_obligation, - } -} - -pub(super) fn fulfillment_error_for_overflow<'db>( - infcx: &InferCtxt<'db>, - root_obligation: PredicateObligation<'db>, -) -> FulfillmentError<'db> { - FulfillmentError { - obligation: find_best_leaf_obligation(infcx, &root_obligation, true), - code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, - root_obligation, - } -} - -#[instrument(level = "debug", skip(infcx), ret)] -fn find_best_leaf_obligation<'db>( - infcx: &InferCtxt<'db>, - obligation: &PredicateObligation<'db>, - consider_ambiguities: bool, -) -> PredicateObligation<'db> { - let obligation = infcx.resolve_vars_if_possible(obligation.clone()); - // FIXME: we use a probe here as the `BestObligation` visitor does not - // check whether it uses candidates which get shadowed by where-bounds. - // - // We should probably fix the visitor to not do so instead, as this also - // means the leaf obligation may be incorrect. - let obligation = infcx - .fudge_inference_if_ok(|| { - infcx - .visit_proof_tree( - obligation.as_goal(), - &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, - ) - .break_value() - .ok_or(()) - }) - .unwrap_or(obligation); - deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) -} - -struct BestObligation<'db> { - obligation: PredicateObligation<'db>, - consider_ambiguities: bool, -} - -impl<'db> BestObligation<'db> { - fn with_derived_obligation( - &mut self, - derived_obligation: PredicateObligation<'db>, - and_then: impl FnOnce(&mut Self) -> >::Result, - ) -> >::Result { - let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); - let res = and_then(self); - self.obligation = old_obligation; - res - } - - /// Filter out the candidates that aren't interesting to visit for the - /// purposes of reporting errors. For ambiguities, we only consider - /// candidates that may hold. For errors, we only consider candidates that - /// *don't* hold and which have impl-where clauses that also don't hold. - fn non_trivial_candidates<'a>( - &self, - goal: &'a inspect::InspectGoal<'a, 'db>, - ) -> Vec> { - let mut candidates = goal.candidates(); - match self.consider_ambiguities { - true => { - // If we have an ambiguous obligation, we must consider *all* candidates - // that hold, or else we may guide inference causing other goals to go - // from ambig -> pass/fail. - candidates.retain(|candidate| candidate.result().is_ok()); - } - false => { - // We always handle rigid alias candidates separately as we may not add them for - // aliases whose trait bound doesn't hold. - candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); - // If we have >1 candidate, one may still be due to "boring" reasons, like - // an alias-relate that failed to hold when deeply evaluated. We really - // don't care about reasons like this. - if candidates.len() > 1 { - candidates.retain(|candidate| { - goal.infcx().probe(|_| { - candidate.instantiate_nested_goals().iter().any(|nested_goal| { - matches!( - nested_goal.source(), - GoalSource::ImplWhereBound - | GoalSource::AliasBoundConstCondition - | GoalSource::AliasWellFormed - ) && nested_goal.result().is_err() - }) - }) - }); - } - } - } - - candidates - } - - /// HACK: We walk the nested obligations for a well-formed arg manually, - /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. - /// Ideally we'd be able to track this better. - fn visit_well_formed_goal( - &mut self, - candidate: &inspect::InspectCandidate<'_, 'db>, - term: Term<'db>, - ) -> ControlFlow> { - let infcx = candidate.goal().infcx(); - let param_env = candidate.goal().goal().param_env; - - for obligation in wf::unnormalized_obligations(infcx, param_env, term).into_iter().flatten() - { - let nested_goal = candidate - .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal()); - // Skip nested goals that aren't the *reason* for our goal's failure. - match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) - | (false, Err(_)) => {} - _ => continue, - } - - self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; - } - - ControlFlow::Break(self.obligation.clone()) - } - - /// If a normalization of an associated item or a trait goal fails without trying any - /// candidates it's likely that normalizing its self type failed. We manually detect - /// such cases here. - fn detect_error_in_self_ty_normalization( - &mut self, - goal: &inspect::InspectGoal<'_, 'db>, - self_ty: Ty<'db>, - ) -> ControlFlow> { - assert!(!self.consider_ambiguities); - let interner = goal.infcx().interner; - if let TyKind::Alias(..) = self_ty.kind() { - let infer_term = goal.infcx().next_ty_var(); - let pred = PredicateKind::AliasRelate( - self_ty.into(), - infer_term.into(), - AliasRelationDirection::Equate, - ); - let obligation = Obligation::new( - interner, - self.obligation.cause.clone(), - goal.goal().param_env, - pred, - ); - self.with_derived_obligation(obligation, |this| { - goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(interner, pred), - goal.depth() + 1, - this, - ) - }) - } else { - ControlFlow::Continue(()) - } - } - - /// When a higher-ranked projection goal fails, check that the corresponding - /// higher-ranked trait goal holds or not. This is because the process of - /// instantiating and then re-canonicalizing the binder of the projection goal - /// forces us to be unable to see that the leak check failed in the nested - /// `NormalizesTo` goal, so we don't fall back to the rigid projection check - /// that should catch when a projection goal fails due to an unsatisfied trait - /// goal. - fn detect_trait_error_in_higher_ranked_projection( - &mut self, - goal: &inspect::InspectGoal<'_, 'db>, - ) -> ControlFlow> { - let interner = goal.infcx().interner; - if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() - && !projection_clause.bound_vars().is_empty() - { - let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner)); - let obligation = Obligation::new( - interner, - self.obligation.cause.clone(), - goal.goal().param_env, - deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), - ); - self.with_derived_obligation(obligation, |this| { - goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(interner, pred), - goal.depth() + 1, - this, - ) - }) - } else { - ControlFlow::Continue(()) - } - } - - /// It is likely that `NormalizesTo` failed without any applicable candidates - /// because the alias is not well-formed. - /// - /// As we only enter `RigidAlias` candidates if the trait bound of the associated type - /// holds, we discard these candidates in `non_trivial_candidates` and always manually - /// check this here. - fn detect_non_well_formed_assoc_item( - &mut self, - goal: &inspect::InspectGoal<'_, 'db>, - alias: AliasTerm<'db>, - ) -> ControlFlow> { - let interner = goal.infcx().interner; - let obligation = Obligation::new( - interner, - self.obligation.cause.clone(), - goal.goal().param_env, - alias.trait_ref(interner), - ); - self.with_derived_obligation(obligation, |this| { - goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(interner, alias.trait_ref(interner)), - goal.depth() + 1, - this, - ) - }) - } - - /// If we have no candidates, then it's likely that there is a - /// non-well-formed alias in the goal. - fn detect_error_from_empty_candidates( - &mut self, - goal: &inspect::InspectGoal<'_, 'db>, - ) -> ControlFlow> { - let interner = goal.infcx().interner; - let pred_kind = goal.goal().predicate.kind(); - - match pred_kind.no_bound_vars() { - Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => { - self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; - } - Some(PredicateKind::NormalizesTo(pred)) => { - if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst = - pred.alias.kind(interner) - { - self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; - self.detect_non_well_formed_assoc_item(goal, pred.alias)?; - } - } - Some(_) | None => {} - } - - ControlFlow::Break(self.obligation.clone()) - } -} - -impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { - type Result = ControlFlow>; - - #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] - fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result { - let interner = goal.infcx().interner; - // Skip goals that aren't the *reason* for our goal's failure. - match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { - } - _ => return ControlFlow::Continue(()), - } - - let pred = goal.goal().predicate; - - let candidates = self.non_trivial_candidates(goal); - let candidate = match candidates.as_slice() { - [candidate] => candidate, - [] => return self.detect_error_from_empty_candidates(goal), - _ => return ControlFlow::Break(self.obligation.clone()), - }; - - // // Don't walk into impls that have `do_not_recommend`. - // if let inspect::ProbeKind::TraitCandidate { - // source: CandidateSource::Impl(impl_def_id), - // result: _, - // } = candidate.kind() - // && interner.do_not_recommend_impl(impl_def_id) - // { - // trace!("#[do_not_recommend] -> exit"); - // return ControlFlow::Break(self.obligation.clone()); - // } - - // FIXME: Also, what about considering >1 layer up the stack? May be necessary - // for normalizes-to. - let child_mode = match pred.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { - ChildMode::Trait(pred.kind().rebind(trait_pred)) - } - PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => { - ChildMode::Host(pred.kind().rebind(host_pred)) - } - PredicateKind::NormalizesTo(normalizes_to) - if matches!( - normalizes_to.alias.kind(interner), - AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst - ) => - { - ChildMode::Trait(pred.kind().rebind(TraitPredicate { - trait_ref: normalizes_to.alias.trait_ref(interner), - polarity: PredicatePolarity::Positive, - })) - } - PredicateKind::Clause(ClauseKind::WellFormed(term)) => { - return self.visit_well_formed_goal(candidate, term); - } - _ => ChildMode::PassThrough, - }; - - let nested_goals = candidate.instantiate_nested_goals(); - - // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as - // an actual candidate, instead we should treat them as if the impl was never considered to - // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written - // instead of `impl Trait for T`. - // - // We do this as a separate loop so that we do not choose to tell the user about some nested - // goal before we encounter a `T: FnPtr` nested goal. - for nested_goal in &nested_goals { - if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() - && interner - .is_trait_lang_item(poly_trait_pred.def_id(), SolverTraitLangItem::FnPtrTrait) - && let Err(NoSolution) = nested_goal.result() - { - return ControlFlow::Break(self.obligation.clone()); - } - } - - for nested_goal in nested_goals { - trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); - - let nested_pred = nested_goal.goal().predicate; - - let make_obligation = || Obligation { - cause: ObligationCause::dummy(), - param_env: nested_goal.goal().param_env, - predicate: nested_pred, - recursion_depth: self.obligation.recursion_depth + 1, - }; - - let obligation = match (child_mode, nested_goal.source()) { - ( - ChildMode::Trait(_) | ChildMode::Host(_), - GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), - ) => { - continue; - } - (ChildMode::Trait(_parent_trait_pred), GoalSource::ImplWhereBound) => { - make_obligation() - } - ( - ChildMode::Host(_parent_host_pred), - GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, - ) => make_obligation(), - (ChildMode::PassThrough, _) - | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { - make_obligation() - } - }; - - self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; - } - - // alias-relate may fail because the lhs or rhs can't be normalized, - // and therefore is treated as rigid. - if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { - goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(interner, ClauseKind::WellFormed(lhs)), - goal.depth() + 1, - self, - )?; - goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(interner, ClauseKind::WellFormed(rhs)), - goal.depth() + 1, - self, - )?; - } - - self.detect_trait_error_in_higher_ranked_projection(goal)?; - - ControlFlow::Break(self.obligation.clone()) - } -} - -#[derive(Debug, Copy, Clone)] -enum ChildMode<'db> { - // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, - // and skip all `GoalSource::Misc`, which represent useless obligations - // such as alias-eq which may not hold. - Trait(PolyTraitPredicate<'db>), - // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, - // and skip all `GoalSource::Misc`, which represent useless obligations - // such as alias-eq which may not hold. - Host(Binder<'db, HostEffectPredicate>>), - // Skip trying to derive an `ObligationCause` from this obligation, and - // report *all* sub-obligations as if they came directly from the parent - // obligation. - PassThrough, -} - -impl<'db> NextSolverError<'db> { - pub fn to_debuggable_error(&self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> { - match self { - NextSolverError::TrueError(obligation) => { - fulfillment_error_for_no_solution(infcx, obligation.clone()) - } - NextSolverError::Ambiguity(obligation) => { - fulfillment_error_for_stalled(infcx, obligation.clone()) - } - NextSolverError::Overflow(obligation) => { - fulfillment_error_for_overflow(infcx, obligation.clone()) - } - } - } -} - -mod wf { - use hir_def::signatures::ImplSignature; - use hir_def::{GeneralConstId, ItemContainerId}; - use rustc_type_ir::inherent::{ - AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Term as _, - Ty as _, - }; - use rustc_type_ir::lang_items::SolverTraitLangItem; - use rustc_type_ir::{ - Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - }; - use tracing::{debug, instrument}; - - use crate::next_solver::infer::InferCtxt; - use crate::next_solver::infer::traits::{Obligation, ObligationCause, PredicateObligations}; - use crate::next_solver::{ - Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, GenericArgs, - ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, - }; - - /// Compute the predicates that are required for a type to be well-formed. - /// - /// This is only intended to be used in the new solver, since it does not - /// take into account recursion depth or proper error-reporting spans. - pub(crate) fn unnormalized_obligations<'db>( - infcx: &InferCtxt<'db>, - param_env: ParamEnv<'db>, - term: Term<'db>, - ) -> Option> { - debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); - - // However, if `arg` IS an unresolved inference variable, returns `None`, - // because we are not able to make any progress at all. This is to prevent - // cycles where we say "?0 is WF if ?0 is WF". - if term.is_infer() { - return None; - } - - let mut wf = - WfPredicates { infcx, param_env, out: PredicateObligations::new(), recursion_depth: 0 }; - wf.add_wf_preds_for_term(term); - Some(wf.out) - } - - struct WfPredicates<'a, 'db> { - infcx: &'a InferCtxt<'db>, - param_env: ParamEnv<'db>, - out: PredicateObligations<'db>, - recursion_depth: usize, - } - - impl<'a, 'db> WfPredicates<'a, 'db> { - fn interner(&self) -> DbInterner<'db> { - self.infcx.interner - } - - fn require_sized(&mut self, subty: Ty<'db>) { - if !subty.has_escaping_bound_vars() { - let cause = ObligationCause::new(); - let trait_ref = TraitRef::new( - self.interner(), - self.interner().require_trait_lang_item(SolverTraitLangItem::Sized), - [subty], - ); - self.out.push(Obligation::with_depth( - self.interner(), - cause, - self.recursion_depth, - self.param_env, - Binder::dummy(trait_ref), - )); - } - } - - /// Pushes all the predicates needed to validate that `term` is WF into `out`. - #[instrument(level = "debug", skip(self))] - fn add_wf_preds_for_term(&mut self, term: Term<'db>) { - term.visit_with(self); - debug!(?self.out); - } - - #[instrument(level = "debug", skip(self))] - fn nominal_obligations( - &mut self, - def_id: SolverDefId, - args: GenericArgs<'db>, - ) -> PredicateObligations<'db> { - // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker - // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining - // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make - // the compiler do a bunch of work needlessly. - if let SolverDefId::TraitId(def_id) = def_id - && self.interner().is_trait_lang_item(def_id.into(), SolverTraitLangItem::Sized) - { - return Default::default(); - } - - self.interner() - .predicates_of(def_id) - .iter_instantiated(self.interner(), args) - .map(|pred| { - let cause = ObligationCause::new(); - Obligation::with_depth( - self.interner(), - cause, - self.recursion_depth, - self.param_env, - pred, - ) - }) - .filter(|pred| !pred.has_escaping_bound_vars()) - .collect() - } - - fn add_wf_preds_for_dyn_ty( - &mut self, - _ty: Ty<'db>, - data: &[Binder<'db, ExistentialPredicate<'db>>], - region: Region<'db>, - ) { - // Imagine a type like this: - // - // trait Foo { } - // trait Bar<'c> : 'c { } - // - // &'b (Foo+'c+Bar<'d>) - // ^ - // - // In this case, the following relationships must hold: - // - // 'b <= 'c - // 'd <= 'c - // - // The first conditions is due to the normal region pointer - // rules, which say that a reference cannot outlive its - // referent. - // - // The final condition may be a bit surprising. In particular, - // you may expect that it would have been `'c <= 'd`, since - // usually lifetimes of outer things are conservative - // approximations for inner things. However, it works somewhat - // differently with trait objects: here the idea is that if the - // user specifies a region bound (`'c`, in this case) it is the - // "master bound" that *implies* that bounds from other traits are - // all met. (Remember that *all bounds* in a type like - // `Foo+Bar+Zed` must be met, not just one, hence if we write - // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and - // 'y.) - // - // Note: in fact we only permit builtin traits, not `Bar<'d>`, I - // am looking forward to the future here. - if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.interner(), data); - - let explicit_bound = region; - - self.out.reserve(implicit_bounds.len()); - for implicit_bound in implicit_bounds { - let cause = ObligationCause::new(); - let outlives = Binder::dummy(rustc_type_ir::OutlivesPredicate( - explicit_bound, - implicit_bound, - )); - self.out.push(Obligation::with_depth( - self.interner(), - cause, - self.recursion_depth, - self.param_env, - outlives, - )); - } - - // We don't add any wf predicates corresponding to the trait ref's generic arguments - // which allows code like this to compile: - // ```rust - // trait Trait {} - // fn foo(_: &dyn Trait<[u32]>) {} - // ``` - } - } - } - - impl<'a, 'db> TypeVisitor> for WfPredicates<'a, 'db> { - type Result = (); - - fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { - debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); - - let tcx = self.interner(); - - match t.kind() { - TyKind::Bool - | TyKind::Char - | TyKind::Int(..) - | TyKind::Uint(..) - | TyKind::Float(..) - | TyKind::Error(_) - | TyKind::Str - | TyKind::CoroutineWitness(..) - | TyKind::Never - | TyKind::Param(_) - | TyKind::Bound(..) - | TyKind::Placeholder(..) - | TyKind::Foreign(..) => { - // WfScalar, WfParameter, etc - } - - // Can only infer to `TyKind::Int(_) | TyKind::Uint(_)`. - TyKind::Infer(rustc_type_ir::IntVar(_)) => {} - - // Can only infer to `TyKind::Float(_)`. - TyKind::Infer(rustc_type_ir::FloatVar(_)) => {} - - TyKind::Slice(subty) => { - self.require_sized(subty); - } - - TyKind::Array(subty, len) => { - self.require_sized(subty); - // Note that the len being WF is implicitly checked while visiting. - // Here we just check that it's of type usize. - let cause = ObligationCause::new(); - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - Binder::dummy(PredicateKind::Clause(ClauseKind::ConstArgHasType( - len, - Ty::new_unit(self.interner()), - ))), - )); - } - - TyKind::Pat(base_ty, _pat) => { - self.require_sized(base_ty); - } - - TyKind::Tuple(tys) => { - if let Some((_last, rest)) = tys.split_last() { - for &elem in rest { - self.require_sized(elem); - } - } - } - - TyKind::RawPtr(_, _) => { - // Simple cases that are WF if their type args are WF. - } - - TyKind::Alias( - rustc_type_ir::Projection | rustc_type_ir::Opaque | rustc_type_ir::Free, - data, - ) => { - let obligations = self.nominal_obligations(data.def_id, data.args); - self.out.extend(obligations); - } - TyKind::Alias(rustc_type_ir::Inherent, _data) => { - return; - } - - TyKind::Adt(def, args) => { - // WfNominalType - let obligations = self.nominal_obligations(def.def_id().0.into(), args); - self.out.extend(obligations); - } - - TyKind::FnDef(did, args) => { - // HACK: Check the return type of function definitions for - // well-formedness to mostly fix #84533. This is still not - // perfect and there may be ways to abuse the fact that we - // ignore requirements with escaping bound vars. That's a - // more general issue however. - let fn_sig = tcx.fn_sig(did).instantiate(tcx, args); - fn_sig.output().skip_binder().visit_with(self); - - let did = match did.0 { - hir_def::CallableDefId::FunctionId(id) => id.into(), - hir_def::CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), - hir_def::CallableDefId::EnumVariantId(id) => { - SolverDefId::Ctor(Ctor::Enum(id)) - } - }; - let obligations = self.nominal_obligations(did, args); - self.out.extend(obligations); - } - - TyKind::Ref(r, rty, _) => { - // WfReference - if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { - let cause = ObligationCause::new(); - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives( - rustc_type_ir::OutlivesPredicate(rty, r), - ))), - )); - } - } - - TyKind::Coroutine(did, args, ..) => { - // Walk ALL the types in the coroutine: this will - // include the upvar types as well as the yield - // type. Note that this is mildly distinct from - // the closure case, where we have to be careful - // about the signature of the closure. We don't - // have the problem of implied bounds here since - // coroutines don't take arguments. - let obligations = self.nominal_obligations(did.0.into(), args); - self.out.extend(obligations); - } - - TyKind::Closure(did, args) => { - // Note that we cannot skip the generic types - // types. Normally, within the fn - // body where they are created, the generics will - // always be WF, and outside of that fn body we - // are not directly inspecting closure types - // anyway, except via auto trait matching (which - // only inspects the upvar types). - // But when a closure is part of a type-alias-impl-trait - // then the function that created the defining site may - // have had more bounds available than the type alias - // specifies. This may cause us to have a closure in the - // hidden type that is not actually well formed and - // can cause compiler crashes when the user abuses unsafe - // code to procure such a closure. - // See tests/ui/type-alias-impl-trait/wf_check_closures.rs - let obligations = self.nominal_obligations(did.0.into(), args); - self.out.extend(obligations); - // Only check the upvar types for WF, not the rest - // of the types within. This is needed because we - // capture the signature and it may not be WF - // without the implied bounds. Consider a closure - // like `|x: &'a T|` -- it may be that `T: 'a` is - // not known to hold in the creator's context (and - // indeed the closure may not be invoked by its - // creator, but rather turned to someone who *can* - // verify that). - // - // The special treatment of closures here really - // ought not to be necessary either; the problem - // is related to #25860 -- there is no way for us - // to express a fn type complete with the implied - // bounds that it is assuming. I think in reality - // the WF rules around fn are a bit messed up, and - // that is the rot problem: `fn(&'a T)` should - // probably always be WF, because it should be - // shorthand for something like `where(T: 'a) { - // fn(&'a T) }`, as discussed in #25860. - let upvars = args.as_closure().tupled_upvars_ty(); - return upvars.visit_with(self); - } - - TyKind::CoroutineClosure(did, args) => { - // See the above comments. The same apply to coroutine-closures. - let obligations = self.nominal_obligations(did.0.into(), args); - self.out.extend(obligations); - let upvars = args.as_coroutine_closure().tupled_upvars_ty(); - return upvars.visit_with(self); - } - - TyKind::FnPtr(..) => { - // Let the visitor iterate into the argument/return - // types appearing in the fn signature. - } - TyKind::UnsafeBinder(_ty) => {} - - TyKind::Dynamic(data, r) => { - // WfObject - // - // Here, we defer WF checking due to higher-ranked - // regions. This is perhaps not ideal. - self.add_wf_preds_for_dyn_ty(t, data.as_slice(), r); - - // FIXME(#27579) RFC also considers adding trait - // obligations that don't refer to Self and - // checking those - if let Some(principal) = data.principal_def_id() { - self.out.push(Obligation::with_depth( - tcx, - ObligationCause::new(), - self.recursion_depth, - self.param_env, - Binder::dummy(PredicateKind::DynCompatible(principal)), - )); - } - } - - // Inference variables are the complicated case, since we don't - // know what type they are. We do two things: - // - // 1. Check if they have been resolved, and if so proceed with - // THAT type. - // 2. If not, we've at least simplified things (e.g., we went - // from `Vec?0>: WF` to `?0: WF`), so we can - // register a pending obligation and keep - // moving. (Goal is that an "inductive hypothesis" - // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing cycle - // prevention, which happens before this can be reached. - TyKind::Infer(_) => { - let cause = ObligationCause::new(); - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(t.into()))), - )); - } - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: Const<'db>) -> Self::Result { - let tcx = self.interner(); - - match c.kind() { - ConstKind::Unevaluated(uv) => { - if !c.has_escaping_bound_vars() { - let predicate = - Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); - let cause = ObligationCause::new(); - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - - if let GeneralConstId::ConstId(uv_def) = uv.def.0 - && let ItemContainerId::ImplId(impl_) = - uv_def.loc(self.interner().db).container - && ImplSignature::of(self.interner().db, impl_).target_trait.is_none() - { - return; // Subtree is handled by above function - } else { - let obligations = self.nominal_obligations(uv.def.into(), uv.args); - self.out.extend(obligations); - } - } - } - ConstKind::Infer(_) => { - let cause = ObligationCause::new(); - - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(c.into()))), - )); - } - ConstKind::Expr(_) => { - // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the - // trait bound `typeof(N): Add` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` - // which means that the `DefId` would have been typeck'd elsewhere. However in - // the future we may allow directly lowering to `ConstKind::Expr` in which case - // we would not be proving bounds we should. - - let predicate = - Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); - let cause = ObligationCause::new(); - self.out.push(Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - predicate, - )); - } - - ConstKind::Error(_) - | ConstKind::Param(_) - | ConstKind::Bound(..) - | ConstKind::Placeholder(..) => { - // These variants are trivially WF, so nothing to do here. - } - ConstKind::Value(..) => { - // FIXME: Enforce that values are structurally-matchable. - } - } - - c.super_visit_with(self) - } - - fn visit_predicate(&mut self, _p: Predicate<'db>) -> Self::Result { - panic!("predicate should not be checked for well-formedness"); - } - } - - /// Given an object type like `SomeTrait + Send`, computes the lifetime - /// bounds that must hold on the elided self type. These are derived - /// from the declarations of `SomeTrait`, `Send`, and friends -- if - /// they declare `trait SomeTrait : 'static`, for example, then - /// `'static` would appear in the list. - /// - /// N.B., in some cases, particularly around higher-ranked bounds, - /// this function returns a kind of conservative approximation. - /// That is, all regions returned by this function are definitely - /// required, but there may be other region bounds that are not - /// returned, as well as requirements like `for<'a> T: 'a`. - /// - /// Requires that trait definitions have been processed so that we can - /// elaborate predicates and walk supertraits. - pub(crate) fn object_region_bounds<'db>( - interner: DbInterner<'db>, - existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>], - ) -> Vec> { - let erased_self_ty = Ty::new_unit(interner); - - let predicates = existential_predicates - .iter() - .map(|predicate| predicate.with_self_ty(interner, erased_self_ty)); - - rustc_type_ir::elaborate::elaborate(interner, predicates) - .filter_map(|pred| { - debug!(?pred); - match pred.kind().skip_binder() { - ClauseKind::TypeOutlives(rustc_type_ir::OutlivesPredicate(ref t, ref r)) => { - // Search for a bound of the form `erased_self_ty - // : 'a`, but be wary of something like `for<'a> - // erased_self_ty : 'a` (we interpret a - // higher-ranked bound like that as 'static, - // though at present the code in `fulfill.rs` - // considers such bounds to be unsatisfiable, so - // it's kind of a moot point since you could never - // construct such an object, but this seems - // correct even if that code changes). - if t == &erased_self_ty && !r.has_escaping_bound_vars() { - Some(*r) - } else { - None - } - } - ClauseKind::Trait(_) - | ClauseKind::HostEffect(..) - | ClauseKind::RegionOutlives(_) - | ClauseKind::Projection(_) - | ClauseKind::ConstArgHasType(_, _) - | ClauseKind::WellFormed(_) - | ClauseKind::UnstableFeature(_) - | ClauseKind::ConstEvaluatable(_) => None, - } - }) - .collect() - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index ccd93590107fc..33e4c175d0635 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -16,11 +16,11 @@ use rustc_type_ir::{ use smallvec::SmallVec; use tracing::debug; -use crate::next_solver::infer::InferCtxt; use crate::next_solver::{ - Binder, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, - Placeholder, Region, Ty, TyKind, + Binder, Canonical, CanonicalVarKind, CanonicalVarKinds, Const, ConstKind, DbInterner, + GenericArg, PlaceholderConst, PlaceholderRegion, Region, Ty, TyKind, }; +use crate::next_solver::{PlaceholderType, infer::InferCtxt}; /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores @@ -498,7 +498,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { { let base = Canonical { max_universe: UniverseIndex::ROOT, - variables: CanonicalVars::empty(tcx), + var_kinds: CanonicalVarKinds::empty(tcx), value: (), }; Canonicalizer::canonicalize_with_base( @@ -539,7 +539,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { tcx, canonicalize_mode: canonicalize_region_mode, needs_canonical_flags, - variables: SmallVec::from_slice(base.variables.as_slice()), + variables: SmallVec::from_slice(base.var_kinds.as_slice()), query_state, indices: FxHashMap::default(), sub_root_lookup_table: Default::default(), @@ -562,7 +562,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); let canonical_variables = - CanonicalVars::new_from_slice(&canonicalizer.universe_canonicalized_variables()); + CanonicalVarKinds::new_from_slice(&canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -570,7 +570,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { .max() .unwrap_or(UniverseIndex::ROOT); - Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + Canonical { max_universe, var_kinds: canonical_variables, value: (base.value, out_value) } } /// Creates a canonical variable replacing `kind` from the input, @@ -670,22 +670,22 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { - CanonicalVarKind::PlaceholderTy(Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) + CanonicalVarKind::PlaceholderTy(PlaceholderType::new( + reverse_universe_map[&placeholder.universe], + placeholder.bound, + )) } CanonicalVarKind::PlaceholderRegion(placeholder) => { - CanonicalVarKind::PlaceholderRegion(Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) + CanonicalVarKind::PlaceholderRegion(PlaceholderRegion::new( + reverse_universe_map[&placeholder.universe], + placeholder.bound, + )) } CanonicalVarKind::PlaceholderConst(placeholder) => { - CanonicalVarKind::PlaceholderConst(Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }) + CanonicalVarKind::PlaceholderConst(PlaceholderConst::new( + reverse_universe_map[&placeholder.universe], + placeholder.bound, + )) } }) .collect() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 61d1e97746224..1738552a8e015 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -69,7 +69,7 @@ impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> { where T: TypeFoldable>, { - assert_eq!(self.variables.len(), var_values.len()); + assert_eq!(self.var_kinds.len(), var_values.len()); let value = projection_fn(&self.value); instantiate_value(tcx, var_values, value) } @@ -90,15 +90,15 @@ where value } else { let delegate = FnMutDelegate { - regions: &mut |br: BoundRegion| match var_values[br.var].kind() { + regions: &mut |br: BoundRegion<'db>| match var_values[br.var].kind() { GenericArgKind::Lifetime(l) => l, r => panic!("{br:?} is a region but value is {r:?}"), }, - types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() { + types: &mut |bound_ty: BoundTy<'db>| match var_values[bound_ty.var].kind() { GenericArgKind::Type(ty) => ty, r => panic!("{bound_ty:?} is a type but value is {r:?}"), }, - consts: &mut |bound_ct: BoundConst| match var_values[bound_ct.var].kind() { + consts: &mut |bound_ct: BoundConst<'db>| match var_values[bound_ct.var].kind() { GenericArgKind::Const(ct) => ct, c => panic!("{bound_ct:?} is a const but value is {c:?}"), }, @@ -356,7 +356,7 @@ impl<'db> InferCtxt<'db> { // result, then we can type the corresponding value from the // input. See the example above. let mut opt_values: IndexVec>> = - IndexVec::from_elem_n(None, query_response.variables.len()); + IndexVec::from_elem_n(None, query_response.var_kinds.len()); for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) { @@ -368,7 +368,7 @@ impl<'db> InferCtxt<'db> { // more involved. They are also a lot rarer than region variables. if let TyKind::Bound(index_kind, b) = result_value.kind() && !matches!( - query_response.variables.as_slice()[b.var.as_usize()], + query_response.var_kinds.as_slice()[b.var.as_usize()], CanonicalVarKind::Ty { .. } ) { @@ -398,7 +398,7 @@ impl<'db> InferCtxt<'db> { // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. let interner = self.interner; - let variables = query_response.variables; + let variables = query_response.var_kinds; let var_values = CanonicalVarValues::instantiate(interner, variables, |var_values, kind| { if kind.universe() != UniverseIndex::ROOT { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index a0420a5a00b9d..1fefc0f265c55 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -23,7 +23,7 @@ use crate::next_solver::{ ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, - OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Region, Ty, TyKind, + OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind, infer::InferCtxt, }; use instantiate::CanonicalExt; @@ -70,7 +70,7 @@ impl<'db> InferCtxt<'db> { let var_values = CanonicalVarValues::instantiate( self.interner, - canonical.variables, + canonical.var_kinds, |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]), ); let result = canonical.instantiate(self.interner, &var_values); @@ -110,9 +110,9 @@ impl<'db> InferCtxt<'db> { CanonicalVarKind::Float => self.next_float_var().into(), - CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { + CanonicalVarKind::PlaceholderTy(PlaceholderType { universe, bound, .. }) => { let universe_mapped = universe_map(universe); - let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound }; + let placeholder_mapped = PlaceholderType::new(universe_mapped, bound); Ty::new_placeholder(self.interner, placeholder_mapped).into() } @@ -120,18 +120,16 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(universe_map(ui)).into() } - CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => { + CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound, .. }) => { let universe_mapped = universe_map(universe); - let placeholder_mapped: crate::next_solver::Placeholder< - crate::next_solver::BoundRegion, - > = PlaceholderRegion { universe: universe_mapped, bound }; + let placeholder_mapped = PlaceholderRegion::new(universe_mapped, bound); Region::new_placeholder(self.interner, placeholder_mapped).into() } CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), - CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => { + CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound, .. }) => { let universe_mapped = universe_map(universe); - let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound }; + let placeholder_mapped = PlaceholderConst::new(universe_mapped, bound); Const::new_placeholder(self.interner, placeholder_mapped).into() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 21baacb116938..a6957c66ff0c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -30,7 +30,7 @@ use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; pub use crate::next_solver::infer::traits::ObligationInspector; use crate::next_solver::{ - ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, Predicate, + ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate, SolverContext, fold::BoundVarReplacerDelegate, infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation}, @@ -53,7 +53,7 @@ mod outlives; pub mod region_constraints; pub mod relate; pub mod resolve; -pub(crate) mod select; +pub mod select; pub(crate) mod snapshot; pub(crate) mod traits; mod type_variable; @@ -366,7 +366,7 @@ impl<'db> InferCtxtBuilder<'db> { where T: TypeFoldable>, { - let infcx = self.build(input.typing_mode); + let infcx = self.build(input.typing_mode.0); let (value, args) = infcx.instantiate_canonical(&input.canonical); (infcx, value, args) } @@ -1098,9 +1098,9 @@ impl<'db> InferCtxt<'db> { for bound_var_kind in bound_vars { let arg: GenericArg<'db> = match bound_var_kind { - BoundVarKind::Ty(_) => self.next_ty_var().into(), - BoundVarKind::Region(_) => self.next_region_var().into(), - BoundVarKind::Const => self.next_const_var().into(), + BoundVariableKind::Ty(_) => self.next_ty_var().into(), + BoundVariableKind::Region(_) => self.next_region_var().into(), + BoundVariableKind::Const => self.next_const_var().into(), }; args.push(arg); } @@ -1110,13 +1110,13 @@ impl<'db> InferCtxt<'db> { } impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> { - fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> { self.args[br.var.index()].expect_region() } - fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> { self.args[bt.var.index()].expect_ty() } - fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> { self.args[bv.var.index()].expect_const() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index ae5930d55c72d..7bb39519f50ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -17,7 +17,7 @@ use super::MemberConstraint; use super::unify_key::RegionVidKey; use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; use crate::next_solver::infer::unify_key::RegionVariableValue; -use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderTy, Region, Ty}; +use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderType, Region, Ty}; #[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { @@ -122,7 +122,7 @@ pub struct Verify<'db> { #[derive(Clone, PartialEq, Eq, Hash)] pub enum GenericKind<'db> { Param(ParamTy), - Placeholder(PlaceholderTy), + Placeholder(PlaceholderType<'db>), Alias(AliasTy<'db>), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index 0f7ae99fa41d0..d621dd4906e81 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -531,7 +531,7 @@ impl<'db> TypeRelation> for Generalizer<'_, 'db> { } } - TyKind::Alias(_, data) => match self.structurally_relate_aliases { + TyKind::Alias(data) => match self.structurally_relate_aliases { StructurallyRelateAliases::No => self.generalize_alias_ty(data), StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t), }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs index c523751e03e32..cfa864406c1e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -8,7 +8,7 @@ use crate::next_solver::fold::FnMutDelegate; use crate::next_solver::infer::InferCtxt; use crate::next_solver::{ Binder, BoundConst, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, - PlaceholderRegion, PlaceholderTy, Region, Ty, + PlaceholderRegion, PlaceholderType, Region, Ty, }; impl<'db> InferCtxt<'db> { @@ -35,23 +35,14 @@ impl<'db> InferCtxt<'db> { let next_universe = self.create_next_universe(); let delegate = FnMutDelegate { - regions: &mut |br: BoundRegion| { - Region::new_placeholder( - self.interner, - PlaceholderRegion { universe: next_universe, bound: br }, - ) + regions: &mut |br: BoundRegion<'db>| { + Region::new_placeholder(self.interner, PlaceholderRegion::new(next_universe, br)) }, - types: &mut |bound_ty: BoundTy| { - Ty::new_placeholder( - self.interner, - PlaceholderTy { universe: next_universe, bound: bound_ty }, - ) + types: &mut |bound_ty: BoundTy<'db>| { + Ty::new_placeholder(self.interner, PlaceholderType::new(next_universe, bound_ty)) }, - consts: &mut |bound: BoundConst| { - Const::new_placeholder( - self.interner, - PlaceholderConst { universe: next_universe, bound }, - ) + consts: &mut |bound: BoundConst<'db>| { + Const::new_placeholder(self.interner, PlaceholderConst::new(next_universe, bound)) }, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs index 1abe6a93f4dd5..3522827a9e959 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -165,8 +165,8 @@ impl<'db> TypeRelation> for LatticeOp<'_, 'db> { } ( - TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: a_def_id, .. }), - TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: b_def_id, .. }), + TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id: a_def_id }, .. }), + TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id: b_def_id }, .. }), ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b), _ => super_combine_tys(infcx, self, a, b), 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 5286977549597..f30589d91be97 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 @@ -1,5 +1,3 @@ -pub(crate) use rustc_next_trait_solver::solve::inspect::*; - use rustc_ast_ir::try_visit; use rustc_next_trait_solver::{ canonical::instantiate_canonical_state, @@ -329,10 +327,6 @@ impl<'a, 'db> InspectGoal<'a, 'db> { self.result } - pub(crate) fn source(&self) -> GoalSource { - self.source - } - pub(crate) fn depth(&self) -> usize { self.depth } 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 4f30fc7a89bc3..9a94d1a865929 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 @@ -25,9 +25,9 @@ use rustc_abi::{ReprFlags, ReprOptions}; use rustc_hash::FxHashSet; use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ - AliasTermKind, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder, - FlagComputation, Flags, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner, - TraitRef, TypeFlags, TypeVisitableExt, UniverseIndex, Upcast, Variance, + AliasTermKind, AliasTy, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex, + EarlyBinder, FlagComputation, Flags, GenericArgKind, GenericTypeVisitable, ImplPolarity, + InferTy, Interner, TraitRef, TypeFlags, TypeVisitableExt, Upcast, Variance, elaborate::elaborate, error::TypeError, fast_reject, @@ -43,9 +43,9 @@ use crate::{ method_resolution::TraitImpls, next_solver::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, - GeneralConstIdWrapper, OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, - SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, + Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, + GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, SimplifiedType, + SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -53,14 +53,11 @@ use crate::{ use super::{ Binder, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, ClauseKind, Clauses, Const, ErrorGuaranteed, ExprConst, ExternalConstraints, GenericArg, GenericArgs, ParamConst, ParamEnv, - ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, - TyKind, Tys, Valtree, ValueConst, + ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst, abi::Safety, fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, generics::{Generics, generics}, - region::{ - BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region, - }, + region::{BoundRegion, BoundRegionKind, EarlyParamRegion, Region}, util::sizedness_constraint_for_ty, }; @@ -390,43 +387,15 @@ interned_slice!( BoundVarKinds, StoredBoundVarKinds, bound_var_kinds, - BoundVarKind, - BoundVarKind, + BoundVariableKind<'db>, + BoundVariableKind<'static>, ); -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum BoundVarKind { - Ty(BoundTyKind), - Region(BoundRegionKind), - Const, -} - -impl BoundVarKind { - pub fn expect_region(self) -> BoundRegionKind { - match self { - BoundVarKind::Region(lt) => lt, - _ => panic!("expected a region, but found another kind"), - } - } - - pub fn expect_ty(self) -> BoundTyKind { - match self { - BoundVarKind::Ty(ty) => ty, - _ => panic!("expected a type, but found another kind"), - } - } - - pub fn expect_const(self) { - match self { - BoundVarKind::Const => (), - _ => panic!("expected a const, but found another kind"), - } - } -} +pub type BoundVariableKind<'db> = rustc_type_ir::BoundVariableKind>; interned_slice!( CanonicalVarsStorage, - CanonicalVars, + CanonicalVarKinds, StoredCanonicalVars, canonical_vars, CanonicalVarKind<'db>, @@ -438,22 +407,6 @@ pub struct DepNodeIndex; #[derive(Debug)] pub struct Tracked(T); -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Placeholder { - pub universe: UniverseIndex, - pub bound: T, -} - -impl std::fmt::Debug for Placeholder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - if self.universe == UniverseIndex::ROOT { - write!(f, "!{:?}", self.bound) - } else { - write!(f, "!{}_{:?}", self.universe.index(), self.bound) - } - } -} - #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct AllocId; @@ -773,6 +726,19 @@ impl<'db> inherent::AdtDef> for AdtDef { fn is_manually_drop(self) -> bool { self.inner().flags.is_manually_drop } + + fn is_packed(self) -> bool { + self.repr().packed() + } + + fn field_representing_type_info( + self, + _interner: DbInterner<'db>, + _args: GenericArgs<'db>, + ) -> Option>> { + // FIXME + None + } } impl fmt::Debug for AdtDef { @@ -806,11 +772,16 @@ impl<'db> inherent::Features> for Features { false } - fn associated_const_equality(self) -> bool { + fn feature_bound_holds_in_crate(self, _symbol: Symbol) -> bool { false } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, GenericTypeVisitable)] +pub struct Symbol; - fn feature_bound_holds_in_crate(self, _symbol: ()) -> bool { +impl<'db> inherent::Symbol> for Symbol { + fn is_kw_underscore_lifetime(self) -> bool { false } } @@ -1036,7 +1007,6 @@ impl<'db> Interner for DbInterner<'db> { type Term = Term<'db>; type BoundVarKinds = BoundVarKinds<'db>; - type BoundVarKind = BoundVarKind; type PredefinedOpaques = PredefinedOpaques<'db>; @@ -1047,13 +1017,13 @@ impl<'db> Interner for DbInterner<'db> { PredefinedOpaques::new_from_slice(data) } - type CanonicalVarKinds = CanonicalVars<'db>; + type CanonicalVarKinds = CanonicalVarKinds<'db>; fn mk_canonical_var_kinds( self, kinds: &[rustc_type_ir::CanonicalVarKind], ) -> Self::CanonicalVarKinds { - CanonicalVars::new_from_slice(kinds) + CanonicalVarKinds::new_from_slice(kinds) } type ExternalConstraints = ExternalConstraints<'db>; @@ -1073,9 +1043,7 @@ impl<'db> Interner for DbInterner<'db> { type Tys = Tys<'db>; type FnInputTys = &'db [Ty<'db>]; type ParamTy = ParamTy; - type BoundTy = BoundTy; - type PlaceholderTy = PlaceholderTy; - type Symbol = (); + type Symbol = Symbol; type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = BoundExistentialPredicates<'db>; @@ -1086,18 +1054,16 @@ impl<'db> Interner for DbInterner<'db> { type Abi = FnAbi; type Const = Const<'db>; - type PlaceholderConst = PlaceholderConst; type ParamConst = ParamConst; - type BoundConst = BoundConst; type ValueConst = ValueConst<'db>; type ValTree = Valtree<'db>; + type Consts = Consts<'db>; + type ScalarInt = (); type ExprConst = ExprConst; type Region = Region<'db>; type EarlyParamRegion = EarlyParamRegion; - type LateParamRegion = LateParamRegion; - type BoundRegion = BoundRegion; - type PlaceholderRegion = PlaceholderRegion; + type LateParamRegion = LateParamRegion<'db>; type RegionAssumptions = RegionAssumptions<'db>; @@ -1231,22 +1197,6 @@ impl<'db> Interner for DbInterner<'db> { AdtDef::new(def_id.0, self) } - fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { - match alias.def_id { - SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque, - SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { - ItemContainerId::ImplId(impl_) - if ImplSignature::of(self.db, impl_).target_trait.is_none() => - { - AliasTyKind::Inherent - } - ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => AliasTyKind::Projection, - _ => AliasTyKind::Free, - }, - _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), - } - } - fn alias_term_kind( self, alias: rustc_type_ir::AliasTerm, @@ -1346,7 +1296,7 @@ impl<'db> Interner for DbInterner<'db> { 50 } - fn features(self) -> Self::Features { + fn features(self) -> Features { Features } @@ -1476,7 +1426,9 @@ impl<'db> Interner for DbInterner<'db> { fn is_ty_assoc_of_self(ty: Ty<'_>) -> bool { // FIXME: Is this correct wrt. combined kind of assoc type bounds, i.e. `where Self::Assoc: Trait` // wrt. `Assoc2`, which we should exclude? - if let TyKind::Alias(AliasTyKind::Projection, alias) = ty.kind() { + if let TyKind::Alias(alias @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) = + ty.kind() + { is_ty_assoc_of_self(alias.self_ty()) } else { is_ty_self(ty) @@ -1540,6 +1492,8 @@ impl<'db> Interner for DbInterner<'db> { SolverLangItem::DynMetadata => { return lang_items.DynMetadata.expect("Lang item required but not found.").into(); } + SolverLangItem::FieldBase => unimplemented!(), + SolverLangItem::FieldType => unimplemented!(), }; lang_item.expect("Lang item required but not found.").into() } @@ -1580,6 +1534,7 @@ impl<'db> Interner for DbInterner<'db> { unimplemented!() } SolverTraitLangItem::TrivialClone => lang_items.TrivialClone, + SolverTraitLangItem::Field => unimplemented!(), }; lang_item.expect("Lang item required but not found.").into() } @@ -1607,6 +1562,7 @@ impl<'db> Interner for DbInterner<'db> { AsyncIterator, BikeshedGuaranteedNoDrop, FusedIterator, + Field, AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. } @@ -1652,6 +1608,8 @@ impl<'db> Interner for DbInterner<'db> { ignore = { AsyncFnKindUpvars, DynMetadata, + FieldBase, + FieldType, } Metadata, @@ -1676,6 +1634,8 @@ impl<'db> Interner for DbInterner<'db> { CallRefFuture, CallOnceFuture, AsyncFnOnceOutput, + FieldBase, + FieldType, } DynMetadata, @@ -1694,6 +1654,7 @@ impl<'db> Interner for DbInterner<'db> { AsyncIterator, BikeshedGuaranteedNoDrop, FusedIterator, + Field, AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. } @@ -1871,7 +1832,7 @@ impl<'db> Interner for DbInterner<'db> { // // Impls which apply to an alias after normalization are handled by // `assemble_candidates_after_normalizing_self_ty`. - TyKind::Alias(_, _) | TyKind::Placeholder(..) | TyKind::Error(_) => (), + TyKind::Alias(..) | TyKind::Placeholder(..) | TyKind::Error(_) => (), // FIXME: These should ideally not exist as a self type. It would be nice for // the builtin auto trait impls of coroutines to instead directly recurse @@ -1964,12 +1925,6 @@ impl<'db> Interner for DbInterner<'db> { trait_data.flags.contains(TraitFlags::FUNDAMENTAL) } - fn trait_may_be_implemented_via_object(self, _trait_def_id: Self::TraitId) -> bool { - // FIXME(next-solver): should check the `TraitFlags` for - // the `#[rustc_do_not_implement_via_object]` flag - true - } - fn is_impl_trait_in_trait(self, _def_id: Self::DefId) -> bool { // FIXME(next-solver) false @@ -2068,32 +2023,33 @@ impl<'db> Interner for DbInterner<'db> { ) -> rustc_type_ir::Binder { struct Anonymize<'a, 'db> { interner: DbInterner<'db>, - map: &'a mut FxIndexMap, + map: &'a mut FxIndexMap>, } impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> { - fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> { let entry = self.map.entry(br.var); let index = entry.index(); let var = BoundVar::from_usize(index); - let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon))) - .expect_region(); + let kind = (*entry + .or_insert_with(|| BoundVariableKind::Region(BoundRegionKind::Anon))) + .expect_region(); let br = BoundRegion { var, kind }; Region::new_bound(self.interner, DebruijnIndex::ZERO, br) } - fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> { let entry = self.map.entry(bt.var); let index = entry.index(); let var = BoundVar::from_usize(index); - let kind = - (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); + let kind = (*entry.or_insert_with(|| BoundVariableKind::Ty(BoundTyKind::Anon))) + .expect_ty(); Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) } - fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> { let entry = self.map.entry(bv.var); let index = entry.index(); let var = BoundVar::from_usize(index); - let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); - Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst { var }) + let () = (*entry.or_insert_with(|| BoundVariableKind::Const)).expect_const(); + Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst::new(var)) } } @@ -2273,6 +2229,34 @@ impl<'db> Interner for DbInterner<'db> { UnevaluatedConst { def: GeneralConstIdWrapper(id), args: GenericArgs::empty(self) }, )) } + + fn anon_const_kind(self, _def_id: Self::DefId) -> rustc_type_ir::AnonConstKind { + // FIXME + rustc_type_ir::AnonConstKind::GCE + } + + fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind> { + match def_id { + SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + AliasTyKind::Free { def_id } + } + ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id }, + ItemContainerId::TraitId(_) => AliasTyKind::Projection { def_id }, + }, + SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque { def_id }, + _ => unreachable!(), + } + } + + fn closure_is_const(self, _def_id: Self::ClosureId) -> bool { + // FIXME + false + } + + fn item_name(self, _item_index: Self::DefId) -> Self::Symbol { + Symbol + } } fn is_ty_self(ty: Ty<'_>) -> bool { @@ -2302,14 +2286,14 @@ impl<'db> DbInterner<'db> { self.replace_escaping_bound_vars_uncached( value, FnMutDelegate { - regions: &mut |r: BoundRegion| { + regions: &mut |r: BoundRegion<'db>| { Region::new_bound( self, DebruijnIndex::ZERO, BoundRegion { var: shift_bv(r.var), kind: r.kind }, ) }, - types: &mut |t: BoundTy| { + types: &mut |t: BoundTy<'db>| { Ty::new_bound( self, DebruijnIndex::ZERO, @@ -2317,7 +2301,7 @@ impl<'db> DbInterner<'db> { ) }, consts: &mut |c| { - Const::new_bound(self, DebruijnIndex::ZERO, BoundConst { var: shift_bv(c.var) }) + Const::new_bound(self, DebruijnIndex::ZERO, BoundConst::new(shift_bv(c.var))) }, }, ) @@ -2430,17 +2414,8 @@ TrivialTypeTraversalImpls! { Span, ParamConst, ParamTy, - BoundRegion, - Placeholder, - Placeholder, - Placeholder, - Placeholder, - BoundVarKind, EarlyParamRegion, - LateParamRegion, AdtDef, - BoundTy, - BoundConst, } mod tls_db { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index e0732b3473748..5dd372a367563 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -12,7 +12,7 @@ impl<'db> IrPrint> for DbInterner<'db> { } fn print_debug(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - crate::with_attached_db(|db| match t.def_id { + crate::with_attached_db(|db| match t.kind.def_id() { SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( "AliasTy({:?}[{:?}])", TypeAliasSignature::of(db, id).name.as_str(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index bd678b3e78ff3..5d8f3fe5194aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -229,6 +229,7 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { } // Deeply normalize a value and return it +#[expect(dead_code, reason = "rustc has this")] pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable>>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index dc2441f76e3ae..3f0aebac2dea7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -1,12 +1,12 @@ //! Things related to regions. use hir_def::LifetimeParamId; -use intern::{Interned, InternedRef, Symbol, impl_internable}; +use intern::{Interned, InternedRef, impl_internable}; use macros::GenericTypeVisitable; use rustc_type_ir::{ - BoundVar, BoundVarIndexKind, DebruijnIndex, Flags, GenericTypeVisitable, INNERMOST, RegionVid, - TypeFlags, TypeFoldable, TypeVisitable, - inherent::{IntoKind, PlaceholderLike, SliceLike}, + BoundVarIndexKind, DebruijnIndex, Flags, GenericTypeVisitable, INNERMOST, RegionVid, TypeFlags, + TypeFoldable, TypeVisitable, + inherent::{IntoKind, SliceLike}, relate::Relate, }; @@ -15,10 +15,7 @@ use crate::next_solver::{ interned_slice, }; -use super::{ - SolverDefId, - interner::{BoundVarKind, DbInterner, Placeholder}, -}; +use super::{SolverDefId, interner::DbInterner}; pub type RegionKind<'db> = rustc_type_ir::RegionKind>; @@ -57,7 +54,7 @@ impl<'db> Region<'db> { Region::new(interner, RegionKind::ReEarlyParam(early_bound_region)) } - pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self { + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion<'db>) -> Self { Region::new(interner, RegionKind::RePlaceholder(placeholder)) } @@ -72,7 +69,7 @@ impl<'db> Region<'db> { pub fn new_bound( interner: DbInterner<'db>, index: DebruijnIndex, - bound: BoundRegion, + bound: BoundRegion<'db>, ) -> Region<'db> { Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(index), bound)) } @@ -147,7 +144,7 @@ impl<'db> Region<'db> { } } -pub type PlaceholderRegion = Placeholder; +pub type PlaceholderRegion<'db> = rustc_type_ir::PlaceholderRegion>; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct EarlyParamRegion { @@ -156,7 +153,7 @@ pub struct EarlyParamRegion { pub index: u32, } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericTypeVisitable)] /// The parameter representation of late-bound function parameters, "some region /// at least as big as the scope `fr.scope`". /// @@ -165,50 +162,19 @@ pub struct EarlyParamRegion { /// between others we use the `DefId` of the parameter. For this reason the `bound_region` field /// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling /// different parameters apart. -pub struct LateParamRegion { +pub struct LateParamRegion<'db> { pub scope: SolverDefId, - pub bound_region: BoundRegionKind, + pub bound_region: BoundRegionKind<'db>, } -impl std::fmt::Debug for LateParamRegion { +impl std::fmt::Debug for LateParamRegion<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub enum BoundRegionKind { - /// An anonymous region parameter for a given fn (&T) - Anon, - - /// Named region parameters for functions (a in &'a T) - /// - /// The `DefId` is needed to distinguish free regions in - /// the event of shadowing. - Named(SolverDefId), - - /// Anonymous region for the implicit env pointer parameter - /// to a closure - ClosureEnv, -} - -impl std::fmt::Debug for BoundRegionKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - BoundRegionKind::Anon => write!(f, "BrAnon"), - BoundRegionKind::Named(did) => { - write!(f, "BrNamed({did:?})") - } - BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct BoundRegion { - pub var: BoundVar, - pub kind: BoundRegionKind, -} +pub type BoundRegion<'db> = rustc_type_ir::BoundRegion>; +pub type BoundRegionKind<'db> = rustc_type_ir::BoundRegionKind>; impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { fn index(self) -> u32 { @@ -223,45 +189,6 @@ impl std::fmt::Debug for EarlyParamRegion { } } -impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: BoundVarKind) { - assert_eq!(self.kind, var.expect_region()) - } -} - -impl core::fmt::Debug for BoundRegion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.kind { - BoundRegionKind::Anon => write!(f, "{:?}", self.var), - BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), - BoundRegionKind::Named(def) => { - write!(f, "{:?}.Named({:?})", self.var, def) - } - } - } -} - -impl BoundRegionKind { - pub fn is_named(&self) -> bool { - matches!(self, BoundRegionKind::Named(_)) - } - - pub fn get_name(&self) -> Option { - None - } - - pub fn get_id(&self) -> Option { - match self { - BoundRegionKind::Named(id) => Some(*id), - _ => None, - } - } -} - impl std::fmt::Debug for Region<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.kind().fmt(f) @@ -323,15 +250,15 @@ impl<'db> Flags for Region<'db> { impl<'db> rustc_type_ir::inherent::Region> for Region<'db> { fn new_bound( interner: DbInterner<'db>, - debruijn: rustc_type_ir::DebruijnIndex, - var: BoundRegion, + debruijn: DebruijnIndex, + var: BoundRegion<'db>, ) -> Self { Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), var)) } fn new_anon_bound( interner: DbInterner<'db>, - debruijn: rustc_type_ir::DebruijnIndex, + debruijn: DebruijnIndex, var: rustc_type_ir::BoundVar, ) -> Self { Region::new( @@ -357,38 +284,11 @@ impl<'db> rustc_type_ir::inherent::Region> for Region<'db> { interner.default_types().regions.statik } - fn new_placeholder( - interner: DbInterner<'db>, - var: as rustc_type_ir::Interner>::PlaceholderRegion, - ) -> Self { + fn new_placeholder(interner: DbInterner<'db>, var: PlaceholderRegion<'db>) -> Self { Region::new(interner, RegionKind::RePlaceholder(var)) } } -impl<'db> PlaceholderLike> for PlaceholderRegion { - type Bound = BoundRegion; - - fn universe(self) -> rustc_type_ir::UniverseIndex { - self.universe - } - - fn var(self) -> rustc_type_ir::BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { - Placeholder { universe: ui, bound: self.bound } - } - - fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self { - Placeholder { universe: ui, bound } - } - - fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { - Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } - } -} - impl<'db, V: super::WorldExposer> GenericTypeVisitable for Region<'db> { fn generic_visit_with(&self, visitor: &mut V) { if visitor.on_interned(self.interned).is_continue() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 848bb110af2d0..a9e7de16132a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -155,10 +155,11 @@ impl<'db> SolverDelegate for SolverContext<'db> { fold_tys(interner, clause, |ty| match ty.kind() { // Replace all other mentions of the same opaque type with the hidden type, // as the bounds must hold on the hidden type after all. - TyKind::Alias( - AliasTyKind::Opaque, - AliasTy { def_id: def_id2, args: args2, .. }, - ) if def_id == def_id2 && args == args2 => hidden_ty, + TyKind::Alias(AliasTy { + kind: AliasTyKind::Opaque { def_id: def_id2 }, + args: args2, + .. + }) if def_id == def_id2 && args == args2 => hidden_ty, _ => ty, }) }; 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 dccb8c7936319..3811bddb38043 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 @@ -17,8 +17,8 @@ use rustc_type_ir::{ IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, inherent::{ - AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _, - IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _, + AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike, + Safety as _, SliceLike, Ty as _, }, relate::Relate, solve::SizedTraitKind, @@ -40,7 +40,7 @@ use crate::{ }; use super::{ - BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId, + DbInterner, GenericArgs, SolverDefId, util::{FloatExt, IntegerExt}, }; @@ -97,7 +97,7 @@ impl<'db> Ty<'db> { Ty::new(interner, TyKind::Param(ParamTy { id, index })) } - pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderType<'db>) -> Self { Ty::new(interner, TyKind::Placeholder(placeholder)) } @@ -177,7 +177,10 @@ impl<'db> Ty<'db> { def_id: SolverDefId, args: GenericArgs<'db>, ) -> Self { - Ty::new_alias(interner, AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args)) + Ty::new_alias( + interner, + AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id }, args), + ) } /// Returns the `Size` for primitive types (bool, uint, int, char, float). @@ -681,12 +684,11 @@ impl<'db> Ty<'db> { let interner = DbInterner::new_no_crate(db); match self.kind() { - TyKind::Alias(AliasTyKind::Opaque, opaque_ty) => Some( - opaque_ty - .def_id + TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => Some( + def_id .expect_opaque_ty() .predicates(db) - .iter_instantiated_copied(interner, opaque_ty.args.as_slice()) + .iter_instantiated_copied(interner, args.as_slice()) .collect(), ), TyKind::Param(param) => { @@ -743,9 +745,7 @@ impl<'db> Ty<'db> { true } (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, - (TyKind::Alias(_, alias, ..), TyKind::Alias(_, alias2)) => { - alias.def_id == alias2.def_id - } + (TyKind::Alias(alias), TyKind::Alias(alias2)) => alias.kind == alias2.kind, (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2, (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, (TyKind::Ref(.., mutability), TyKind::Ref(.., mutability2)) @@ -858,7 +858,7 @@ impl<'db> TypeSuperVisitable> for Ty<'db> { TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor), TyKind::Closure(_did, ref args) => args.visit_with(visitor), TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor), - TyKind::Alias(_, ref data) => data.visit_with(visitor), + TyKind::Alias(ref data) => data.visit_with(visitor), TyKind::Pat(ty, pat) => { try_visit!(ty.visit_with(visitor)); @@ -925,7 +925,7 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { TyKind::CoroutineClosure(did, args) => { TyKind::CoroutineClosure(did, args.try_fold_with(folder)?) } - TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?), + TyKind::Alias(data) => TyKind::Alias(data.try_fold_with(folder)?), TyKind::Pat(ty, pat) => { TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?) } @@ -974,7 +974,7 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { TyKind::CoroutineClosure(did, args) => { TyKind::CoroutineClosure(did, args.fold_with(folder)) } - TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)), + TyKind::Alias(data) => TyKind::Alias(data.fold_with(folder)), TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)), TyKind::Bool @@ -1045,11 +1045,11 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { Ty::new(interner, TyKind::Param(param)) } - fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self { + fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderType<'db>) -> Self { Ty::new(interner, TyKind::Placeholder(param)) } - fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundTy) -> Self { + fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundTy<'db>) -> Self { Ty::new(interner, TyKind::Bound(BoundVarIndexKind::Bound(debruijn), var)) } @@ -1070,8 +1070,8 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { ) } - fn new_alias(interner: DbInterner<'db>, kind: AliasTyKind, alias_ty: AliasTy<'db>) -> Self { - Ty::new(interner, TyKind::Alias(kind, alias_ty)) + fn new_alias(interner: DbInterner<'db>, alias_ty: AliasTy<'db>) -> Self { + Ty::new(interner, TyKind::Alias(alias_ty)) } fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { @@ -1352,7 +1352,7 @@ impl<'db> rustc_type_ir::inherent::Tys> for Tys<'db> { } } -pub type PlaceholderTy = Placeholder; +pub type PlaceholderType<'db> = rustc_type_ir::PlaceholderType>; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ParamTy { @@ -1375,27 +1375,8 @@ impl std::fmt::Debug for ParamTy { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct BoundTy { - pub var: BoundVar, - // FIXME: This is for diagnostics in rustc, do we really need it? - pub kind: BoundTyKind, -} - -impl std::fmt::Debug for BoundTy { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.kind { - BoundTyKind::Anon => write!(f, "{:?}", self.var), - BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum BoundTyKind { - Anon, - Param(SolverDefId), -} +pub type BoundTy<'db> = rustc_type_ir::BoundTy>; +pub type BoundTyKind<'db> = rustc_type_ir::BoundTyKind>; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct ErrorGuaranteed; @@ -1431,40 +1412,6 @@ impl ParamLike for ParamTy { } } -impl<'db> BoundVarLike> for BoundTy { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: BoundVarKind) { - assert_eq!(self.kind, var.expect_ty()) - } -} - -impl<'db> PlaceholderLike> for PlaceholderTy { - type Bound = BoundTy; - - fn universe(self) -> rustc_type_ir::UniverseIndex { - self.universe - } - - fn var(self) -> BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { - Placeholder { universe: ui, bound: self.bound } - } - - fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self { - Placeholder { universe: ui, bound } - } - - fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { - Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } - } -} - impl<'db> DbInterner<'db> { /// Given a closure signature, returns an equivalent fn signature. Detuples /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then 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 c175062bda37c..858233cb2c900 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 @@ -15,7 +15,7 @@ use rustc_type_ir::{ use crate::{ next_solver::{ - BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + BoundConst, FxIndexMap, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderType, PolyTraitRef, infer::{ InferCtxt, @@ -446,9 +446,10 @@ pub fn apply_args_to_binder<'db, T: TypeFoldable>>( args: GenericArgs<'db>, interner: DbInterner<'db>, ) -> T { - let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); - let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); - let consts = &mut |const_: BoundConst| args.as_slice()[const_.var.index()].expect_const(); + let types = &mut |ty: BoundTy<'db>| args.as_slice()[ty.var.index()].expect_ty(); + let regions = + &mut |region: BoundRegion<'db>| args.as_slice()[region.var.index()].expect_region(); + let consts = &mut |const_: BoundConst<'db>| args.as_slice()[const_.var.index()].expect_const(); let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); b.skip_binder().fold_with(&mut instantiate) } @@ -497,9 +498,9 @@ impl<'db> TypeVisitor> for ContainsTypeErrors { /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'a, 'db> { infcx: &'a InferCtxt<'db>, - mapped_regions: FxIndexMap, - mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_regions: FxIndexMap, BoundRegion<'db>>, + mapped_types: FxIndexMap, BoundTy<'db>>, + mapped_consts: FxIndexMap, BoundConst<'db>>, universe_indices: &'a [Option], current_index: DebruijnIndex, } @@ -507,9 +508,9 @@ pub struct PlaceholderReplacer<'a, 'db> { impl<'a, 'db> PlaceholderReplacer<'a, 'db> { pub fn replace_placeholders>>( infcx: &'a InferCtxt<'db>, - mapped_regions: FxIndexMap, - mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_regions: FxIndexMap, BoundRegion<'db>>, + mapped_types: FxIndexMap, BoundTy<'db>>, + mapped_consts: FxIndexMap, BoundConst<'db>>, universe_indices: &'a [Option], value: T, ) -> T { 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 1945b04bb3cc3..a88457e3c745b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -216,7 +216,7 @@ impl<'db> Context<'db> { TyKind::Adt(def, args) => { self.add_constraints_from_args(def.def_id().0.into(), args, variance); } - TyKind::Alias(_, alias) => { + TyKind::Alias(alias) => { // FIXME: Probably not correct wrt. opaques. self.add_constraints_from_invariant_args(alias.args); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 53240259e0922..e10257214879a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5736,8 +5736,11 @@ impl<'db> Type<'db> { // FIXME: We don't handle GATs yet. let projection = Ty::new_alias( interner, - AliasTyKind::Projection, - AliasTy::new_from_args(interner, alias.id.into(), args), + AliasTy::new_from_args( + interner, + AliasTyKind::Projection { def_id: alias.id.into() }, + args, + ), ); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); @@ -6353,8 +6356,12 @@ impl<'db> Type<'db> { } pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option { - let TyKind::Alias(AliasTyKind::Projection, alias) = self.ty.kind() else { return None }; - match alias.def_id.expect_type_alias().loc(db).container { + let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) = + self.ty.kind() + else { + return None; + }; + match def_id.expect_type_alias().loc(db).container { ItemContainerId::TraitId(id) => Some(Trait { id }), _ => None, } @@ -6670,8 +6677,8 @@ impl Layout { let offset = stride.bytes() * tail; self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size) }), - layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - let tail = memory_index.last_index()?; + layout::FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => { + let tail = in_memory_order[in_memory_order.len().checked_sub(1)? as u32]; let tail_field_size = field_size(tail.0.into_raw().into_u32() as usize)?; let offset = offsets.get(tail)?.bytes(); self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size) @@ -6691,10 +6698,11 @@ impl Layout { let size = field_size(0)?; stride.bytes().checked_sub(size) } - layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - let mut reverse_index = vec![None; memory_index.len()]; - for (src, (mem, offset)) in memory_index.iter().zip(offsets.iter()).enumerate() { - reverse_index[*mem as usize] = Some((src, offset.bytes())); + layout::FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => { + let mut reverse_index = vec![None; in_memory_order.len()]; + for (mem, src) in in_memory_order.iter().enumerate() { + reverse_index[mem] = + Some((src.0.into_raw().into_u32() as usize, offsets[*src].bytes())); } if reverse_index.iter().any(|it| it.is_none()) { stdx::never!(); 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 1a34fa913425e..6c43f80ce8789 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -37,7 +37,7 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, + AliasTy, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, @@ -1293,10 +1293,14 @@ impl<'db> SourceAnalyzer<'db> { PathResolution::Def(ModuleDef::Adt(adt_id.into())), ) } - TyKind::Alias(AliasTyKind::Projection, alias) => { - let assoc_id = alias.def_id.expect_type_alias(); + TyKind::Alias(AliasTy { + kind: AliasTyKind::Projection { def_id }, + args, + .. + }) => { + let assoc_id = def_id.expect_type_alias(); ( - GenericSubstitution::new(assoc_id.into(), alias.args, env), + GenericSubstitution::new(assoc_id.into(), args, env), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), ) } From 9a32199f92fd47e8e9f71dcf2257a32d95725caf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 15 Apr 2026 05:02:14 +0300 Subject: [PATCH 42/98] Separate between normal evaluated consts and type-system consts Type system consts (consts that participate in the type system, e.g. const generic params) have special properties: they must have strict equality independent of the user. For example, unions are not possible there. Therefore they are stored as a `ValTree`, a very simple representation that is good, mostly, for comparing things. General consts can be anything (almost) - arbitrary bytes, even uninit bytes. They are stored like all consts previously were (in the type-system's `Const`), with bytes memory, in a new type called `Allocation` (named borrowed from rustc, although it isn't exactly accurate because in r-a it can represent multiple allocations). The trigger for this change was a new requirement from rustc_type_ir, that type-system `Const` will be able to represent as a `ValTree`. --- src/tools/rust-analyzer/Cargo.toml | 7 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 10 +- .../crates/hir-ty/src/consteval.rs | 251 +++--- .../crates/hir-ty/src/consteval/tests.rs | 43 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 8 +- .../crates/hir-ty/src/display.rs | 165 +++- .../rust-analyzer/crates/hir-ty/src/mir.rs | 23 +- .../crates/hir-ty/src/mir/borrowck.rs | 4 +- .../crates/hir-ty/src/mir/eval.rs | 193 ++++- .../crates/hir-ty/src/mir/lower.rs | 37 +- .../crates/hir-ty/src/mir/monomorphization.rs | 17 +- .../crates/hir-ty/src/mir/pretty.rs | 3 + .../crates/hir-ty/src/next_solver.rs | 16 +- .../hir-ty/src/next_solver/allocation.rs | 73 ++ .../crates/hir-ty/src/next_solver/consts.rs | 164 +--- .../hir-ty/src/next_solver/consts/valtree.rs | 712 ++++++++++++++++++ .../crates/hir-ty/src/next_solver/interner.rs | 25 +- .../crates/hir-ty/src/next_solver/solver.rs | 36 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 19 +- 19 files changed, 1381 insertions(+), 425 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/allocation.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 4372afa0f5066..0ae65922d0072 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -127,7 +127,7 @@ object = { version = "0.36.7", default-features = false, features = [ "macho", "pe", ] } -postcard = {version = "1.1.3", features = ["alloc"]} +postcard = { version = "1.1.3", features = ["alloc"] } process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } @@ -186,7 +186,10 @@ hashbrown = { version = "0.14.*", features = [ elided_lifetimes_in_paths = "warn" explicit_outlives_requirements = "warn" unsafe_op_in_unsafe_fn = "warn" -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)', "cfg(no_salsa_async_drops)"] } +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(bootstrap)', + "cfg(no_salsa_async_drops)", +] } unused_extern_crates = "warn" unused_lifetimes = "warn" unreachable_pub = "warn" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 4dd113d419476..9e51d0eac98a1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -21,7 +21,7 @@ use std::fmt; use hir_expand::{MacroDefId, name::Name}; use intern::Symbol; use la_arena::Idx; -use rustc_apfloat::ieee::{Half as f16, Quad as f128}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use syntax::ast; use type_ref::TypeRefId; @@ -94,19 +94,19 @@ impl FloatTypeWrapper { Self(sym) } - pub fn to_f128(&self) -> f128 { + pub fn to_f128(&self) -> Quad { self.0.as_str().parse().unwrap_or_default() } - pub fn to_f64(&self) -> f64 { + pub fn to_f64(&self) -> Double { self.0.as_str().parse().unwrap_or_default() } - pub fn to_f32(&self) -> f32 { + pub fn to_f32(&self) -> Single { self.0.as_str().parse().unwrap_or_default() } - pub fn to_f16(&self) -> f16 { + pub fn to_f16(&self) -> Half { self.0.as_str().parse().unwrap_or_default() } } 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 928396c63aaf7..80e7e05d76fed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -8,28 +8,30 @@ use hir_def::{ ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule, StaticId, attrs::AttrFlags, - builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, expr_store::{Body, ExpressionStore}, hir::{Expr, ExprId, Literal}, }; use hir_expand::Lookup; +use rustc_abi::Size; +use rustc_apfloat::Float; use rustc_type_ir::inherent::IntoKind; +use stdx::never; use triomphe::Arc; use crate::{ - LifetimeElisionKind, MemoryMap, ParamEnvAndCrate, TyLoweringContext, + LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext, db::HirDatabase, display::DisplayTarget, infer::InferenceContext, - mir::{MirEvalError, MirLowerError}, + mir::{MirEvalError, MirLowerError, pad16}, next_solver::{ - Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - StoredConst, StoredGenericArgs, Ty, ValueConst, + Allocation, Const, ConstKind, Consts, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ScalarInt, StoredAllocation, StoredGenericArgs, Ty, TyKind, ValTreeKind, default_types, }, traits::StoredParamEnvAndCrate, }; -use super::mir::{interpret_mir, lower_body_to_mir, pad16}; +use super::mir::{interpret_mir, lower_body_to_mir}; pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> { Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) @@ -84,140 +86,87 @@ pub fn intern_const_ref<'a>( db: &'a dyn HirDatabase, value: &Literal, ty: Ty<'a>, - _krate: Crate, + krate: Crate, ) -> Const<'a> { let interner = DbInterner::new_no_crate(db); - let kind = match value { - &Literal::Uint(i, builtin_ty) - if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) => - { - let memory = match ty.as_builtin() { - Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint { - BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>, - BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()), - BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()), - BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()), - BuiltinUint::U128 => Box::new((i).to_le_bytes()), - BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()), - }, - _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), - }; - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory, memory_map: MemoryMap::default() }, - )) + let Ok(data_layout) = db.target_data_layout(krate) else { + return Const::error(interner); + }; + let valtree = match (ty.kind(), value) { + (TyKind::Uint(uint), Literal::Uint(value, _)) => { + let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); + let scalar = ScalarInt::try_from_uint(*value, size).unwrap(); + ValTreeKind::Leaf(scalar) } - &Literal::Int(i, None) - if ty - .as_builtin() - .is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) => - { - let memory = match ty.as_builtin() { - Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint { - BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>, - BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()), - BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()), - BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()), - BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()), - BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()), - }, - _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), - }; - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory, memory_map: MemoryMap::default() }, - )) + (TyKind::Uint(uint), Literal::Int(value, _)) => { + // `Literal::Int` is the default, so we also need to account for the type being uint. + let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); + let scalar = ScalarInt::try_from_uint(*value as u128, size).unwrap(); + ValTreeKind::Leaf(scalar) } - &Literal::Int(i, builtin_ty) - if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) => - { - let memory = match ty.as_builtin() { - Some(BuiltinType::Int(builtin_int)) => match builtin_int { - BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>, - BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()), - BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()), - BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()), - BuiltinInt::I128 => Box::new((i).to_le_bytes()), - BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()), - }, - _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), - }; - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory, memory_map: MemoryMap::default() }, - )) + (TyKind::Int(int), Literal::Int(value, _)) => { + let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); + let scalar = ScalarInt::try_from_int(*value, size).unwrap(); + ValTreeKind::Leaf(scalar) } - Literal::Float(float_type_wrapper, builtin_float) - if builtin_float.is_none() - || ty.as_builtin() == builtin_float.map(BuiltinType::Float) => - { - let memory = match ty.as_builtin().unwrap() { - BuiltinType::Float(builtin_float) => match builtin_float { - // FIXME: - hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>, - hir_def::builtin_type::BuiltinFloat::F32 => { - Box::new(float_type_wrapper.to_f32().to_le_bytes()) - } - hir_def::builtin_type::BuiltinFloat::F64 => { - Box::new(float_type_wrapper.to_f64().to_le_bytes()) - } - // FIXME: - hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]), - }, - _ => unreachable!(), + (TyKind::Bool, Literal::Bool(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)), + (TyKind::Char, Literal::Char(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)), + (TyKind::Float(float), Literal::Float(value, _)) => { + let size = Size::from_bits(float.bit_width()); + let value = match float { + rustc_ast_ir::FloatTy::F16 => value.to_f16().to_bits(), + rustc_ast_ir::FloatTy::F32 => value.to_f32().to_bits(), + rustc_ast_ir::FloatTy::F64 => value.to_f64().to_bits(), + rustc_ast_ir::FloatTy::F128 => value.to_f128().to_bits(), }; - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory, memory_map: MemoryMap::default() }, + let scalar = ScalarInt::try_from_uint(value, size).unwrap(); + ValTreeKind::Leaf(scalar) + } + (_, Literal::String(value)) => { + let u8_values = &interner.default_types().consts.u8_values; + ValTreeKind::Branch(Consts::new_from_iter( + interner, + value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]), )) } - Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() }, - )), - Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { - memory: (*c as u32).to_le_bytes().into(), - memory_map: MemoryMap::default(), - }, - )), - Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { - memory: symbol.as_str().as_bytes().into(), - memory_map: MemoryMap::default(), - }, - )), - Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => { - rustc_type_ir::ConstKind::Value(ValueConst::new( - ty, - ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() }, + (_, Literal::ByteString(value)) => { + let u8_values = &interner.default_types().consts.u8_values; + ValTreeKind::Branch(Consts::new_from_iter( + interner, + value.iter().map(|&byte| u8_values[usize::from(byte)]), )) } - // FIXME - Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), - _ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + (_, Literal::CString(_)) => { + // FIXME: + return Const::error(interner); + } + _ => { + never!("mismatching type for literal"); + return Const::error(interner); + } }; - Const::new(interner, kind) + Const::new_valtree(interner, ty, valtree) } /// Interns a possibly-unknown target usize pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { - intern_const_ref( - db, - &match value { - Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)), - None => { - return Const::new( - DbInterner::new_no_crate(db), - rustc_type_ir::ConstKind::Error(ErrorGuaranteed), - ); - } - }, - Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize), - krate, - ) + let interner = DbInterner::new_no_crate(db); + let value = match value { + Some(value) => value, + None => { + return Const::error(interner); + } + }; + let Ok(data_layout) = db.target_data_layout(krate) else { + return Const::error(interner); + }; + let usize_ty = interner.default_types().types.usize; + let scalar = ScalarInt::try_from_uint(value, data_layout.pointer_size()).unwrap(); + Const::new_valtree(interner, usize_ty, ValTreeKind::Leaf(scalar)) +} + +pub fn allocation_as_usize(ec: Allocation<'_>) -> u128 { + u128::from_le_bytes(pad16(&ec.memory, false)) } pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { @@ -230,20 +179,30 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { let subst = unevaluated_const.args; let ec = db.const_eval(id, subst, None).ok()?; - try_const_usize(db, ec) + Some(allocation_as_usize(ec)) } GeneralConstId::StaticId(id) => { let ec = db.const_eval_static(id).ok()?; - try_const_usize(db, ec) + Some(allocation_as_usize(ec)) } GeneralConstId::AnonConstId(_) => None, }, - ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))), + ConstKind::Value(val) => { + if val.ty == default_types(db).types.usize { + Some(val.value.inner().to_leaf().to_uint_unchecked()) + } else { + None + } + } ConstKind::Error(_) => None, ConstKind::Expr(_) => None, } } +pub fn allocation_as_isize(ec: Allocation<'_>) -> i128 { + i128::from_le_bytes(pad16(&ec.memory, true)) +} + pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { match (*c).kind() { ConstKind::Param(_) => None, @@ -254,15 +213,21 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< GeneralConstId::ConstId(id) => { let subst = unevaluated_const.args; let ec = db.const_eval(id, subst, None).ok()?; - try_const_isize(db, &ec) + Some(allocation_as_isize(ec)) } GeneralConstId::StaticId(id) => { let ec = db.const_eval_static(id).ok()?; - try_const_isize(db, &ec) + Some(allocation_as_isize(ec)) } GeneralConstId::AnonConstId(_) => None, }, - ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))), + ConstKind::Value(val) => { + if val.ty == default_types(db).types.isize { + Some(val.value.inner().to_leaf().to_int_unchecked()) + } else { + None + } + } ConstKind::Error(_) => None, ConstKind::Expr(_) => None, } @@ -299,11 +264,7 @@ pub(crate) fn const_eval_discriminant_variant( .store(), )?; let c = interpret_mir(db, mir_body, false, None)?.0?; - let c = if is_signed { - try_const_isize(db, &c).unwrap() - } else { - try_const_usize(db, c).unwrap() as i128 - }; + let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 }; Ok(c) } @@ -341,7 +302,11 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr) && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) { - return result; + return Const::new_from_allocation( + ctx.interner(), + &result, + ParamEnvAndCrate { param_env: ctx.table.param_env, krate: ctx.resolver.krate() }, + ); } Const::error(ctx.interner()) } @@ -359,7 +324,7 @@ pub(crate) fn const_eval<'db>( def: ConstId, subst: GenericArgs<'db>, trait_env: Option>, -) -> Result, ConstEvalError> { +) -> Result, ConstEvalError> { return match const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) { Ok(konst) => Ok(konst.as_ref()), Err(err) => Err(err.clone()), @@ -371,7 +336,7 @@ pub(crate) fn const_eval<'db>( def: ConstId, subst: StoredGenericArgs, trait_env: Option, - ) -> Result { + ) -> Result { let body = db.monomorphized_mir_body( def.into(), subst, @@ -392,7 +357,7 @@ pub(crate) fn const_eval<'db>( _: ConstId, _: StoredGenericArgs, _: Option, - ) -> Result { + ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } } @@ -400,7 +365,7 @@ pub(crate) fn const_eval<'db>( pub(crate) fn const_eval_static<'db>( db: &'db dyn HirDatabase, def: StaticId, -) -> Result, ConstEvalError> { +) -> Result, ConstEvalError> { return match const_eval_static_query(db, def) { Ok(konst) => Ok(konst.as_ref()), Err(err) => Err(err.clone()), @@ -410,7 +375,7 @@ pub(crate) fn const_eval_static<'db>( pub(crate) fn const_eval_static_query<'db>( db: &'db dyn HirDatabase, def: StaticId, - ) -> Result { + ) -> Result { let interner = DbInterner::new_no_crate(db); let body = db.monomorphized_mir_body( def.into(), @@ -430,7 +395,7 @@ pub(crate) fn const_eval_static<'db>( _: &dyn HirDatabase, _: salsa::Id, _: StaticId, - ) -> Result { + ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index aee27dcfdef9e..5d5b5a8e648d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -5,17 +5,16 @@ use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, }; -use rustc_type_ir::inherent::IntoKind; use test_fixture::WithFixture; use test_utils::skip_slow_tests; use crate::{ MemoryMap, - consteval::try_const_usize, + consteval::allocation_as_usize, db::HirDatabase, display::DisplayTarget, mir::pad16, - next_solver::{Const, ConstBytes, ConstKind, DbInterner, GenericArgs}, + next_solver::{Allocation, DbInterner, GenericArgs}, setup_tracing, test_db::TestDB, }; @@ -45,7 +44,11 @@ fn check_fail( crate::attach_db(&db, || match eval_goal(&db, file_id) { Ok(_) => panic!("Expected fail, but it succeeded"), Err(e) => { - assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db)) + assert!( + error(simplify(e.clone())), + "Actual error was: {}\n{e:?}", + pretty_print_err(e.clone(), &db) + ) } }) } @@ -94,13 +97,7 @@ fn check_answer( panic!("Error in evaluating goal: {err}"); } }; - match r.kind() { - ConstKind::Value(value) => { - let ConstBytes { memory, memory_map } = value.value.inner(); - check(memory, memory_map); - } - _ => panic!("Expected number but found {r:?}"), - } + check(&r.memory, &r.memory_map); }); } @@ -121,7 +118,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String { err } -fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result, ConstEvalError> { +fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result, ConstEvalError> { let _tracing = setup_tracing(); let interner = DbInterner::new_no_crate(db); let module_id = db.module_for_file(file_id.file_id(db)); @@ -2524,7 +2521,7 @@ fn enums() { ); crate::attach_db(&db, || { let r = eval_goal(&db, file_id).unwrap(); - assert_eq!(try_const_usize(&db, r), Some(1)); + assert_eq!(allocation_as_usize(r), 1); }) } @@ -2537,7 +2534,15 @@ fn const_loop() { const F2: i32 = 2 * F1; const GOAL: i32 = F3; "#, - |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop), + |e| { + if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e + && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner + { + true + } else { + false + } + }, ); } @@ -2940,6 +2945,14 @@ fn recursive_adt() { TAG_TREE }; "#, - |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::Loop)), + |e| { + if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e + && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner + { + true + } else { + false + } + }, ); } 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 54c4b8d3acd4f..3bf2d9a6a60b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -21,8 +21,8 @@ use crate::{ lower::{Diagnostics, GenericDefaults}, mir::{BorrowckResult, MirBody, MirLowerError}, next_solver::{ - Const, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, StoredGenericArgs, - StoredTy, TraitRef, Ty, VariancesOf, + Allocation, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, + StoredGenericArgs, StoredTy, TraitRef, Ty, VariancesOf, }, traits::{ParamEnvAndCrate, StoredParamEnvAndCrate}, }; @@ -68,11 +68,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: ConstId, subst: GenericArgs<'db>, trait_env: Option>, - ) -> Result, ConstEvalError>; + ) -> Result, ConstEvalError>; #[salsa::invoke(crate::consteval::const_eval_static)] #[salsa::transparent] - fn const_eval_static<'db>(&'db self, def: StaticId) -> Result, ConstEvalError>; + fn const_eval_static<'db>(&'db self, def: StaticId) -> Result, ConstEvalError>; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 1e4c2985cf5c6..1e9ac5dce63fb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -54,9 +54,10 @@ use crate::{ lower::GenericPredicates, mir::pad16, next_solver::{ - AliasTy, Clause, ClauseKind, Const, ConstKind, DbInterner, ExistentialPredicate, FnSig, - GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, - StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, TypingMode, + AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner, + ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, + Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, + TypingMode, ValTree, abi::Safety, infer::{DbInternerInferExt, traits::ObligationCause}, }, @@ -691,6 +692,12 @@ impl<'db> HirDisplay<'db> for GenericArg<'db> { } } +impl<'db> HirDisplay<'db> for Allocation<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { + render_const_scalar(f, &self.memory, &self.memory_map, self.ty) + } +} + impl<'db> HirDisplay<'db> for Const<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { match self.kind() { @@ -710,12 +717,7 @@ impl<'db> HirDisplay<'db> for Const<'db> { f.end_location_link(); Ok(()) } - ConstKind::Value(const_bytes) => render_const_scalar( - f, - &const_bytes.value.inner().memory, - &const_bytes.value.inner().memory_map, - const_bytes.ty, - ), + ConstKind::Value(value) => render_const_scalar_from_valtree(f, value.ty, value.value), ConstKind::Unevaluated(unev) => { let c = unev.def.0; write!(f, "{}", c.name(f.db))?; @@ -1014,6 +1016,151 @@ fn render_const_scalar_inner<'db>( } } +fn render_const_scalar_from_valtree<'db>( + f: &mut HirFormatter<'_, 'db>, + ty: Ty<'db>, + valtree: ValTree<'db>, +) -> Result { + let param_env = ParamEnv::empty(); + let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); + let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty); + render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) +} + +fn render_const_scalar_from_valtree_inner<'db>( + f: &mut HirFormatter<'_, 'db>, + ty: Ty<'db>, + valtree: ValTree<'db>, + _param_env: ParamEnv<'db>, +) -> Result { + use TyKind; + match ty.kind() { + TyKind::Bool => write!(f, "{}", valtree.inner().to_leaf().try_to_bool().unwrap()), + TyKind::Char => { + let it = valtree.inner().to_leaf().to_u32(); + let Ok(c) = char::try_from(it) else { + return f.write_str(""); + }; + write!(f, "{c:?}") + } + TyKind::Int(_) => { + let it = valtree.inner().to_leaf().to_int_unchecked(); + write!(f, "{it}") + } + TyKind::Uint(_) => { + let it = valtree.inner().to_leaf().to_uint_unchecked(); + write!(f, "{it}") + } + TyKind::Float(fl) => match fl { + FloatTy::F16 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f16::from_bits(valtree.inner().to_leaf().to_u16() as u128); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") + } + } + FloatTy::F32 => { + let it = f32::from_bits(valtree.inner().to_leaf().to_u32()); + write!(f, "{it:?}") + } + FloatTy::F64 => { + let it = f64::from_bits(valtree.inner().to_leaf().to_u64()); + write!(f, "{it:?}") + } + FloatTy::F128 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f128::from_bits(valtree.inner().to_leaf().to_u128()); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") + } + } + }, + TyKind::Ref(_, inner_ty, _) => { + render_const_scalar_from_valtree_inner(f, inner_ty, valtree, _param_env) + } + TyKind::Str => { + let bytes = valtree + .inner() + .to_branch() + .iter() + .map(|konst| match konst.kind() { + ConstKind::Value(value) => Some(value.value.inner().to_leaf().to_u8()), + _ => None, + }) + .collect::>>(); + let Some(bytes) = bytes else { return f.write_str("") }; + let s = std::str::from_utf8(&bytes).unwrap_or(""); + write!(f, "{s:?}") + } + TyKind::Slice(inner_ty) | TyKind::Array(inner_ty, _) => { + let mut first = true; + write!(f, "[")?; + for item in valtree.inner().to_branch() { + if !first { + write!(f, ", ")?; + } else { + first = false; + } + let ConstKind::Value(value) = item.kind() else { + return f.write_str(""); + }; + render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?; + } + write!(f, "]") + } + TyKind::Tuple(tys) => { + let mut first = true; + write!(f, "(")?; + for (inner_ty, item) in std::iter::zip(tys, valtree.inner().to_branch()) { + if !first { + write!(f, ", ")?; + } else { + first = false; + } + let ConstKind::Value(value) = item.kind() else { + return f.write_str(""); + }; + render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?; + } + write!(f, ")") + } + TyKind::Adt(..) => { + // FIXME: ADTs, requires `adt_const_params`. + f.write_str("") + } + TyKind::FnDef(..) => ty.hir_fmt(f), + TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => { + let it = valtree.inner().to_leaf().to_uint_unchecked(); + write!(f, "{it:#X} as ")?; + ty.hir_fmt(f) + } + TyKind::Never => f.write_str("!"), + TyKind::Closure(_, _) => f.write_str(""), + TyKind::Coroutine(_, _) => f.write_str(""), + TyKind::CoroutineWitness(_, _) => f.write_str(""), + TyKind::CoroutineClosure(_, _) => f.write_str(""), + TyKind::UnsafeBinder(_) => f.write_str(""), + // The below arms are unreachable, since const eval will bail out before here. + TyKind::Foreign(_) => f.write_str(""), + TyKind::Pat(_, _) => f.write_str(""), + TyKind::Error(..) + | TyKind::Placeholder(_) + | TyKind::Alias(..) + | TyKind::Param(_) + | TyKind::Bound(_, _) + | TyKind::Infer(_) => f.write_str(""), + TyKind::Dynamic(_, _) => f.write_str(""), + } +} + fn render_variant_after_name<'db>( data: &VariantFields, f: &mut HirFormatter<'_, 'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index a8865cd54e6a5..837a9847af1bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -23,8 +23,8 @@ use crate::{ display::{DisplayTarget, HirDisplay}, infer::PointerCast, next_solver::{ - Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredConst, StoredGenericArgs, - StoredTy, Ty, TyKind, + Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, + StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, infer::{InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -107,7 +107,13 @@ pub enum OperandKind { /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188 Move(Place), /// Constants are already semantically values, and remain unchanged. - Constant { konst: StoredConst, ty: StoredTy }, + Constant { + konst: StoredConst, + ty: StoredTy, + }, + Allocation { + allocation: StoredAllocation, + }, /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc /// handles it with the `Constant` variant somehow. Static(StaticId), @@ -115,11 +121,10 @@ pub enum OperandKind { impl<'db> Operand { fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self { - let interner = DbInterner::conjure(); Operand { - kind: OperandKind::Constant { - konst: Const::new_valtree(interner, ty, data, memory_map).store(), - ty: ty.store(), + kind: OperandKind::Allocation { + allocation: Allocation::new(AllocationData { ty, memory: data, memory_map }) + .store(), }, span: None, } @@ -1095,7 +1100,9 @@ impl MirBody { OperandKind::Copy(p) | OperandKind::Move(p) => { f(p, store); } - OperandKind::Constant { .. } | OperandKind::Static(_) => (), + OperandKind::Constant { .. } + | OperandKind::Static(_) + | OperandKind::Allocation { .. } => (), } } for (_, block) in self.basic_blocks.iter_mut() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index d843359dcbe87..664734c85ed62 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -160,7 +160,7 @@ fn moved_out_of_ref<'db>( result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.store() }); } } - OperandKind::Constant { .. } | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {} }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -254,7 +254,7 @@ fn partially_moved<'db>( result.push(PartiallyMoved { span, ty: ty.store(), local: p.local }); } } - OperandKind::Constant { .. } | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {} }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); 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 17af4ad961da0..22ecbe2fa2028 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 @@ -21,7 +21,7 @@ use hir_expand::{InFile, mod_path::path, name::Name}; use intern::sym; use la_arena::ArenaMap; use macros::GenericTypeVisitable; -use rustc_abi::TargetDataLayout; +use rustc_abi::{Size, TargetDataLayout}; use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, @@ -46,8 +46,8 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - AliasTy, Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, - StoredConst, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst, + AliasTy, Allocation, AllocationData, Const, ConstKind, DbInterner, ErrorGuaranteed, + GenericArgs, Region, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValTree, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -359,7 +359,7 @@ pub enum MirEvalError { MirLowerErrorForClosure(InternedClosureId, MirLowerError), TypeIsUnsized(StoredTy, &'static str), NotSupported(String), - InvalidConst(StoredConst), + InvalidConst, InFunction( Box, Vec<(Either, MirSpan, DefWithBodyId)>, @@ -484,7 +484,7 @@ impl MirEvalError { | MirEvalError::MirLowerErrorForClosure(_, _) | MirEvalError::TypeIsUnsized(_, _) | MirEvalError::NotSupported(_) - | MirEvalError::InvalidConst(_) + | MirEvalError::InvalidConst | MirEvalError::ExecutionLimitExceeded | MirEvalError::StackOverflow | MirEvalError::CoerceUnsizedError(_) @@ -537,7 +537,7 @@ impl std::fmt::Debug for MirEvalError { Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(), Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), - Self::InvalidConst(arg0) => f.debug_tuple("InvalidConst").field(&arg0).finish(), + Self::InvalidConst => f.write_str("InvalidConst"), Self::InFunction(e, stack) => { f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } @@ -606,10 +606,10 @@ pub fn interpret_mir<'db>( // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, trait_env: Option>, -) -> Result<'db, (Result<'db, Const<'db>>, MirOutput)> { +) -> Result<'db, (Result<'db, Allocation<'db>>, MirOutput)> { let ty = body.locals[return_slot()].ty.as_ref(); let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?; - let it: Result<'db, Const<'db>> = (|| { + let it: Result<'db, Allocation<'db>> = (|| { if evaluator.ptr_size() != size_of::() { not_supported!("targets with different pointer size from host"); } @@ -620,7 +620,7 @@ pub fn interpret_mir<'db>( ty, &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, )?; - let bytes = bytes.into(); + let bytes = Box::from(bytes); let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() { MemoryMap::Empty } else { @@ -628,7 +628,7 @@ pub fn interpret_mir<'db>( memory_map.vtable.shrink_to_fit(); MemoryMap::Complex(Box::new(memory_map)) }; - Ok(Const::new_valtree(evaluator.interner(), ty, bytes, memory_map)) + Ok(Allocation::new(AllocationData { ty, memory: bytes, memory_map })) })(); Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } @@ -898,6 +898,7 @@ impl<'db> Evaluator<'db> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, OperandKind::Constant { konst: _, ty } => ty.as_ref(), + OperandKind::Allocation { allocation } => allocation.as_ref().ty, &OperandKind::Static(s) => { let ty = InferenceResult::of(self.db, DefWithBodyId::from(s)) .expr_ty(Body::of(self.db, s.into()).root_expr()); @@ -1927,19 +1928,152 @@ impl<'db> Evaluator<'db> { OperandKind::Constant { konst, .. } => { self.allocate_const_in_heap(locals, konst.as_ref())? } + OperandKind::Allocation { allocation } => { + self.allocate_allocation_in_heap(locals, allocation.as_ref())? + } }) } + fn allocate_valtree_in_heap( + &mut self, + ty: Ty<'db>, + valtree: ValTree<'db>, + ) -> Result<'db, Interval> { + match ty.kind() { + TyKind::Bool => { + let value = valtree.inner().to_leaf().try_to_bool().unwrap(); + let addr = self.heap_allocate(1, 1)?; + self.write_memory(addr, &[u8::from(value)])?; + Ok(Interval::new(addr, 1)) + } + TyKind::Char => { + let value = valtree.inner().to_leaf().to_u32(); + let addr = self.heap_allocate(4, 4)?; + self.write_memory(addr, &value.to_le_bytes())?; + Ok(Interval::new(addr, 4)) + } + TyKind::Int(int_ty) => { + let size = int_ty.bit_width().unwrap_or(self.ptr_size() as u64); + let value = valtree.inner().to_leaf().to_int(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::Uint(uint_ty) => { + let size = uint_ty.bit_width().unwrap_or(self.ptr_size() as u64); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::Float(float_ty) => { + let size = float_ty.bit_width(); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size as usize, size as usize)?; + self.write_memory(addr, &value.to_le_bytes()[..size as usize])?; + Ok(Interval::new(addr, size as usize)) + } + TyKind::RawPtr(..) => { + let size = self.ptr_size(); + let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size)); + let addr = self.heap_allocate(size, size)?; + self.write_memory(addr, &value.to_le_bytes()[..size])?; + Ok(Interval::new(addr, size)) + } + TyKind::Ref(_, inner_ty, _) => match inner_ty.kind() { + TyKind::Str => { + let bytes = valtree + .inner() + .to_branch() + .iter() + .map(|konst| match konst.kind() { + ConstKind::Value(value) => Ok(value.value.inner().to_leaf().to_u8()), + _ => not_supported!("unsupported const"), + }) + .collect::>>()?; + let bytes_addr = self.heap_allocate(bytes.len(), 1)?; + self.write_memory(bytes_addr, &bytes)?; + let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?; + self.write_memory(ref_addr, &bytes_addr.to_bytes())?; + let mut len = [0; 16]; + len[..size_of::()].copy_from_slice(&bytes.len().to_le_bytes()); + self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?; + Ok(Interval::new(ref_addr, self.ptr_size() * 2)) + } + TyKind::Slice(inner_ty) => { + let item_layout = self.layout(inner_ty)?; + let items = valtree + .inner() + .to_branch() + .iter() + .map(|konst| match konst.kind() { + ConstKind::Value(value) => { + self.allocate_valtree_in_heap(value.ty, value.value) + } + _ => not_supported!("unsupported const"), + }) + .collect::>>()?; + let items_addr = self.heap_allocate( + items.len() * (item_layout.size.bits() as usize), + item_layout.align.bits_usize(), + )?; + for (i, item) in items.iter().enumerate() { + self.copy_from_interval( + items_addr.offset(i * (item_layout.size.bits() as usize)), + *item, + )?; + } + let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?; + self.write_memory(ref_addr, &items_addr.to_bytes())?; + let mut len = [0; 16]; + len[..size_of::()].copy_from_slice(&items.len().to_le_bytes()); + self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?; + Ok(Interval::new(ref_addr, self.ptr_size() * 2)) + } + TyKind::Dynamic(..) => not_supported!("`dyn Trait` consts not supported yet"), + _ => { + let inner_addr = self.allocate_valtree_in_heap(inner_ty, valtree)?; + let ref_addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?; + self.write_memory(ref_addr, &inner_addr.addr.to_bytes())?; + Ok(Interval::new(ref_addr, self.ptr_size())) + } + }, + TyKind::Adt(_, _) | TyKind::Array(_, _) | TyKind::Tuple(_) => { + not_supported!( + "ADTs, arrays and tuples are unsupported in consts currently (requires `adt_const_params`)" + ) + } + TyKind::Pat(_, _) + | TyKind::Slice(_) + | TyKind::FnDef(_, _) + | TyKind::Foreign(_) + | TyKind::Dynamic(_, _) + | TyKind::UnsafeBinder(..) + | TyKind::FnPtr(..) + | TyKind::Closure(_, _) + | TyKind::CoroutineClosure(_, _) + | TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::Never + | TyKind::Alias(..) + | TyKind::Param(_) + | TyKind::Bound(..) + | TyKind::Placeholder(_) + | TyKind::Infer(_) + | TyKind::Str + | TyKind::Error(_) => not_supported!("unsupported const"), + } + } + #[allow(clippy::double_parens)] fn allocate_const_in_heap( &mut self, locals: &Locals, konst: Const<'db>, ) -> Result<'db, Interval> { - let result_owner; - let value = match konst.kind() { - ConstKind::Value(value) => value, - ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => 'b: { + match konst.kind() { + ConstKind::Value(value) => self.allocate_valtree_in_heap(value.ty, value.value), + ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => { let mut id = const_id.0; let mut subst = subst; if let hir_def::GeneralConstId::ConstId(c) = id { @@ -1947,7 +2081,7 @@ impl<'db> Evaluator<'db> { id = hir_def::GeneralConstId::ConstId(c); subst = s; } - result_owner = match id { + let allocation = match id { GeneralConstId::ConstId(const_id) => { self.db.const_eval(const_id, subst, Some(self.param_env)).map_err(|e| { let name = id.name(self.db); @@ -1964,21 +2098,24 @@ impl<'db> Evaluator<'db> { not_supported!("anonymous const evaluation") } }; - if let ConstKind::Value(value) = result_owner.kind() { - break 'b value; - } - not_supported!("unevaluatable constant"); + self.allocate_allocation_in_heap(locals, allocation) } _ => not_supported!("evaluating unknown const"), - }; - let ValueConst { ty, value } = value; - let ConstBytes { memory: v, memory_map } = value.inner(); + } + } + + fn allocate_allocation_in_heap( + &mut self, + locals: &Locals, + allocation: Allocation<'db>, + ) -> Result<'db, Interval> { + let AllocationData { ty, memory: ref v, ref memory_map } = *allocation; let patch_map = memory_map.transform_addresses(|b, align| { let addr = self.heap_allocate(b.len(), align)?; self.write_memory(addr, b)?; Ok(addr.to_usize()) })?; - let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); + let (size, align) = self.size_align_of(allocation.ty, locals)?.unwrap_or((v.len(), 1)); let v: Cow<'_, [u8]> = if size != v.len() { // Handle self enum if size == 16 && v.len() < 16 { @@ -1986,7 +2123,7 @@ impl<'db> Evaluator<'db> { } else if size < 16 && v.len() == 16 { Cow::Borrowed(&v[0..size]) } else { - return Err(MirEvalError::InvalidConst(konst.store())); + return Err(MirEvalError::InvalidConst); } } else { Cow::Borrowed(v) @@ -2840,10 +2977,10 @@ impl<'db> Evaluator<'db> { }; let static_data = StaticSignature::of(self.db, st); let result = if !static_data.flags.contains(StaticFlags::EXTERN) { - let konst = self.db.const_eval_static(st).map_err(|e| { + let allocation = self.db.const_eval_static(st).map_err(|e| { MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e)) })?; - self.allocate_const_in_heap(locals, konst)? + self.allocate_allocation_in_heap(locals, allocation)? } else { let ty = InferenceResult::of(self.db, DefWithBodyId::from(st)) .expr_ty(Body::of(self.db, st.into()).root_expr()); @@ -3003,7 +3140,7 @@ impl<'db> Evaluator<'db> { pub fn render_const_using_debug_impl<'db>( db: &'db dyn HirDatabase, owner: DefWithBodyId, - c: Const<'db>, + c: Allocation<'db>, ty: Ty<'db>, ) -> Result<'db, String> { let mut evaluator = Evaluator::new(db, owner, false, None)?; @@ -3014,7 +3151,7 @@ pub fn render_const_using_debug_impl<'db>( .map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?, drop_flags: DropFlags::default(), }; - let data = evaluator.allocate_const_in_heap(locals, c)?; + let data = evaluator.allocate_allocation_in_heap(locals, c)?; let resolver = owner.resolver(db); let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( db, 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 d044019629ca4..f1365e4df8262 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 @@ -1493,8 +1493,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]), hir_def::hir::Literal::Float(f, _) => match size()? { 16 => Box::new(f.to_f128().to_bits().to_le_bytes()), - 8 => Box::new(f.to_f64().to_le_bytes()), - 4 => Box::new(f.to_f32().to_le_bytes()), + 8 => Box::new(f.to_f64().to_bits().to_le_bytes()), + 4 => Box::new(f.to_f32().to_bits().to_le_bytes()), 2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()), _ => { return Err(MirLowerError::TypeError( @@ -1528,31 +1528,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { subst: GenericArgs<'db>, const_id: GeneralConstId, ) -> Result<'db, Operand> { - let konst = if !subst.is_empty() { - // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. - Const::new_unevaluated( - self.interner(), - UnevaluatedConst { def: const_id.into(), args: subst }, - ) - } else { - match const_id { - id @ GeneralConstId::ConstId(const_id) => { - self.db.const_eval(const_id, subst, None).map_err(|e| { - let name = id.name(self.db); - MirLowerError::ConstEvalError(name.into(), Box::new(e)) - })? - } - GeneralConstId::StaticId(static_id) => { - self.db.const_eval_static(static_id).map_err(|e| { - let name = const_id.name(self.db); - MirLowerError::ConstEvalError(name.into(), Box::new(e)) - })? - } - GeneralConstId::AnonConstId(_) => { - return Err(MirLowerError::IncompleteExpr); - } - } - }; + if matches!(const_id, GeneralConstId::AnonConstId(_)) { + // FIXME: + not_supported!("anon consts are not supported yet in const eval"); + } + let konst = Const::new_unevaluated( + self.interner(), + UnevaluatedConst { def: const_id.into(), args: subst }, + ); let ty = self .db .value_ty(match const_id { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 5752a3d7fae4b..41044f00c2e96 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -16,7 +16,10 @@ use triomphe::Arc; use crate::{ ParamEnvAndCrate, - next_solver::{Const, ConstKind, Region, RegionKind, StoredConst, StoredGenericArgs, StoredTy}, + next_solver::{ + Allocation, AllocationData, Const, ConstKind, Region, RegionKind, StoredConst, + StoredGenericArgs, StoredTy, + }, traits::StoredParamEnvAndCrate, }; use crate::{ @@ -138,6 +141,18 @@ impl<'db> Filler<'db> { self.fill_const(konst)?; self.fill_ty(ty)?; } + OperandKind::Allocation { allocation } => { + let alloc = allocation.as_ref(); + let mut ty = alloc.ty.store(); + self.fill_ty(&mut ty)?; + *allocation = Allocation::new(AllocationData { + ty: ty.as_ref(), + memory: alloc.memory.clone(), + // FIXME: Do we need to fill the memory map too? + memory_map: alloc.memory_map.clone(), + }) + .store(); + } OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (), } Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 4b654a0fbe085..de5ee223a1487 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -387,6 +387,9 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { w!(self, "Const({})", self.hir_display(&konst.as_ref())) } OperandKind::Static(s) => w!(self, "Static({:?})", s), + OperandKind::Allocation { allocation } => { + w!(self, "Allocation({})", self.hir_display(&allocation.as_ref())) + } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 354ad16ffec98..161a3142df556 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -5,6 +5,7 @@ // incorrect lifetime here. pub mod abi; +mod allocation; mod binder; mod consts; mod def_id; @@ -29,6 +30,7 @@ pub mod util; use std::{mem::ManuallyDrop, sync::OnceLock}; +pub use allocation::*; pub use binder::*; pub use consts::*; pub use def_id::*; @@ -89,6 +91,7 @@ pub struct DefaultTypes<'db> { pub struct DefaultConsts<'db> { pub error: Const<'db>, + pub u8_values: [Const<'db>; 256], } pub struct DefaultRegions<'db> { @@ -232,10 +235,11 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let statik = create_region(RegionKind::ReStatic); let empty_tys = create_tys(&[]); let unit = create_ty(TyKind::Tuple(empty_tys)); + let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)); DefaultAny { types: DefaultTypes { usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)), - u8: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)), + u8, u16: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U16)), u32: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U32)), u64: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U64)), @@ -259,7 +263,15 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)), mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)), }, - consts: DefaultConsts { error: create_const(ConstKind::Error(ErrorGuaranteed)) }, + consts: DefaultConsts { + error: create_const(ConstKind::Error(ErrorGuaranteed)), + u8_values: std::array::from_fn(|u8_value| { + create_const(ConstKind::Value(ValueConst { + ty: u8, + value: ValTree::new(ValTreeKind::Leaf(ScalarInt::from(u8_value as u8))), + })) + }), + }, regions: DefaultRegions { error: create_region(RegionKind::ReError(ErrorGuaranteed)), statik, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/allocation.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/allocation.rs new file mode 100644 index 0000000000000..d299c89c12eae --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/allocation.rs @@ -0,0 +1,73 @@ +use std::{fmt, hash::Hash}; + +use intern::{Interned, InternedRef, impl_internable}; +use macros::GenericTypeVisitable; +use rustc_type_ir::GenericTypeVisitable; + +use crate::{ + MemoryMap, + next_solver::{Ty, impl_stored_interned}, +}; + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Allocation<'db> { + interned: InternedRef<'db, AllocationInterned>, +} + +impl<'db> Allocation<'db> { + pub fn new(data: AllocationData<'db>) -> Self { + let data = + unsafe { std::mem::transmute::, AllocationData<'static>>(data) }; + Self { interned: Interned::new_gc(AllocationInterned(data)) } + } +} + +impl<'db> std::ops::Deref for Allocation<'db> { + type Target = AllocationData<'db>; + + #[inline] + fn deref(&self) -> &Self::Target { + let inner = &self.interned.0; + unsafe { std::mem::transmute::<&AllocationData<'static>, &AllocationData<'db>>(inner) } + } +} + +impl<'db, V: super::WorldExposer> GenericTypeVisitable for Allocation<'db> { + fn generic_visit_with(&self, visitor: &mut V) { + if visitor.on_interned(self.interned).is_continue() { + (**self).generic_visit_with(visitor); + } + } +} + +impl fmt::Debug for Allocation<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let AllocationData { ty, memory, memory_map } = &**self; + f.debug_struct("Allocation") + .field("ty", ty) + .field("memory", memory) + .field("memory_map", memory_map) + .finish() + } +} + +#[derive(PartialEq, Eq, Hash, GenericTypeVisitable)] +pub(super) struct AllocationInterned(AllocationData<'static>); + +#[derive(Debug, PartialEq, Eq, GenericTypeVisitable)] +pub struct AllocationData<'db> { + pub ty: Ty<'db>, + pub memory: Box<[u8]>, + pub memory_map: MemoryMap<'db>, +} + +impl<'db> Hash for AllocationData<'db> { + fn hash(&self, state: &mut H) { + let Self { ty, memory, memory_map: _ } = self; + ty.hash(state); + memory.hash(state); + } +} + +impl_internable!(gc; AllocationInterned); +impl_stored_interned!(AllocationInterned, Allocation, StoredAllocation); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 8ed515b452695..fa90e3d8a004b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -1,5 +1,7 @@ //! Things related to consts in the next-trait-solver. +mod valtree; + use std::hash::Hash; use hir_def::ConstParamId; @@ -9,21 +11,20 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_type_ir::{ BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, - inherent::{IntoKind, ParamEnv as _, SliceLike}, - relate::Relate, + TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate, }; use crate::{ - MemoryMap, + ParamEnvAndCrate, next_solver::{ - ClauseKind, ParamEnv, impl_foldable_for_interned_slice, impl_stored_interned, - interned_slice, + AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, }, }; use super::{DbInterner, ErrorGuaranteed, GenericArgs, Ty}; +pub use self::valtree::*; + pub type ConstKind<'db> = rustc_type_ir::ConstKind>; pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst>; @@ -86,18 +87,21 @@ impl<'db> Const<'db> { Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound)) } - pub fn new_valtree( + pub fn new_valtree(interner: DbInterner<'db>, ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self { + Const::new(interner, ConstKind::Value(ValueConst { ty, value: ValTree::new(kind) })) + } + + pub fn new_from_allocation( interner: DbInterner<'db>, - ty: Ty<'db>, - memory: Box<[u8]>, - memory_map: MemoryMap<'db>, + allocation: &AllocationData<'db>, + param_env: ParamEnvAndCrate<'db>, ) -> Self { - Const::new( + allocation_to_const( interner, - ConstKind::Value(ValueConst { - ty, - value: Valtree::new(ConstBytes { memory, memory_map }), - }), + allocation.ty, + &allocation.memory, + &allocation.memory_map, + param_env, ) } @@ -142,136 +146,6 @@ impl std::fmt::Debug for ParamConst { } } -impl ParamConst { - pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { - let mut candidates = env.caller_bounds().iter().filter_map(|clause| { - // `ConstArgHasType` are never desugared to be higher ranked. - match clause.kind().skip_binder() { - ClauseKind::ConstArgHasType(param_ct, ty) => { - assert!(!(param_ct, ty).has_escaping_bound_vars()); - - match param_ct.kind() { - ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), - _ => None, - } - } - _ => None, - } - }); - - // N.B. it may be tempting to fix ICEs by making this function return - // `Option>` instead of `Ty<'db>`; however, this is generally - // considered to be a bandaid solution, since it hides more important - // underlying issues with how we construct generics and predicates of - // items. It's advised to fix the underlying issue rather than trying - // to modify this function. - let ty = candidates.next().unwrap_or_else(|| { - panic!("cannot find `{self:?}` in param-env: {env:#?}"); - }); - assert!( - candidates.next().is_none(), - "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" - ); - ty - } -} - -pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind>; - -/// A type-level constant value. -/// -/// Represents a typed, fully evaluated constant. -#[derive( - Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable, -)] -pub struct ValueConst<'db> { - pub ty: Ty<'db>, - // FIXME: Should we ignore this for TypeVisitable, TypeFoldable? - #[type_visitable(ignore)] - #[type_foldable(identity)] - pub value: Valtree<'db>, -} - -impl<'db> ValueConst<'db> { - pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self { - let value = Valtree::new(bytes); - ValueConst { ty, value } - } -} - -impl<'db> rustc_type_ir::inherent::ValueConst> for ValueConst<'db> { - fn ty(self) -> Ty<'db> { - self.ty - } - - fn valtree(self) -> Valtree<'db> { - self.value - } -} - -#[derive(Debug, Clone, PartialEq, Eq, GenericTypeVisitable)] -pub struct ConstBytes<'db> { - pub memory: Box<[u8]>, - pub memory_map: MemoryMap<'db>, -} - -impl Hash for ConstBytes<'_> { - fn hash(&self, state: &mut H) { - self.memory.hash(state) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Valtree<'db> { - interned: InternedRef<'db, ValtreeInterned>, -} - -impl<'db, V: super::WorldExposer> GenericTypeVisitable for Valtree<'db> { - fn generic_visit_with(&self, visitor: &mut V) { - if visitor.on_interned(self.interned).is_continue() { - self.inner().generic_visit_with(visitor); - } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)] -pub(super) struct ValtreeInterned(ConstBytes<'static>); - -impl_internable!(gc; ValtreeInterned); - -const _: () = { - const fn is_copy() {} - is_copy::>(); -}; - -impl<'db> IntoKind for Valtree<'db> { - type Kind = ValTreeKind<'db>; - - fn kind(self) -> Self::Kind { - todo!() - } -} - -impl<'db> Valtree<'db> { - #[inline] - pub fn new(bytes: ConstBytes<'db>) -> Self { - let bytes = unsafe { std::mem::transmute::, ConstBytes<'static>>(bytes) }; - Self { interned: Interned::new_gc(ValtreeInterned(bytes)) } - } - - #[inline] - pub fn inner(&self) -> &ConstBytes<'db> { - let inner = &self.interned.0; - unsafe { std::mem::transmute::<&ConstBytes<'static>, &ConstBytes<'db>>(inner) } - } -} - -impl std::fmt::Debug for Valtree<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.interned.fmt(f) - } -} - #[derive( Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable, )] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs new file mode 100644 index 0000000000000..b856ee5a85a76 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -0,0 +1,712 @@ +use std::{fmt, hash::Hash, num::NonZero}; + +use intern::{Interned, InternedRef, impl_internable}; +use macros::{GenericTypeVisitable, TypeFoldable, TypeVisitable}; +use rustc_abi::{Size, TargetDataLayout}; +use rustc_type_ir::{GenericTypeVisitable, TypeFoldable, TypeVisitable, inherent::IntoKind}; +use stdx::never; + +use crate::{ + MemoryMap, ParamEnvAndCrate, consteval, + mir::pad16, + next_solver::{Const, Consts, TyKind, WorldExposer}, +}; + +use super::{DbInterner, Ty}; + +pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind>; + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive( + Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable, +)] +pub struct ValueConst<'db> { + pub ty: Ty<'db>, + pub value: ValTree<'db>, +} + +impl<'db> ValueConst<'db> { + pub fn new(ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self { + let value = ValTree::new(kind); + ValueConst { ty, value } + } +} + +pub(super) fn allocation_to_const<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + memory: &[u8], + memory_map: &MemoryMap<'db>, + param_env: ParamEnvAndCrate<'db>, +) -> Const<'db> { + let Ok(data_layout) = interner.db.target_data_layout(param_env.krate) else { + return Const::error(interner); + }; + let valtree = match ty.kind() { + TyKind::Bool => ValTreeKind::Leaf(ScalarInt::from(memory[0] != 0)), + TyKind::Char => { + let it = u128::from_le_bytes(pad16(memory, false)) as u32; + let Ok(c) = char::try_from(it) else { + return Const::error(interner); + }; + ValTreeKind::Leaf(ScalarInt::from(c)) + } + TyKind::Int(int) => { + let it = i128::from_le_bytes(pad16(memory, true)); + let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); + let scalar = ScalarInt::try_from_int(it, size).unwrap(); + ValTreeKind::Leaf(scalar) + } + TyKind::Uint(uint) => { + let it = u128::from_le_bytes(pad16(memory, false)); + let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); + let scalar = ScalarInt::try_from_uint(it, size).unwrap(); + ValTreeKind::Leaf(scalar) + } + TyKind::Float(float) => { + let scalar = match float { + rustc_ast_ir::FloatTy::F16 => { + ScalarInt::from(u16::from_le_bytes(memory.try_into().unwrap())) + } + rustc_ast_ir::FloatTy::F32 => { + ScalarInt::from(u32::from_le_bytes(memory.try_into().unwrap())) + } + rustc_ast_ir::FloatTy::F64 => { + ScalarInt::from(u64::from_le_bytes(memory.try_into().unwrap())) + } + rustc_ast_ir::FloatTy::F128 => { + ScalarInt::from(u128::from_le_bytes(memory.try_into().unwrap())) + } + }; + ValTreeKind::Leaf(scalar) + } + TyKind::Ref(_, t, _) => match t.kind() { + TyKind::Str => { + let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap()); + let size = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap()); + let Some(bytes) = memory_map.get(addr, size) else { + return Const::error(interner); + }; + let u8_values = &interner.default_types().consts.u8_values; + ValTreeKind::Branch(Consts::new_from_iter( + interner, + bytes.iter().map(|&byte| u8_values[usize::from(byte)]), + )) + } + TyKind::Slice(ty) => { + let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap()); + let count = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap()); + let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else { + return Const::error(interner); + }; + let size_one = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size_one * count) else { + return Const::error(interner); + }; + let expected_len = count * size_one; + if bytes.len() < expected_len { + never!( + "Memory map size is too small. Expected {expected_len}, got {}", + bytes.len(), + ); + return Const::error(interner); + } + let items = (0..count).map(|i| { + let offset = size_one * i; + let bytes = &bytes[offset..offset + size_one]; + allocation_to_const(interner, ty, bytes, memory_map, param_env) + }); + ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + } + TyKind::Dynamic(_, _) => { + let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap()); + let ty_id = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap()); + let Ok(t) = memory_map.vtable_ty(ty_id) else { + return Const::error(interner); + }; + let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else { + return Const::error(interner); + }; + let size = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size) else { + return Const::error(interner); + }; + return allocation_to_const(interner, t, bytes, memory_map, param_env); + } + TyKind::Adt(..) if memory.len() == 2 * size_of::() => { + // FIXME: Unsized ADT. + return Const::error(interner); + } + _ => { + let addr = usize::from_le_bytes(match memory.try_into() { + Ok(b) => b, + Err(_) => { + never!( + "tried rendering ty {:?} in const ref with incorrect byte count {}", + t, + memory.len() + ); + return Const::error(interner); + } + }); + let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else { + return Const::error(interner); + }; + let size = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size) else { + return Const::error(interner); + }; + return allocation_to_const(interner, t, bytes, memory_map, param_env); + } + }, + TyKind::Tuple(tys) => { + let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else { + return Const::error(interner); + }; + let items = tys.iter().enumerate().map(|(id, ty)| { + let offset = layout.fields.offset(id).bytes_usize(); + let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else { + return Const::error(interner); + }; + let size = layout.size.bytes_usize(); + allocation_to_const( + interner, + ty, + &memory[offset..offset + size], + memory_map, + param_env, + ) + }); + ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + } + TyKind::Adt(..) => { + // FIXME: This requires `adt_const_params`. + return Const::error(interner); + } + TyKind::FnDef(..) => { + // FIXME: Fn items. + return Const::error(interner); + } + TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => { + let it = u128::from_le_bytes(pad16(memory, false)); + // FIXME: Unsized pointers. + let scalar = ScalarInt::try_from_uint(it, data_layout.pointer_size()).unwrap(); + ValTreeKind::Leaf(scalar) + } + TyKind::Array(ty, len) => { + let Some(len) = consteval::try_const_usize(interner.db, len) else { + return Const::error(interner); + }; + let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else { + return Const::error(interner); + }; + let size_one = layout.size.bytes_usize(); + let items = (0..len as usize).map(|i| { + let offset = size_one * i; + allocation_to_const( + interner, + ty, + &memory[offset..offset + size_one], + memory_map, + param_env, + ) + }); + ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + } + TyKind::Never => return Const::error(interner), + // FIXME: + TyKind::Closure(_, _) + | TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::CoroutineClosure(_, _) + | TyKind::UnsafeBinder(_) => return Const::error(interner), + // The below arms are unreachable, since const eval will bail out before here. + TyKind::Foreign(_) => return Const::error(interner), + TyKind::Pat(_, _) => return Const::error(interner), + TyKind::Error(..) + | TyKind::Placeholder(_) + | TyKind::Alias(..) + | TyKind::Param(_) + | TyKind::Bound(_, _) + | TyKind::Infer(_) => return Const::error(interner), + // The below arms are unreachable, since we handled them in ref case. + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => { + return Const::error(interner); + } + }; + Const::new_valtree(interner, ty, valtree) +} + +impl<'db> rustc_type_ir::inherent::ValueConst> for ValueConst<'db> { + fn ty(self) -> Ty<'db> { + self.ty + } + + fn valtree(self) -> ValTree<'db> { + self.value + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct ValTree<'db> { + interned: InternedRef<'db, ValTreeInterned>, +} + +impl<'db, V: WorldExposer> GenericTypeVisitable for ValTree<'db> { + fn generic_visit_with(&self, visitor: &mut V) { + if visitor.on_interned(self.interned).is_continue() { + self.inner().generic_visit_with(visitor); + } + } +} + +impl<'db> TypeVisitable> for ValTree<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.inner().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for ValTree<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + self.inner().try_fold_with(folder).map(ValTree::new) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + ValTree::new(self.inner().fold_with(folder)) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)] +pub(in super::super) struct ValTreeInterned(ValTreeKind<'static>); + +impl_internable!(gc; ValTreeInterned); + +const _: () = { + const fn is_copy() {} + is_copy::>(); +}; + +impl<'db> IntoKind for ValTree<'db> { + type Kind = ValTreeKind<'db>; + + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> ValTree<'db> { + #[inline] + pub fn new(kind: ValTreeKind<'db>) -> Self { + let kind = unsafe { std::mem::transmute::, ValTreeKind<'static>>(kind) }; + Self { interned: Interned::new_gc(ValTreeInterned(kind)) } + } + + #[inline] + pub fn inner(&self) -> &ValTreeKind<'db> { + let inner = &self.interned.0; + unsafe { std::mem::transmute::<&ValTreeKind<'static>, &ValTreeKind<'db>>(inner) } + } +} + +impl std::fmt::Debug for ValTree<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.interned.fmt(f) + } +} + +/// The raw bytes of a simple value. +/// +/// This is a packed struct in order to allow this type to be optimally embedded in enums +/// (like Scalar). +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[repr(Rust, packed)] +pub struct ScalarInt { + /// The first `size` bytes of `data` are the value. + /// Do not try to read less or more bytes than that. The remaining bytes must be 0. + data: u128, + size: NonZero, +} + +impl ScalarInt { + pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() }; + pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() }; + + fn raw(data: u128, size: Size) -> Self { + Self { data, size: NonZero::new(size.bytes() as u8).unwrap() } + } + + #[inline] + pub fn size(self) -> Size { + Size::from_bytes(self.size.get()) + } + + /// Make sure the `data` fits in `size`. + /// This is guaranteed by all constructors here, but having had this check saved us from + /// bugs many times in the past, so keeping it around is definitely worth it. + #[inline(always)] + fn check_data(self) { + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `debug_assert_eq` takes references to its arguments and formatting + // arguments and would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + debug_assert_eq!( + self.size().truncate(self.data), + { self.data }, + "Scalar value {:#x} exceeds size of {} bytes", + { self.data }, + self.size + ); + } + + #[inline] + pub fn null(size: Size) -> Self { + Self::raw(0, size) + } + + #[inline] + pub fn is_null(self) -> bool { + self.data == 0 + } + + #[inline] + pub fn try_from_uint(i: impl Into, size: Size) -> Option { + let (r, overflow) = Self::truncate_from_uint(i, size); + if overflow { None } else { Some(r) } + } + + /// Returns the truncated result, and whether truncation changed the value. + #[inline] + pub fn truncate_from_uint(i: impl Into, size: Size) -> (Self, bool) { + let data = i.into(); + let r = Self::raw(size.truncate(data), size); + (r, r.data != data) + } + + #[inline] + pub fn try_from_int(i: impl Into, size: Size) -> Option { + let (r, overflow) = Self::truncate_from_int(i, size); + if overflow { None } else { Some(r) } + } + + /// Returns the truncated result, and whether truncation changed the value. + #[inline] + pub fn truncate_from_int(i: impl Into, size: Size) -> (Self, bool) { + let data = i.into(); + // `into` performed sign extension, we have to truncate + let r = Self::raw(size.truncate(data as u128), size); + (r, size.sign_extend(r.data) != data) + } + + #[inline] + pub fn try_from_target_usize( + i: impl Into, + data_layout: &TargetDataLayout, + ) -> Option { + Self::try_from_uint(i, data_layout.pointer_size()) + } + + /// Try to convert this ScalarInt to the raw underlying bits. + /// Fails if the size is wrong. Generally a wrong size should lead to a panic, + /// but Miri sometimes wants to be resilient to size mismatches, + /// so the interpreter will generally use this `try` method. + #[inline] + pub fn try_to_bits(self, target_size: Size) -> Result { + assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); + if target_size.bytes() == u64::from(self.size.get()) { + self.check_data(); + Ok(self.data) + } else { + Err(self.size()) + } + } + + #[inline] + pub fn to_bits(self, target_size: Size) -> u128 { + self.try_to_bits(target_size).unwrap_or_else(|size| { + panic!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes()) + }) + } + + /// Extracts the bits from the scalar without checking the size. + #[inline] + pub fn to_bits_unchecked(self) -> u128 { + self.check_data(); + self.data + } + + /// Converts the `ScalarInt` to an unsigned integer of the given size. + /// Panics if the size of the `ScalarInt` is not equal to `size`. + #[inline] + pub fn to_uint(self, size: Size) -> u128 { + self.to_bits(size) + } + + #[inline] + pub fn to_uint_unchecked(self) -> u128 { + self.data + } + + /// Converts the `ScalarInt` to `u8`. + /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte. + #[inline] + pub fn to_u8(self) -> u8 { + self.to_uint(Size::from_bits(8)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u16`. + /// Panics if the size of the `ScalarInt` in not equal to 2 bytes. + #[inline] + pub fn to_u16(self) -> u16 { + self.to_uint(Size::from_bits(16)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u32`. + /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes. + #[inline] + pub fn to_u32(self) -> u32 { + self.to_uint(Size::from_bits(32)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u64`. + /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes. + #[inline] + pub fn to_u64(self) -> u64 { + self.to_uint(Size::from_bits(64)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u128`. + /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes. + #[inline] + pub fn to_u128(self) -> u128 { + self.to_uint(Size::from_bits(128)) + } + + #[inline] + pub fn to_target_usize(&self, data_layout: &TargetDataLayout) -> u64 { + self.to_uint(data_layout.pointer_size()).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `bool`. + /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte. + /// Errors if it is not a valid `bool`. + #[inline] + pub fn try_to_bool(self) -> Result { + match self.to_u8() { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(()), + } + } + + /// Converts the `ScalarInt` to a signed integer of the given size. + /// Panics if the size of the `ScalarInt` is not equal to `size`. + #[inline] + pub fn to_int(self, size: Size) -> i128 { + let b = self.to_bits(size); + size.sign_extend(b) + } + + #[inline] + pub fn to_int_unchecked(self) -> i128 { + self.size().sign_extend(self.data) + } + + /// Converts the `ScalarInt` to i8. + /// Panics if the size of the `ScalarInt` is not equal to 1 byte. + pub fn to_i8(self) -> i8 { + self.to_int(Size::from_bits(8)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to i16. + /// Panics if the size of the `ScalarInt` is not equal to 2 bytes. + pub fn to_i16(self) -> i16 { + self.to_int(Size::from_bits(16)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to i32. + /// Panics if the size of the `ScalarInt` is not equal to 4 bytes. + pub fn to_i32(self) -> i32 { + self.to_int(Size::from_bits(32)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to i64. + /// Panics if the size of the `ScalarInt` is not equal to 8 bytes. + pub fn to_i64(self) -> i64 { + self.to_int(Size::from_bits(64)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to i128. + /// Panics if the size of the `ScalarInt` is not equal to 16 bytes. + pub fn to_i128(self) -> i128 { + self.to_int(Size::from_bits(128)) + } + + #[inline] + pub fn to_target_isize(&self, data_layout: &TargetDataLayout) -> i64 { + self.to_int(data_layout.pointer_size()).try_into().unwrap() + } +} + +macro_rules! from_x_for_scalar_int { + ($($ty:ty),*) => { + $( + impl From<$ty> for ScalarInt { + #[inline] + fn from(u: $ty) -> Self { + Self { + data: u128::from(u), + size: NonZero::new(size_of::<$ty>() as u8).unwrap(), + } + } + } + )* + } +} + +macro_rules! from_scalar_int_for_x { + ($($ty:ty),*) => { + $( + impl From for $ty { + #[inline] + fn from(int: ScalarInt) -> Self { + // The `unwrap` cannot fail because to_uint (if it succeeds) + // is guaranteed to return a value that fits into the size. + int.to_uint(Size::from_bytes(size_of::<$ty>())) + .try_into().unwrap() + } + } + )* + } +} + +from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool); +from_scalar_int_for_x!(u8, u16, u32, u64, u128); + +impl TryFrom for bool { + type Error = (); + #[inline] + fn try_from(int: ScalarInt) -> Result { + int.try_to_bool() + } +} + +impl From for ScalarInt { + #[inline] + fn from(c: char) -> Self { + (c as u32).into() + } +} + +macro_rules! from_x_for_scalar_int_signed { + ($($ty:ty),*) => { + $( + impl From<$ty> for ScalarInt { + #[inline] + fn from(u: $ty) -> Self { + Self { + data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size + size: NonZero::new(size_of::<$ty>() as u8).unwrap(), + } + } + } + )* + } +} + +macro_rules! from_scalar_int_for_x_signed { + ($($ty:ty),*) => { + $( + impl From for $ty { + #[inline] + fn from(int: ScalarInt) -> Self { + // The `unwrap` cannot fail because to_int (if it succeeds) + // is guaranteed to return a value that fits into the size. + int.to_int(Size::from_bytes(size_of::<$ty>())) + .try_into().unwrap() + } + } + )* + } +} + +from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128); +from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128); + +impl From for ScalarInt { + #[inline] + fn from(c: std::cmp::Ordering) -> Self { + // Here we rely on `cmp::Ordering` having the same values in host and target! + ScalarInt::from(c as i8) + } +} + +/// Error returned when a conversion from ScalarInt to char fails. +#[derive(Debug)] +pub struct CharTryFromScalarInt; + +impl TryFrom for char { + type Error = CharTryFromScalarInt; + + #[inline] + fn try_from(int: ScalarInt) -> Result { + match char::from_u32(int.to_u32()) { + Some(c) => Ok(c), + None => Err(CharTryFromScalarInt), + } + } +} + +impl fmt::Debug for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Dispatch to LowerHex below. + write!(f, "0x{self:x}") + } +} + +impl fmt::LowerHex for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.check_data(); + if f.alternate() { + // Like regular ints, alternate flag adds leading `0x`. + write!(f, "0x")?; + } + // Format as hex number wide enough to fit any value of the given `size`. + // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `write!` takes references to its formatting arguments and + // would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2) + } +} + +impl fmt::UpperHex for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.check_data(); + // Format as hex number wide enough to fit any value of the given `size`. + // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `write!` takes references to its formatting arguments and + // would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2) + } +} + +impl fmt::Display for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.check_data(); + write!(f, "{}", { self.data }) + } +} 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 9a94d1a865929..1078a6af420e9 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 @@ -44,8 +44,9 @@ use crate::{ next_solver::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, - GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, SimplifiedType, - SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, + GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, ScalarInt, + SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, + UnevaluatedConst, util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -53,7 +54,7 @@ use crate::{ use super::{ Binder, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, ClauseKind, Clauses, Const, ErrorGuaranteed, ExprConst, ExternalConstraints, GenericArg, GenericArgs, ParamConst, ParamEnv, - ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst, + ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, ValTree, ValueConst, abi::Safety, fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, generics::{Generics, generics}, @@ -370,6 +371,11 @@ impl<'db> DbInterner<'db> { pub fn default_types<'a>(&self) -> &'a crate::next_solver::DefaultAny<'db> { crate::next_solver::default_types(self.db) } + + #[inline] + pub(crate) fn expect_crate(&self) -> Crate { + self.krate.expect("should have a crate") + } } // This is intentionally left as `()` @@ -1056,9 +1062,9 @@ impl<'db> Interner for DbInterner<'db> { type Const = Const<'db>; type ParamConst = ParamConst; type ValueConst = ValueConst<'db>; - type ValTree = Valtree<'db>; + type ValTree = ValTree<'db>; type Consts = Consts<'db>; - type ScalarInt = (); + type ScalarInt = ScalarInt; type ExprConst = ExprConst; type Region = Region<'db>; @@ -2416,6 +2422,7 @@ TrivialTypeTraversalImpls! { ParamTy, EarlyParamRegion, AdtDef, + ScalarInt, } mod tls_db { @@ -2608,13 +2615,15 @@ pub unsafe fn collect_ty_garbage() { let mut gc = intern::GarbageCollector::default(); gc.add_storage::(); - gc.add_storage::(); + gc.add_storage::(); + gc.add_storage::(); gc.add_storage::(); gc.add_storage::(); gc.add_storage::(); gc.add_storage::(); gc.add_storage::(); + gc.add_slice_storage::(); gc.add_slice_storage::(); gc.add_slice_storage::(); gc.add_slice_storage::(); @@ -2649,7 +2658,8 @@ macro_rules! impl_gc_visit { impl_gc_visit!( super::consts::ConstInterned, - super::consts::ValtreeInterned, + super::consts::ValTreeInterned, + super::allocation::AllocationInterned, PatternInterned, super::opaques::ExternalConstraintsInterned, super::predicate::PredicateInterned, @@ -2688,4 +2698,5 @@ impl_gc_visit_slice!( super::predicate::BoundExistentialPredicatesStorage, super::region::RegionAssumptionsStorage, super::ty::TysStorage, + super::consts::ConstsStorage, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index a9e7de16132a1..d45ac6c959695 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -14,10 +14,13 @@ use rustc_type_ir::{ }; use tracing::debug; -use crate::next_solver::{ - AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, - ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys, - util::sizedness_fast_path, +use crate::{ + ParamEnvAndCrate, + next_solver::{ + AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, + ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst, + fold::fold_tys, util::sizedness_fast_path, + }, }; use super::{ @@ -248,25 +251,26 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn evaluate_const( &self, - _param_env: ParamEnv<'db>, - uv: rustc_type_ir::UnevaluatedConst, - ) -> Option<::Const> { - match uv.def.0 { + param_env: ParamEnv<'db>, + uv: UnevaluatedConst<'db>, + ) -> Option> { + let ec = match uv.def.0 { GeneralConstId::ConstId(c) => { let subst = uv.args; - let ec = self.cx().db.const_eval(c, subst, None).ok()?; - Some(ec) - } - GeneralConstId::StaticId(c) => { - let ec = self.cx().db.const_eval_static(c).ok()?; - Some(ec) + self.cx().db.const_eval(c, subst, None).ok()? } + GeneralConstId::StaticId(c) => self.cx().db.const_eval_static(c).ok()?, // TODO: Wire up const_eval_anon query in Phase 5. // For now, return an error const so normalization resolves the // unevaluated const to Error (matching the old behavior where // complex expressions produced ConstKind::Error directly). - GeneralConstId::AnonConstId(_) => Some(Const::error(self.cx())), - } + GeneralConstId::AnonConstId(_) => return Some(Const::error(self.cx())), + }; + Some(Const::new_from_allocation( + self.interner, + &ec, + ParamEnvAndCrate { param_env, krate: self.interner.expect_crate() }, + )) } fn compute_goal_fast_path( diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index e10257214879a..ce0956c5effae 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3124,7 +3124,7 @@ impl Const { let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity(); db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst { - const_: it, + allocation: it, def: self.id.into(), ty, }) @@ -3139,22 +3139,19 @@ impl HasVisibility for Const { pub struct EvaluatedConst<'db> { def: DefWithBodyId, - const_: hir_ty::next_solver::Const<'db>, + allocation: hir_ty::next_solver::Allocation<'db>, ty: Ty<'db>, } impl<'db> EvaluatedConst<'db> { pub fn render(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String { - format!("{}", self.const_.display(db, display_target)) + format!("{}", self.allocation.display(db, display_target)) } pub fn render_debug(&self, db: &'db dyn HirDatabase) -> Result { - let kind = self.const_.kind(); - if let ConstKind::Value(c) = kind - && let ty = c.ty.kind() - && let TyKind::Int(_) | TyKind::Uint(_) = ty - { - let b = &c.value.inner().memory; + let ty = self.allocation.ty.kind(); + if let TyKind::Int(_) | TyKind::Uint(_) = ty { + let b = &self.allocation.memory; let value = u128::from_le_bytes(mir::pad16(b, false)); let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(ty, TyKind::Int(_)))); let mut result = @@ -3166,7 +3163,7 @@ impl<'db> EvaluatedConst<'db> { return Ok(result); } } - mir::render_const_using_debug_impl(db, self.def, self.const_, self.ty) + mir::render_const_using_debug_impl(db, self.def, self.allocation, self.ty) } } @@ -3207,7 +3204,7 @@ impl Static { pub fn eval(self, db: &dyn HirDatabase) -> Result, ConstEvalError> { let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity(); db.const_eval_static(self.id).map(|it| EvaluatedConst { - const_: it, + allocation: it, def: self.id.into(), ty, }) From 567d676388794347b1ce76ab66be26a2d3f5d83f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 16 Apr 2026 00:04:49 +0530 Subject: [PATCH 43/98] introduce interior mutability to SyntaxEditor to let go of &mut usage --- .../crates/syntax/src/ast/edit.rs | 27 ++-- .../crates/syntax/src/syntax_editor.rs | 128 +++++++++--------- .../syntax/src/syntax_editor/edit_algo.rs | 4 +- .../crates/syntax/src/syntax_editor/edits.rs | 50 ++++--- 4 files changed, 104 insertions(+), 105 deletions(-) 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 ae1293fb2f74a..b20aa90d06f23 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -108,7 +108,7 @@ impl IndentLevel { } pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode { - let (mut editor, node) = SyntaxEditor::new(node.clone()); + let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { @@ -142,7 +142,7 @@ impl IndentLevel { } pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { - let (mut editor, node) = SyntaxEditor::new(node.clone()); + let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { @@ -198,11 +198,8 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl AstNodeEdit for N {} impl ast::IdentPat { - pub fn set_pat( - &self, - pat: Option, - syntax_editor: &mut SyntaxEditor, - ) -> ast::IdentPat { + pub fn set_pat(&self, pat: Option, editor: &SyntaxEditor) -> ast::IdentPat { + let make = editor.make(); match pat { None => { if let Some(at_token) = self.at_token() { @@ -212,7 +209,7 @@ impl ast::IdentPat { .pat() .map(|it| it.syntax().clone().into()) .unwrap_or_else(|| at_token.into()); - syntax_editor.delete_all(start..=end); + editor.delete_all(start..=end); // Remove any trailing ws if let Some(last) = @@ -225,28 +222,28 @@ impl ast::IdentPat { Some(pat) => { if let Some(old_pat) = self.pat() { // Replace existing pattern - syntax_editor.replace(old_pat.syntax(), pat.syntax()) + editor.replace(old_pat.syntax(), pat.syntax()) } else if let Some(at_token) = self.at_token() { // Have an `@` token but not a pattern yet - syntax_editor.insert(Position::after(at_token), pat.syntax()); + editor.insert(Position::after(at_token), pat.syntax()); } else { // Don't have an `@`, should have a name let name = self.name().unwrap(); let elements = vec![ - syntax_editor.make().whitespace(" ").into(), - syntax_editor.make().token(T![@]).into(), - syntax_editor.make().whitespace(" ").into(), + make.whitespace(" ").into(), + make.token(T![@]).into(), + make.whitespace(" ").into(), pat.syntax().clone().into(), ]; if self.syntax().parent().is_none() { - let (mut local, local_self) = SyntaxEditor::with_ast_node(self); + let (local, local_self) = SyntaxEditor::with_ast_node(self); let local_name = local_self.name().unwrap(); local.insert_all(Position::after(local_name.syntax()), elements); let edit = local.finish(); return ast::IdentPat::cast(edit.new_root().clone()).unwrap(); } else { - syntax_editor.insert_all(Position::after(name.syntax()), elements); + editor.insert_all(Position::after(name.syntax()), elements); } } } 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 4c20dc7a15663..b2bd10b354e9d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -5,6 +5,7 @@ //! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs use std::{ + cell::RefCell, fmt, iter, num::NonZeroU32, ops::RangeInclusive, @@ -29,8 +30,8 @@ pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; #[derive(Debug)] pub struct SyntaxEditor { root: SyntaxNode, - changes: Vec, - annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + changes: RefCell>, + annotations: RefCell>, make: SyntaxFactory, } @@ -50,8 +51,8 @@ impl SyntaxEditor { let editor = Self { root: root.clone(), - changes: Vec::new(), - annotations: Vec::new(), + changes: RefCell::new(Vec::new()), + annotations: RefCell::new(Vec::new()), make: SyntaxFactory::with_mappings(), }; @@ -72,20 +73,17 @@ impl SyntaxEditor { &self.make } - pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { - self.annotations.push((element.syntax_element(), annotation)) + pub fn add_annotation(&self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.borrow_mut().push((element.syntax_element(), annotation)) } - pub fn add_annotation_all( - &mut self, - elements: Vec, - annotation: SyntaxAnnotation, - ) { + pub fn add_annotation_all(&self, elements: Vec, annotation: SyntaxAnnotation) { self.annotations + .borrow_mut() .extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation))); } - pub fn merge(&mut self, mut other: SyntaxEditor) { + pub fn merge(&self, other: SyntaxEditor) { debug_assert!( self.root == other.root || other.root.ancestors().any(|node| node == self.root), "{:?} is not in the same tree as {:?}", @@ -93,32 +91,28 @@ impl SyntaxEditor { self.root ); - self.changes.append(&mut other.changes); + self.changes.borrow_mut().append(&mut other.changes.into_inner()); if let Some(mut m) = self.make.mappings() { m.merge(other.make.take()); } - self.annotations.append(&mut other.annotations); + self.annotations.borrow_mut().append(&mut other.annotations.into_inner()); } - pub fn insert(&mut self, position: Position, element: impl Element) { + pub fn insert(&self, position: Position, element: impl Element) { debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); - self.changes.push(Change::Insert(position, element.syntax_element())) + self.changes.borrow_mut().push(Change::Insert(position, element.syntax_element())) } - pub fn insert_all(&mut self, position: Position, elements: Vec) { + pub fn insert_all(&self, position: Position, elements: Vec) { debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); - self.changes.push(Change::InsertAll(position, elements)) + self.changes.borrow_mut().push(Change::InsertAll(position, elements)) } - pub fn insert_with_whitespace(&mut self, position: Position, element: impl Element) { + pub fn insert_with_whitespace(&self, position: Position, element: impl Element) { self.insert_all_with_whitespace(position, vec![element.syntax_element()]) } - pub fn insert_all_with_whitespace( - &mut self, - position: Position, - mut elements: Vec, - ) { + pub fn insert_all_with_whitespace(&self, position: Position, mut elements: Vec) { if let Some(first) = elements.first() && let Some(ws) = ws_before(&position, first, &self.make) { @@ -132,50 +126,52 @@ impl SyntaxEditor { self.insert_all(position, elements) } - pub fn delete(&mut self, element: impl Element) { + pub fn delete(&self, element: impl Element) { let element = element.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); debug_assert!( !matches!(&element, SyntaxElement::Node(node) if node == &self.root), "should not delete root node" ); - self.changes.push(Change::Replace(element.syntax_element(), None)); + self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None)); } - pub fn delete_all(&mut self, range: RangeInclusive) { + pub fn delete_all(&self, range: RangeInclusive) { if range.start() == range.end() { self.delete(range.start()); return; } debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); - self.changes.push(Change::ReplaceAll(range, Vec::new())) + self.changes.borrow_mut().push(Change::ReplaceAll(range, Vec::new())) } - pub fn replace(&mut self, old: impl Element, new: impl Element) { + pub fn replace(&self, old: impl Element, new: impl Element) { let old = old.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); - self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + self.changes + .borrow_mut() + .push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); } - pub fn replace_with_many(&mut self, old: impl Element, new: Vec) { + pub fn replace_with_many(&self, old: impl Element, new: Vec) { let old = old.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); debug_assert!( !(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1), "cannot replace root node with many elements" ); - self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new)); + self.changes.borrow_mut().push(Change::ReplaceWithMany(old.syntax_element(), new)); } - pub fn replace_all(&mut self, range: RangeInclusive, new: Vec) { + pub fn replace_all(&self, range: RangeInclusive, new: Vec) { if range.start() == range.end() { self.replace_with_many(range.start(), new); return; } debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); - self.changes.push(Change::ReplaceAll(range, new)) + self.changes.borrow_mut().push(Change::ReplaceAll(range, new)) } pub fn finish(self) -> SyntaxEdit { @@ -555,7 +551,8 @@ mod tests { .into(), ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); @@ -567,11 +564,11 @@ mod tests { editor.add_annotation(name.syntax(), placeholder_snippet); editor.add_annotation(name_ref.syntax(), placeholder_snippet); - let new_block = editor.make().block_expr( + let new_block = make.block_expr( [editor .make() .let_stmt( - editor.make().ident_pat(false, false, name.clone()).into(), + make.ident_pat(false, false, name.clone()).into(), None, Some(to_replace.clone().into()), ) @@ -612,31 +609,28 @@ mod tests { None, ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); editor.insert( Position::first_child_of(root.stmt_list().unwrap().syntax()), - editor - .make() - .let_stmt( - make::ext::simple_ident_pat(make::name("first")).into(), - None, - Some(make::expr_literal("1").into()), - ) - .syntax(), + make.let_stmt( + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), ); editor.insert( Position::after(second_let.syntax()), - editor - .make() - .let_stmt( - make::ext::simple_ident_pat(make::name("third")).into(), - None, - Some(make::expr_literal("3").into()), - ) - .syntax(), + make.let_stmt( + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), ); let edit = editor.finish(); @@ -666,22 +660,22 @@ mod tests { ), ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let inner_block = root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let new_block_expr = - editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = editor.make().let_stmt( + let first_let = make.let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), ); - let third_let = editor.make().let_stmt( + let third_let = make.let_stmt( make::ext::simple_ident_pat(make::name("third")).into(), None, Some(make::expr_literal("3").into()), @@ -719,14 +713,14 @@ mod tests { None, ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let inner_block = root; - let new_block_expr = - editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = editor.make().let_stmt( + let first_let = make.let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), @@ -766,7 +760,7 @@ mod tests { false, ); - let (mut editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn); + let (editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn); if let Some(ret_ty) = parent_fn.ret_type() { editor.delete(ret_ty.syntax().clone()); @@ -793,7 +787,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::token(parser::SyntaxKind::UNDERSCORE); @@ -812,7 +806,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::expr_literal("3").clone_for_update(); @@ -831,7 +825,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::ext::expr_unit().clone_for_update(); 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 4e08daba7bcf0..27ea03ec09e7d 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 @@ -35,7 +35,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) // - Propagate annotations - let SyntaxEditor { root, mut changes, annotations, make } = editor; + let SyntaxEditor { root, changes, annotations, make } = editor; + let mut changes = changes.into_inner(); + let annotations = annotations.into_inner(); let mappings = make.take(); let mut node_depths = FxHashMap::::default(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 1718f1a51c740..62fd7db8d3312 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -12,20 +12,21 @@ pub trait GetOrCreateWhereClause: ast::HasGenericParams { fn get_or_create_where_clause( &self, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, new_preds: impl Iterator, ) { + let make = editor.make(); let existing = self.where_clause(); let all_preds: Vec<_> = existing.iter().flat_map(|wc| wc.predicates()).chain(new_preds).collect(); - let new_where = editor.make().where_clause(all_preds); + let new_where = make.where_clause(all_preds); if let Some(existing) = &existing { editor.replace(existing.syntax(), new_where.syntax()); } else if let Some(pos) = self.where_clause_position() { editor.insert_all( pos, - vec![editor.make().whitespace(" ").into(), new_where.syntax().clone().into()], + vec![make.whitespace(" ").into(), new_where.syntax().clone().into()], ); } } @@ -109,7 +110,7 @@ impl GetOrCreateWhereClause for ast::Enum { impl SyntaxEditor { /// Adds a new generic param to the function using `SyntaxEditor` - pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) { + pub fn add_generic_param(&self, function: &Fn, new_param: GenericParam) { match function.generic_param_list() { Some(generic_param_list) => match generic_param_list.generic_params().last() { Some(last_param) => { @@ -173,7 +174,8 @@ impl SyntaxEditor { } } -fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { +fn get_or_insert_comma_after(editor: &SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { + let make = editor.make(); match syntax .siblings_with_tokens(Direction::Next) .filter_map(|it| it.into_token()) @@ -181,7 +183,7 @@ fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> { Some(it) => it, None => { - let comma = editor.make().token(T![,]); + let comma = make.token(T![,]); editor.insert(Position::after(syntax), &comma); comma } @@ -193,7 +195,7 @@ impl ast::AssocItemList { /// /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). - pub fn add_items(&self, editor: &mut SyntaxEditor, items: Vec) { + pub fn add_items(&self, editor: &SyntaxEditor, items: Vec) { let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -227,15 +229,16 @@ impl ast::AssocItemList { impl ast::Impl { pub fn get_or_create_assoc_item_list_with_editor( &self, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, ) -> ast::AssocItemList { + let make = editor.make(); if let Some(list) = self.assoc_item_list() { list } else { - let list = editor.make().assoc_item_list_empty(); + let list = make.assoc_item_list_empty(); editor.insert_all( Position::last_child_of(self.syntax()), - vec![editor.make().whitespace(" ").into(), list.syntax().clone().into()], + vec![make.whitespace(" ").into(), list.syntax().clone().into()], ); list } @@ -243,7 +246,8 @@ impl ast::Impl { } impl ast::VariantList { - pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { + pub fn add_variant(&self, editor: &SyntaxEditor, variant: &ast::Variant) { + let make = editor.make(); let (indent, position) = match self.variants().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -258,16 +262,16 @@ impl ast::VariantList { }, }; let elements: Vec = vec![ - editor.make().whitespace(&format!("{}{indent}", "\n")).into(), + make.whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), - editor.make().token(T![,]).into(), + make.token(T![,]).into(), ]; editor.insert_all(position, elements); } } impl ast::Fn { - pub fn replace_or_insert_body(&self, editor: &mut SyntaxEditor, body: ast::BlockExpr) { + pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { @@ -283,7 +287,8 @@ impl ast::Fn { } } -fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> { +fn normalize_ws_between_braces(editor: &SyntaxEditor, node: &SyntaxNode) -> Option<()> { + let make = editor.make(); let l = node .children_with_tokens() .filter_map(|it| it.into_token()) @@ -298,11 +303,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> match l.next_sibling_or_token() { Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { if ws.next_sibling_or_token()?.into_token()? == r { - editor.replace(ws, editor.make().whitespace(&format!("\n{indent}"))); + editor.replace(ws, make.whitespace(&format!("\n{indent}"))); } } Some(ws) if ws.kind() == T!['}'] => { - editor.insert(Position::after(l), editor.make().whitespace(&format!("\n{indent}"))); + editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}"))); } _ => (), } @@ -310,11 +315,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> } pub trait Removable: AstNode { - fn remove(&self, editor: &mut SyntaxEditor); + fn remove(&self, editor: &SyntaxEditor); } impl Removable for ast::TypeBoundList { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { Some(colon) => editor.delete_all(colon..=self.syntax().clone().into()), None => editor.delete(self.syntax()), @@ -323,7 +328,8 @@ impl Removable for ast::TypeBoundList { } impl Removable for ast::Use { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { + let make = editor.make(); let next_ws = self .syntax() .next_sibling_or_token() @@ -335,7 +341,7 @@ impl Removable for ast::Use { if rest.is_empty() { editor.delete(next_ws.syntax()); } else { - editor.replace(next_ws.syntax(), editor.make().whitespace(rest)); + editor.replace(next_ws.syntax(), make.whitespace(rest)); } } } @@ -345,7 +351,7 @@ impl Removable for ast::Use { } impl Removable for ast::UseTree { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { for dir in [Direction::Next, Direction::Prev] { if let Some(next_use_tree) = neighbor(self, dir) { let separators = self From 444522080024e2545436472720d37b26f869ba85 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 16 Apr 2026 00:09:58 +0530 Subject: [PATCH 44/98] Adapt changes to assist so that we no longer use mutable editor --- .../hir-expand/src/builtin/derive_macro.rs | 4 +- .../ide-assists/src/handlers/add_braces.rs | 5 +- .../src/handlers/add_explicit_dot_deref.rs | 7 ++- .../src/handlers/add_label_to_loop.rs | 18 +++--- .../src/handlers/add_missing_impl_members.rs | 15 ++--- .../src/handlers/add_missing_match_arms.rs | 14 ++--- .../src/handlers/add_turbo_fish.rs | 14 +++-- .../src/handlers/apply_demorgan.rs | 40 ++++++------ .../src/handlers/convert_bool_then.rs | 23 ++++--- .../src/handlers/convert_for_to_while_let.rs | 38 ++++++------ .../src/handlers/convert_from_to_tryfrom.rs | 32 ++++------ .../handlers/convert_iter_for_each_to_for.rs | 9 ++- .../src/handlers/convert_let_else_to_match.rs | 51 ++++++++-------- .../src/handlers/convert_match_to_let_else.rs | 5 +- .../convert_named_struct_to_tuple_struct.rs | 41 +++++++------ .../handlers/convert_range_for_to_while.rs | 31 +++++----- .../src/handlers/convert_to_guarded_return.rs | 25 ++++---- .../convert_tuple_return_type_to_struct.rs | 61 ++++++------------- .../convert_tuple_struct_to_named_struct.rs | 39 ++++++------ .../src/handlers/convert_while_to_loop.rs | 25 ++++---- .../handlers/destructure_struct_binding.rs | 55 ++++++++--------- .../src/handlers/destructure_tuple_binding.rs | 50 +++++++-------- .../src/handlers/desugar_try_expr.rs | 46 ++++++-------- .../src/handlers/expand_glob_import.rs | 9 +-- .../src/handlers/expand_rest_pattern.rs | 40 ++++++------ .../extract_expressions_from_format_string.rs | 16 ++--- .../extract_struct_from_enum_variant.rs | 48 +++++++-------- .../src/handlers/extract_type_alias.rs | 23 +++---- .../src/handlers/extract_variable.rs | 39 ++++++------ .../src/handlers/fix_visibility.rs | 2 +- .../ide-assists/src/handlers/flip_binexpr.rs | 21 ++++--- .../ide-assists/src/handlers/flip_comma.rs | 2 +- .../src/handlers/flip_or_pattern.rs | 2 +- .../src/handlers/flip_trait_bound.rs | 2 +- .../handlers/generate_blanket_trait_impl.rs | 30 +++++---- .../src/handlers/generate_default_from_new.rs | 7 ++- .../src/handlers/generate_delegate_methods.rs | 38 ++++++------ .../src/handlers/generate_delegate_trait.rs | 6 +- .../src/handlers/generate_deref.rs | 58 +++++++----------- .../src/handlers/generate_derive.rs | 14 +++-- .../src/handlers/generate_enum_variant.rs | 10 +-- .../src/handlers/generate_fn_type_alias.rs | 23 +++---- .../handlers/generate_from_impl_for_enum.rs | 7 ++- .../src/handlers/generate_getter_or_setter.rs | 17 +++--- .../ide-assists/src/handlers/generate_impl.rs | 45 ++++++-------- .../src/handlers/generate_mut_trait_impl.rs | 31 +++++----- .../ide-assists/src/handlers/generate_new.rs | 57 +++++++---------- .../generate_single_field_struct_from.rs | 45 +++++++------- .../src/handlers/generate_trait_from_impl.rs | 23 +++---- .../src/handlers/inline_local_variable.rs | 15 ++--- .../src/handlers/inline_type_alias.rs | 12 ++-- .../src/handlers/introduce_named_lifetime.rs | 49 ++++++++------- .../introduce_named_type_parameter.rs | 9 ++- .../ide-assists/src/handlers/merge_imports.rs | 6 +- .../ide-assists/src/handlers/move_bounds.rs | 20 +++--- .../ide-assists/src/handlers/move_guard.rs | 38 ++++++------ .../src/handlers/promote_local_to_const.rs | 17 ++---- .../src/handlers/pull_assignment_up.rs | 10 +-- .../src/handlers/qualify_method_call.rs | 10 +-- .../ide-assists/src/handlers/qualify_path.rs | 24 ++++---- .../ide-assists/src/handlers/raw_string.rs | 2 +- .../ide-assists/src/handlers/remove_dbg.rs | 4 +- .../src/handlers/remove_else_branches.rs | 2 +- .../ide-assists/src/handlers/remove_mut.rs | 2 +- .../src/handlers/remove_parentheses.rs | 5 +- .../src/handlers/remove_unused_param.rs | 4 +- .../src/handlers/reorder_fields.rs | 10 ++- .../src/handlers/reorder_impl_items.rs | 2 +- .../src/handlers/replace_arith_op.rs | 13 ++-- .../replace_derive_with_manual_impl.rs | 44 ++++++------- .../src/handlers/replace_if_let_with_match.rs | 57 ++++++++--------- .../replace_is_method_with_if_let_method.rs | 17 +++--- .../src/handlers/replace_let_with_if_let.rs | 20 +++--- .../replace_named_generic_with_impl.rs | 7 ++- .../replace_qualified_name_with_use.rs | 10 +-- .../ide-assists/src/handlers/sort_items.rs | 2 +- .../src/handlers/toggle_macro_delimiter.rs | 20 +++--- .../src/handlers/unmerge_imports.rs | 14 ++--- .../src/handlers/unmerge_match_arm.rs | 11 ++-- .../src/handlers/unqualify_method_call.rs | 13 ++-- .../ide-assists/src/handlers/unwrap_block.rs | 18 +++--- .../src/handlers/unwrap_return_type.rs | 16 ++--- .../handlers/unwrap_type_to_generic_arg.rs | 2 +- .../src/handlers/wrap_return_type.rs | 23 +++---- .../src/handlers/wrap_unwrap_cfg_attr.rs | 37 ++++++----- .../crates/ide-assists/src/utils.rs | 15 ++--- .../crates/ide-db/src/imports/insert_use.rs | 25 ++++---- .../crates/ide-db/src/path_transform.rs | 20 +++--- .../src/handlers/type_mismatch.rs | 9 +-- .../crates/syntax/src/syntax_editor/edits.rs | 4 +- 90 files changed, 883 insertions(+), 1018 deletions(-) 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 11e1f9dd0cadd..8f513a2bcf666 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 @@ -1294,8 +1294,8 @@ fn coerce_pointee_expand( )); } - let (mut editor, strukt) = SyntaxEditor::with_ast_node(strukt); - strukt.get_or_create_where_clause(&mut editor, new_predicates.into_iter()); + let (editor, strukt) = SyntaxEditor::with_ast_node(strukt); + strukt.get_or_create_where_clause(&editor, new_predicates.into_iter()); let edit = editor.finish(); let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap(); let adt = ast::Adt::Struct(strukt.clone()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index 1370fe3226190..c5ec88ffb88a3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs @@ -56,10 +56,11 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }, expr.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(expr.syntax()); + let editor = builder.make_editor(expr.syntax()); + let make = editor.make(); let new_expr = expr.reset_indent().indent(1.into()); - let block_expr = editor.make().block_expr(None, Some(new_expr)); + let block_expr = make.block_expr(None, Some(new_expr)); editor.replace(expr.syntax(), block_expr.indent(expr.indent_level()).syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs index 5251860ba0527..1809b8f305b62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs @@ -53,14 +53,15 @@ pub(crate) fn add_explicit_method_call_deref( "Insert explicit method call derefs", dot_token.text_range(), |builder| { - let mut editor = builder.make_editor(method_call_expr.syntax()); + let editor = builder.make_editor(method_call_expr.syntax()); + let make = editor.make(); let mut expr = receiver.clone(); for adjust_kind in adjustments { - expr = adjust_kind.wrap_expr(expr, editor.make()); + expr = adjust_kind.wrap_expr(expr, make); } - expr = editor.make().expr_paren(expr).into(); + expr = make.expr_paren(expr).into(); editor.replace(receiver.syntax(), expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 7e3b55b673e4f..41e9b6cc84539 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -42,13 +42,14 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O "Add Label", loop_expr.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(loop_expr.syntax()); + let editor = builder.make_editor(loop_expr.syntax()); + let make = editor.make(); - let label = editor.make().lifetime("'l"); + let label = make.lifetime("'l"); let elements = vec![ label.syntax().clone().into(), - editor.make().token(T![:]).into(), - editor.make().whitespace(" ").into(), + make.token(T![:]).into(), + make.whitespace(" ").into(), ]; editor.insert_all(Position::before(&loop_kw), elements); @@ -64,7 +65,7 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O _ => return, }; if let Some(token) = token { - insert_label_after_token(&mut editor, &token, ctx, builder); + insert_label_after_token(&editor, &token, ctx, builder); } }); @@ -83,13 +84,14 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { } fn insert_label_after_token( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, token: &SyntaxToken, ctx: &AssistContext<'_>, builder: &mut SourceChangeBuilder, ) { - let label = editor.make().lifetime("'l"); - let elements = vec![editor.make().whitespace(" ").into(), label.syntax().clone().into()]; + let make = editor.make(); + let label = make.lifetime("'l"); + let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); if let Some(cap) = ctx.config.snippet_cap { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 71a0758773696..d1f1f9f123387 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -148,9 +148,10 @@ fn add_missing_impl_members_inner( let target = impl_def.syntax().text_range(); acc.add(AssistId::quick_fix(assist_id), label, target, |edit| { - let mut editor = edit.make_editor(impl_def.syntax()); + let editor = edit.make_editor(impl_def.syntax()); + let make = editor.make(); let new_item = add_trait_assoc_items_to_impl( - editor.make(), + make, &ctx.sema, ctx.config, &missing_items, @@ -166,7 +167,7 @@ fn add_missing_impl_members_inner( let mut first_new_item = if let DefaultMethods::No = mode && let ast::AssocItem::Fn(func) = &first_new_item && let Some(body) = try_gen_trait_body( - editor.make(), + make, ctx, func, trait_ref, @@ -175,7 +176,7 @@ fn add_missing_impl_members_inner( ) && let Some(func_body) = func.body() { - let (mut func_editor, _) = SyntaxEditor::new(first_new_item.syntax().clone()); + let (func_editor, _) = SyntaxEditor::new(first_new_item.syntax().clone()); func_editor.replace(func_body.syntax(), body.syntax()); ast::AssocItem::cast(func_editor.finish().new_root().clone()) } else { @@ -189,12 +190,12 @@ fn add_missing_impl_members_inner( .collect::>(); if let Some(assoc_item_list) = impl_def.assoc_item_list() { - assoc_item_list.add_items(&mut editor, new_assoc_items); + assoc_item_list.add_items(&editor, new_assoc_items); } else { - let assoc_item_list = editor.make().assoc_item_list(new_assoc_items); + let assoc_item_list = make.assoc_item_list(new_assoc_items); editor.insert_all( Position::after(impl_def.syntax()), - vec![editor.make().whitespace(" ").into(), assoc_item_list.syntax().clone().into()], + vec![make.whitespace(" ").into(), assoc_item_list.syntax().clone().into()], ); first_new_item = assoc_item_list.assoc_items().next(); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 588e65900cb4a..9f267daa10ca8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -271,12 +271,12 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } }; - let mut editor = builder.make_editor(&old_place); + let editor = builder.make_editor(&old_place); let mut arms_edit = ArmsEdit { match_arm_list, place: old_place, last_arm: None }; - arms_edit.remove_wildcard_arms(ctx, &mut editor); - arms_edit.add_comma_after_last_arm(ctx, &make, &mut editor); - arms_edit.append_arms(&missing_arms, &make, &mut editor); + arms_edit.remove_wildcard_arms(ctx, &editor); + arms_edit.add_comma_after_last_arm(ctx, &make, &editor); + arms_edit.append_arms(&missing_arms, &make, &editor); if let Some(cap) = ctx.config.snippet_cap { if let Some(it) = missing_arms @@ -357,7 +357,7 @@ struct ArmsEdit { } impl ArmsEdit { - fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &mut SyntaxEditor) { + fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &SyntaxEditor) { for arm in self.match_arm_list.arms() { if !matches!(arm.pat(), Some(Pat::WildcardPat(_))) { self.last_arm = Some(arm); @@ -386,7 +386,7 @@ impl ArmsEdit { } } - fn append_arms(&self, arms: &[ast::MatchArm], make: &SyntaxFactory, editor: &mut SyntaxEditor) { + fn append_arms(&self, arms: &[ast::MatchArm], make: &SyntaxFactory, editor: &SyntaxEditor) { let Some(mut before) = self.place.last_token() else { stdx::never!("match arm list not contain any token"); return; @@ -419,7 +419,7 @@ impl ArmsEdit { &self, ctx: &AssistContext<'_>, make: &SyntaxFactory, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, ) { if let Some(last_arm) = &self.last_arm && last_arm.comma_token().is_none() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 45d559152bf71..490a91d594939 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -93,21 +93,23 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti "Add `: _` before assignment operator", ident.text_range(), |builder| { - let mut editor = builder.make_editor(let_stmt.syntax()); + let editor = builder.make_editor(let_stmt.syntax()); + let make = editor.make(); if let_stmt.semicolon_token().is_none() { editor.insert( Position::last_child_of(let_stmt.syntax()), - editor.make().token(syntax::SyntaxKind::SEMICOLON), + make.token(syntax::SyntaxKind::SEMICOLON), ); } - let placeholder_ty = editor.make().ty_placeholder(); + let make = editor.make(); + let placeholder_ty = make.ty_placeholder(); if let Some(pat) = let_stmt.pat() { let elements = vec![ - editor.make().token(syntax::SyntaxKind::COLON).into(), - editor.make().whitespace(" ").into(), + make.token(syntax::SyntaxKind::COLON).into(), + make.whitespace(" ").into(), placeholder_ty.syntax().clone().into(), ]; editor.insert_all(Position::after(pat.syntax()), elements); @@ -140,7 +142,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ident.text_range(), |builder| { builder.trigger_parameter_hints(); - let mut editor = match &turbofish_target { + let editor = match &turbofish_target { Either::Left(it) => builder.make_editor(it.syntax()), Either::Right(it) => builder.make_editor(it.syntax()), }; 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 a3a0c252f6a46..b87a757047ac5 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 @@ -80,8 +80,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti _ => return None, }; - let (mut editor, demorganed) = SyntaxEditor::with_ast_node(&bin_expr); - editor.replace(demorganed.op_token()?, editor.make().token(inv_token)); + let (editor, demorganed) = SyntaxEditor::with_ast_node(&bin_expr); + let make = editor.make(); + editor.replace(demorganed.op_token()?, make.token(inv_token)); let mut exprs = VecDeque::from([ (bin_expr.lhs()?, demorganed.lhs()?, prec), @@ -92,13 +93,13 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti if let BinExpr(bin_expr) = &expr { if let BinExpr(cbin_expr) = &demorganed { if op == bin_expr.op_kind()? { - editor.replace(cbin_expr.op_token()?, editor.make().token(inv_token)); + editor.replace(cbin_expr.op_token()?, make.token(inv_token)); exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec)); exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec)); } else { - let mut inv = invert_boolean_expression(editor.make(), expr); + let mut inv = invert_boolean_expression(make, expr); if precedence(&inv).needs_parentheses_in(prec) { - inv = editor.make().expr_paren(inv).into(); + inv = make.expr_paren(inv).into(); } editor.replace(demorganed.syntax(), inv.syntax()); } @@ -106,9 +107,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti return None; } } else { - let mut inv = invert_boolean_expression(editor.make(), demorganed.clone()); + let mut inv = invert_boolean_expression(make, demorganed.clone()); if precedence(&inv).needs_parentheses_in(prec) { - inv = editor.make().expr_paren(inv).into(); + inv = make.expr_paren(inv).into(); } editor.replace(demorganed.syntax(), inv.syntax()); } @@ -123,7 +124,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti "Apply De Morgan's law", op_range, |builder| { - let mut editor = builder.make_editor(bin_expr.syntax()); + let editor = builder.make_editor(bin_expr.syntax()); + let make = editor.make(); let (target_node, result_expr) = if let Some(neg_expr) = bin_expr .syntax() @@ -139,9 +141,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti bin_expr.syntax().parent().and_then(ast::ParenExpr::cast) { cov_mark::hit!(demorgan_double_parens); - (paren_expr.syntax().clone(), add_bang_paren(editor.make(), demorganed)) + (paren_expr.syntax().clone(), add_bang_paren(make, demorganed)) } else { - (bin_expr.syntax().clone(), add_bang_paren(editor.make(), demorganed)) + (bin_expr.syntax().clone(), add_bang_paren(make, demorganed)) }; let final_expr = if target_node @@ -149,7 +151,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti .is_some_and(|p| result_expr.needs_parens_in_place_of(&p, &target_node)) { cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); - editor.make().expr_paren(result_expr).into() + make.expr_paren(result_expr).into() } else { result_expr }; @@ -202,17 +204,18 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> label, op_range, |builder| { - let mut editor = builder.make_editor(method_call.syntax()); + let editor = builder.make_editor(method_call.syntax()); + let make = editor.make(); // replace the method name let new_name = match name.text().as_str() { - "all" => editor.make().name_ref("any"), - "any" => editor.make().name_ref("all"), + "all" => make.name_ref("any"), + "any" => make.name_ref("all"), _ => unreachable!(), }; editor.replace(name.syntax(), new_name.syntax()); // negate all tail expressions in the closure body - let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, e); + let tail_cb = &mut |e: &_| tail_cb_impl(&editor, e); walk_expr(&closure_body, &mut |expr| { if let ast::Expr::ReturnExpr(ret_expr) = expr && let Some(ret_expr_arg) = &ret_expr.expr() @@ -233,10 +236,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> prefix_expr.op_token().expect("prefix expression always has an operator"), ); } else { - editor.insert( - Position::before(method_call.syntax()), - editor.make().token(SyntaxKind::BANG), - ); + editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG)); } builder.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -265,7 +265,7 @@ fn validate_method_call_expr( it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr)) } -fn tail_cb_impl(editor: &mut SyntaxEditor, e: &ast::Expr) { +fn tail_cb_impl(editor: &SyntaxEditor, e: &ast::Expr) { match e { ast::Expr::BreakExpr(break_expr) => { if let Some(break_expr_arg) = break_expr.expr() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 8b0b7d3b0713f..cb2e7d6d100ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -77,7 +77,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> "Convert `if` expression to `bool::then` call", target, |builder| { - let (mut editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); + let (editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); // Rewrite all `Some(e)` in tail position to `e` for_each_tail_expr(&closure_body, &mut |e| { let e = match e { @@ -95,13 +95,13 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> let edit = editor.finish(); let closure_body = ast::Expr::cast(edit.new_root().clone()).unwrap(); - let mut editor = builder.make_editor(expr.syntax()); + let editor = builder.make_editor(expr.syntax()); + let make = editor.make(); let closure_body = match closure_body { ast::Expr::BlockExpr(block) => unwrap_trivial_block(block), e => e, }; - let cond = - if invert_cond { invert_boolean_expression(editor.make(), cond) } else { cond }; + let cond = if invert_cond { invert_boolean_expression(make, cond) } else { cond }; let parenthesize = matches!( cond, @@ -124,11 +124,9 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::YieldExpr(_) ); - let cond = if parenthesize { editor.make().expr_paren(cond).into() } else { cond }; - let arg_list = - editor.make().arg_list(Some(editor.make().expr_closure(None, closure_body).into())); - let mcall = - editor.make().expr_method_call(cond, editor.make().name_ref("then"), arg_list); + let cond = if parenthesize { make.expr_paren(cond).into() } else { cond }; + let arg_list = make.arg_list(Some(make.expr_closure(None, closure_body).into())); + let mcall = make.expr_method_call(cond, make.name_ref("then"), arg_list); editor.replace(expr.syntax(), mcall.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -187,7 +185,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> e => mapless_make.block_expr(None, Some(e)), }; - let (mut editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); + let (editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body); // Wrap all tails in `Some(...)` let none_path = mapless_make.expr_path(mapless_make.ident_path("None")); let some_path = mapless_make.expr_path(mapless_make.ident_path("Some")); @@ -210,7 +208,8 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> let edit = editor.finish(); let closure_body = ast::BlockExpr::cast(edit.new_root().clone()).unwrap(); - let mut editor = builder.make_editor(mcall.syntax()); + let editor = builder.make_editor(mcall.syntax()); + let make = editor.make(); let cond = match &receiver { ast::Expr::ParenExpr(expr) => expr.expr().unwrap_or(receiver), @@ -221,7 +220,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> .expr_if( cond, closure_body, - Some(ast::ElseBranch::Block(editor.make().block_expr(None, Some(none_path)))), + Some(ast::ElseBranch::Block(make.block_expr(None, Some(none_path)))), ) .indent(mcall.indent_level()); editor.replace(mcall.syntax().clone(), if_expr.syntax().clone()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index 07ebe981b4f1c..9eb4c0584b362 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -48,24 +48,22 @@ pub(crate) fn convert_for_loop_to_while_let( "Replace this for loop with `while let`", for_loop.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(for_loop.syntax()); + let editor = builder.make_editor(for_loop.syntax()); + let make = editor.make(); let (iterable, method) = if impls_core_iter(&ctx.sema, &iterable) { (iterable, None) } else if let Some((expr, method)) = is_ref_and_impls_iter_method(&ctx.sema, &iterable) { - (expr, Some(editor.make().name_ref(method.as_str()))) + (expr, Some(make.name_ref(method.as_str()))) } else if let ast::Expr::RefExpr(_) = iterable { - ( - editor.make().expr_paren(iterable).into(), - Some(editor.make().name_ref("into_iter")), - ) + (make.expr_paren(iterable).into(), Some(make.name_ref("into_iter"))) } else { - (iterable, Some(editor.make().name_ref("into_iter"))) + (iterable, Some(make.name_ref("into_iter"))) }; let iterable = if let Some(method) = method { - editor.make().expr_method_call(iterable, method, editor.make().arg_list([])).into() + make.expr_method_call(iterable, method, make.arg_list([])).into() } else { iterable }; @@ -75,8 +73,8 @@ pub(crate) fn convert_for_loop_to_while_let( ); let tmp_var = new_name.suggest_name("iter"); - let mut_expr = editor.make().let_stmt( - editor.make().ident_pat(false, true, editor.make().name(&tmp_var)).into(), + let mut_expr = make.let_stmt( + make.ident_pat(false, true, make.name(&tmp_var)).into(), None, Some(iterable), ); @@ -84,26 +82,26 @@ pub(crate) fn convert_for_loop_to_while_let( if let Some(label) = for_loop.label() { let label = label.syntax(); - editor.insert(Position::before(for_loop.syntax()), editor.make().whitespace(" ")); + editor.insert(Position::before(for_loop.syntax()), make.whitespace(" ")); editor.insert(Position::before(for_loop.syntax()), label); } - crate::utils::insert_attributes(for_loop.syntax(), &mut editor, for_loop.attrs()); + crate::utils::insert_attributes(for_loop.syntax(), &editor, for_loop.attrs()); editor.insert( Position::before(for_loop.syntax()), - editor.make().whitespace(format!("\n{indent}").as_str()), + make.whitespace(format!("\n{indent}").as_str()), ); editor.insert(Position::before(for_loop.syntax()), mut_expr.syntax()); - let opt_pat = editor.make().tuple_struct_pat(editor.make().ident_path("Some"), [pat]); - let iter_next_expr = editor.make().expr_method_call( - editor.make().expr_path(editor.make().ident_path(&tmp_var)), - editor.make().name_ref("next"), - editor.make().arg_list([]), + let opt_pat = make.tuple_struct_pat(make.ident_path("Some"), [pat]); + let iter_next_expr = make.expr_method_call( + make.expr_path(make.ident_path(&tmp_var)), + make.name_ref("next"), + make.arg_list([]), ); - let cond = editor.make().expr_let(opt_pat.into(), iter_next_expr.into()); + let cond = make.expr_let(opt_pat.into(), iter_next_expr.into()); - let while_loop = editor.make().expr_while_loop(cond.into(), body); + let while_loop = make.expr_while_loop(cond.into(), body); editor.replace(for_loop.syntax(), while_loop.syntax()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index dc977b3447419..18f3ced414026 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -74,32 +74,24 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> "Convert From to TryFrom", impl_.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(impl_.syntax()); + let editor = builder.make_editor(impl_.syntax()); + let make = editor.make(); - editor.replace( - trait_ty.syntax(), - editor.make().ty(&format!("TryFrom<{from_type}>")).syntax(), - ); + editor.replace(trait_ty.syntax(), make.ty(&format!("TryFrom<{from_type}>")).syntax()); editor.replace( from_fn_return_type.syntax(), - editor.make().ty("Result").syntax(), + make.ty("Result").syntax(), ); - editor.replace(from_fn_name.syntax(), editor.make().name("try_from").syntax()); - editor.replace(tail_expr.syntax(), wrap_ok(editor.make(), tail_expr.clone()).syntax()); + editor.replace(from_fn_name.syntax(), make.name("try_from").syntax()); + editor.replace(tail_expr.syntax(), wrap_ok(make, tail_expr.clone()).syntax()); for r in return_exprs { - let t = r.expr().unwrap_or_else(|| editor.make().expr_unit()); - editor.replace(t.syntax(), wrap_ok(editor.make(), t.clone()).syntax()); + let t = r.expr().unwrap_or_else(|| make.expr_unit()); + editor.replace(t.syntax(), wrap_ok(make, t.clone()).syntax()); } - let error_type_alias = editor.make().ty_alias( - None, - "Error", - None, - None, - None, - Some((editor.make().ty("()"), None)), - ); + let error_type_alias = + make.ty_alias(None, "Error", None, None, None, Some((make.ty("()"), None))); let error_type = ast::AssocItem::TypeAlias(error_type_alias); if let Some(cap) = ctx.config.snippet_cap @@ -114,9 +106,9 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> editor.insert_all( Position::after(associated_l_curly), vec![ - editor.make().whitespace(&format!("\n{indent}")).syntax_element(), + make.whitespace(&format!("\n{indent}")).syntax_element(), error_type.syntax().syntax_element(), - editor.make().whitespace("\n").syntax_element(), + make.whitespace("\n").syntax_element(), ], ); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index a0dcf62e25d46..ecfbc3a07daf9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -58,19 +58,18 @@ pub(crate) fn convert_iter_for_each_to_for( range, |builder| { let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax); - let mut editor = builder.make_editor(target_node); + let editor = builder.make_editor(target_node); + let make = editor.make(); let indent = stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level); let block = match body { ast::Expr::BlockExpr(block) => block.reset_indent(), - _ => { - editor.make().block_expr(Vec::new(), Some(body.reset_indent().indent(1.into()))) - } + _ => make.block_expr(Vec::new(), Some(body.reset_indent().indent(1.into()))), } .indent(indent); - let expr_for_loop = editor.make().expr_for_loop(param, receiver, block); + let expr_for_loop = make.expr_for_loop(param, receiver, block); editor.replace(target_node, expr_for_loop.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 75d7d0a836e3f..885750b1aabe9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let (mut editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); // Should focus on the `else` token to trigger let let_stmt = ctx .find_token_syntax_at_offset(T![else]) @@ -47,7 +47,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' } let pat = let_stmt.pat()?; let mut idents = Vec::default(); - let pat_without_mut = remove_mut_and_collect_idents(&mut editor, &pat, &mut idents)?; + let pat_without_mut = remove_mut_and_collect_idents(&editor, &pat, &mut idents)?; let bindings = idents .into_iter() .filter_map(|ref pat| { @@ -69,14 +69,13 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' }, let_stmt.syntax().text_range(), |builder| { + let make = editor.make(); let binding_paths = bindings .iter() - .map(|(name, _)| { - editor.make().expr_path(editor.make().ident_path(&name.to_string())) - }) + .map(|(name, _)| make.expr_path(make.ident_path(&name.to_string()))) .collect::>(); - let binding_arm = editor.make().match_arm( + let binding_arm = make.match_arm( pat_without_mut, None, // There are three possible cases: @@ -85,16 +84,15 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' // - Single binding: `Some(it) => it` // - Multiple bindings: `Foo::Bar { a, b, .. } => (a, b)` match binding_paths.len() { - 0 => editor.make().expr_empty_block().into(), + 0 => make.expr_empty_block().into(), 1 => binding_paths[0].clone(), - _ => editor.make().expr_tuple(binding_paths).into(), + _ => make.expr_tuple(binding_paths).into(), }, ); - let else_arm = - editor.make().match_arm(editor.make().wildcard_pat().into(), None, else_expr); + let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr); let arms = [binding_arm, else_arm].map(|arm| arm.indent(1.into())); - let match_ = editor.make().expr_match(init, editor.make().match_arm_list(arms)); + let match_ = make.expr_match(init, make.match_arm_list(arms)); let match_ = match_.indent(let_stmt.indent_level()); if bindings.is_empty() { @@ -102,13 +100,13 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' } else { let ident_pats = bindings .into_iter() - .map(|(name, is_mut)| editor.make().ident_pat(false, is_mut, name).into()) + .map(|(name, is_mut)| make.ident_pat(false, is_mut, name).into()) .collect::>(); - let new_let_stmt = editor.make().let_stmt( + let new_let_stmt = make.let_stmt( if ident_pats.len() == 1 { ident_pats[0].clone() } else { - editor.make().tuple_pat(ident_pats).into() + make.tuple_pat(ident_pats).into() }, None, Some(match_.into()), @@ -121,14 +119,15 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' } fn remove_mut_and_collect_idents( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, pat: &ast::Pat, acc: &mut Vec, ) -> Option { + let make = editor.make(); Some(match pat { ast::Pat::IdentPat(p) => { acc.push(p.clone()); - let non_mut_pat = editor.make().ident_pat( + let non_mut_pat = make.ident_pat( p.ref_token().is_some(), p.ref_token().is_some() && p.mut_token().is_some(), p.name()?, @@ -142,18 +141,18 @@ fn remove_mut_and_collect_idents( } ast::Pat::BoxPat(p) => { let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; - editor.make().box_pat(pat).into() + make.box_pat(pat).into() } ast::Pat::OrPat(p) => { let pats = p .pats() .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc)) .collect::>>()?; - editor.make().or_pat(pats, p.leading_pipe().is_some()).into() + make.or_pat(pats, p.leading_pipe().is_some()).into() } ast::Pat::ParenPat(p) => { let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; - editor.make().paren_pat(pat).into() + make.paren_pat(pat).into() } ast::Pat::RangePat(p) => { let start = if let Some(start) = p.start() { @@ -166,7 +165,7 @@ fn remove_mut_and_collect_idents( } else { None }; - editor.make().range_pat(start, end).into() + make.range_pat(start, end).into() } ast::Pat::RecordPat(p) => { let fields = p @@ -175,9 +174,9 @@ fn remove_mut_and_collect_idents( .map(|field| { remove_mut_and_collect_idents(editor, &field.pat()?, acc).map(|pat| { if let Some(name_ref) = field.name_ref() { - editor.make().record_pat_field(name_ref, pat) + make.record_pat_field(name_ref, pat) } else { - editor.make().record_pat_field_shorthand(pat) + make.record_pat_field_shorthand(pat) } }) }) @@ -199,7 +198,7 @@ fn remove_mut_and_collect_idents( p.clone().into() } else { let pat = remove_mut_and_collect_idents(editor, &inner, acc)?; - editor.make().ref_pat(pat).into() + make.ref_pat(pat).into() } } ast::Pat::SlicePat(p) => { @@ -207,21 +206,21 @@ fn remove_mut_and_collect_idents( .pats() .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc)) .collect::>>()?; - editor.make().slice_pat(pats).into() + make.slice_pat(pats).into() } ast::Pat::TuplePat(p) => { let pats = p .fields() .map(|field| remove_mut_and_collect_idents(editor, &field, acc)) .collect::>>()?; - editor.make().tuple_pat(pats).into() + make.tuple_pat(pats).into() } ast::Pat::TupleStructPat(p) => { let fields = p .fields() .map(|field| remove_mut_and_collect_idents(editor, &field, acc)) .collect::>>()?; - editor.make().tuple_struct_pat(p.path()?, fields).into() + make.tuple_struct_pat(p.path()?, fields).into() } ast::Pat::RestPat(_) | ast::Pat::LiteralPat(_) 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 54784983d7bdf..2b658248171a0 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 @@ -121,7 +121,8 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti // Rename `extracted` with `binding` in `pat`. fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode { - let (mut editor, syntax) = SyntaxEditor::new(pat.syntax().clone()); + let (editor, syntax) = SyntaxEditor::new(pat.syntax().clone()); + let make = editor.make(); let extracted = extracted .iter() .map(|e| e.syntax().text_range() - pat.syntax().text_range().start()) @@ -138,7 +139,7 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn record_pat_field.syntax(), editor .make() - .record_pat_field(editor.make().name_ref(&name_ref.text()), binding.clone()) + .record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) .syntax(), ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 42f310c35eb75..21404564fac86 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -100,7 +100,7 @@ fn edit_struct_def( // Note that we don't need to consider macro files in this function because this is // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields.fields().filter_map(|f| { - let (mut editor, field) = + let (editor, field) = SyntaxEditor::with_ast_node(&ast::make::tuple_field(f.visibility(), f.ty()?)); editor.insert_all( Position::first_child_of(field.syntax()), @@ -111,9 +111,10 @@ fn edit_struct_def( Some(field) }); - let mut editor = builder.make_editor(strukt.syntax()); + let editor = builder.make_editor(strukt.syntax()); + let make = editor.make(); - let tuple_fields = editor.make().tuple_field_list(tuple_fields); + let tuple_fields = make.tuple_field_list(tuple_fields); let mut elements = vec![tuple_fields.syntax().clone().into()]; if let Either::Left(strukt) = strukt { @@ -121,10 +122,10 @@ fn edit_struct_def( editor.delete(w.syntax()); elements.extend([ - editor.make().whitespace("\n").into(), + make.whitespace("\n").into(), remove_trailing_comma(w).into(), - editor.make().token(T![;]).into(), - editor.make().whitespace("\n").into(), + make.token(T![;]).into(), + make.whitespace("\n").into(), ]); if let Some(tok) = strukt @@ -136,7 +137,7 @@ fn edit_struct_def( editor.delete(tok); } } else { - elements.push(editor.make().token(T![;]).into()); + elements.push(make.token(T![;]).into()); } } editor.replace_with_many(record_fields.syntax(), elements); @@ -165,18 +166,18 @@ fn edit_struct_references( for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let mut edit = builder.make_editor(source.syntax()); + let editor = builder.make_editor(source.syntax()); for r in refs { - process_struct_name_reference(ctx, r, &mut edit, &source); + process_struct_name_reference(ctx, r, &editor, &source); } - builder.add_file_edits(file_id.file_id(ctx.db()), edit); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); } } fn process_struct_name_reference( ctx: &AssistContext<'_>, r: FileReference, - edit: &mut SyntaxEditor, + edit: &SyntaxEditor, source: &ast::SourceFile, ) -> Option<()> { // First check if it's the last semgnet of a path that directly belongs to a record @@ -229,7 +230,7 @@ fn process_struct_name_reference( fn record_to_tuple_struct_like( ctx: &AssistContext<'_>, source: &ast::SourceFile, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, field_list: T, fields: impl FnOnce(&T) -> I, ) -> Option<()> @@ -237,6 +238,7 @@ where T: AstNode, I: IntoIterator, { + let make = editor.make(); let orig = ctx.sema.original_range_opt(field_list.syntax())?; let list_range = cover_edit_range(source.syntax(), orig.range); @@ -252,11 +254,11 @@ where if l_curly.kind() == T!['{'] { delete_whitespace(editor, l_curly.prev_token()); delete_whitespace(editor, l_curly.next_token()); - editor.replace(l_curly, editor.make().token(T!['('])); + editor.replace(l_curly, make.token(T!['('])); } if r_curly.kind() == T!['}'] { delete_whitespace(editor, r_curly.prev_token()); - editor.replace(r_curly, editor.make().token(T![')'])); + editor.replace(r_curly, make.token(T![')'])); } for name_ref in fields(&field_list) { @@ -294,7 +296,8 @@ fn edit_field_references( let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let mut editor = builder.make_editor(source.syntax()); + let editor = builder.make_editor(source.syntax()); + let make = editor.make(); for r in refs { if let Some(name_ref) = r.name.as_name_ref() { @@ -302,9 +305,7 @@ fn edit_field_references( if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { editor.replace_all( cover_edit_range(source.syntax(), r.range), - vec![ - editor.make().name_ref(&index.to_string()).syntax().clone().into(), - ], + vec![make.name_ref(&index.to_string()).syntax().clone().into()], ); } } @@ -315,7 +316,7 @@ fn edit_field_references( } } -fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option) { +fn delete_whitespace(edit: &SyntaxEditor, whitespace: Option) { let Some(whitespace) = whitespace else { return }; let NodeOrToken::Token(token) = whitespace.syntax_element() else { return }; @@ -325,7 +326,7 @@ fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option) } fn remove_trailing_comma(w: ast::WhereClause) -> SyntaxNode { - let (mut editor, w) = SyntaxEditor::new(w.syntax().clone()); + let (editor, w) = SyntaxEditor::new(w.syntax().clone()); if let Some(last) = w.last_child_or_token() && last.kind() == T![,] { 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 b0b9695f0f3a8..a17bf02b3c4f1 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 @@ -52,34 +52,35 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext< description, for_.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(for_.syntax()); + let editor = builder.make_editor(for_.syntax()); + let make = editor.make(); let indent = for_.indent_level(); - let pat = editor.make().ident_pat(pat.ref_token().is_some(), true, name.clone()); - let let_stmt = editor.make().let_stmt(pat.into(), None, Some(start)); + let pat = make.ident_pat(pat.ref_token().is_some(), true, name.clone()); + let let_stmt = make.let_stmt(pat.into(), None, Some(start)); editor.insert_all( Position::before(for_.syntax()), vec![ let_stmt.syntax().syntax_element(), - editor.make().whitespace(&format!("\n{}", indent)).syntax_element(), + make.whitespace(&format!("\n{}", indent)).syntax_element(), ], ); let mut elements = vec![]; - let var_expr = editor.make().expr_path(editor.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, }); if let Some(end) = end { elements.extend([ - editor.make().token(T![while]).syntax_element(), - editor.make().whitespace(" ").syntax_element(), - editor.make().expr_bin(var_expr.clone(), op, end).syntax().syntax_element(), + make.token(T![while]).syntax_element(), + make.whitespace(" ").syntax_element(), + make.expr_bin(var_expr.clone(), op, end).syntax().syntax_element(), ]); } else { - elements.push(editor.make().token(T![loop]).syntax_element()); + elements.push(make.token(T![loop]).syntax_element()); } editor.replace_all( @@ -89,11 +90,11 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext< let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) }; let incrementer = vec![ - editor.make().whitespace(&format!("\n{}", indent + 1)).syntax_element(), - editor.make().expr_bin(var_expr, op, step).syntax().syntax_element(), - editor.make().token(T![;]).syntax_element(), + make.whitespace(&format!("\n{}", indent + 1)).syntax_element(), + make.expr_bin(var_expr, op, step).syntax().syntax_element(), + make.token(T![;]).syntax_element(), ]; - process_loop_body(body, label, &mut editor, incrementer); + process_loop_body(body, label, &editor, incrementer); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -118,7 +119,7 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option, fn process_loop_body( body: ast::StmtList, label: Option, - edit: &mut SyntaxEditor, + edit: &SyntaxEditor, incrementer: Vec, ) -> Option<()> { let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element(); @@ -146,7 +147,7 @@ fn process_loop_body( let continue_label = make::lifetime("'cont"); let break_expr = make::expr_break(Some(continue_label.clone()), None); - let (mut new_edit, _) = SyntaxEditor::new(new_body.syntax().clone()); + let (new_edit, _) = SyntaxEditor::new(new_body.syntax().clone()); for continue_expr in &continues { new_edit.replace(continue_expr.syntax(), break_expr.syntax()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index f62cf462b1df3..791a6a26af381 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -130,9 +130,10 @@ fn if_expr_to_guarded_return( "Convert to guarded return", target, |edit| { - let mut editor = edit.make_editor(if_expr.syntax()); + let editor = edit.make_editor(if_expr.syntax()); + let make = editor.make(); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); - let early_expression = else_block.make_early_block(&ctx.sema, &make); + let early_expression = else_block.make_early_block(&ctx.sema, make); let replacement = let_chains.into_iter().map(|expr| { if let ast::Expr::LetExpr(let_expr) = &expr && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr()) @@ -145,8 +146,8 @@ fn if_expr_to_guarded_return( } else { // If. let new_expr = { - let then_branch = clean_stmt_block(&early_expression, &make); - let cond = invert_boolean_expression(&make, expr); + let then_branch = clean_stmt_block(&early_expression, make); + let cond = invert_boolean_expression(make, expr); make.expr_if(cond, then_branch, None).indent(if_indent_level) }; new_expr.syntax().clone() @@ -208,15 +209,16 @@ fn let_stmt_to_guarded_return( "Convert to guarded return", target, |edit| { - let mut editor = edit.make_editor(let_stmt.syntax()); + let editor = edit.make_editor(let_stmt.syntax()); + let make = editor.make(); let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); let replacement = { - let let_else_stmt = editor.make().let_else_stmt( + let let_else_stmt = make.let_else_stmt( happy_pattern, let_stmt.ty(), expr.reset_indent(), - else_block.make_early_block(&ctx.sema, editor.make()), + else_block.make_early_block(&ctx.sema, make), ); let let_else_stmt = let_else_stmt.indent(let_indent_level); let_else_stmt.syntax().clone() @@ -265,7 +267,8 @@ impl<'db> ElseBlock<'db> { return block_expr.reset_indent(); } - let (mut edit, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent()); + let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent()); + let make = editor.make(); let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone()); let tail_expr = block_expr.tail_expr().map(|it| it.syntax().clone()); @@ -278,7 +281,7 @@ impl<'db> ElseBlock<'db> { && !self.kind.is_unit() { let early_expr = self.kind.make_early_expr(sema, make, Some(tail_expr.clone())); - edit.replace(tail_expr.syntax(), early_expr.syntax()); + editor.replace(tail_expr.syntax(), early_expr.syntax()); } else { let last_stmt = match block_expr.tail_expr() { Some(expr) => make.expr_stmt(expr).syntax().clone(), @@ -287,13 +290,13 @@ impl<'db> ElseBlock<'db> { let whitespace = make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string())); let early_expr = self.kind.make_early_expr(sema, make, None).syntax().clone().into(); - edit.replace_with_many( + editor.replace_with_many( last_element, vec![last_stmt.into(), whitespace.into(), early_expr], ); } - ast::BlockExpr::cast(edit.finish().new_root().clone()).unwrap() + ast::BlockExpr::cast(editor.finish().new_root().clone()).unwrap() } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 3d03ccc484770..a99425cef92ae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -72,14 +72,15 @@ pub(crate) fn convert_tuple_return_type_to_struct( "Convert tuple return type to tuple struct", target, move |edit| { - let mut editor = edit.make_editor(ret_type.syntax()); + let editor = edit.make_editor(ret_type.syntax()); + let make = editor.make(); let usages = Definition::Function(fn_def).usages(&ctx.sema).all(); let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string())); let parent = fn_.syntax().ancestors().find_map(>::cast); add_tuple_struct_def( edit, - editor.make(), + make, ctx, &usages, parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()), @@ -88,17 +89,10 @@ pub(crate) fn convert_tuple_return_type_to_struct( &target_module, ); - editor.replace( - ret_type.syntax(), - editor.make().ret_type(editor.make().ty(&struct_name)).syntax(), - ); + editor.replace(ret_type.syntax(), make.ret_type(make.ty(&struct_name)).syntax()); if let Some(fn_body) = fn_.body() { - replace_body_return_values( - &mut editor, - ast::Expr::BlockExpr(fn_body), - &struct_name, - ); + replace_body_return_values(&editor, ast::Expr::BlockExpr(fn_body), &struct_name); } edit.add_file_edits(ctx.vfs_file_id(), editor); @@ -118,33 +112,22 @@ fn replace_usages( for (file_id, references) in usages.iter() { let Some(first_ref) = references.first() else { continue }; - let mut editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap()); + let editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap()); + let make = editor.make(); - let refs_with_imports = augment_references_with_imports( - editor.make(), - ctx, - references, - struct_name, - target_module, - ); + let refs_with_imports = + augment_references_with_imports(make, ctx, references, struct_name, target_module); refs_with_imports.into_iter().rev().for_each(|(name, import_data)| { if let Some(fn_) = name.syntax().parent().and_then(ast::Fn::cast) { cov_mark::hit!(replace_trait_impl_fns); if let Some(ret_type) = fn_.ret_type() { - editor.replace( - ret_type.syntax(), - editor.make().ret_type(editor.make().ty(struct_name)).syntax(), - ); + editor.replace(ret_type.syntax(), make.ret_type(make.ty(struct_name)).syntax()); } if let Some(fn_body) = fn_.body() { - replace_body_return_values( - &mut editor, - ast::Expr::BlockExpr(fn_body), - struct_name, - ); + replace_body_return_values(&editor, ast::Expr::BlockExpr(fn_body), struct_name); } } else { // replace tuple patterns @@ -168,16 +151,13 @@ fn replace_usages( tuple_pat.syntax(), editor .make() - .tuple_struct_pat( - editor.make().path_from_text(struct_name), - tuple_pat.fields(), - ) + .tuple_struct_pat(make.path_from_text(struct_name), tuple_pat.fields()) .syntax(), ); } } if let Some((import_scope, path)) = import_data { - insert_use_with_editor(&import_scope, path, &ctx.config.insert_use, &mut editor); + insert_use_with_editor(&import_scope, path, &ctx.config.insert_use, &editor); } }); edit.add_file_edits(file_id.file_id(ctx.db()), editor); @@ -283,11 +263,8 @@ fn add_tuple_struct_def( } /// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`. -fn replace_body_return_values( - syntax_editor: &mut SyntaxEditor, - body: ast::Expr, - struct_name: &str, -) { +fn replace_body_return_values(editor: &SyntaxEditor, body: ast::Expr, struct_name: &str) { + let make = editor.make(); let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); @@ -302,11 +279,11 @@ fn replace_body_return_values( for ret_expr in exprs_to_wrap { if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr { - let struct_constructor = syntax_editor.make().expr_call( - syntax_editor.make().expr_path(syntax_editor.make().ident_path(struct_name)), - syntax_editor.make().arg_list(tuple_expr.fields()), + let struct_constructor = make.expr_call( + make.expr_path(make.ident_path(struct_name)), + make.arg_list(tuple_expr.fields()), ); - syntax_editor.replace(ret_expr.syntax(), struct_constructor.syntax()); + editor.replace(ret_expr.syntax(), struct_constructor.syntax()); } } } 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 5a57e12253731..a1b33a0558b1d 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 @@ -86,24 +86,25 @@ pub(crate) fn convert_tuple_struct_to_named_struct( "Convert to named struct", target, |edit| { - let mut editor = edit.make_editor(syntax); + let editor = edit.make_editor(syntax); let names = generate_names(tuple_fields.fields(), editor.make()); edit_field_references(ctx, edit, tuple_fields.fields(), &names); edit_struct_references(ctx, edit, strukt_def, &names); - edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names); + edit_struct_def(&editor, &strukt_or_variant, tuple_fields, names); edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } fn edit_struct_def( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, strukt: &Either, tuple_fields: ast::TupleFieldList, names: Vec, ) { + let make = editor.make(); let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| { - let (mut field_editor, field) = + let (field_editor, field) = SyntaxEditor::with_ast_node(&ast::make::record_field(f.visibility(), name, f.ty()?)); field_editor.insert_all( Position::first_child_of(field.syntax()), @@ -111,7 +112,7 @@ fn edit_struct_def( ); ast::RecordField::cast(field_editor.finish().new_root().clone()) }); - let record_fields = editor.make().record_field_list(record_fields); + let record_fields = make.record_field_list(record_fields); let tuple_fields_before = Position::before(tuple_fields.syntax()); if let Either::Left(strukt) = strukt { @@ -152,10 +153,10 @@ fn edit_struct_references( for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let mut editor = edit.make_editor(source.syntax()); + let editor = edit.make_editor(source.syntax()); for r in refs { - process_struct_name_reference(ctx, r, &mut editor, &source, &strukt_def, names); + process_struct_name_reference(ctx, r, &editor, &source, &strukt_def, names); } edit.add_file_edits(file_id.file_id(ctx.db()), editor); @@ -165,11 +166,12 @@ fn edit_struct_references( fn process_struct_name_reference( ctx: &AssistContext<'_>, r: FileReference, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, source: &ast::SourceFile, strukt_def: &Definition, names: &[ast::Name], ) -> Option<()> { + let make = editor.make(); let name_ref = r.name.as_name_ref()?; let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; let full_path = path_segment.syntax().parent().and_then(ast::Path::cast)?.top_path(); @@ -185,7 +187,7 @@ fn process_struct_name_reference( match parent { ast::TupleStructPat(tuple_struct_pat) => { let range = ctx.sema.original_range_opt(tuple_struct_pat.syntax())?.range; - let new = editor.make().record_pat_with_fields( + let new = make.record_pat_with_fields( full_path, generate_record_pat_list(&tuple_struct_pat, names), ); @@ -207,9 +209,9 @@ 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![ - editor.make().name_ref(&name.text()).syntax().clone().into(), - editor.make().token(T![:]).into(), - editor.make().whitespace(" ").into(), + make.name_ref(&name.text()).syntax().clone().into(), + make.token(T![:]).into(), + make.whitespace(" ").into(), ]; if first_insert.is_empty() { // XXX: SyntaxEditor cannot insert after deleted element @@ -229,10 +231,11 @@ fn process_struct_name_reference( fn process_delimiter( ctx: &AssistContext<'_>, source: &ast::SourceFile, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, list: &impl AstNode, first_insert: Vec, ) { + let make = editor.make(); let Some(range) = ctx.sema.original_range_opt(list.syntax()) else { return }; let place = cover_edit_range(source.syntax(), range.range); @@ -249,9 +252,9 @@ fn process_delimiter( && l_paren.kind() == T!['('] { let mut open_delim = vec![ - editor.make().whitespace(" ").into(), - editor.make().token(T!['{']).into(), - editor.make().whitespace(" ").into(), + make.whitespace(" ").into(), + make.token(T!['{']).into(), + make.whitespace(" ").into(), ]; open_delim.extend(first_insert); editor.replace_with_many(l_paren, open_delim); @@ -261,7 +264,7 @@ fn process_delimiter( { editor.replace_with_many( r_paren, - vec![editor.make().whitespace(" ").into(), editor.make().token(T!['}']).into()], + vec![make.whitespace(" ").into(), make.token(T!['}']).into()], ); } } @@ -281,7 +284,7 @@ fn edit_field_references( let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { let source = ctx.sema.parse(file_id); - let mut editor = edit.make_editor(source.syntax()); + let editor = edit.make_editor(source.syntax()); for r in refs { if let Some(name_ref) = r.name.as_name_ref() && let Some(original) = ctx.sema.original_range_opt(name_ref.syntax()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index f758a4dab6108..bbfac8f095f57 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -51,45 +51,42 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) "Convert while to loop", target, |builder| { - let mut editor = builder.make_editor(while_expr.syntax()); + let editor = builder.make_editor(while_expr.syntax()); + let make = editor.make(); let while_indent_level = IndentLevel::from_node(while_expr.syntax()); let break_block = editor .make() .block_expr( - iter::once( - editor.make().expr_stmt(editor.make().expr_break(None, None).into()).into(), - ), + iter::once(make.expr_stmt(make.expr_break(None, None).into()).into()), None, ) .indent(IndentLevel(1)); editor.replace_all( while_kw.syntax_element()..=while_cond.syntax().syntax_element(), - vec![editor.make().token(T![loop]).syntax_element()], + vec![make.token(T![loop]).syntax_element()], ); if is_pattern_cond(while_cond.clone()) { let then_branch = while_body.reset_indent().indent(IndentLevel(1)); - let if_expr = - editor.make().expr_if(while_cond, then_branch, Some(break_block.into())); - let stmts = iter::once(editor.make().expr_stmt(if_expr.into()).into()); - let block_expr = editor.make().block_expr(stmts, None); + let if_expr = make.expr_if(while_cond, then_branch, Some(break_block.into())); + let stmts = iter::once(make.expr_stmt(if_expr.into()).into()); + let block_expr = make.block_expr(stmts, None); editor.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax()); } else { - let if_cond = invert_boolean_expression(editor.make(), while_cond); - let if_expr = - editor.make().expr_if(if_cond, break_block, None).indent(while_indent_level); + let if_cond = invert_boolean_expression(make, while_cond); + let if_expr = make.expr_if(if_cond, break_block, None).indent(while_indent_level); if !while_body.syntax().text().contains_char('\n') { editor.insert( Position::after(&l_curly), - editor.make().whitespace(&format!("\n{while_indent_level}")), + make.whitespace(&format!("\n{while_indent_level}")), ); } editor.insert_all( Position::after(&l_curly), vec![ - editor.make().whitespace(&format!("\n{}", while_indent_level + 1)).into(), + make.whitespace(&format!("\n{}", while_indent_level + 1)).into(), if_expr.syntax().syntax_element(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 1ccbd80e60ccd..f23957e647e46 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -124,9 +124,9 @@ fn destructure_struct_binding_impl( data: &StructEditData, ) { let field_names = generate_field_names(ctx, data); - let mut editor = builder.make_editor(data.target.syntax()); - destructure_pat(ctx, &mut editor, data, &field_names); - update_usages(ctx, &mut editor, data, &field_names.into_iter().collect()); + let editor = builder.make_editor(data.target.syntax()); + destructure_pat(ctx, &editor, data, &field_names); + update_usages(ctx, &editor, data, &field_names.into_iter().collect()); builder.add_file_edits(ctx.vfs_file_id(), editor); } @@ -145,7 +145,8 @@ struct StructEditData { } impl StructEditData { - fn apply_to_destruct(&self, new_pat: ast::Pat, editor: &mut SyntaxEditor) { + fn apply_to_destruct(&self, new_pat: ast::Pat, editor: &SyntaxEditor) { + let make = editor.make(); match &self.target { Target::IdentPat(pat) => { // If the binding is nested inside a record, we need to wrap the new @@ -153,7 +154,7 @@ impl StructEditData { if self.need_record_field_name { let new_pat = editor .make() - .record_pat_field(editor.make().name_ref(&self.name.to_string()), new_pat); + .record_pat_field(make.name_ref(&self.name.to_string()), new_pat); editor.replace(pat.syntax(), new_pat.syntax()) } else { editor.replace(pat.syntax(), new_pat.syntax()) @@ -161,9 +162,9 @@ impl StructEditData { } Target::SelfParam { insert_after, .. } => { let indent = IndentLevel::from_token(insert_after) + 1; - let newline = editor.make().whitespace(&format!("\n{indent}")); - let initializer = editor.make().expr_path(editor.make().ident_path("self")); - let let_stmt = editor.make().let_stmt(new_pat, None, Some(initializer)); + let newline = make.whitespace(&format!("\n{indent}")); + let initializer = make.expr_path(make.ident_path("self")); + let let_stmt = make.let_stmt(new_pat, None, Some(initializer)); editor.insert_all( Position::after(insert_after), vec![newline.into(), let_stmt.syntax().clone().into()], @@ -271,10 +272,11 @@ fn get_names_in_scope( fn destructure_pat( _ctx: &AssistContext<'_>, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, data: &StructEditData, field_names: &[(SmolStr, SmolStr)], ) { + let make = editor.make(); let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition); let is_ref = data.target.is_ref(); let is_mut = data.target.is_mut(); @@ -282,40 +284,31 @@ fn destructure_pat( let new_pat = match data.kind { hir::StructKind::Tuple => { let ident_pats = field_names.iter().map(|(_, new_name)| { - let name = editor.make().name(new_name); - ast::Pat::from(editor.make().ident_pat(is_ref, is_mut, name)) + let name = make.name(new_name); + ast::Pat::from(make.ident_pat(is_ref, is_mut, name)) }); - ast::Pat::TupleStructPat(editor.make().tuple_struct_pat(struct_path, ident_pats)) + ast::Pat::TupleStructPat(make.tuple_struct_pat(struct_path, ident_pats)) } hir::StructKind::Record => { let fields = field_names.iter().map(|(old_name, new_name)| { // Use shorthand syntax if possible if old_name == new_name { - editor.make().record_pat_field_shorthand( - editor - .make() - .ident_pat(is_ref, is_mut, editor.make().name(old_name)) - .into(), + make.record_pat_field_shorthand( + editor.make().ident_pat(is_ref, is_mut, make.name(old_name)).into(), ) } else { - editor.make().record_pat_field( - editor.make().name_ref(old_name), - ast::Pat::IdentPat(editor.make().ident_pat( - is_ref, - is_mut, - editor.make().name(new_name), - )), + make.record_pat_field( + make.name_ref(old_name), + ast::Pat::IdentPat(make.ident_pat(is_ref, is_mut, make.name(new_name))), ) } }); - let field_list = editor.make().record_pat_field_list( - fields, - data.has_private_members.then_some(editor.make().rest_pat()), - ); + let field_list = make + .record_pat_field_list(fields, data.has_private_members.then_some(make.rest_pat())); - ast::Pat::RecordPat(editor.make().record_pat_with_fields(struct_path, field_list)) + ast::Pat::RecordPat(make.record_pat_with_fields(struct_path, field_list)) } - hir::StructKind::Unit => editor.make().path_pat(struct_path), + hir::StructKind::Unit => make.path_pat(struct_path), }; data.apply_to_destruct(new_pat, editor); @@ -357,7 +350,7 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet) -> Sm fn update_usages( ctx: &AssistContext<'_>, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, data: &StructEditData, field_names: &FxHashMap, ) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index e3acbf22f3074..b0f257e0028f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -89,20 +89,17 @@ fn destructure_tuple_edit_impl( data: &TupleData, in_sub_pattern: bool, ) { - let mut syntax_editor = edit.make_editor(data.ident_pat.syntax()); + let editor = edit.make_editor(data.ident_pat.syntax()); + let make = editor.make(); - let assignment_edit = - edit_tuple_assignment(ctx, edit, &mut syntax_editor, data, in_sub_pattern); - let current_file_usages_edit = - edit_tuple_usages(data, ctx, syntax_editor.make(), in_sub_pattern); + let assignment_edit = edit_tuple_assignment(ctx, edit, &editor, data, in_sub_pattern); + let current_file_usages_edit = edit_tuple_usages(data, ctx, make, in_sub_pattern); - assignment_edit.apply(&mut syntax_editor); + assignment_edit.apply(&editor); if let Some(usages_edit) = current_file_usages_edit { - usages_edit - .into_iter() - .for_each(|usage_edit| usage_edit.apply(ctx, edit, &mut syntax_editor)) + usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(ctx, edit, &editor)) } - edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); + edit.add_file_edits(ctx.vfs_file_id(), editor); } fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option { @@ -173,18 +170,20 @@ struct TupleData { fn edit_tuple_assignment( ctx: &AssistContext<'_>, edit: &mut SourceChangeBuilder, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, data: &TupleData, in_sub_pattern: bool, ) -> AssignmentEdit { + let make = editor.make(); let tuple_pat = { let original = &data.ident_pat; let is_ref = original.ref_token().is_some(); let is_mut = original.mut_token().is_some(); - let fields = data.field_names.iter().map(|name| { - ast::Pat::from(editor.make().ident_pat(is_ref, is_mut, editor.make().name(name))) - }); - editor.make().tuple_pat(fields) + let fields = data + .field_names + .iter() + .map(|name| ast::Pat::from(make.ident_pat(is_ref, is_mut, make.name(name)))); + make.tuple_pat(fields) }; let is_shorthand_field = data .ident_pat @@ -219,22 +218,17 @@ struct AssignmentEdit { } impl AssignmentEdit { - fn apply(self, syntax_editor: &mut SyntaxEditor) { + fn apply(self, editor: &SyntaxEditor) { + let make = editor.make(); // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { - self.ident_pat.set_pat(Some(self.tuple_pat.into()), syntax_editor); + self.ident_pat.set_pat(Some(self.tuple_pat.into()), editor); } else if self.is_shorthand_field { - syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); - syntax_editor.insert( - Position::after(self.ident_pat.syntax()), - syntax_editor.make().whitespace(" "), - ); - syntax_editor.insert( - Position::after(self.ident_pat.syntax()), - syntax_editor.make().token(T![:]), - ); + editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); + editor.insert(Position::after(self.ident_pat.syntax()), make.whitespace(" ")); + editor.insert(Position::after(self.ident_pat.syntax()), make.token(T![:])); } else { - syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) + editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) } } } @@ -313,7 +307,7 @@ impl EditTupleUsage { self, ctx: &AssistContext<'_>, edit: &mut SourceChangeBuilder, - syntax_editor: &mut SyntaxEditor, + syntax_editor: &SyntaxEditor, ) { match self { EditTupleUsage::NoIndex(range) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index c8753574cde49..399b2aa69be88 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -65,33 +65,30 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Replace try expression with match", target, |builder| { - let mut editor = builder.make_editor(try_expr.syntax()); - + let editor = builder.make_editor(try_expr.syntax()); + let make = editor.make(); let sad_pat = match try_enum { - TryEnum::Option => editor.make().path_pat(editor.make().ident_path("None")), + TryEnum::Option => make.path_pat(make.ident_path("None")), TryEnum::Result => editor .make() .tuple_struct_pat( - editor.make().ident_path("Err"), - iter::once(editor.make().path_pat(editor.make().ident_path("err"))), + make.ident_path("Err"), + iter::once(make.path_pat(make.ident_path("err"))), ) .into(), }; - let sad_expr = - editor.make().expr_return(Some(sad_expr(try_enum, editor.make(), || { - editor.make().expr_path(editor.make().ident_path("err")) - }))); + let sad_expr = make.expr_return(Some(sad_expr(try_enum, make, || { + make.expr_path(make.ident_path("err")) + }))); - let happy_arm = editor.make().match_arm( - try_enum.happy_pattern( - editor.make().ident_pat(false, false, editor.make().name("it")).into(), - ), + let happy_arm = make.match_arm( + try_enum.happy_pattern(make.ident_pat(false, false, make.name("it")).into()), None, - editor.make().expr_path(editor.make().ident_path("it")), + make.expr_path(make.ident_path("it")), ); - let sad_arm = editor.make().match_arm(sad_pat, None, sad_expr.into()); + let sad_arm = make.match_arm(sad_pat, None, sad_expr.into()); - let match_arm_list = editor.make().match_arm_list([happy_arm, sad_arm]); + let match_arm_list = make.match_arm_list([happy_arm, sad_arm]); let expr_match = editor .make() @@ -112,17 +109,16 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Replace try expression with let else", target, |builder| { - let mut editor = builder.make_editor(let_stmt.syntax()); + let editor = builder.make_editor(let_stmt.syntax()); + let make = editor.make(); let indent_level = IndentLevel::from_node(let_stmt.syntax()); let fill_expr = || crate::utils::expr_fill_default(ctx.config); - let new_let_stmt = editor.make().let_else_stmt( + let new_let_stmt = make.let_else_stmt( try_enum.happy_pattern(pat), let_stmt.ty().map(|ty| match try_enum { - TryEnum::Option => editor.make().ty_option(ty).into(), - TryEnum::Result => { - editor.make().ty_result(ty, editor.make().ty_infer().into()).into() - } + TryEnum::Option => make.ty_option(ty).into(), + TryEnum::Result => make.ty_result(ty, make.ty_infer().into()).into(), }), expr, editor @@ -134,11 +130,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .expr_stmt( editor .make() - .expr_return(Some(sad_expr( - try_enum, - editor.make(), - fill_expr, - ))) + .expr_return(Some(sad_expr(try_enum, make, fill_expr))) .into(), ) .into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index da6900b99b1de..1c8cbf5af5941 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -148,7 +148,8 @@ fn build_expanded_import( current_module: Module, reexport_public_items: bool, ) { - let mut editor = builder.make_editor(use_tree.syntax()); + let editor = builder.make_editor(use_tree.syntax()); + let make = editor.make(); let (must_be_pub, visible_from) = if !reexport_public_items { (false, current_module) } else { @@ -168,11 +169,11 @@ fn build_expanded_import( if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; let names_to_import = find_names_to_import(filtered_defs, imported_defs); - let expanded = editor.make().use_tree_list(names_to_import.iter().map(|n| { - let path = editor.make().ident_path( + let expanded = make.use_tree_list(names_to_import.iter().map(|n| { + let path = make.ident_path( &n.display(ctx.db(), current_module.krate(ctx.db()).edition(ctx.db())).to_string(), ); - editor.make().use_tree(path, None, None, false) + make.use_tree(path, None, None, false) })); match use_tree.star_token() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 5ca6d4ca2c3b2..c75e4cede747b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -51,9 +51,10 @@ fn expand_record_rest_pattern( "Fill struct fields", rest_pat.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(rest_pat.syntax()); + let editor = builder.make_editor(rest_pat.syntax()); + let make = editor.make(); let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| { - editor.make().record_pat_field_shorthand( + make.record_pat_field_shorthand( editor .make() .ident_pat( @@ -66,7 +67,7 @@ fn expand_record_rest_pattern( .into(), ) })); - let new_field_list = editor.make().record_pat_field_list(new_fields, None); + let new_field_list = make.record_pat_field_list(new_fields, None); editor.replace(old_field_list.syntax(), new_field_list.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -131,17 +132,18 @@ fn expand_tuple_struct_rest_pattern( "Fill tuple struct fields", rest_pat.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(rest_pat.syntax()); + let editor = builder.make_editor(rest_pat.syntax()); + let make = editor.make(); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = editor.make().tuple_struct_pat( + let new_pat = make.tuple_struct_pat( path, pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { gen_unnamed_pat( ctx, - editor.make(), + make, &mut name_gen, &f.ty(ctx.db()).to_type(ctx.sema.db), f.index(), @@ -198,21 +200,15 @@ fn expand_tuple_rest_pattern( "Fill tuple fields", rest_pat.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(rest_pat.syntax()); - + let editor = builder.make_editor(rest_pat.syntax()); + let make = editor.make(); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = editor.make().tuple_pat( + let new_pat = make.tuple_pat( pat.fields() .take(prefix_count) .chain(fields[prefix_count..len - suffix_count].iter().enumerate().map( |(index, ty)| { - gen_unnamed_pat( - ctx, - editor.make(), - &mut name_gen, - ty, - prefix_count + index, - ) + gen_unnamed_pat(ctx, make, &mut name_gen, ty, prefix_count + index) }, )) .chain(pat.fields().skip(prefix_count + 1)), @@ -265,15 +261,17 @@ fn expand_slice_rest_pattern( "Fill slice fields", rest_pat.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(rest_pat.syntax()); + let editor = builder.make_editor(rest_pat.syntax()); + let make = editor.make(); let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax())); - let new_pat = editor.make().slice_pat( + let new_pat = make.slice_pat( pat.pats() .take(prefix_count) - .chain((prefix_count..len - suffix_count).map(|index| { - gen_unnamed_pat(ctx, editor.make(), &mut name_gen, &ty, index) - })) + .chain( + (prefix_count..len - suffix_count) + .map(|index| gen_unnamed_pat(ctx, make, &mut name_gen, &ty, index)), + ) .chain(pat.pats().skip(prefix_count + 1)), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 0b96acaee3a9f..c87ded9dc47b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -57,7 +57,8 @@ pub(crate) fn extract_expressions_from_format_string( "Extract format expressions", tt.syntax().text_range(), |edit| { - let mut editor = edit.make_editor(tt.syntax()); + let editor = edit.make_editor(tt.syntax()); + let make = editor.make(); // Extract existing arguments in macro let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); let format_string_index = format_str_index(&raw_tokens, &fmt_string); @@ -95,15 +96,14 @@ pub(crate) fn extract_expressions_from_format_string( let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; - new_tt_bits - .push(NodeOrToken::Token(editor.make().expr_literal(&new_fmt).token().clone())); + new_tt_bits.push(NodeOrToken::Token(make.expr_literal(&new_fmt).token().clone())); for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg new_tt_bits.extend_from_slice(&[ - NodeOrToken::Token(editor.make().token(T![,])), - NodeOrToken::Token(editor.make().whitespace(" ")), + NodeOrToken::Token(make.token(T![,])), + NodeOrToken::Token(make.whitespace(" ")), ]); } @@ -111,7 +111,7 @@ pub(crate) fn extract_expressions_from_format_string( Arg::Expr(s) => { // insert arg let expr = ast::Expr::parse(&s, ctx.edition()).syntax_node(); - let mut expr_tt = utils::tt_from_syntax(expr, editor.make()); + let mut expr_tt = utils::tt_from_syntax(expr, make); new_tt_bits.append(&mut expr_tt); } Arg::Placeholder => { @@ -122,7 +122,7 @@ pub(crate) fn extract_expressions_from_format_string( } None => { placeholder_indexes.push(new_tt_bits.len()); - new_tt_bits.push(NodeOrToken::Token(editor.make().token(T![_]))); + new_tt_bits.push(NodeOrToken::Token(make.token(T![_]))); } } } @@ -131,7 +131,7 @@ pub(crate) fn extract_expressions_from_format_string( } // Insert new args - let new_tt = editor.make().token_tree(tt_delimiter, new_tt_bits); + let new_tt = make.token_tree(tt_delimiter, new_tt_bits); editor.replace(tt.syntax(), new_tt.syntax()); if let Some(cap) = ctx.config.snippet_cap { 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 6975789b982bc..3272fa739fe69 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 @@ -60,7 +60,8 @@ pub(crate) fn extract_struct_from_enum_variant( "Extract struct from enum variant", target, |builder| { - let mut editor = builder.make_editor(variant.syntax()); + let editor = builder.make_editor(variant.syntax()); + let make = editor.make(); let edition = enum_hir.krate(ctx.db()).edition(ctx.db()); let variant_hir_name = variant_hir.name(ctx.db()); let enum_module_def = ModuleDef::from(enum_hir); @@ -86,7 +87,7 @@ pub(crate) fn extract_struct_from_enum_variant( if processed.is_empty() { continue; } - let mut file_editor = builder.make_editor(processed[0].0.syntax()); + let file_editor = builder.make_editor(processed[0].0.syntax()); processed.into_iter().for_each(|(path, node, import)| { apply_references( ctx.config.insert_use, @@ -94,7 +95,7 @@ pub(crate) fn extract_struct_from_enum_variant( node, import, edition, - &mut file_editor, + &file_editor, ) }); builder.add_file_edits(file_id.file_id(ctx.db()), file_editor); @@ -109,19 +110,12 @@ pub(crate) fn extract_struct_from_enum_variant( references, ); processed.into_iter().for_each(|(path, node, import)| { - apply_references( - ctx.config.insert_use, - path, - node, - import, - edition, - &mut editor, - ) + apply_references(ctx.config.insert_use, path, node, import, edition, &editor) }); } let generic_params = enum_ast.generic_param_list().and_then(|known_generics| { - extract_generic_params(editor.make(), &known_generics, &field_list) + extract_generic_params(make, &known_generics, &field_list) }); // resolve GenericArg in field_list to actual type @@ -144,13 +138,13 @@ pub(crate) fn extract_struct_from_enum_variant( }; let (comments_for_struct, comments_to_delete) = - collect_variant_comments(editor.make(), variant.syntax()); + collect_variant_comments(make, variant.syntax()); for element in &comments_to_delete { editor.delete(element.clone()); } let def = create_struct_def( - editor.make(), + make, variant_name.clone(), &field_list, generic_params.clone(), @@ -164,14 +158,14 @@ pub(crate) fn extract_struct_from_enum_variant( let mut insert_items: Vec = Vec::new(); for attr in enum_ast.attrs() { insert_items.push(attr.syntax().clone().into()); - insert_items.push(editor.make().whitespace("\n").into()); + insert_items.push(make.whitespace("\n").into()); } insert_items.extend(comments_for_struct); insert_items.push(def.syntax().clone().into()); - insert_items.push(editor.make().whitespace(&format!("\n\n{indent}")).into()); + insert_items.push(make.whitespace(&format!("\n\n{indent}")).into()); editor.insert_all_with_whitespace(Position::before(enum_ast.syntax()), insert_items); - update_variant(&mut editor, &variant, generic_params); + update_variant(&editor, &variant, generic_params); builder.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -331,23 +325,24 @@ fn create_struct_def( } fn update_variant( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, variant: &ast::Variant, generics: Option, ) -> Option<()> { + let make = editor.make(); let name = variant.name()?; let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) .map(|generics| generics.to_generic_args()); // FIXME: replace with a `ast::make` constructor let ty = match generic_args { - Some(generic_args) => editor.make().ty(&format!("{name}{generic_args}")), - None => editor.make().ty(&name.text()), + Some(generic_args) => make.ty(&format!("{name}{generic_args}")), + None => make.ty(&name.text()), }; // change from a record to a tuple field list - let tuple_field = editor.make().tuple_field(None, ty); - let field_list = editor.make().tuple_field_list(iter::once(tuple_field)); + let tuple_field = make.tuple_field(None, ty); + let field_list = make.tuple_field_list(iter::once(tuple_field)); editor.replace(variant.field_list()?.syntax(), field_list.syntax()); // remove any ws after the name @@ -397,16 +392,17 @@ fn apply_references( node: SyntaxNode, import: Option<(ImportScope, hir::ModPath)>, edition: Edition, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, ) { + let make = editor.make(); if let Some((scope, path)) = import { insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor); } // deep clone to prevent cycle - let path = editor.make().path_from_segments(iter::once(segment.clone()), false); - editor.insert(Position::before(segment.syntax()), editor.make().token(T!['('])); + let path = make.path_from_segments(iter::once(segment.clone()), false); + editor.insert(Position::before(segment.syntax()), make.token(T!['('])); editor.insert(Position::before(segment.syntax()), path.syntax()); - editor.insert(Position::after(&node), editor.make().token(T![')'])); + editor.insert(Position::after(&node), make.token(T![')'])); } fn process_references( 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 f077260fa3de4..23573dc3388d3 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 @@ -53,9 +53,10 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Extract type as type alias", target, |builder| { - let mut editor = builder.make_editor(node); + let editor = builder.make_editor(node); + let make = editor.make(); - let resolved_ty = editor.make().ty(&resolved_ty); + let resolved_ty = make.ty(&resolved_ty); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -69,26 +70,20 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> } let generics = collect_used_generics(&ty, &known_generics); let generic_params = - generics.map(|it| editor.make().generic_param_list(it.into_iter().cloned())); + generics.map(|it| make.generic_param_list(it.into_iter().cloned())); // Replace original type with the alias let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); let new_ty = if let Some(ty_args) = ty_args { - editor.make().generic_ty_path_segment(editor.make().name_ref("Type"), ty_args) + make.generic_ty_path_segment(make.name_ref("Type"), ty_args) } else { - editor.make().path_segment(editor.make().name_ref("Type")) + make.path_segment(make.name_ref("Type")) }; editor.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let ty_alias = editor.make().ty_alias( - None, - "Type", - generic_params, - None, - None, - Some((resolved_ty, None)), - ); + let ty_alias = + make.ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None))); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() @@ -101,7 +96,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> syntax_editor::Position::before(node), vec![ ty_alias.syntax().clone().into(), - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index edf12cde5bdf6..c5c57c76b47ca 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -205,10 +205,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op to_replace.clone() }; - let mut editor = edit.make_editor(&place); + let editor = edit.make_editor(&place); + let make = editor.make(); - let pat_name = editor.make().name(&var_name); - let name_expr = editor.make().expr_path(editor.make().ident_path(&var_name)); + let pat_name = make.name(&var_name); + let name_expr = make.expr_path(make.ident_path(&var_name)); if let Some(cap) = ctx.config.snippet_cap { let tabstop = edit.make_tabstop_before(cap); @@ -217,33 +218,27 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let initializer = match ty.as_ref().filter(|_| needs_ref) { Some(receiver_type) if receiver_type.is_mutable_reference() => { - editor.make().expr_ref(to_extract_no_ref.clone(), true) + make.expr_ref(to_extract_no_ref.clone(), true) } Some(receiver_type) if receiver_type.is_reference() => { - editor.make().expr_ref(to_extract_no_ref.clone(), false) + make.expr_ref(to_extract_no_ref.clone(), false) } _ => to_extract_no_ref.clone(), }; let new_stmt: ast::Stmt = match kind { ExtractionKind::Variable => { - let ident_pat = editor.make().ident_pat(false, needs_mut, pat_name); - editor.make().let_stmt(ident_pat.into(), None, Some(initializer)).into() + let ident_pat = make.ident_pat(false, needs_mut, pat_name); + make.let_stmt(ident_pat.into(), None, Some(initializer)).into() } ExtractionKind::Constant => { - let ast_ty = editor.make().ty(&ty_string); - ast::Item::Const(editor.make().item_const( - None, - None, - pat_name, - ast_ty, - initializer, - )) - .into() + let ast_ty = make.ty(&ty_string); + ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer)) + .into() } ExtractionKind::Static => { - let ast_ty = editor.make().ty(&ty_string); - ast::Item::Static(editor.make().item_static( + let ast_ty = make.ty(&ty_string); + ast::Item::Static(make.item_static( None, false, false, @@ -271,7 +266,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op Position::before(place), vec![ new_stmt.syntax().clone().into(), - editor.make().whitespace(&trailing_ws).into(), + make.whitespace(&trailing_ws).into(), ], ); @@ -288,15 +283,15 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let block = if to_wrap.syntax() == &expr_replace { // Since `expr_replace` is the same that needs to be wrapped in a block, // we can just directly replace it with a block - editor.make().block_expr([new_stmt], Some(name_expr)) + make.block_expr([new_stmt], Some(name_expr)) } else { // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`. editor .replace_all(to_replace, vec![name_expr.syntax().syntax_element()]); - editor.make().block_expr([new_stmt], Some(to_wrap.clone())) + make.block_expr([new_stmt], Some(to_wrap.clone())) } // fixup indentation of block - .indent_with_mapping(indent_to, editor.make()); + .indent_with_mapping(indent_to, make); editor.replace(to_wrap.syntax(), block.syntax()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 440f2d5f17ca4..d8714dd49c2db 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -78,7 +78,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) }; acc.add(AssistId::quick_fix("fix_visibility"), assist_label, target, |builder| { - let mut editor = builder.make_editor(vis_owner.syntax()); + let editor = builder.make_editor(vis_owner.syntax()); if let Some(current_visibility) = vis_owner.visibility() { editor.replace(current_visibility.syntax(), missing_visibility.syntax()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs index 679369b9b1418..17911150f5e7b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs @@ -48,9 +48,10 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option "Flip binary expression", op_token.text_range(), |builder| { - let mut editor = builder.make_editor(&expr.syntax().parent().unwrap()); + let editor = builder.make_editor(&expr.syntax().parent().unwrap()); + let make = editor.make(); if let FlipAction::FlipAndReplaceOp(binary_op) = action { - editor.replace(op_token, editor.make().token(binary_op)) + editor.replace(op_token, make.token(binary_op)) }; editor.replace(lhs.syntax(), rhs.syntax()); editor.replace(rhs.syntax(), lhs.syntax()); @@ -131,25 +132,25 @@ pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt "Flip range expression", op.text_range(), |builder| { - let mut edit = builder.make_editor(range_expr.syntax()); + let editor = builder.make_editor(range_expr.syntax()); match (start, end) { (Some(start), Some(end)) => { - edit.replace(start.syntax(), end.syntax()); - edit.replace(end.syntax(), start.syntax()); + editor.replace(start.syntax(), end.syntax()); + editor.replace(end.syntax(), start.syntax()); } (Some(start), None) => { - edit.delete(start.syntax()); - edit.insert(Position::after(&op), start.syntax()); + editor.delete(start.syntax()); + editor.insert(Position::after(&op), start.syntax()); } (None, Some(end)) => { - edit.delete(end.syntax()); - edit.insert(Position::before(&op), end.syntax()); + editor.delete(end.syntax()); + editor.insert(Position::before(&op), end.syntax()); } (None, None) => (), } - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index 722a6c1457411..65dc36cdca7ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -41,7 +41,7 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let target = comma.text_range(); acc.add(AssistId::refactor_rewrite("flip_comma"), "Flip comma", target, |builder| { let parent = comma.parent().unwrap(); - let mut editor = builder.make_editor(&parent); + let editor = builder.make_editor(&parent); if let Some(parent) = ast::TokenTree::cast(parent) { // An attribute. It often contains a path followed by a diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs index 4829f5bec206b..bd56331f4128b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs @@ -32,7 +32,7 @@ pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let target = pipe.text_range(); acc.add(AssistId::refactor_rewrite("flip_or_pattern"), "Flip patterns", target, |builder| { - let mut editor = builder.make_editor(parent.syntax()); + let editor = builder.make_editor(parent.syntax()); editor.replace(before.clone(), after.clone()); editor.replace(after, before); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs index 9756268c7cc33..dfd280efa6303 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs @@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Flip trait bounds", target, |builder| { - let mut editor = builder.make_editor(parent.syntax()); + let editor = builder.make_editor(parent.syntax()); editor.replace(before.clone(), after.clone()); editor.replace(after, before); builder.add_file_edits(ctx.vfs_file_id(), editor); 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 ee98e1f7d49b7..0bb90f187c684 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 @@ -73,27 +73,25 @@ pub(crate) fn generate_blanket_trait_impl( "Generate blanket trait implementation", name.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(traitd.syntax()); - let namety = editor.make().ty_path(editor.make().path_from_text(&name.text())); + let editor = builder.make_editor(traitd.syntax()); + let make = editor.make(); + 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(editor.make(), list)); + 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(editor.make(), &traitd); - let thisty = editor.make().ty_path(editor.make().path_from_text(&thisname.text())); + let thisname = this_name(make, &traitd); + let thisty = make.ty_path(make.path_from_text(&thisname.text())); let indent = traitd.indent_level(); - let gendecl = editor.make().generic_param_list([GenericParam::TypeParam( - editor.make().type_param( - thisname.clone(), - apply_sized(editor.make(), has_sized(&traitd, &ctx.sema), bounds), - ), - )]); + let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param( + thisname.clone(), + apply_sized(make, has_sized(&traitd, &ctx.sema), bounds), + ))]); let trait_gen_args = traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); - let impl_ = editor.make().impl_trait( + let impl_ = make.impl_trait( cfg_attrs(&traitd), is_unsafe, traitd.generic_param_list(), @@ -109,11 +107,11 @@ pub(crate) fn generate_blanket_trait_impl( ); if let Some(trait_assoc_list) = traitd.assoc_item_list() { - let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&mut editor); + let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&editor); for item in trait_assoc_list.assoc_items() { let item = match item { ast::AssocItem::Fn(method) if method.body().is_none() => { - todo_fn(editor.make(), &method, ctx.config).into() + todo_fn(make, &method, ctx.config).into() } ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, _ => continue, @@ -127,7 +125,7 @@ pub(crate) fn generate_blanket_trait_impl( editor.insert_all( Position::after(traitd.syntax()), vec![ - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs index d4a570b8ba5fb..739b63173694a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs @@ -73,15 +73,16 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<' "Generate a Default impl from a new fn", target, move |builder| { - let mut editor = builder.make_editor(impl_.syntax()); - let default_impl = generate_default_impl(editor.make(), &impl_, self_ty); + let editor = builder.make_editor(impl_.syntax()); + let make = editor.make(); + let default_impl = generate_default_impl(make, &impl_, self_ty); let indent = IndentLevel::from_node(impl_.syntax()); let default_impl = default_impl.indent(indent); editor.insert_all( Position::after(impl_.syntax()), vec![ - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), default_impl.syntax().clone().into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 09baea2694049..d63ef33debec0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -106,7 +106,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' format!("Generate delegate for `{field_name}.{name}()`",), target, |edit| { - let mut editor = edit.make_editor(strukt.syntax()); + let editor = edit.make_editor(strukt.syntax()); + let make = editor.make(); let field = editor .make() .field_from_idents(["self", &field_name]) @@ -135,27 +136,24 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let is_unsafe = method_source.unsafe_token().is_some(); let is_gen = method_source.gen_token().is_some(); - let fn_name = editor.make().name(&name); + let fn_name = make.name(&name); let type_params = method_source.generic_param_list(); let where_clause = method_source.where_clause(); - let params = method_source - .param_list() - .unwrap_or_else(|| editor.make().param_list(None, [])); + let params = + method_source.param_list().unwrap_or_else(|| make.param_list(None, [])); // compute the `body` let arg_list = method_source .param_list() - .map(|v| convert_param_list_to_arg_list(v, editor.make())) - .unwrap_or_else(|| editor.make().arg_list([])); + .map(|v| convert_param_list_to_arg_list(v, make)) + .unwrap_or_else(|| make.arg_list([])); - let tail_expr = editor - .make() - .expr_method_call(field, editor.make().name_ref(&name), arg_list) - .into(); + let tail_expr = + editor.make().expr_method_call(field, make.name_ref(&name), arg_list).into(); let tail_expr_finished = - if is_async { editor.make().expr_await(tail_expr).into() } else { tail_expr }; - let body = editor.make().block_expr([], Some(tail_expr_finished)); + if is_async { make.expr_await(tail_expr).into() } else { tail_expr }; + let body = make.block_expr([], Some(tail_expr_finished)); let ret_type = method_source.ret_type(); @@ -182,11 +180,11 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' Some(impl_def) => match impl_def.assoc_item_list() { Some(assoc_item_list) => { let item = item.indent(IndentLevel::from_node(impl_def.syntax())); - assoc_item_list.add_items(&mut editor, vec![item.clone()]); + assoc_item_list.add_items(&editor, vec![item.clone()]); Some(item) } None => { - let assoc_item_list = editor.make().assoc_item_list(vec![item]); + let assoc_item_list = make.assoc_item_list(vec![item]); editor.insert( Position::last_child_of(impl_def.syntax()), assoc_item_list.syntax(), @@ -199,15 +197,13 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ty_params = strukt.generic_param_list(); let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); let where_clause = strukt.where_clause(); - let assoc_item_list = editor.make().assoc_item_list(vec![item]); + let assoc_item_list = make.assoc_item_list(vec![item]); - let impl_def = editor.make().impl_( + let impl_def = make.impl_( None, ty_params, ty_args, - syntax::ast::Type::PathType( - editor.make().ty_path(editor.make().ident_path(name)), - ), + syntax::ast::Type::PathType(make.ty_path(make.ident_path(name))), where_clause, Some(assoc_item_list), ); @@ -221,7 +217,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' editor.insert_all( Position::after(strukt.syntax()), vec![ - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_def.syntax().clone().into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index abe447d9d9b7d..6639f10c1f360 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -494,7 +494,7 @@ fn remove_instantiated_params( } } -fn remove_useless_where_clauses(editor: &mut SyntaxEditor, delegate: &ast::Impl) { +fn remove_useless_where_clauses(editor: &SyntaxEditor, delegate: &ast::Impl) { let Some(wc) = delegate.where_clause() else { return; }; @@ -563,7 +563,7 @@ fn finalize_delegate( return Some(delegate.clone()); } - let (mut editor, delegate) = SyntaxEditor::with_ast_node(delegate); + let (editor, delegate) = SyntaxEditor::with_ast_node(delegate); // 1. Replace assoc_item_list if we have new items if let Some(items) = assoc_items @@ -577,7 +577,7 @@ fn finalize_delegate( // 2. Remove useless where clauses if remove_where_clauses { - remove_useless_where_clauses(&mut editor, &delegate); + remove_useless_where_clauses(&editor, &delegate); } ast::Impl::cast(editor.finish().new_root().clone()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 5a514fca09d0f..89666dc804768 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -138,35 +138,30 @@ fn generate_edit( trait_path: ModPath, edition: Edition, ) { - let mut editor = edit.make_editor(strukt.syntax()); + let editor = edit.make_editor(strukt.syntax()); + let make = editor.make(); let strukt_adt = ast::Adt::Struct(strukt.clone()); - let trait_ty = editor.make().ty(&trait_path.display(db, edition).to_string()); + let trait_ty = make.ty(&trait_path.display(db, edition).to_string()); let assoc_items: Vec = match deref_type { DerefType::Deref => { let target_alias = - editor.make().ty_alias([], "Target", None, None, None, Some((field_type, None))); - let ret_ty = editor.make().ty_ref( - editor.make().ty_path(editor.make().path_from_text("Self::Target")).into(), - false, - ); - let field_expr = editor - .make() - .expr_field(editor.make().expr_path(editor.make().ident_path("self")), field_name); - let body = editor - .make() - .block_expr([], Some(editor.make().expr_ref(field_expr.into(), false))); + make.ty_alias([], "Target", None, None, None, Some((field_type, None))); + let ret_ty = + make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), false); + let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); + let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), false))); let fn_ = editor .make() .fn_( [], None, - editor.make().name("deref"), + make.name("deref"), None, None, - editor.make().param_list(Some(editor.make().self_param()), []), + make.param_list(Some(make.self_param()), []), body, - Some(editor.make().ret_type(ret_ty)), + Some(make.ret_type(ret_ty)), false, false, false, @@ -176,26 +171,21 @@ fn generate_edit( vec![ast::AssocItem::TypeAlias(target_alias), ast::AssocItem::Fn(fn_)] } DerefType::DerefMut => { - let ret_ty = editor.make().ty_ref( - editor.make().ty_path(editor.make().path_from_text("Self::Target")).into(), - true, - ); - let field_expr = editor - .make() - .expr_field(editor.make().expr_path(editor.make().ident_path("self")), field_name); - let body = - editor.make().block_expr([], Some(editor.make().expr_ref(field_expr.into(), true))); + let ret_ty = + make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), true); + let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); + let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), true))); let fn_ = editor .make() .fn_( [], None, - editor.make().name("deref_mut"), + make.name("deref_mut"), None, None, - editor.make().param_list(Some(editor.make().mut_self_param()), []), + make.param_list(Some(make.mut_self_param()), []), body, - Some(editor.make().ret_type(ret_ty)), + Some(make.ret_type(ret_ty)), false, false, false, @@ -206,17 +196,13 @@ fn generate_edit( } }; - let body = editor.make().assoc_item_list(assoc_items); + let body = make.assoc_item_list(assoc_items); let indent = strukt.indent_level(); - let impl_ = - generate_trait_impl_intransitive_with_item(editor.make(), &strukt_adt, trait_ty, body) - .indent(indent); + let impl_ = generate_trait_impl_intransitive_with_item(make, &strukt_adt, trait_ty, body) + .indent(indent); editor.insert_all( Position::after(strukt.syntax()), - vec![ - editor.make().whitespace(&format!("\n\n{indent}")).into(), - impl_.syntax().clone().into(), - ], + vec![make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into()], ); edit.add_file_edits(file_id, editor); } 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 7631d13aa8aa8..0129b1db396b2 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 @@ -44,11 +44,13 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| { match derive_attr { None => { - let mut editor = edit.make_editor(nominal.syntax()); - let derive = editor.make().attr_outer(editor.make().meta_token_tree( - editor.make().ident_path("derive"), - editor.make().token_tree(T!['('], vec![]), - )); + let editor = edit.make_editor(nominal.syntax()); + let make = editor.make(); + let derive = + make.attr_outer(make.meta_token_tree( + make.ident_path("derive"), + make.token_tree(T!['('], vec![]), + )); let indent = IndentLevel::from_node(nominal.syntax()); let after_attrs_and_comments = nominal .syntax() @@ -60,7 +62,7 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt after_attrs_and_comments, vec![ derive.syntax().syntax_element(), - editor.make().whitespace(&format!("\n{indent}")).syntax_element(), + make.whitespace(&format!("\n{indent}")).syntax_element(), ], ); 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 096c445d183b8..9b4d44d8b5177 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 @@ -59,12 +59,12 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) let InRealFile { file_id, value: enum_node } = e.source(db)?.original_ast_node_rooted(db)?; acc.add(AssistId::generate("generate_enum_variant"), "Generate variant", target, |builder| { - let mut editor = builder.make_editor(enum_node.syntax()); - let field_list = parent.make_field_list(ctx, editor.make()); - let variant = - editor.make().variant(None, editor.make().name(&name_ref.text()), field_list, None); + 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); if let Some(it) = enum_node.variant_list() { - it.add_variant(&mut editor, &variant); + it.add_variant(&editor, &variant); } builder.add_file_edits(file_id.file_id(ctx.db()), editor); }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 8d4f014389c5a..55e5083811347 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -55,8 +55,8 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) style.label(), func_node.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(func); - + let editor = builder.make_editor(func); + let make = editor.make(); let alias_name = format!("{}Fn", stdx::to_camel_case(&name.to_string())); let mut fn_params_vec = Vec::new(); @@ -68,27 +68,24 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) let is_mut = self_ty.is_mutable_reference(); if let Some(adt) = self_ty.strip_references().as_adt() { - let inner_type = editor.make().ty(adt.name(ctx.db()).as_str()); + let inner_type = make.ty(adt.name(ctx.db()).as_str()); - let ast_self_ty = if is_ref { - editor.make().ty_ref(inner_type, is_mut) - } else { - inner_type - }; + let ast_self_ty = + if is_ref { make.ty_ref(inner_type, is_mut) } else { inner_type }; - fn_params_vec.push(editor.make().unnamed_param(ast_self_ty)); + fn_params_vec.push(make.unnamed_param(ast_self_ty)); } } fn_params_vec.extend(param_list.params().filter_map(|p| match style { ParamStyle::Named => Some(p), - ParamStyle::Unnamed => p.ty().map(|ty| editor.make().unnamed_param(ty)), + ParamStyle::Unnamed => p.ty().map(|ty| make.unnamed_param(ty)), })); let generic_params = func_node.generic_param_list(); let is_unsafe = func_node.unsafe_token().is_some(); - let ty = editor.make().ty_fn_ptr( + let ty = make.ty_fn_ptr( is_unsafe, func_node.abi(), fn_params_vec.into_iter(), @@ -96,7 +93,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ); // Insert new alias - let ty_alias = editor.make().ty_alias( + let ty_alias = make.ty_alias( None, &alias_name, generic_params, @@ -110,7 +107,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) syntax_editor::Position::before(insertion_node), vec![ ty_alias.syntax().clone().into(), - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), ], ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 744ba2ba78b7c..76246c3e8efd0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -40,13 +40,14 @@ pub(crate) fn generate_from_impl_for_enum( "Generate `From` impl for this enum variant(s)", target, |edit| { - let mut editor = edit.make_editor(adt.syntax()); + let editor = edit.make_editor(adt.syntax()); + let make = editor.make(); let indent = adt.indent_level(); let mut elements = Vec::new(); for variant_info in variants { - let impl_ = build_from_impl(editor.make(), &adt, variant_info).indent(indent); - elements.push(editor.make().whitespace(&format!("\n\n{indent}")).into()); + let impl_ = build_from_impl(make, &adt, variant_info).indent(indent); + elements.push(make.whitespace(&format!("\n\n{indent}")).into()); elements.push(impl_.syntax().clone().into()); } 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 f6cbecf81a0a8..d9719aaaf2892 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 @@ -438,9 +438,9 @@ fn build_source_change( ) { if let Some(impl_def) = &assist_info.impl_def { // We have an existing impl to add to - let mut editor = builder.make_editor(impl_def.syntax()); + let editor = builder.make_editor(impl_def.syntax()); let items = items(ctx, info_of_record_fields, &assist_info, editor.make()); - impl_def.assoc_item_list().unwrap().add_items(&mut editor, items.clone()); + impl_def.assoc_item_list().unwrap().add_items(&editor, items.clone()); if let Some(cap) = ctx.config.snippet_cap && let Some(ast::AssocItem::Fn(fn_)) = items.last() @@ -454,24 +454,25 @@ fn build_source_change( return; } - let mut editor = builder.make_editor(assist_info.strukt.syntax()); - let items = items(ctx, info_of_record_fields, &assist_info, editor.make()); + let editor = builder.make_editor(assist_info.strukt.syntax()); + let make = editor.make(); + let items = items(ctx, info_of_record_fields, &assist_info, make); let ty_params = assist_info.strukt.generic_param_list(); let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); - let impl_def = editor.make().impl_( + let impl_def = make.impl_( None, ty_params, ty_args, editor .make() - .ty_path(editor.make().ident_path(&assist_info.strukt.name().unwrap().to_string())) + .ty_path(make.ident_path(&assist_info.strukt.name().unwrap().to_string())) .into(), None, - Some(editor.make().assoc_item_list(items)), + Some(make.assoc_item_list(items)), ); editor.insert_all( Position::after(assist_info.strukt.syntax()), - vec![editor.make().whitespace("\n\n").into(), impl_def.syntax().clone().into()], + vec![make.whitespace("\n\n").into(), impl_def.syntax().clone().into()], ); if let Some(cap) = ctx.config.snippet_cap 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 835675f1b6a34..e0f01b58c2c2a 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 @@ -11,11 +11,8 @@ use crate::{ }, }; -fn insert_impl( - editor: &mut SyntaxEditor, - impl_: &ast::Impl, - nominal: &impl AstNodeEdit, -) -> ast::Impl { +fn insert_impl(editor: &SyntaxEditor, impl_: &ast::Impl, nominal: &impl AstNodeEdit) -> ast::Impl { + let make = editor.make(); let indent = nominal.indent_level(); let impl_ = impl_.indent(indent); @@ -23,7 +20,7 @@ fn insert_impl( Position::after(nominal.syntax()), vec![ // Add a blank line after the ADT, and indentation for the impl to match the ADT - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); @@ -62,10 +59,11 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio format!("Generate impl for `{name}`"), target, |edit| { - let mut editor = edit.make_editor(nominal.syntax()); - let impl_ = generate_impl_with_factory(editor.make(), &nominal); + let editor = edit.make_editor(nominal.syntax()); + let make = editor.make(); + let impl_ = generate_impl_with_factory(make, &nominal); - let impl_ = insert_impl(&mut editor, &impl_, &nominal); + let impl_ = insert_impl(&editor, &impl_, &nominal); // Add a tabstop after the left curly brace if let Some(cap) = ctx.config.snippet_cap && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) @@ -109,13 +107,10 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Generate trait impl for `{name}`"), target, |edit| { - let mut editor = edit.make_editor(nominal.syntax()); - let impl_ = generate_trait_impl_intransitive( - editor.make(), - &nominal, - editor.make().ty_placeholder(), - ); - let impl_ = insert_impl(&mut editor, &impl_, &nominal); + let editor = edit.make_editor(nominal.syntax()); + let make = editor.make(); + let impl_ = generate_trait_impl_intransitive(make, &nominal, make.ty_placeholder()); + let impl_ = insert_impl(&editor, &impl_, &nominal); // Make the trait type a placeholder snippet if let Some(cap) = ctx.config.snippet_cap { if let Some(trait_) = impl_.trait_() { @@ -166,10 +161,10 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Generate `{name}` impl for type"), target, |edit| { - let mut editor = edit.make_editor(trait_.syntax()); + let editor = edit.make_editor(trait_.syntax()); + let make = editor.make(); - let holder_arg = - ast::GenericArg::TypeArg(editor.make().type_arg(editor.make().ty_placeholder())); + let holder_arg = ast::GenericArg::TypeArg(make.type_arg(make.ty_placeholder())); let missing_items = utils::filter_assoc_items( &ctx.sema, &hir_trait.items(ctx.db()), @@ -184,7 +179,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> }); let make_impl_ = |body| { - editor.make().impl_trait( + make.impl_trait( None, trait_.unsafe_token().is_some(), None, @@ -192,8 +187,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> None, None, false, - editor.make().ty(&name.text()), - editor.make().ty_placeholder(), + make.ty(&name.text()), + make.ty_placeholder(), None, None, body, @@ -205,7 +200,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> } else { let impl_ = make_impl_(None); let assoc_items = add_trait_assoc_items_to_impl( - editor.make(), + make, &ctx.sema, ctx.config, &missing_items, @@ -213,11 +208,11 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> &impl_, &target_scope, ); - let assoc_item_list = editor.make().assoc_item_list(assoc_items); + let assoc_item_list = make.assoc_item_list(assoc_items); make_impl_(Some(assoc_item_list)) }; - let impl_ = insert_impl(&mut editor, &impl_, &trait_); + let impl_ = insert_impl(&editor, &impl_, &trait_); if let Some(cap) = ctx.config.snippet_cap { if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { 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 f63b907d86884..acf0819222d53 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 @@ -67,9 +67,9 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - let (mut editor, impl_clone) = SyntaxEditor::with_ast_node(&impl_def.reset_indent()); + let (editor, impl_clone) = SyntaxEditor::with_ast_node(&impl_def.reset_indent()); - apply_generate_mut_impl(&mut editor, &impl_clone, trait_new); + apply_generate_mut_impl(&editor, &impl_clone, trait_new); let new_root = editor.finish(); let new_root = new_root.new_root(); @@ -78,12 +78,13 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let new_impl = new_impl.indent(indent); - let mut editor = edit.make_editor(impl_def.syntax()); + let editor = edit.make_editor(impl_def.syntax()); + let make = editor.make(); editor.insert_all( Position::before(impl_def.syntax()), vec![ new_impl.syntax().syntax_element(), - editor.make().whitespace(&format!("\n\n{indent}")).syntax_element(), + make.whitespace(&format!("\n\n{indent}")).syntax_element(), ], ); @@ -97,7 +98,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> ) } -fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { +fn delete_with_trivia(editor: &SyntaxEditor, node: &SyntaxNode) { let mut end: SyntaxElement = node.clone().into(); if let Some(next) = node.next_sibling_or_token() @@ -111,22 +112,23 @@ fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { } fn apply_generate_mut_impl( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, impl_def: &ast::Impl, trait_new: &str, ) -> Option<()> { + let make = editor.make(); let path = impl_def.trait_().and_then(|t| t.syntax().descendants().find_map(ast::Path::cast))?; let seg = path.segment()?; let name_ref = seg.name_ref()?; - let new_name_ref = editor.make().name_ref(trait_new); + let new_name_ref = make.name_ref(trait_new); editor.replace(name_ref.syntax(), new_name_ref.syntax()); if let Some((name, new_name)) = impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name) { - let new_name_node = editor.make().name(new_name); + let new_name_node = make.name(new_name); editor.replace(name.syntax(), new_name_node.syntax()); } @@ -135,14 +137,14 @@ fn apply_generate_mut_impl( } if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) { - let mut_self = editor.make().mut_self_param(); + let mut_self = make.mut_self_param(); editor.replace(self_param.syntax(), mut_self.syntax()); } if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast) - && let Some(new_ty) = process_ret_type(editor.make(), &ret_type) + && let Some(new_ty) = process_ret_type(make, &ret_type) { - let new_ret = editor.make().ret_type(new_ty); + let new_ret = make.ret_type(new_ty); editor.replace(ret_type.syntax(), new_ret.syntax()) } @@ -158,7 +160,8 @@ fn apply_generate_mut_impl( Some(()) } -fn process_ref_mut(editor: &mut SyntaxEditor, fn_: &ast::Fn) { +fn process_ref_mut(editor: &SyntaxEditor, fn_: &ast::Fn) { + let make = editor.make(); let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return }; let ast::Expr::RefExpr(ref_expr) = expr else { return }; @@ -169,8 +172,8 @@ fn process_ref_mut(editor: &mut SyntaxEditor, fn_: &ast::Fn) { let Some(amp) = ref_expr.amp_token() else { return }; - let mut_kw = editor.make().token(T![mut]); - let space = editor.make().whitespace(" "); + let mut_kw = make.token(T![mut]); + let space = make.whitespace(" "); editor.insert(Position::after(amp.clone()), space.syntax_element()); editor.insert(Position::after(amp), mut_kw.syntax_element()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 7a90428cad692..ea6f0186d18b5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -69,7 +69,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let target = strukt.syntax().text_range(); acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| { - let mut editor = builder.make_editor(strukt.syntax()); + let editor = builder.make_editor(strukt.syntax()); + let make = editor.make(); let trivial_constructors = field_list .iter() @@ -96,64 +97,53 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option edition, )?; - Some((editor.make().name_ref(name), Some(expr))) + Some((make.name_ref(name), Some(expr))) }) .collect::>(); let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| { if trivial_constructors[i].is_none() { - Some(editor.make().param( - editor.make().ident_pat(false, false, editor.make().name(name)).into(), - ty.clone(), - )) + Some(make.param(make.ident_pat(false, false, make.name(name)).into(), ty.clone())) } else { None } }); - let params = editor.make().param_list(None, params); + let params = make.param_list(None, params); let fields = field_list.iter().enumerate().map(|(i, (name, _))| { if let Some(constructor) = trivial_constructors[i].clone() { constructor } else { - (editor.make().name_ref(name), None) + (make.name_ref(name), None) } }); let tail_expr: ast::Expr = match strukt.kind() { StructKind::Record(_) => { - let fields = fields.map(|(name, expr)| editor.make().record_expr_field(name, expr)); - let fields = editor.make().record_expr_field_list(fields); - editor.make().record_expr(editor.make().ident_path("Self"), fields).into() + let fields = fields.map(|(name, expr)| make.record_expr_field(name, expr)); + let fields = make.record_expr_field_list(fields); + make.record_expr(make.ident_path("Self"), fields).into() } StructKind::Tuple(_) => { let args = fields.map(|(arg, expr)| { - let arg = || { - editor.make().expr_path( - editor.make().path_unqualified(editor.make().path_segment(arg)), - ) - }; + let arg = || make.expr_path(make.path_unqualified(make.path_segment(arg))); expr.unwrap_or_else(arg) }); - let arg_list = editor.make().arg_list(args); - editor - .make() - .expr_call(editor.make().expr_path(editor.make().ident_path("Self")), arg_list) - .into() + let arg_list = make.arg_list(args); + make.expr_call(make.expr_path(make.ident_path("Self")), arg_list).into() } StructKind::Unit => unreachable!(), }; - let body = editor.make().block_expr(None, tail_expr.into()); + let body = make.block_expr(None, tail_expr.into()); - let ret_type = - editor.make().ret_type(editor.make().ty_path(editor.make().ident_path("Self")).into()); + let ret_type = make.ret_type(make.ty_path(make.ident_path("Self")).into()); let fn_ = editor .make() .fn_( [], strukt.visibility(), - editor.make().name("new"), + make.name("new"), None, None, params, @@ -180,31 +170,28 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option .whitespace(&format!("\n{}", impl_def.indent_level() + 1)) .into(), fn_.syntax().clone().into(), - editor.make().whitespace("\n").into(), + make.whitespace("\n").into(), ], ); fn_.syntax().clone() } else { - let list = editor.make().assoc_item_list([ast::AssocItem::Fn(fn_)]); + let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]); editor.insert(Position::after(impl_def.syntax()), list.syntax()); list.syntax().clone() } } else { // Generate a new impl to add the method to let indent_level = strukt.indent_level(); - let list = editor.make().assoc_item_list([ast::AssocItem::Fn(fn_)]); - let impl_def = generate_impl_with_item( - editor.make(), - &ast::Adt::Struct(strukt.clone()), - Some(list), - ) - .indent(strukt.indent_level()); + let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]); + let impl_def = + generate_impl_with_item(make, &ast::Adt::Struct(strukt.clone()), Some(list)) + .indent(strukt.indent_level()); // Insert it after the adt editor.insert_all( Position::after(strukt.syntax()), vec![ - editor.make().whitespace(&format!("\n\n{indent_level}")).into(), + make.whitespace(&format!("\n\n{indent_level}")).into(), impl_def.syntax().clone().into(), ], ); 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 d46514dce19e9..7160400a969c6 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 @@ -80,55 +80,52 @@ pub(crate) fn generate_single_field_struct_from( "Generate single field `From`", strukt.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(strukt.syntax()); + let editor = builder.make_editor(strukt.syntax()); + let make = editor.make(); let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); - let trait_gen_args = Some(editor.make().generic_arg_list( - [ast::GenericArg::TypeArg(editor.make().type_arg(main_field_ty.clone()))], + let trait_gen_args = Some(make.generic_arg_list( + [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))], false, )); - let ty = editor.make().ty(&strukt_name.text()); + let ty = make.ty(&strukt_name.text()); - let constructor = make_adt_constructor( - names.as_deref(), - constructors, - &main_field_name, - editor.make(), - ); - let body = editor.make().block_expr([], Some(constructor)); + let constructor = + make_adt_constructor(names.as_deref(), constructors, &main_field_name, make); + let body = make.block_expr([], Some(constructor)); let fn_ = editor .make() .fn_( [], None, - editor.make().name("from"), + make.name("from"), None, None, - editor.make().param_list( + make.param_list( None, - [editor.make().param( - editor.make().path_pat(editor.make().path_from_text(&main_field_name)), + [make.param( + make.path_pat(make.path_from_text(&main_field_name)), main_field_ty, )], ), body, - Some(editor.make().ret_type(editor.make().ty("Self"))), + Some(make.ret_type(make.ty("Self"))), false, false, false, false, ) - .indent_with_mapping(1.into(), editor.make()); + .indent_with_mapping(1.into(), make); let cfg_attrs = strukt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); - let impl_ = editor.make().impl_trait( + let impl_ = make.impl_trait( cfg_attrs, false, None, @@ -136,24 +133,24 @@ pub(crate) fn generate_single_field_struct_from( type_gen_params, type_gen_args, false, - editor.make().ty("From"), + make.ty("From"), ty.clone(), None, ty_where_clause.map(|wc| wc.reset_indent()), None, ); - let (mut impl_editor, impl_root) = SyntaxEditor::with_ast_node(&impl_); - let assoc_list = impl_root.get_or_create_assoc_item_list_with_editor(&mut impl_editor); - assoc_list.add_items(&mut impl_editor, vec![fn_.into()]); + let (impl_editor, impl_root) = SyntaxEditor::with_ast_node(&impl_); + let assoc_list = impl_root.get_or_create_assoc_item_list_with_editor(&impl_editor); + assoc_list.add_items(&impl_editor, vec![fn_.into()]); let impl_ = ast::Impl::cast(impl_editor.finish().new_root().clone()) .unwrap() - .indent_with_mapping(indent, editor.make()); + .indent_with_mapping(indent, make); editor.insert_all( Position::after(strukt.syntax()), vec![ - editor.make().whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into(), ], ); 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 2cbdcaa9da1f0..f5342b8b702d5 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 @@ -98,18 +98,19 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ impl_ast.syntax().text_range(), |builder| { let trait_items: ast::AssocItemList = { - let (mut trait_items_editor, trait_items) = + let (trait_items_editor, trait_items) = SyntaxEditor::with_ast_node(&impl_assoc_items); trait_items.assoc_items().for_each(|item| { - strip_body(&mut trait_items_editor, &item); - remove_items_visibility(&mut trait_items_editor, &item); + strip_body(&trait_items_editor, &item); + remove_items_visibility(&trait_items_editor, &item); }); ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap() }; - let mut editor = builder.make_editor(impl_ast.syntax()); - let trait_ast = editor.make().trait_( + let editor = builder.make_editor(impl_ast.syntax()); + let make = editor.make(); + let trait_ast = make.trait_( false, &trait_name(&impl_assoc_items).text(), impl_ast.generic_param_list(), @@ -118,7 +119,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ ); let trait_name = trait_ast.name().expect("new trait should have a name"); - let trait_name_ref = editor.make().name_ref(&trait_name.to_string()); + let trait_name_ref = make.name_ref(&trait_name.to_string()); // Change `impl Foo` to `impl NewTrait for Foo` let mut elements = vec![ @@ -134,8 +135,8 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ } impl_assoc_items.assoc_items().for_each(|item| { - remove_items_visibility(&mut editor, &item); - remove_doc_comments(&mut editor, &item); + remove_items_visibility(&editor, &item); + remove_doc_comments(&editor, &item); }); editor.insert_all(Position::before(impl_name.syntax()), elements); @@ -175,7 +176,7 @@ fn trait_name(items: &ast::AssocItemList) -> ast::Name { } /// `E0449` Trait items always share the visibility of their trait -fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) { +fn remove_items_visibility(editor: &SyntaxEditor, item: &ast::AssocItem) { if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) { if let Some(vis) = has_vis.visibility() && let Some(token) = vis.syntax().next_sibling_or_token() @@ -189,7 +190,7 @@ fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) { } } -fn remove_doc_comments(editor: &mut SyntaxEditor, item: &ast::AssocItem) { +fn remove_doc_comments(editor: &SyntaxEditor, item: &ast::AssocItem) { for doc in item.doc_comments() { if let Some(next) = doc.syntax().next_token() && next.kind() == SyntaxKind::WHITESPACE @@ -200,7 +201,7 @@ fn remove_doc_comments(editor: &mut SyntaxEditor, item: &ast::AssocItem) { } } -fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) { +fn strip_body(editor: &SyntaxEditor, item: &ast::AssocItem) { if let ast::AssocItem::Fn(f) = item && let Some(body) = f.body() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 5bf7e290e2dac..ad539071561a0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -83,7 +83,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) "Inline variable", target.text_range(), move |builder| { - let mut editor = builder.make_editor(&target); + let editor = builder.make_editor(&target); + let make = editor.make(); if delete_let { editor.delete(let_stmt.syntax()); @@ -91,23 +92,23 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) && let Some(op_token) = bin_expr.op_token() { editor.delete(&op_token); - remove_whitespace(op_token, Direction::Prev, &mut editor); - remove_whitespace(let_stmt.syntax(), Direction::Prev, &mut editor); + remove_whitespace(op_token, Direction::Prev, &editor); + remove_whitespace(let_stmt.syntax(), Direction::Prev, &editor); } else { - remove_whitespace(let_stmt.syntax(), Direction::Next, &mut editor); + remove_whitespace(let_stmt.syntax(), Direction::Next, &editor); } } for (name, should_wrap) in wrap_in_parens { let replacement = if should_wrap { - editor.make().expr_paren(initializer_expr.clone()).into() + make.expr_paren(initializer_expr.clone()).into() } else { initializer_expr.clone() }; if let Some(record_field) = ast::RecordExprField::for_field_name(&name) { cov_mark::hit!(inline_field_shorthand); - let replacement = editor.make().record_expr_field(name, Some(replacement)); + let replacement = make.record_expr_field(name, Some(replacement)); editor.replace(record_field.syntax(), replacement.syntax()); } else { editor.replace(name.syntax(), replacement.syntax()); @@ -200,7 +201,7 @@ fn inline_usage( Some(InlineData { let_stmt, delete_let, target: ast::NameOrNameRef::NameRef(name), references }) } -fn remove_whitespace(elem: impl Element, dir: Direction, editor: &mut SyntaxEditor) { +fn remove_whitespace(elem: impl Element, dir: Direction, editor: &SyntaxEditor) { let token = match elem.syntax_element() { syntax::NodeOrToken::Node(node) => match dir { Direction::Next => node.last_token(), 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 13c455faad1b6..6d8750afdcff1 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 @@ -70,7 +70,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let mut inline_refs_for_file = |file_id, refs: Vec| { let source = ctx.sema.parse(file_id); - let mut editor = builder.make_editor(source.syntax()); + let editor = builder.make_editor(source.syntax()); let (path_types, path_type_uses) = split_refs_and_uses(builder, refs, |path_type| { @@ -101,7 +101,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) inline_refs_for_file(file_id, refs); } if !definition_deleted { - let mut editor = builder.make_editor(ast_alias.syntax()); + let editor = builder.make_editor(ast_alias.syntax()); editor.delete(ast_alias.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor) } @@ -156,7 +156,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O "Inline type alias", alias_instance.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(alias_instance.syntax()); + let editor = builder.make_editor(alias_instance.syntax()); let replace = replacement.replace_generic(&concrete_type); editor.replace(alias_instance.syntax(), replace); builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -312,8 +312,8 @@ fn create_replacement( const_and_type_map: &ConstAndTypeMap, concrete_type: &ast::Type, ) -> SyntaxNode { - let (mut editor, updated_concrete_type) = SyntaxEditor::new(concrete_type.syntax().clone()); - + let (editor, updated_concrete_type) = SyntaxEditor::new(concrete_type.syntax().clone()); + let make = editor.make(); let mut replacements: Vec<(SyntaxNode, SyntaxNode)> = Vec::new(); let mut removals: Vec> = Vec::new(); @@ -368,7 +368,7 @@ fn create_replacement( }; let new_string = replacement_syntax.to_string(); let new = if new_string == "_" { - editor.make().wildcard_pat().syntax().clone() + make.wildcard_pat().syntax().clone() } else { replacement_syntax.clone() }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 7b6b84f7e8a65..2cbeae1d19c25 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -97,22 +97,23 @@ fn generate_fn_def_assist( }; acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { - let mut editor = edit.make_editor(fn_def.syntax()); + let editor = edit.make_editor(fn_def.syntax()); + let make = editor.make(); if let Some(generic_list) = fn_def.generic_param_list() { - insert_lifetime_param(&mut editor, &generic_list, &new_lifetime_name); + insert_lifetime_param(&editor, &generic_list, &new_lifetime_name); } else { - insert_new_generic_param_list_fn(&mut editor, &fn_def, &new_lifetime_name); + insert_new_generic_param_list_fn(&editor, &fn_def, &new_lifetime_name); } - editor.replace(lifetime.syntax(), editor.make().lifetime(&new_lifetime_name).syntax()); + editor.replace(lifetime.syntax(), make.lifetime(&new_lifetime_name).syntax()); if let Some(pos) = loc_needing_lifetime.and_then(|l| l.to_position()) { editor.insert_all( pos, vec![ - editor.make().lifetime(&new_lifetime_name).syntax().clone().into(), - editor.make().whitespace(" ").into(), + make.lifetime(&new_lifetime_name).syntax().clone().into(), + make.whitespace(" ").into(), ], ); } @@ -122,18 +123,19 @@ fn generate_fn_def_assist( } fn insert_new_generic_param_list_fn( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, fn_def: &ast::Fn, lifetime_name: &str, ) -> Option<()> { + let make = editor.make(); let name = fn_def.name()?; editor.insert_all( Position::after(name.syntax()), vec![ - editor.make().token(T![<]).syntax_element(), - editor.make().lifetime(lifetime_name).syntax().syntax_element(), - editor.make().token(T![>]).syntax_element(), + make.token(T![<]).syntax_element(), + make.lifetime(lifetime_name).syntax().syntax_element(), + make.token(T![>]).syntax_element(), ], ); @@ -164,33 +166,35 @@ fn generate_impl_def_assist( let new_lifetime_name = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { - let mut editor = edit.make_editor(impl_def.syntax()); + let editor = edit.make_editor(impl_def.syntax()); + let make = editor.make(); if let Some(generic_list) = impl_def.generic_param_list() { - insert_lifetime_param(&mut editor, &generic_list, &new_lifetime_name); + insert_lifetime_param(&editor, &generic_list, &new_lifetime_name); } else { - insert_new_generic_param_list_imp(&mut editor, &impl_def, &new_lifetime_name); + insert_new_generic_param_list_imp(&editor, &impl_def, &new_lifetime_name); } - editor.replace(lifetime.syntax(), editor.make().lifetime(&new_lifetime_name).syntax()); + editor.replace(lifetime.syntax(), make.lifetime(&new_lifetime_name).syntax()); edit.add_file_edits(file_id, editor); }) } fn insert_new_generic_param_list_imp( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, impl_: &ast::Impl, lifetime_name: &str, ) -> Option<()> { + let make = editor.make(); let impl_kw = impl_.impl_token()?; editor.insert_all( Position::after(impl_kw), vec![ - editor.make().token(T![<]).syntax_element(), - editor.make().lifetime(lifetime_name).syntax().syntax_element(), - editor.make().token(T![>]).syntax_element(), + make.token(T![<]).syntax_element(), + make.lifetime(lifetime_name).syntax().syntax_element(), + make.token(T![>]).syntax_element(), ], ); @@ -198,21 +202,22 @@ fn insert_new_generic_param_list_imp( } fn insert_lifetime_param( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, generic_list: &ast::GenericParamList, lifetime_name: &str, ) -> Option<()> { + let make = editor.make(); let r_angle = generic_list.r_angle_token()?; let needs_comma = generic_list.generic_params().next().is_some(); let mut elements = Vec::new(); if needs_comma { - elements.push(editor.make().token(T![,]).syntax_element()); - elements.push(editor.make().whitespace(" ").syntax_element()); + elements.push(make.token(T![,]).syntax_element()); + elements.push(make.whitespace(" ").syntax_element()); } - let lifetime = editor.make().lifetime(lifetime_name); + let lifetime = make.lifetime(lifetime_name); elements.push(lifetime.syntax().clone().into()); editor.insert_all(Position::before(r_angle), elements); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 5987d8d9f5e41..95f223420b0b8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -30,7 +30,8 @@ pub(crate) fn introduce_named_type_parameter( "Replace impl trait with type parameter", target, |builder| { - let mut editor = builder.make_editor(fn_.syntax()); + let editor = builder.make_editor(fn_.syntax()); + let make = editor.make(); let existing_names = match fn_.generic_param_list() { Some(generic_param_list) => generic_param_list @@ -47,10 +48,8 @@ pub(crate) fn introduce_named_type_parameter( ) .for_impl_trait_as_generic(&impl_trait_type); - let type_param = editor - .make() - .type_param(editor.make().name(&type_param_name), Some(type_bound_list)); - let new_ty = editor.make().ty(&type_param_name); + let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list)); + let new_ty = make.ty(&type_param_name); editor.replace(impl_trait_type.syntax(), new_ty.syntax()); editor.add_generic_param(&fn_, type_param.clone().into()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index d866da50b173c..1dd0833fad03d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -72,16 +72,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio }; acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| { - let mut editor = builder.make_editor(&parent_node); + let editor = builder.make_editor(&parent_node); for edit in edits { match edit { Remove(it) => { let node = it.as_ref(); if let Some(left) = node.left() { - left.remove(&mut editor); + left.remove(&editor); } else if let Some(right) = node.right() { - right.remove(&mut editor); + right.remove(&editor); } } Replace(old, new) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 25008179b1200..e044068ff7578 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -45,21 +45,21 @@ pub(crate) fn move_bounds_to_where_clause( "Move to where clause", target, |builder| { - let mut edit = builder.make_editor(&parent); + let editor = builder.make_editor(&parent); let new_preds: Vec = type_param_list .generic_params() - .filter_map(|param| build_predicate(param, edit.make())) + .filter_map(|param| build_predicate(param, editor.make())) .collect(); match_ast! { match (&parent) { - ast::Fn(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), - ast::Trait(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), - ast::Impl(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), - ast::Enum(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), - ast::Struct(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), - ast::TypeAlias(it) => it.get_or_create_where_clause(&mut edit, new_preds.into_iter()), + ast::Fn(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), + ast::Trait(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), + ast::Impl(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), + ast::Enum(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), + ast::Struct(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), + ast::TypeAlias(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()), _ => return, } }; @@ -71,11 +71,11 @@ pub(crate) fn move_bounds_to_where_clause( ast::GenericParam::ConstParam(_) => continue, }; if let Some(tbl) = param.type_bound_list() { - tbl.remove(&mut edit); + tbl.remove(&editor); } } - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index ec4acdafa9045..7309cc6d06a5c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -73,24 +73,24 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) "Move guard to arm body", target, |builder| { - let mut edit = builder.make_editor(match_arm.syntax()); + let editor = builder.make_editor(match_arm.syntax()); for element in space_before_delete { if element.kind() == WHITESPACE { - edit.delete(element); + editor.delete(element); } } for rest_arm in &rest_arms { - edit.delete(rest_arm.syntax()); + editor.delete(rest_arm.syntax()); } if let Some(element) = space_after_arrow && element.kind() == WHITESPACE { - edit.replace(element, make.whitespace(" ")); + editor.replace(element, make.whitespace(" ")); } - edit.delete(guard.syntax()); - edit.replace(arm_expr.syntax(), if_expr.syntax()); - builder.add_file_edits(ctx.vfs_file_id(), edit); + editor.delete(guard.syntax()); + editor.replace(arm_expr.syntax(), if_expr.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -156,7 +156,8 @@ pub(crate) fn move_arm_cond_to_match_guard( "Move condition to match guard", replace_node.text_range(), |builder| { - let mut editor = builder.make_editor(match_arm.syntax()); + let editor = builder.make_editor(match_arm.syntax()); + let make = editor.make(); let mut replace_arms = vec![]; // Dedent if if_expr is in a BlockExpr @@ -175,17 +176,17 @@ pub(crate) fn move_arm_cond_to_match_guard( (Some(lhs), Some(rhs)) => { let op_expr = |expr: Expr| { if expr.precedence().needs_parentheses_in(ExprPrecedence::LAnd) { - editor.make().expr_paren(expr).into() + make.expr_paren(expr).into() } else { expr } }; let op = syntax::ast::BinaryOp::LogicOp(syntax::ast::LogicOp::And); - let expr_bin = editor.make().expr_bin(op_expr(lhs), op, op_expr(rhs)); + let expr_bin = make.expr_bin(op_expr(lhs), op, op_expr(rhs)); expr_bin.into() } }; - Some(editor.make().match_guard(condition)) + Some(make.match_guard(condition)) }; for (cond, block) in conds_blocks { @@ -194,8 +195,7 @@ pub(crate) fn move_arm_cond_to_match_guard( Some(then_expr) if only_expr => then_expr, _ => block.dedent(dedent.into()).into(), }; - let new_arm = - editor.make().match_arm(match_pat.clone(), make_guard(Some(cond)), expr); + let new_arm = make.match_arm(match_pat.clone(), make_guard(Some(cond)), expr); replace_arms.push(new_arm); } if let Some(block) = tail { @@ -208,7 +208,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => block.dedent(dedent.into()).into(), }; - let new_arm = editor.make().match_arm(match_pat, make_guard(None), expr); + let new_arm = make.match_arm(match_pat, make_guard(None), expr); replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match @@ -222,17 +222,13 @@ pub(crate) fn move_arm_cond_to_match_guard( cov_mark::hit!(move_guard_ifelse_has_wildcard); } _ => { - let block_expr = editor.make().expr_empty_block().into(); - replace_arms.push(editor.make().match_arm( - match_pat, - make_guard(None), - block_expr, - )); + let block_expr = make.expr_empty_block().into(); + replace_arms.push(make.match_arm(match_pat, make_guard(None), block_expr)); } } } - let newline = editor.make().whitespace(&format!("\n{indent_level}")); + let newline = make.whitespace(&format!("\n{indent_level}")); let replace_arms = replace_arms.iter().map(|it| it.syntax().syntax_element()); let replace_arms = Itertools::intersperse(replace_arms, newline.syntax_element()); editor.replace_with_many(match_arm.syntax(), replace_arms.collect()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 5b6251e3dbe21..ed61d32eb657f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -68,17 +68,18 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) "Promote local to constant", let_stmt.syntax().text_range(), |edit| { - let mut editor = edit.make_editor(let_stmt.syntax()); + let editor = edit.make_editor(let_stmt.syntax()); + let make = editor.make(); let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { - let name_ref = editor.make().name_ref(&name); + let name_ref = make.name_ref(&name); for usage in usages { let Some(usage_name) = usage.name.as_name_ref().cloned() else { continue }; if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage_name) { - let path = editor.make().ident_path(&name); - let name_expr = editor.make().expr_path(path); + let path = make.ident_path(&name); + let name_expr = make.expr_path(path); utils::replace_record_field_expr(ctx, edit, record_field, name_expr); } else { let usage_range = usage.range; @@ -87,13 +88,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let item = editor.make().item_const( - None, - None, - editor.make().name(&name), - editor.make().ty(&ty), - initializer, - ); + let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { let tabstop = edit.make_tabstop_before(cap); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 5d89defd217fb..082052c9d42dc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -70,8 +70,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> } let target = tgt.syntax().text_range(); - let (mut editor, edit_tgt) = SyntaxEditor::new(tgt.syntax().clone()); - + let (editor, edit_tgt) = SyntaxEditor::new(tgt.syntax().clone()); let assignments: Vec<_> = collector .assignments .into_iter() @@ -105,9 +104,10 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Pull assignment up", target, move |edit| { - let mut editor = edit.make_editor(tgt.syntax()); - let assign_expr = editor.make().expr_assignment(collector.common_lhs, new_tgt.clone()); - let assign_stmt = editor.make().expr_stmt(assign_expr.into()); + let editor = edit.make_editor(tgt.syntax()); + let make = editor.make(); + let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone()); + let assign_stmt = make.expr_stmt(assign_expr.into()); editor.replace(tgt.syntax(), assign_stmt.syntax()); edit.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 653e3e3f8776b..d7885d50651c0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -59,14 +59,8 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Qualify `{ident}` method call"), range, |builder| { - let mut editor = builder.make_editor(call.syntax()); - qualify_candidate.qualify( - |_| {}, - &mut editor, - &receiver_path, - item_in_ns, - current_edition, - ); + let editor = builder.make_editor(call.syntax()); + qualify_candidate.qualify(|_| {}, &editor, &receiver_path, item_in_ns, current_edition); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 123269e0ae6e0..e3dd77360cca3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -98,10 +98,10 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option label(ctx.db(), candidate, &import, current_edition), range, |builder| { - let mut editor = builder.make_editor(&syntax_under_caret); + let editor = builder.make_editor(&syntax_under_caret); qualify_candidate.qualify( |replace_with: String| builder.replace(range, replace_with), - &mut editor, + &editor, &import.import_path, import.item_to_import, current_edition, @@ -124,7 +124,7 @@ impl QualifyCandidate<'_> { pub(crate) fn qualify( &self, mut replacer: impl FnMut(String), - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, import: &hir::ModPath, item: hir::ItemInNs, edition: Edition, @@ -154,10 +154,11 @@ impl QualifyCandidate<'_> { fn qualify_fn_call( db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, import: ast::Path, hir_fn: &hir::Function, ) -> Option<()> { + let make = editor.make(); let receiver = mcall_expr.receiver()?; let method_name = mcall_expr.name_ref()?; let generics = @@ -166,17 +167,16 @@ impl QualifyCandidate<'_> { if let Some(self_access) = hir_fn.self_param(db).map(|sp| sp.access(db)) { let receiver = match self_access { - hir::Access::Shared => editor.make().expr_ref(receiver, false), - hir::Access::Exclusive => editor.make().expr_ref(receiver, true), + hir::Access::Shared => make.expr_ref(receiver, false), + hir::Access::Exclusive => make.expr_ref(receiver, true), hir::Access::Owned => receiver, }; let arg_list = match arg_list { - Some(args) => editor.make().arg_list(iter::once(receiver).chain(args)), - None => editor.make().arg_list(iter::once(receiver)), + Some(args) => make.arg_list(iter::once(receiver).chain(args)), + None => make.arg_list(iter::once(receiver)), }; - let call_path = - editor.make().path_from_text(&format!("{import}::{method_name}{generics}")); - let call_expr = editor.make().expr_call(editor.make().expr_path(call_path), arg_list); + let call_path = make.path_from_text(&format!("{import}::{method_name}{generics}")); + let call_expr = make.expr_call(make.expr_path(call_path), arg_list); editor.replace(mcall_expr.syntax(), call_expr.syntax()); } Some(()) @@ -185,7 +185,7 @@ impl QualifyCandidate<'_> { fn qualify_trait_method( db: &RootDatabase, mcall_expr: &ast::MethodCallExpr, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, import: ast::Path, item: hir::ItemInNs, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index d6d99b8b6d9dc..24e41189dac40 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -164,7 +164,7 @@ fn replace_literal( ) { let token = token.syntax(); let node = token.parent().expect("no parent token"); - let mut edit = builder.make_editor(&node); + let edit = builder.make_editor(&node); let new_literal = literal(new); edit.replace(token, mut_token(new_literal)); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index f4c354b8a21d8..32fae8a5bbd43 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -47,7 +47,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( .map(|t| t.text_range()) .reduce(|acc, range| acc.cover(range))?; acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| { - let mut editor = builder.make_editor(ctx.source_file().syntax()); + let editor = builder.make_editor(ctx.source_file().syntax()); for (range, expr) in replacements { if let Some(expr) = expr { editor.insert(Position::before(range[0].clone()), expr.syntax()); @@ -209,7 +209,7 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { return replaced; } - let (mut editor, expanded) = SyntaxEditor::with_ast_node(&expanded); + let (editor, expanded) = SyntaxEditor::with_ast_node(&expanded); // We need to collect to avoid mutation during traversal. let macro_exprs: Vec<_> = expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs index 6a02c37015d33..0c03856417a0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs @@ -55,7 +55,7 @@ pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_>) - "Remove `else` branches", target, |builder| { - let mut editor = builder.make_editor(&else_token.parent().unwrap()); + let editor = builder.make_editor(&else_token.parent().unwrap()); match else_token.prev_token() { Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it), _ => (), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs index b07a361adf48e..2a6024339f605 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs @@ -22,7 +22,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let target = mut_token.text_range(); acc.add(AssistId::refactor("remove_mut"), "Remove `mut` keyword", target, |builder| { - let mut editor = builder.make_editor(&mut_token.parent().unwrap()); + let editor = builder.make_editor(&mut_token.parent().unwrap()); match mut_token.next_token() { Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it), _ => (), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index 480b9d44e1f84..af249c97b9c42 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -40,7 +40,8 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Remove redundant parentheses", target, |builder| { - let mut editor = builder.make_editor(parens.syntax()); + let editor = builder.make_editor(parens.syntax()); + let make = editor.make(); let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token()); let need_to_add_ws = match prev_token { Some(it) => { @@ -50,7 +51,7 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> None => false, }; if need_to_add_ws { - editor.insert(Position::before(parens.syntax()), editor.make().whitespace(" ")); + editor.insert(Position::before(parens.syntax()), make.whitespace(" ")); } editor.replace(parens.syntax(), expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index 8b824c7c7f497..b91d678c9371f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -80,7 +80,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Remove unused parameter", param.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(&parent); + let editor = builder.make_editor(&parent); let elements = elements_to_remove(param.syntax()); for element in elements { editor.delete(element); @@ -116,7 +116,7 @@ fn process_usages( else { continue; }; - let mut editor = builder.make_editor(&parent); + let editor = builder.make_editor(&parent); for element in element_range { editor.delete(element); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 990677d372139..facbab8019b2a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -71,14 +71,12 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti "Reorder record fields", target, |builder| { - let mut editor = builder.make_editor(&parent_node); + let editor = builder.make_editor(&parent_node); match fields { - Either::Left((sorted, field_list)) => { - replace(&mut editor, field_list.fields(), sorted) - } + Either::Left((sorted, field_list)) => replace(&editor, field_list.fields(), sorted), Either::Right((sorted, field_list)) => { - replace(&mut editor, field_list.fields(), sorted) + replace(&editor, field_list.fields(), sorted) } } @@ -88,7 +86,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } fn replace( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, fields: impl Iterator, sorted_fields: impl IntoIterator, ) { 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 0ad5ec9d44246..df5281895abdc 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 @@ -99,7 +99,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> "Sort items by trait definition", target, |builder| { - let mut editor = builder.make_editor(&parent_node); + let editor = builder.make_editor(&parent_node); assoc_items .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 657f19845a72f..5ad5efac05c24 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -83,18 +83,15 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> kind.label(), op_expr.text_range(), |builder| { - let mut editor = builder.make_editor(rhs.syntax()); + let editor = builder.make_editor(rhs.syntax()); + let make = editor.make(); let method_name = kind.method_name(op); let needs_parentheses = lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix); - let receiver = - if needs_parentheses { editor.make().expr_paren(lhs).into() } else { lhs }; - let arith_expr = editor.make().expr_method_call( - receiver, - editor.make().name_ref(&method_name), - editor.make().arg_list([rhs]), - ); + let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs }; + let arith_expr = + make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])); editor.replace(op_expr, arith_expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, 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 fc63c59799ed0..751cd42f6e8df 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 @@ -128,7 +128,8 @@ fn add_assist( let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { - let mut editor = builder.make_editor(attr.syntax()); + let editor = builder.make_editor(attr.syntax()); + let make = editor.make(); let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); let impl_def = impl_def_from_trait( @@ -141,9 +142,9 @@ fn add_assist( replace_trait_path, impl_is_unsafe, ); - update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr); + update_attribute(&editor, old_derives, old_tree, old_trait_path, attr); - let trait_path = editor.make().ty_path(replace_trait_path.clone()).into(); + let trait_path = make.ty_path(replace_trait_path.clone()).into(); let (impl_def, first_assoc_item) = if let Some(impl_def) = impl_def { ( @@ -151,7 +152,7 @@ fn add_assist( impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()), ) } else { - (generate_trait_impl(editor.make(), impl_is_unsafe, adt, trait_path), None) + (generate_trait_impl(make, impl_is_unsafe, adt, trait_path), None) }; if let Some(cap) = ctx.config.snippet_cap { @@ -175,7 +176,7 @@ fn add_assist( editor.insert_all( insert_after, - vec![editor.make().whitespace("\n\n").into(), impl_def.syntax().clone().into()], + vec![make.whitespace("\n\n").into(), impl_def.syntax().clone().into()], ); builder.add_file_edits(ctx.vfs_file_id(), editor); }) @@ -191,6 +192,7 @@ fn impl_def_from_trait( trait_path: &ast::Path, impl_is_unsafe: bool, ) -> Option { + let make = editor.make(); let trait_ = trait_?; let target_scope = sema.scope(annotated_name.syntax())?; @@ -207,11 +209,11 @@ fn impl_def_from_trait( if trait_items.is_empty() { return None; } - let trait_ty: ast::Type = editor.make().ty_path(trait_path.clone()).into(); - let impl_def = generate_trait_impl(editor.make(), impl_is_unsafe, adt, trait_ty.clone()); + let trait_ty: ast::Type = make.ty_path(trait_path.clone()).into(); + let impl_def = generate_trait_impl(make, impl_is_unsafe, adt, trait_ty.clone()); let assoc_items = add_trait_assoc_items_to_impl( - editor.make(), + make, sema, config, &trait_items, @@ -221,10 +223,10 @@ fn impl_def_from_trait( ); let assoc_item_list = if let Some((first, other)) = assoc_items.split_first() { let first_item = if let ast::AssocItem::Fn(func) = first - && let Some(body) = gen_trait_fn_body(editor.make(), func, trait_path, adt, None) + && let Some(body) = gen_trait_fn_body(make, func, trait_path, adt, None) && let Some(func_body) = func.body() { - let (mut editor, _) = SyntaxEditor::new(first.syntax().clone()); + let (editor, _) = SyntaxEditor::new(first.syntax().clone()); editor.replace(func_body.syntax(), body.syntax()); ast::AssocItem::cast(editor.finish().new_root().clone()) } else { @@ -232,27 +234,22 @@ fn impl_def_from_trait( }; let items: Vec = first_item.into_iter().chain(other.iter().cloned()).collect(); - editor.make().assoc_item_list(items) + make.assoc_item_list(items) } else { - editor.make().assoc_item_list_empty() + make.assoc_item_list_empty() }; - Some(generate_trait_impl_with_item( - editor.make(), - impl_is_unsafe, - adt, - trait_ty, - assoc_item_list, - )) + Some(generate_trait_impl_with_item(make, impl_is_unsafe, adt, trait_ty, assoc_item_list)) } fn update_attribute( - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, attr: &ast::Attr, ) { + let make = editor.make(); let new_derives = old_derives .iter() .filter(|t| t.to_string() != old_trait_path.to_string()) @@ -267,16 +264,13 @@ fn update_attribute( .collect::>() }); // ...which are interspersed with ", " - let tt = Itertools::intersperse( - tt, - vec![editor.make().token(T![,]), editor.make().whitespace(" ")], - ); + let tt = Itertools::intersperse(tt, vec![make.token(T![,]), make.whitespace(" ")]); // ...wrap them into the appropriate `NodeOrToken` variant let tt = tt.flatten().map(syntax::NodeOrToken::Token); // ...and make them into a flat list of tokens let tt = tt.collect::>(); - let new_tree = editor.make().token_tree(T!['('], tt); + let new_tree = make.token_tree(T!['('], tt); editor.replace(old_tree.syntax(), new_tree.syntax()); } else { // Remove the attr and any trailing whitespace 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 d1619e020dd2f..3e8afd8936746 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 @@ -111,34 +111,28 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' format!("Replace if{let_} with match"), available_range, move |builder| { - let mut editor = builder.make_editor(if_expr.syntax()); + let editor = builder.make_editor(if_expr.syntax()); + let make = editor.make(); let match_expr: ast::Expr = { - let else_arm = make_else_arm(ctx, editor.make(), else_block, &cond_bodies); + let else_arm = make_else_arm(ctx, make, else_block, &cond_bodies); let make_match_arm = |(pat, guard, body): (_, Option, ast::BlockExpr)| { // Dedent from original position, then indent for match arm let body = body.dedent(indent); let body = unwrap_trivial_block(body); - match (pat, guard.map(|it| editor.make().match_guard(it))) { - (Some(pat), guard) => editor.make().match_arm(pat, guard, body), - (None, _) if !pat_seen => editor.make().match_arm( - editor.make().literal_pat("true").into(), - None, - body, - ), - (None, guard) => editor.make().match_arm( - editor.make().wildcard_pat().into(), - guard, - body, - ), + match (pat, guard.map(|it| make.match_guard(it))) { + (Some(pat), guard) => make.match_arm(pat, guard, body), + (None, _) if !pat_seen => { + make.match_arm(make.literal_pat("true").into(), None, body) + } + (None, guard) => { + make.match_arm(make.wildcard_pat().into(), guard, body) + } } }; let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); let expr = scrutinee_to_be_expr.reset_indent(); - let match_expr = editor - .make() - .expr_match(expr, editor.make().match_arm_list(arms)) - .indent(indent); + let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent); match_expr.into() }; @@ -272,14 +266,15 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' format!("Replace match with if{let_}"), match_expr.syntax().text_range(), move |builder| { - let mut editor = builder.make_editor(match_expr.syntax()); + let editor = builder.make_editor(match_expr.syntax()); + let make = editor.make(); let make_block_expr = |expr: ast::Expr| { // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are // formatted without enclosing braces. If we encounter such block exprs, // wrap them in another BlockExpr. match expr { ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, - expr => editor.make().block_expr([], Some(expr.indent(IndentLevel(1)))), + expr => make.block_expr([], Some(expr.indent(IndentLevel(1)))), } }; @@ -292,16 +287,13 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { - editor.make().expr_prefix(T![!], scrutinee).into() + make.expr_prefix(T![!], scrutinee).into() } - _ => editor.make().expr_let(if_let_pat, scrutinee).into(), + _ => make.expr_let(if_let_pat, scrutinee).into(), }; let condition = if let Some(guard) = guard { - let guard = wrap_paren(guard, editor.make(), ast::prec::ExprPrecedence::LAnd); - editor - .make() - .expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard) - .into() + let guard = wrap_paren(guard, make, ast::prec::ExprPrecedence::LAnd); + make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() } else { condition }; @@ -409,21 +401,20 @@ fn let_and_guard(cond: &ast::Expr) -> (Option, Option) } else if let ast::Expr::BinExpr(bin_expr) = cond && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs() { - let (mut edit, new_expr) = SyntaxEditor::with_ast_node(bin_expr); - + let (editor, new_expr) = SyntaxEditor::with_ast_node(bin_expr); let left_bin = and_bin_expr_left(&new_expr); if let Some(rhs) = left_bin.rhs() { - edit.replace(left_bin.syntax(), rhs.syntax()); + editor.replace(left_bin.syntax(), rhs.syntax()); } else { if let Some(next) = left_bin.syntax().next_sibling_or_token() && next.kind() == SyntaxKind::WHITESPACE { - edit.delete(next); + editor.delete(next); } - edit.delete(left_bin.syntax()); + editor.delete(left_bin.syntax()); } - let new_expr = edit.finish().new_root().clone(); + let new_expr = editor.finish().new_root().clone(); (Some(let_expr), ast::Expr::cast(new_expr)) } else { (None, Some(cond.clone())) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 35acd8c3eef45..802d5f72b9973 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -44,7 +44,8 @@ pub(crate) fn replace_is_method_with_if_let_method( let method_kind = token.text().strip_suffix("_and").unwrap_or(token.text()); match method_kind { "is_some" | "is_ok" => { - let (mut editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let make = editor.make(); let receiver = call_expr.receiver()?; let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals( ctx.sema.scope(has_cond.syntax()), @@ -54,7 +55,7 @@ pub(crate) fn replace_is_method_with_if_let_method( } else { name_generator.for_variable(&receiver, &ctx.sema) }; - let (pat, predicate) = method_predicate(&call_expr, &var_name, editor.make()); + let (pat, predicate) = method_predicate(&call_expr, &var_name, make); let (assist_id, message, text) = if method_kind == "is_some" { ("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some") @@ -67,11 +68,9 @@ pub(crate) fn replace_is_method_with_if_let_method( message, call_expr.syntax().text_range(), |edit| { - let pat = editor - .make() - .tuple_struct_pat(editor.make().ident_path(text), [pat]) - .into(); - let let_expr = editor.make().expr_let(pat, receiver); + let make = editor.make(); + let pat = make.tuple_struct_pat(make.ident_path(text), [pat]).into(); + let let_expr = make.expr_let(pat, receiver); if let Some(cap) = ctx.config.snippet_cap && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() @@ -84,8 +83,8 @@ pub(crate) fn replace_is_method_with_if_let_method( let new_expr = if let Some(predicate) = predicate { let op = ast::BinaryOp::LogicOp(ast::LogicOp::And); - let predicate = wrap_paren(predicate, editor.make(), ExprPrecedence::LAnd); - editor.make().expr_bin(let_expr.into(), op, predicate).into() + let predicate = wrap_paren(predicate, make, ExprPrecedence::LAnd); + make.expr_bin(let_expr.into(), op, predicate).into() } else { ast::Expr::from(let_expr) }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 5f6909ea3e242..d36f7f1f066af 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -46,7 +46,8 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> "Replace let with if let", target, |builder| { - let mut editor = builder.make_editor(let_stmt.syntax()); + let editor = builder.make_editor(let_stmt.syntax()); + let make = editor.make(); let ty = ctx.sema.type_of_expr(&init); let pat = if let_stmt.let_else().is_some() { // Do not add the wrapper type that implements `Try`, @@ -60,26 +61,23 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> None => original_pat, Some(var_name) => editor .make() - .tuple_struct_pat(editor.make().ident_path(var_name), [original_pat]) + .tuple_struct_pat(make.ident_path(var_name), [original_pat]) .into(), } }; - let init_expr = if let_expr_needs_paren(&init) { - editor.make().expr_paren(init).into() - } else { - init - }; + let init_expr = + if let_expr_needs_paren(&init) { make.expr_paren(init).into() } else { init }; - let block = editor.make().block_expr([], None); + let block = make.block_expr([], None); let block = block.indent(IndentLevel::from_node(let_stmt.syntax())); - let if_expr = editor.make().expr_if( - editor.make().expr_let(pat, init_expr).into(), + let if_expr = make.expr_if( + make.expr_let(pat, init_expr).into(), block, let_stmt .let_else() .and_then(|let_else| let_else.block_expr().map(ast::ElseBranch::from)), ); - let if_stmt = editor.make().expr_stmt(if_expr.into()); + let if_stmt = make.expr_stmt(if_expr.into()); editor.replace(let_stmt.syntax(), if_stmt.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 6403a896bc716..17ef7727eca39 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -71,7 +71,8 @@ pub(crate) fn replace_named_generic_with_impl( "Replace named generic with impl trait", target, |edit| { - let mut editor = edit.make_editor(type_param.syntax()); + let editor = edit.make_editor(type_param.syntax()); + let make = editor.make(); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { @@ -83,12 +84,12 @@ pub(crate) fn replace_named_generic_with_impl( if params.is_empty() { editor.delete(generic_params.syntax()); } else { - let new_generic_param_list = editor.make().generic_param_list(params); + let new_generic_param_list = make.generic_param_list(params); editor.replace(generic_params.syntax(), new_generic_param_list.syntax()); } } - let new_bounds = editor.make().impl_trait_type(type_bound_list); + let new_bounds = make.impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { editor.replace(path_type.syntax(), new_bounds.syntax()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index fd090cc081fa5..eebe93f005f99 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -73,8 +73,8 @@ pub(crate) fn replace_qualified_name_with_use( // Now that we've brought the name into scope, re-qualify all paths that could be // affected (that is, all paths inside the node we added the `use` to). let scope_node = scope.as_syntax_node(); - let mut editor = builder.make_editor(scope_node); - shorten_paths(&mut editor, scope_node, &original_path); + let editor = builder.make_editor(scope_node); + shorten_paths(&editor, scope_node, &original_path); builder.add_file_edits(ctx.vfs_file_id(), editor); let path = drop_generic_args(&original_path); let edition = ctx @@ -111,7 +111,7 @@ fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option< } fn drop_generic_args(path: &ast::Path) -> ast::Path { - let (mut editor, path) = SyntaxEditor::with_ast_node(path); + let (editor, path) = SyntaxEditor::with_ast_node(path); if let Some(segment) = path.segment() && let Some(generic_args) = segment.generic_arg_list() { @@ -122,7 +122,7 @@ fn drop_generic_args(path: &ast::Path) -> ast::Path { } /// Mutates `node` to shorten `path` in all descendants of `node`. -fn shorten_paths(editor: &mut SyntaxEditor, node: &SyntaxNode, path: &ast::Path) { +fn shorten_paths(editor: &SyntaxEditor, node: &SyntaxNode, path: &ast::Path) { for child in node.children() { match_ast! { match child { @@ -141,7 +141,7 @@ fn shorten_paths(editor: &mut SyntaxEditor, node: &SyntaxNode, path: &ast::Path) } } -fn maybe_replace_path(editor: &mut SyntaxEditor, path: ast::Path, target: ast::Path) -> Option<()> { +fn maybe_replace_path(editor: &SyntaxEditor, path: ast::Path, target: ast::Path) -> Option<()> { if !path_eq_no_generics(path.clone(), target) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs index e973e70345dc2..911fa9d14b591 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs @@ -127,7 +127,7 @@ impl AddRewrite for Assists { target: &SyntaxNode, ) -> Option<()> { self.add(AssistId::refactor_rewrite("sort_items"), label, target.text_range(), |builder| { - let mut editor = builder.make_editor(target); + let editor = builder.make_editor(target); old.into_iter() .zip(new) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 7222d25ad3e55..4d375080f50e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -73,31 +73,29 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) }, token_tree.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(token_tree.syntax()); + let editor = builder.make_editor(token_tree.syntax()); + let make = editor.make(); match token { MacroDelims::LPar | MacroDelims::RPar => { - editor.replace(ltoken, editor.make().token(T!['{'])); - editor.replace(rtoken, editor.make().token(T!['}'])); + editor.replace(ltoken, make.token(T!['{'])); + editor.replace(rtoken, make.token(T!['}'])); if let Some(sc) = semicolon { editor.delete(sc); } } MacroDelims::LBra | MacroDelims::RBra => { - editor.replace(ltoken, editor.make().token(T!['('])); - editor.replace(rtoken, editor.make().token(T![')'])); + editor.replace(ltoken, make.token(T!['('])); + editor.replace(rtoken, make.token(T![')'])); } MacroDelims::LCur | MacroDelims::RCur => { - editor.replace(ltoken, editor.make().token(T!['['])); + editor.replace(ltoken, make.token(T!['['])); if semicolon.is_some() || !needs_semicolon(token_tree) { - editor.replace(rtoken, editor.make().token(T![']'])); + editor.replace(rtoken, make.token(T![']'])); } else { editor.replace_with_many( rtoken, - vec![ - editor.make().token(T![']']).into(), - editor.make().token(T![;]).into(), - ], + vec![make.token(T![']']).into(), make.token(T![;]).into()], ); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index 115e8c36bca3a..520b106bd962e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -41,20 +41,16 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let target = tree.syntax().text_range(); acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| { - let mut editor = builder.make_editor(use_.syntax()); - let new_use = editor.make().use_( + let editor = builder.make_editor(use_.syntax()); + let make = editor.make(); + let new_use = make.use_( use_.attrs(), use_.visibility(), - editor.make().use_tree( - path, - tree.use_tree_list(), - tree.rename(), - tree.star_token().is_some(), - ), + make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), ); // Remove the use tree from the current use item - tree.remove(&mut editor); + tree.remove(&editor); // Insert a newline and indentation, followed by the new use item editor.insert_all( Position::after(use_.syntax()), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 1fc9a6c41db95..65300ccefdb91 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -56,14 +56,15 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O "Unmerge match arm", pipe_token.text_range(), |edit| { - let mut editor = edit.make_editor(&new_parent); + let editor = edit.make_editor(&new_parent); + let make = editor.make(); // It is guaranteed that `pats_after` has at least one element let new_pat = if pats_after.len() == 1 { pats_after[0].clone() } else { - editor.make().or_pat(pats_after, or_pat.leading_pipe().is_some()).into() + make.or_pat(pats_after, or_pat.leading_pipe().is_some()).into() }; - let new_match_arm = editor.make().match_arm(new_pat, match_arm.guard(), match_arm_body); + let new_match_arm = make.match_arm(new_pat, match_arm.guard(), match_arm_body); let mut pipe_index = pipe_token.index(); if pipe_token .prev_sibling_or_token() @@ -91,11 +92,11 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // we don't want to insert a comma at all. let has_comma_after = match_arm.comma_token().is_some(); if !has_comma_after && !match_arm.expr().unwrap().is_block_like() { - insert_after_old_arm.push(editor.make().token(T![,]).into()); + insert_after_old_arm.push(make.token(T![,]).into()); } let indent = IndentLevel::from_node(match_arm.syntax()); - insert_after_old_arm.push(editor.make().whitespace(&format!("\n{indent}")).into()); + insert_after_old_arm.push(make.whitespace(&format!("\n{indent}")).into()); insert_after_old_arm.push(new_match_arm.syntax().clone().into()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index 131e4f091cc1c..045a27295297e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -50,15 +50,16 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) "Unqualify method call", call.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(call.syntax()); + let editor = builder.make_editor(call.syntax()); + let make = editor.make(); - let new_arg_list = editor.make().arg_list(args.args().skip(1)); + let new_arg_list = make.arg_list(args.args().skip(1)); let receiver = if first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix) { - ast::Expr::from(editor.make().expr_paren(first_arg.clone())) + ast::Expr::from(make.expr_paren(first_arg.clone())) } else { first_arg.clone() }; - let method_call = editor.make().expr_method_call(receiver, method_name, new_arg_list); + let method_call = make.expr_method_call(receiver, method_name, new_arg_list); editor.replace(call.syntax(), method_call.syntax()); @@ -66,7 +67,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) && let Some(trait_) = fun.container_or_implemented_trait(ctx.db()) && !scope.can_use_trait_methods(trait_) { - add_import(qualifier, ctx, &mut editor); + add_import(qualifier, ctx, &editor); } builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -77,7 +78,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) fn add_import( qualifier: ast::Path, ctx: &AssistContext<'_>, - editor: &mut syntax::syntax_editor::SyntaxEditor, + editor: &syntax::syntax_editor::SyntaxEditor, ) { if let Some(path_segment) = qualifier.segment() { // for `` diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs index 5593ca3eb88fd..23bbe21437193 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs @@ -72,18 +72,18 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let replacement = replacement.stmt_list()?; acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { - let mut edit = builder.make_editor(block.syntax()); + let editor = builder.make_editor(block.syntax()); let replacement = replacement.dedent(from_indent).indent(into_indent); let container = prefer_container.unwrap_or(container); - edit.replace_with_many(&container, extract_statements(replacement)); - delete_else_before(container, &mut edit); + editor.replace_with_many(&container, extract_statements(replacement)); + delete_else_before(container, &editor); - builder.add_file_edits(ctx.vfs_file_id(), edit); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } -fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) { +fn delete_else_before(container: SyntaxNode, edit: &SyntaxEditor) { let Some(else_token) = container .siblings_with_tokens(syntax::Direction::Prev) .skip(1) @@ -103,7 +103,7 @@ fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) { fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr { let try_wrap_assign = || { let initializer = assign.initializer()?.syntax().syntax_element(); - let (mut edit, replacement) = SyntaxEditor::with_ast_node(&replacement); + let (editor, replacement) = SyntaxEditor::with_ast_node(&replacement); let tail_expr = replacement.tail_expr()?; let before = assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect(); @@ -114,9 +114,9 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp .skip(1) .collect(); - edit.insert_all(Position::before(tail_expr.syntax()), before); - edit.insert_all(Position::after(tail_expr.syntax()), after); - ast::BlockExpr::cast(edit.finish().new_root().clone()) + editor.insert_all(Position::before(tail_expr.syntax()), before); + editor.insert_all(Position::after(tail_expr.syntax()), after); + ast::BlockExpr::cast(editor.finish().new_root().clone()) }; try_wrap_assign().unwrap_or(replacement) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs index a5d7240667cdd..1fe9ea4eb8759 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -66,7 +66,8 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> let happy_type = extract_wrapped_type(type_ref)?; acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(&parent); + let editor = builder.make_editor(&parent); + let make = editor.make(); let mut exprs_to_unwrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); @@ -118,15 +119,14 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> .and_then(Either::::cast) .unwrap(); match tail_parent { - Either::Left(ret_expr) => editor.replace( - ret_expr.syntax(), - editor.make().expr_return(None).syntax(), - ), + Either::Left(ret_expr) => { + editor.replace(ret_expr.syntax(), make.expr_return(None).syntax()) + } Either::Right(stmt_list) => { let new_block = if stmt_list.statements().next().is_none() { - editor.make().expr_empty_block() + make.expr_empty_block() } else { - editor.make().block_expr(stmt_list.statements(), None) + make.block_expr(stmt_list.statements(), None) }; editor.replace( stmt_list.syntax(), @@ -147,7 +147,7 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> continue; } - let new_tail_expr = editor.make().expr_unit(); + let new_tail_expr = make.expr_unit(); editor.replace(path_expr.syntax(), new_tail_expr.syntax()); if let Some(cap) = ctx.config.snippet_cap { editor.add_annotation( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs index 7b5adc1858b4f..935ae18905449 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs @@ -46,7 +46,7 @@ pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext< format!("Unwrap type to type argument {generic_arg}"), path_type.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(path_type.syntax()); + let editor = builder.make_editor(path_type.syntax()); editor.replace(path_type.syntax(), generic_arg.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index 001e216568380..81aa068aedea5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -77,19 +77,16 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op kind.label(), type_ref.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(&parent); - let alias = - wrapper_alias(ctx, editor.make(), core_wrapper, type_ref, &ty, kind.symbol()); + let editor = builder.make_editor(&parent); + let make = editor.make(); + let alias = wrapper_alias(ctx, make, core_wrapper, type_ref, &ty, kind.symbol()); let (ast_new_return_ty, semantic_new_return_ty) = alias.unwrap_or_else(|| { let (ast_ty, ty_constructor) = match kind { - WrapperKind::Option => ( - editor.make().ty_option(type_ref.clone()), - famous_defs.core_option_Option(), - ), + WrapperKind::Option => { + (make.ty_option(type_ref.clone()), famous_defs.core_option_Option()) + } WrapperKind::Result => ( - editor - .make() - .ty_result(type_ref.clone(), editor.make().ty_infer().into()), + make.ty_result(type_ref.clone(), make.ty_infer().into()), famous_defs.core_result_Result(), ), }; @@ -124,9 +121,9 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op continue; } - let happy_wrapped = editor.make().expr_call( - editor.make().expr_path(editor.make().ident_path(kind.happy_ident())), - editor.make().arg_list(iter::once(ret_expr_arg.clone())), + let happy_wrapped = make.expr_call( + make.expr_path(make.ident_path(kind.happy_ident())), + make.arg_list(iter::once(ret_expr_arg.clone())), ); editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 90c621c85da86..f40769e0d7913 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -192,25 +192,25 @@ fn wrap_derive( } } let handle_source_change = |edit: &mut SourceChangeBuilder| { - let mut editor = edit.make_editor(attr.syntax()); - let new_derive = editor.make().attr_outer(editor.make().meta_token_tree( - editor.make().ident_path("derive"), - editor.make().token_tree(T!['('], new_derive), - )); - let meta = editor.make().cfg_attr_meta( - editor.make().cfg_flag("cfg"), - [editor.make().meta_token_tree( - editor.make().ident_path("derive"), - editor.make().token_tree(T!['('], cfg_derive_tokens), + let editor = edit.make_editor(attr.syntax()); + let make = editor.make(); + let new_derive = make.attr_outer( + make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)), + ); + let meta = make.cfg_attr_meta( + make.cfg_flag("cfg"), + [make.meta_token_tree( + make.ident_path("derive"), + make.token_tree(T!['('], cfg_derive_tokens), )], ); - let cfg_attr = editor.make().attr_outer(meta.clone().into()); + let cfg_attr = make.attr_outer(meta.clone().into()); editor.replace_with_many( attr.syntax(), vec![ new_derive.syntax().clone().into(), - editor.make().whitespace("\n").into(), + make.whitespace("\n").into(), cfg_attr.syntax().clone().into(), ], ); @@ -237,15 +237,14 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec { - let (mut fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_); + let (fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_); let fill_expr: ast::Expr = match config.expr_fill_default { ExprFillDefaultMode::Todo | ExprFillDefaultMode::Default => make.expr_todo(), ExprFillDefaultMode::Underscore => make.expr_underscore().into(), }; let new_body = make.block_expr(None::, Some(fill_expr)); - fn_.replace_or_insert_body(&mut fn_editor, new_body); + fn_.replace_or_insert_body(&fn_editor, new_body); let new_fn_ = fn_editor.finish().new_root().clone(); ast::AssocItem::cast(new_fn_) } ast::AssocItem::TypeAlias(type_alias) => { - let (mut type_alias_editor, type_alias) = SyntaxEditor::with_ast_node(&type_alias); + let (type_alias_editor, type_alias) = SyntaxEditor::with_ast_node(&type_alias); if let Some(type_bound_list) = type_alias.type_bound_list() { - type_bound_list.remove(&mut type_alias_editor); + type_bound_list.remove(&type_alias_editor); }; let type_alias = type_alias_editor.finish().new_root().clone(); ast::AssocItem::cast(type_alias) @@ -346,9 +346,10 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option, ) { + let make = editor.make(); let mut attrs = attrs.into_iter().peekable(); if attrs.peek().is_none() { return; @@ -357,9 +358,9 @@ pub(crate) fn insert_attributes( let indent = IndentLevel::from_element(&elem); let whitespace = format!("\n{indent}"); let elements: Vec = attrs - .flat_map(|attr| [attr.syntax().clone().into(), edit.make().whitespace(&whitespace).into()]) + .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()]) .collect(); - edit.insert_all(syntax::syntax_editor::Position::before(elem), elements); + editor.insert_all(syntax::syntax_editor::Position::before(elem), elements); } pub(crate) fn next_prev() -> impl Iterator { 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 d793c2799e26f..fe30a4dc5cc92 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 @@ -175,7 +175,7 @@ pub fn insert_use_with_editor( scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig, - syntax_editor: &mut SyntaxEditor, + syntax_editor: &SyntaxEditor, ) { insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor); } @@ -268,8 +268,9 @@ fn insert_use_with_alias_option_with_editor( path: ast::Path, cfg: &InsertUseConfig, alias: Option, - syntax_editor: &mut SyntaxEditor, + syntax_editor: &SyntaxEditor, ) { + let make = syntax_editor.make(); let _p = tracing::info_span!("insert_use_with_alias_option").entered(); let mut mb = match cfg.granularity { ImportGranularity::Crate => Some(MergeBehavior::Crate), @@ -299,7 +300,7 @@ fn insert_use_with_alias_option_with_editor( }; } - let use_tree = syntax_editor.make().use_tree(path, None, alias, false); + let use_tree = make.use_tree(path, None, alias, false); if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { use_tree.wrap_in_tree_list(); } @@ -602,8 +603,9 @@ fn insert_use_with_editor_( scope: &ImportScope, use_item: ast::Use, group_imports: bool, - syntax_editor: &mut SyntaxEditor, + syntax_editor: &SyntaxEditor, ) { + let make = syntax_editor.make(); let scope_syntax = scope.as_syntax_node(); let insert_use_tree = use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); @@ -653,7 +655,7 @@ fn insert_use_with_editor_( cov_mark::hit!(insert_group_new_group); syntax_editor.insert(Position::before(&node), use_item.syntax()); if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - syntax_editor.insert(Position::after(node), syntax_editor.make().whitespace("\n")); + syntax_editor.insert(Position::after(node), make.whitespace("\n")); } return; } @@ -661,7 +663,7 @@ fn insert_use_with_editor_( if let Some(node) = last { cov_mark::hit!(insert_group_no_group); syntax_editor.insert(Position::after(&node), use_item.syntax()); - syntax_editor.insert(Position::after(node), syntax_editor.make().whitespace("\n")); + syntax_editor.insert(Position::after(node), make.whitespace("\n")); return; } } else { @@ -700,21 +702,18 @@ fn insert_use_with_editor_( { cov_mark::hit!(insert_empty_inner_attr); syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); - syntax_editor - .insert(Position::after(last_inner_element), syntax_editor.make().whitespace("\n")); + syntax_editor.insert(Position::after(last_inner_element), make.whitespace("\n")); } else { match l_curly { Some(b) => { cov_mark::hit!(insert_empty_module); - syntax_editor.insert(Position::after(&b), syntax_editor.make().whitespace("\n")); + syntax_editor.insert(Position::after(&b), make.whitespace("\n")); syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax()); } None => { cov_mark::hit!(insert_empty_file); - syntax_editor.insert( - Position::first_child_of(scope_syntax), - syntax_editor.make().whitespace("\n\n"), - ); + syntax_editor + .insert(Position::first_child_of(scope_syntax), make.whitespace("\n\n")); syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); } } 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 407276a2defc9..2d4a6b8b5b1ba 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 @@ -278,7 +278,7 @@ impl Ctx<'_> { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let (mut editor, item) = SyntaxEditor::new(self.transform_path(item)); + let (editor, item) = SyntaxEditor::new(self.transform_path(item)); preorder_rev(&item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { editor.replace(lifetime.syntax(), subst.clone().syntax()); @@ -329,22 +329,22 @@ impl Ctx<'_> { result } - let (mut editor, root_path) = SyntaxEditor::new(path.clone()); + let (editor, root_path) = SyntaxEditor::new(path.clone()); let result = find_child_paths_and_ident_pats(&root_path); for sub_path in result { let new = self.transform_path(sub_path.syntax()); editor.replace(sub_path.syntax(), new); } - let (mut editor, update_sub_item) = SyntaxEditor::new(editor.finish().new_root().clone()); + let (editor, update_sub_item) = SyntaxEditor::new(editor.finish().new_root().clone()); let item = find_child_paths_and_ident_pats(&update_sub_item); for sub_path in item { - self.transform_path_or_ident_pat(&mut editor, &sub_path); + self.transform_path_or_ident_pat(&editor, &sub_path); } editor.finish().new_root().clone() } fn transform_path_or_ident_pat( &self, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, item: &Either, ) -> Option<()> { match item { @@ -353,7 +353,7 @@ impl Ctx<'_> { } } - fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> { + fn transform_path_(&self, editor: &SyntaxEditor, path: &ast::Path) -> Option<()> { if path.qualifier().is_some() { return None; } @@ -448,7 +448,7 @@ impl Ctx<'_> { }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; let res = mod_path_to_ast(&found_path, self.target_edition); - let (mut res_editor, res) = SyntaxEditor::with_ast_node(&res); + let (res_editor, res) = SyntaxEditor::with_ast_node(&res); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) && let Some(segment) = res.segment() { @@ -522,11 +522,7 @@ impl Ctx<'_> { Some(()) } - fn transform_ident_pat( - &self, - editor: &mut SyntaxEditor, - ident_pat: &ast::IdentPat, - ) -> Option<()> { + fn transform_ident_pat(&self, editor: &SyntaxEditor, ident_pat: &ast::IdentPat) -> Option<()> { let name = ident_pat.name()?; let temp_path = make::path_from_text(&name.text()); 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 d99d3043daf55..f0dd9dd75ebc3 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 @@ -234,7 +234,7 @@ fn remove_unnecessary_wrapper( let file_id = expr_ptr.file_id.original_file(db); let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); - let mut editor; + let editor; match inner_arg { // We're returning `()` Expr::TupleExpr(tup) if tup.fields().next().is_none() => { @@ -244,16 +244,17 @@ fn remove_unnecessary_wrapper( .and_then(Either::::cast)?; editor = builder.make_editor(parent.syntax()); + let make = editor.make(); match parent { Either::Left(ret_expr) => { - editor.replace(ret_expr.syntax(), editor.make().expr_return(None).syntax()); + editor.replace(ret_expr.syntax(), make.expr_return(None).syntax()); } Either::Right(stmt_list) => { let new_block = if stmt_list.statements().next().is_none() { - editor.make().expr_empty_block() + make.expr_empty_block() } else { - editor.make().block_expr(stmt_list.statements(), None) + make.block_expr(stmt_list.statements(), None) }; editor.replace(stmt_list.syntax().parent()?, new_block.syntax()); diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 62fd7db8d3312..300d0f02ea778 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -488,9 +488,9 @@ enum Foo { } fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) { - let (mut editor, enum_) = SyntaxEditor::with_ast_node(&ast_from_text::(before)); + let (editor, enum_) = SyntaxEditor::with_ast_node(&ast_from_text::(before)); if let Some(it) = enum_.variant_list() { - it.add_variant(&mut editor, &variant) + it.add_variant(&editor, &variant) } let edit = editor.finish(); let after = edit.new_root.to_string(); From e933b8d1237663d9bd3408412c08161d889971db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:26:41 +0000 Subject: [PATCH 45/98] Bump thin-vec from 0.2.14 to 0.2.16 Bumps [thin-vec](https://github.com/mozilla/thin-vec) from 0.2.14 to 0.2.16. - [Changelog](https://github.com/mozilla/thin-vec/blob/main/RELEASES.md) - [Commits](https://github.com/mozilla/thin-vec/compare/v0.2.14...v0.2.16) --- updated-dependencies: - dependency-name: thin-vec dependency-version: 0.2.16 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 94e9507c021f4..307af79bb355e 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2824,9 +2824,9 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thin-vec" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" +checksum = "259cdf8ed4e4aca6f1e9d011e10bd53f524a2d0637d7b28450f6c64ac298c4c6" [[package]] name = "thiserror" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0ae65922d0072..713f84d4e0e8d 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -170,7 +170,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features = triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" xshell = "0.2.7" -thin-vec = "0.2.14" +thin-vec = "0.2.16" petgraph = { version = "0.8.2", default-features = false } # We need to freeze the version of the crate, as the raw-api feature is considered unstable diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 238d1b08ae4fe..18426f3095b11 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -55,7 +55,7 @@ hir-expand.workspace = true base-db.workspace = true syntax.workspace = true span.workspace = true -thin-vec = "0.2.14" +thin-vec = "0.2.16" [dev-dependencies] expect-test = "1.5.1" From 3083d7996f76e50c3f246f09835758cff74fcf68 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 14 Apr 2026 19:16:58 +0530 Subject: [PATCH 46/98] replace make with syntaxFactory --- .../ide-assists/src/handlers/raw_string.rs | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 24e41189dac40..425ad12d7ea36 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -1,7 +1,7 @@ use ide_db::source_change::SourceChangeBuilder; use syntax::{ - AstToken, - ast::{self, IsString, make::tokens::literal}, + AstNode, AstToken, + ast::{self, IsString, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -162,23 +162,19 @@ fn replace_literal( builder: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, ) { - let token = token.syntax(); - let node = token.parent().expect("no parent token"); - let edit = builder.make_editor(&node); - let new_literal = literal(new); - - edit.replace(token, mut_token(new_literal)); - - builder.add_file_edits(ctx.vfs_file_id(), edit); -} - -fn mut_token(token: syntax::SyntaxToken) -> syntax::SyntaxToken { - let node = token.parent().expect("no parent token"); - node.clone_for_update() - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.text_range() == token.text_range() && it.text() == token.text()) - .unwrap() + let old_token = token.syntax(); + let parent = old_token.parent().expect("no parent token"); + let make = SyntaxFactory::with_mappings(); + let editor = builder.make_editor(&parent); + let new_literal = make.expr_literal(new); + let new_token = new_literal + .syntax() + .first_child_or_token() + .and_then(|it| it.into_token()) + .expect("literal has no token child"); + editor.replace(old_token, new_token); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); } #[cfg(test)] From 84d65db7a694d0d66ec3d0267a31e5884c314a91 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 16 Apr 2026 09:21:33 +0530 Subject: [PATCH 47/98] adapt raw strings to new SyntaxEditor semantics --- .../crates/ide-assists/src/handlers/raw_string.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 425ad12d7ea36..8234a0374e777 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -1,7 +1,7 @@ use ide_db::source_change::SourceChangeBuilder; use syntax::{ AstNode, AstToken, - ast::{self, IsString, syntax_factory::SyntaxFactory}, + ast::{self, IsString}, }; use crate::{ @@ -164,8 +164,8 @@ fn replace_literal( ) { let old_token = token.syntax(); let parent = old_token.parent().expect("no parent token"); - let make = SyntaxFactory::with_mappings(); let editor = builder.make_editor(&parent); + let make = editor.make(); let new_literal = make.expr_literal(new); let new_token = new_literal .syntax() @@ -173,7 +173,6 @@ fn replace_literal( .and_then(|it| it.into_token()) .expect("literal has no token child"); editor.replace(old_token, new_token); - editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); } From 574a1982e52102c46656ade902d422c496382a63 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 16 Apr 2026 09:26:23 +0530 Subject: [PATCH 48/98] remove clone_for_update in move_const_to_impl --- .../crates/ide-assists/src/handlers/move_const_to_impl.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs index b3e79e4663e9d..86bdf3f8b4eb7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -138,7 +138,6 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> let fixup = if last_const.is_none() { "\n" } else { "" }; let indent = IndentLevel::from_node(parent_fn.syntax()); - let const_ = const_.clone_for_update(); let const_ = const_.reset_indent(); let const_ = const_.indent(indent); builder.insert(insert_offset, format!("\n{indent}{const_}{fixup}")); From 202df42f86c317cd750e8ba18e146a0e6e5b8fd2 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 16 Apr 2026 05:08:47 +0000 Subject: [PATCH 49/98] Prepare for merging from rust-lang/rust This updates the rust-version file to e8e4541ff19649d95afab52fdde2c2eaa6829965. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 38f153f78d026..8d1ee326303d3 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -4c4205163abcbd08948b3efab796c543ba1ea687 +e8e4541ff19649d95afab52fdde2c2eaa6829965 From b4fc58d316d1a5fde10d693b82595b1dc70af887 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 16 Apr 2026 13:49:22 +0200 Subject: [PATCH 50/98] Remove `LineIndexDatabase` --- src/tools/rust-analyzer/Cargo.lock | 1 - .../src/handlers/bind_unused_param.rs | 4 +-- .../rust-analyzer/crates/ide-db/Cargo.toml | 1 - .../rust-analyzer/crates/ide-db/src/lib.rs | 25 +++++++++++-------- .../crates/ide-db/src/symbol_index.rs | 12 +++------ .../src/handlers/unlinked_file.rs | 8 +++--- .../crates/ide-diagnostics/src/tests.rs | 5 ++-- .../rust-analyzer/crates/ide/src/interpret.rs | 6 ++--- src/tools/rust-analyzer/crates/ide/src/lib.rs | 5 ++-- .../crates/ide/src/view_syntax_tree.rs | 4 +-- .../rust-analyzer/src/cli/analysis_stats.rs | 11 ++++---- .../rust-analyzer/src/cli/diagnostics.rs | 4 +-- .../crates/rust-analyzer/src/cli/lsif.rs | 10 ++++---- .../crates/rust-analyzer/src/cli/run_tests.rs | 4 +-- .../crates/rust-analyzer/src/cli/scip.rs | 4 +-- .../src/cli/unresolved_references.rs | 4 +-- 16 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 94e9507c021f4..88f5c33cb2724 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1104,7 +1104,6 @@ dependencies = [ "nohash-hasher", "parser", "profile", - "query-group-macro", "rayon", "rustc-hash 2.1.1", "salsa", diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs index 0e85a77822bed..50e4a367e9a1b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -1,5 +1,5 @@ use crate::assist_context::{AssistContext, Assists}; -use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition}; +use ide_db::{assists::AssistId, defs::Definition, line_index}; use syntax::{ AstNode, ast::{self, HasName, edit::AstNodeEdit}, @@ -43,7 +43,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O format!("Bind as `let _ = {name};`"), param.syntax().text_range(), |builder| { - let line_index = ctx.db().line_index(ctx.vfs_file_id()); + let line_index = line_index(ctx.db(), ctx.vfs_file_id()); let indent = func.indent_level(); let text_indent = indent + 1; diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index fca06b69d1bb8..2c0919a183709 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -25,7 +25,6 @@ arrayvec.workspace = true memchr = "2.7.5" salsa.workspace = true salsa-macros.workspace = true -query-group.workspace = true triomphe.workspace = true nohash-hasher.workspace = true bitflags.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 8d16826e191da..99af1c32d896b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -61,7 +61,7 @@ use std::{fmt, mem::ManuallyDrop}; use base_db::{ CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, SourceDatabase, - SourceRoot, SourceRootId, SourceRootInput, query_group, set_all_crates_with_durability, + SourceRoot, SourceRootId, SourceRootInput, set_all_crates_with_durability, }; use hir::{ FilePositionWrapper, FileRangeWrapper, @@ -252,15 +252,20 @@ impl RootDatabase { } } -#[query_group::query_group] -pub trait LineIndexDatabase: base_db::SourceDatabase { - #[salsa::invoke_interned(line_index)] - fn line_index(&self, file_id: FileId) -> Arc; -} - -fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc { - let text = db.file_text(file_id).text(db); - Arc::new(LineIndex::new(text)) +pub fn line_index(db: &dyn SourceDatabase, file_id: FileId) -> &Arc { + #[salsa::interned] + pub struct InternedFileId { + id: FileId, + } + #[salsa::tracked(returns(ref))] + fn line_index<'db>( + db: &'db dyn SourceDatabase, + file_id: InternedFileId<'db>, + ) -> Arc { + let text = db.file_text(file_id.id(db)).text(db); + Arc::new(LineIndex::new(text)) + } + line_index(db, InternedFileId::new(db, file_id)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 2ad3a51c3d9a3..55acf5abf868d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -400,22 +400,16 @@ impl<'db> SymbolIndex<'db> { /// The symbol index for a given module. These modules should only be in source roots that /// are inside local_roots. pub fn module_symbols(db: &dyn HirDatabase, module: Module) -> &SymbolIndex<'_> { - // FIXME: - #[salsa::interned] - struct InternedModuleId { - id: hir::ModuleId, - } - #[salsa::tracked(returns(ref))] fn module_symbols<'db>( db: &'db dyn HirDatabase, - module: InternedModuleId<'db>, + module: hir::ModuleId, ) -> SymbolIndex<'db> { let _p = tracing::info_span!("module_symbols").entered(); // We call this without attaching because this runs in parallel, so we need to attach here. hir::attach_db(db, || { - let module: Module = module.id(db).into(); + let module: Module = module.into(); SymbolIndex::new(SymbolCollector::new_module( db, module, @@ -424,7 +418,7 @@ impl<'db> SymbolIndex<'db> { }) } - module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module))) + module_symbols(db, hir::ModuleId::from(module)) } /// The symbol index for all extern prelude crates. 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 d7a0a3b0f59d4..570319c347d49 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 @@ -4,11 +4,9 @@ use std::iter; use hir::crate_def_map; use hir::{InFile, ModuleSource}; -use ide_db::base_db; use ide_db::text_edit::TextEdit; -use ide_db::{ - FileId, FileRange, LineIndexDatabase, base_db::SourceDatabase, source_change::SourceChange, -}; +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, @@ -26,7 +24,7 @@ pub(crate) fn unlinked_file( acc: &mut Vec, file_id: FileId, ) { - let mut range = TextRange::up_to(ctx.sema.db.line_index(file_id).len()); + let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len()); let fixes = fixes(ctx, 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 diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index 3dc155efe96b9..fc49542e3ccdd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -4,9 +4,10 @@ mod overly_long_real_world_cases; use hir::setup_tracing; use ide_db::{ - LineIndexDatabase, RootDatabase, + RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, base_db::SourceDatabase, + line_index, }; use itertools::Itertools; use stdx::trim_indent; @@ -242,7 +243,7 @@ pub(crate) fn check_diagnostics_with_config( .into_group_map(); for file_id in files { let file_id = file_id.file_id(&db); - let line_index = db.line_index(file_id); + let line_index = line_index(&db, file_id); let mut actual = annotations.remove(&file_id).unwrap_or_default(); let mut expected = extract_annotations(db.file_text(file_id).text(&db)); diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index 3741822547e45..994d325cccde6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -1,5 +1,5 @@ use hir::{ConstEvalError, DefWithBody, DisplayTarget, Semantics}; -use ide_db::{FilePosition, LineIndexDatabase, RootDatabase, base_db::SourceDatabase}; +use ide_db::{FilePosition, RootDatabase, base_db::SourceDatabase, line_index}; use std::time::{Duration, Instant}; use stdx::format_to; use syntax::{AstNode, TextRange, algo::ancestors_at_offset, ast}; @@ -40,7 +40,7 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura let path = source_root.path_for_file(&file_id).map(|x| x.to_string()); let path = path.as_deref().unwrap_or(""); - match db.line_index(file_id).try_line_col(text_range.start()) { + match line_index(db, file_id).try_line_col(text_range.start()) { Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col), None => format!("file://{path} range {text_range:?}"), } @@ -68,7 +68,7 @@ pub(crate) fn render_const_eval_error( let source_root = db.source_root(source_root).source_root(db); let path = source_root.path_for_file(&file_id).map(|x| x.to_string()); let path = path.as_deref().unwrap_or(""); - match db.line_index(file_id).try_line_col(text_range.start()) { + match line_index(db, file_id).try_line_col(text_range.start()) { Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col), None => format!("file://{path} range {text_range:?}"), } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 270998cdf751c..8a6ef6418063b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -64,9 +64,10 @@ use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; use ide_db::base_db::relevant_crates; +use ide_db::line_index; use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ - FxHashMap, FxIndexSet, LineIndexDatabase, + FxHashMap, FxIndexSet, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, salsa::{Cancelled, Database}, @@ -358,7 +359,7 @@ impl Analysis { /// Gets the file's `LineIndex`: data structure to convert between absolute /// offsets and line/column representation. pub fn file_line_index(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| db.line_index(file_id)) + self.with_db(|db| line_index(db, file_id).clone()) } /// Selects the next syntactic nodes encompassing the range. diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index ecd93e8b28190..7732b180829ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -1,6 +1,6 @@ use hir::Semantics; use ide_db::{ - FileId, LineIndexDatabase, RootDatabase, + FileId, RootDatabase, line_index, line_index::{LineCol, LineIndex}, }; use span::{TextRange, TextSize}; @@ -20,7 +20,7 @@ use triomphe::Arc; // | VS Code | **Rust Syntax Tree** | pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); - let line_index = db.line_index(file_id); + let line_index = line_index(db, file_id).clone(); let parse = sema.parse_guess_edition(file_id); let ctx = SyntaxTreeCtx { line_index, in_string: None }; 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 e56727d39d671..bf9a66bf3fc29 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 @@ -26,8 +26,9 @@ use ide::{ InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, }; use ide_db::{ - EditionedFileId, LineIndexDatabase, SnippetCap, + EditionedFileId, SnippetCap, base_db::{SourceDatabase, salsa::Database}, + line_index, }; use itertools::Itertools; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; @@ -1487,7 +1488,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); - let line_index = db.line_index(original_range.file_id.file_id(db)); + let line_index = line_index(db, original_range.file_id.file_id(db)); let text_range = original_range.range; let (start, end) = (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); @@ -1503,7 +1504,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); - let line_index = db.line_index(original_range.file_id.file_id(db)); + let line_index = line_index(db, original_range.file_id.file_id(db)); let text_range = original_range.range; let (start, end) = (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); @@ -1522,7 +1523,7 @@ fn expr_syntax_range<'a>( let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); - let line_index = db.line_index(original_range.file_id.file_id(db)); + let line_index = line_index(db, original_range.file_id.file_id(db)); let text_range = original_range.range; let (start, end) = (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); @@ -1543,7 +1544,7 @@ fn pat_syntax_range<'a>( let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); - let line_index = db.line_index(original_range.file_id.file_id(db)); + let line_index = line_index(db, original_range.file_id.file_id(db)); let text_range = original_range.range; let (start, end) = (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); 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 efbaad3c4936e..e50e1c26bb971 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 @@ -6,7 +6,7 @@ use rustc_hash::FxHashSet; use hir::{Crate, Module, db::HirDatabase, sym}; use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity}; -use ide_db::{LineIndexDatabase, base_db::SourceDatabase}; +use ide_db::{base_db::SourceDatabase, line_index}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; use crate::cli::{flags, progress_report::ProgressReport}; @@ -99,7 +99,7 @@ impl flags::Diagnostics { } let Diagnostic { code, message, range, severity, .. } = diagnostic; - let line_index = db.line_index(range.file_id); + let line_index = line_index(db, range.file_id); let start = line_index.line_col(range.range.start()); let end = line_index.line_col(range.range.end()); bar.println(format!( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 3950a581fd776..4f6de6850abbd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -8,7 +8,7 @@ use ide::{ RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig, }; -use ide_db::{LineIndexDatabase, line_index::WideEncoding}; +use ide_db::{line_index, line_index::WideEncoding}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; use lsp_types::lsif; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; @@ -120,9 +120,9 @@ impl LsifManager<'_, '_> { } let file_id = id.file_id; let doc_id = self.get_file_id(file_id); - let line_index = self.db.line_index(file_id); + let line_index = line_index(self.db, file_id); let line_index = LineIndex { - index: line_index, + index: line_index.clone(), encoding: PositionEncoding::Wide(WideEncoding::Utf16), endings: LineEndings::Unix, }; @@ -241,9 +241,9 @@ impl LsifManager<'_, '_> { let StaticIndexedFile { file_id, tokens, folds, .. } = file; let doc_id = self.get_file_id(file_id); let text = self.analysis.file_text(file_id).unwrap(); - let line_index = self.db.line_index(file_id); + let line_index = line_index(self.db, file_id); let line_index = LineIndex { - index: line_index, + index: line_index.clone(), encoding: PositionEncoding::Wide(WideEncoding::Utf16), endings: LineEndings::Unix, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index e8c88cadf6f0a..0f7ef84a0eb81 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -2,7 +2,7 @@ use hir::{Crate, Module}; use hir_ty::db::HirDatabase; -use ide_db::{LineIndexDatabase, base_db::SourceDatabase}; +use ide_db::{base_db::SourceDatabase, line_index}; use profile::StopWatch; use project_model::{CargoConfig, RustLibSource}; use syntax::TextRange; @@ -38,7 +38,7 @@ impl flags::RunTests { }) .filter(|x| x.is_test(db)); let span_formatter = |file_id, text_range: TextRange| { - let line_col = match db.line_index(file_id).try_line_col(text_range.start()) { + let line_col = match line_index(db, file_id).try_line_col(text_range.start()) { None => " (unknown line col)".to_owned(), Some(x) => format!("#{}:{}", x.line + 1, x.col), }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index ef6d4399e663c..bca38ed82fdd5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -7,7 +7,7 @@ use ide::{ RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId, TokenStaticData, VendoredLibrariesConfig, }; -use ide_db::LineIndexDatabase; +use ide_db::line_index; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; use rustc_hash::{FxHashMap, FxHashSet}; use scip::types::{self as scip_types, SymbolInformation}; @@ -348,7 +348,7 @@ fn get_relative_filepath( fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex { LineIndex { - index: db.line_index(file_id), + index: line_index(db, file_id).clone(), encoding: PositionEncoding::Utf8, endings: LineEndings::Unix, } 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 2d9b870f4de82..f8eacbb670587 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 @@ -1,7 +1,7 @@ //! Reports references in code that the IDE layer cannot resolve. use hir::{AnyDiagnostic, Crate, Module, Semantics, db::HirDatabase, sym}; use ide::{AnalysisHost, RootDatabase, TextRange}; -use ide_db::{FxHashSet, LineIndexDatabase as _, base_db::SourceDatabase, defs::NameRefClass}; +use ide_db::{FxHashSet, base_db::SourceDatabase, defs::NameRefClass, line_index}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; use parser::SyntaxKind; use syntax::{AstNode, WalkEvent, ast}; @@ -75,7 +75,7 @@ impl flags::UnresolvedReferences { let file_path = vfs.file_path(file_id); eprintln!("processing crate: {crate_name}, module: {file_path}",); - let line_index = db.line_index(file_id); + let line_index = line_index(db, file_id); let file_text = db.file_text(file_id); for range in find_unresolved_references(db, &sema, file_id, &module) { From fa147019d03761d856aa409368ec3764839e58f9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 17 Apr 2026 03:41:57 +0300 Subject: [PATCH 51/98] Fix 1.95.0 Clippy Quite a lot of false positives this time. --- .../crates/hir-def/src/expr_store/lower.rs | 22 ++++---- .../crates/hir-def/src/item_scope.rs | 46 ++++++++------- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 +- .../crates/hir-ty/src/opaques.rs | 16 +++--- .../crates/hir/src/diagnostics.rs | 56 +++++++++---------- .../src/handlers/extract_function.rs | 44 +++++++-------- .../extract_struct_from_enum_variant.rs | 5 ++ .../src/handlers/generate_function.rs | 8 +-- .../src/utils/gen_trait_fn_body.rs | 4 +- .../ide-completion/src/completions/expr.rs | 7 ++- .../ide-completion/src/completions/type.rs | 42 +++++++------- .../ide-completion/src/context/analysis.rs | 14 ++--- .../rust-analyzer/crates/ide-db/src/search.rs | 23 ++++---- .../src/syntax_helpers/format_string_exprs.rs | 2 +- .../ide-db/src/syntax_helpers/node_ext.rs | 7 ++- .../crates/ide/src/folding_ranges.rs | 2 +- .../crates/ide/src/inlay_hints/lifetime.rs | 26 ++++----- .../rust-analyzer/crates/ide/src/runnables.rs | 8 +-- .../ide/src/syntax_highlighting/highlight.rs | 12 ++-- .../crates/load-cargo/src/lib.rs | 10 ++-- .../crates/parser/src/grammar.rs | 19 ++++--- .../src/legacy_protocol/msg/flat.rs | 4 ++ .../crates/project-model/src/env.rs | 2 +- .../crates/rust-analyzer/src/main_loop.rs | 11 ++-- .../crates/syntax/src/ast/edit_in_place.rs | 9 +-- .../crates/syntax/src/syntax_editor.rs | 4 +- .../crates/syntax/src/syntax_editor/edits.rs | 9 +-- src/tools/rust-analyzer/xtask/src/tidy.rs | 4 ++ 28 files changed, 215 insertions(+), 205 deletions(-) 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 8a23ea69b8f75..2a1ed0afa9b15 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 @@ -2737,17 +2737,17 @@ impl<'db> ExprCollector<'db> { for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { match &rib.kind { - RibKind::Normal(label_name, id, label_hygiene) => { - if *label_name == name && *label_hygiene == hygiene_id { - return if self.is_label_valid_from_rib(rib_idx) { - Ok(Some(*id)) - } else { - Err(ExpressionStoreDiagnostics::UnreachableLabel { - name, - node: self.expander.in_file(AstPtr::new(&lifetime)), - }) - }; - } + RibKind::Normal(label_name, id, label_hygiene) + if *label_name == name && *label_hygiene == hygiene_id => + { + return if self.is_label_valid_from_rib(rib_idx) { + Ok(Some(*id)) + } else { + Err(ExpressionStoreDiagnostics::UnreachableLabel { + name, + node: self.expander.in_file(AstPtr::new(&lifetime)), + }) + }; } RibKind::MacroDef(macro_id) => { if let Some((parent_ctx, label_macro_id)) = hygiene_info 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 b11a8bcd9097d..fe7d1806857b1 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 @@ -680,20 +680,19 @@ impl ItemScope { changed = true; } Entry::Occupied(mut entry) - if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) + && glob_imports.values.remove(&lookup) => { - if glob_imports.values.remove(&lookup) { - 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)); - } - entry.insert(fld); - changed = true; + 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)); } + entry.insert(fld); + changed = true; } _ => {} } @@ -720,20 +719,19 @@ impl ItemScope { changed = true; } Entry::Occupied(mut entry) - if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) + && glob_imports.macros.remove(&lookup) => { - if glob_imports.macros.remove(&lookup) { - cov_mark::hit!(import_shadowed); - let prev = std::mem::replace(&mut fld.import, import); - if let Some(import) = import { - self.use_imports_macros.insert( - import, - prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), - ); - } - entry.insert(fld); - changed = true; + cov_mark::hit!(import_shadowed); + let prev = std::mem::replace(&mut fld.import, import); + if let Some(import) = import { + self.use_imports_macros.insert( + import, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), + ); } + entry.insert(fld); + changed = true; } _ => {} } 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 70083dbe3e794..d16bef1c947aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1440,9 +1440,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }); if placeholder_ids.len() == type_variables.len() { - for (placeholder_id, type_variable) in - placeholder_ids.into_iter().zip(type_variables.into_iter()) - { + for (placeholder_id, type_variable) in placeholder_ids.into_iter().zip(type_variables) { self.write_type_placeholder_ty(placeholder_id, type_variable); } } 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 ce93a334221c1..2e85beea9163d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -55,10 +55,10 @@ pub(crate) fn opaque_types_defined_by( .for_each(extend_with_taits); }; let extend_with_atpit_from_container = |container| match container { - ItemContainerId::ImplId(impl_id) => { - if ImplSignature::of(db, impl_id).target_trait.is_some() { - extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items); - } + ItemContainerId::ImplId(impl_id) + if ImplSignature::of(db, impl_id).target_trait.is_some() => + { + extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items); } ItemContainerId::TraitId(trait_id) => { extend_with_atpit_from_assoc_items(&trait_id.trait_items(db).items); @@ -196,10 +196,10 @@ fn tait_defining_bodies( .collect() }; match loc.container { - ItemContainerId::ImplId(impl_id) => { - if ImplSignature::of(db, impl_id).target_trait.is_some() { - return from_assoc_items(&impl_id.impl_items(db).items); - } + ItemContainerId::ImplId(impl_id) + if ImplSignature::of(db, impl_id).target_trait.is_some() => + { + return from_assoc_items(&impl_id.impl_items(db).items); } ItemContainerId::TraitId(trait_id) => { return from_assoc_items(&trait_id.trait_items(db).items); diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 555270bad8309..6cfb79d5a1f4b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -491,35 +491,35 @@ impl<'db> AnyDiagnostic<'db> { let file = record.file_id; let root = record.file_syntax(db); match record.value.to_node(&root) { - Either::Left(ast::Expr::RecordExpr(record_expr)) => { - if record_expr.record_expr_field_list().is_some() { - let field_list_parent_path = - record_expr.path().map(|path| AstPtr::new(&path)); - return Some( - MissingFields { - file, - field_list_parent: AstPtr::new(&Either::Left(record_expr)), - field_list_parent_path, - missed_fields, - } - .into(), - ); - } + Either::Left(ast::Expr::RecordExpr(record_expr)) + if record_expr.record_expr_field_list().is_some() => + { + let field_list_parent_path = + record_expr.path().map(|path| AstPtr::new(&path)); + return Some( + MissingFields { + file, + field_list_parent: AstPtr::new(&Either::Left(record_expr)), + field_list_parent_path, + missed_fields, + } + .into(), + ); } - Either::Right(ast::Pat::RecordPat(record_pat)) => { - if record_pat.record_pat_field_list().is_some() { - let field_list_parent_path = - record_pat.path().map(|path| AstPtr::new(&path)); - return Some( - MissingFields { - file, - field_list_parent: AstPtr::new(&Either::Right(record_pat)), - field_list_parent_path, - missed_fields, - } - .into(), - ); - } + Either::Right(ast::Pat::RecordPat(record_pat)) + if record_pat.record_pat_field_list().is_some() => + { + let field_list_parent_path = + record_pat.path().map(|path| AstPtr::new(&path)); + return Some( + MissingFields { + file, + field_list_parent: AstPtr::new(&Either::Right(record_pat)), + field_list_parent_path, + missed_fields, + } + .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 4865aa71735fc..4219e6845fad5 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 @@ -1362,26 +1362,26 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option break, - SyntaxKind::IMPL => { - if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) { - let impl_node = find_non_trait_impl(&next_ancestor); - if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) { - return target_node; - } + SyntaxKind::IMPL + if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) => + { + let impl_node = find_non_trait_impl(&next_ancestor); + if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) { + return target_node; } } SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue, - SyntaxKind::ITEM_LIST => { - if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) { - break; - } + SyntaxKind::ITEM_LIST + if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) => + { + break; } SyntaxKind::ASSOC_ITEM_LIST if !matches!(anchor, Anchor::Method) => continue, SyntaxKind::ASSOC_ITEM_LIST if body.extracted_from_trait_impl() => continue, - SyntaxKind::ASSOC_ITEM_LIST => { - if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) { - break; - } + SyntaxKind::ASSOC_ITEM_LIST + if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) => + { + break; } _ => (), } @@ -2128,19 +2128,19 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) for event in syntax.preorder() { match event { WalkEvent::Enter(e) => match e.kind() { - SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { - if nested_loop.is_none() { - nested_loop = Some(e.clone()); - } + SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR + if nested_loop.is_none() => + { + nested_loop = Some(e.clone()); } SyntaxKind::FN | SyntaxKind::CONST | SyntaxKind::STATIC | SyntaxKind::IMPL - | SyntaxKind::MODULE => { - if nested_scope.is_none() { - nested_scope = Some(e.clone()); - } + | SyntaxKind::MODULE + if nested_scope.is_none() => + { + nested_scope = Some(e.clone()); } _ => {} }, 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 3272fa739fe69..21013e2e614c7 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 @@ -215,6 +215,7 @@ fn extract_generic_params( ) -> Option { let mut generics = known_generics.generic_params().map(|param| (param, false)).collect_vec(); + #[expect(clippy::unnecessary_fold, reason = "this function has side effects")] let tagged_one = match field_list { Either::Left(field_list) => field_list .fields() @@ -248,6 +249,10 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } } param if matches!(token.kind(), T![ident]) => { + #[expect( + clippy::collapsible_match, + reason = "it won't compile since in the guard, `param` is immutable" + )] if match param { ast::GenericParam::ConstParam(konst) => konst .name() 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 fbf6241e43a38..6ef492619b50c 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 @@ -1166,10 +1166,10 @@ fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option { break; } - SyntaxKind::ITEM_LIST => { - if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) { - break; - } + SyntaxKind::ITEM_LIST + if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) => + { + break; } _ => {} } 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 b0d88737fe0f7..c0ddcb950cbac 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 @@ -95,7 +95,7 @@ fn gen_clone_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option Option MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(), - let pat = make.tuple_struct_pat(variant_name.clone(), pats.into_iter()); + let pat = make.tuple_struct_pat(variant_name.clone(), pats); arms.push(make.match_arm(pat.into(), None, expr)); } None => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 99ca55bdaf74a..c15c67173ea3d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -317,7 +317,12 @@ pub(crate) fn complete_expr_path( } // synthetic names currently leak out as we lack synthetic hygiene, so filter them // out here - ScopeDef::Local(_) => { + ScopeDef::Local(_) => + { + #[expect( + clippy::collapsible_match, + reason = "this changes meaning, causing the next arm to be selected" + )] if !name.as_str().starts_with('<') { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } 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 e2125a9678234..20bbf0dd8bacc 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 @@ -162,29 +162,27 @@ pub(crate) fn complete_type_path( } TypeLocation::GenericArg { args: Some(arg_list), of_trait: Some(trait_), .. - } => { - if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { - let arg_idx = arg_list - .generic_args() - .filter(|arg| { - arg.syntax().text_range().end() - < ctx.original_token.text_range().start() - }) - .count(); - - let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true); - if arg_idx >= n_required_params { - trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { - if let hir::AssocItem::TypeAlias(alias) = it { - cov_mark::hit!(complete_assoc_type_in_generics_list); - acc.add_type_alias_with_eq(ctx, alias); - } - }); - - let n_params = trait_.type_or_const_param_count(ctx.sema.db, false); - if arg_idx >= n_params { - return; // only show assoc types + } if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() => { + let arg_idx = arg_list + .generic_args() + .filter(|arg| { + arg.syntax().text_range().end() + < ctx.original_token.text_range().start() + }) + .count(); + + let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true); + if arg_idx >= n_required_params { + trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + cov_mark::hit!(complete_assoc_type_in_generics_list); + acc.add_type_alias_with_eq(ctx, alias); } + }); + + let n_params = trait_.type_or_const_param_count(ctx.sema.db, false); + if arg_idx >= n_params { + return; // only show assoc types } } } 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 60f5dabefc62e..58c0f683a344c 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 @@ -1183,18 +1183,16 @@ fn classify_name_ref<'db>( let arg_name = arg_name.text(); for item in trait_.items_with_supertraits(sema.db) { match item { - hir::AssocItem::TypeAlias(assoc_ty) => { - if assoc_ty.name(sema.db).as_str() == arg_name { + hir::AssocItem::TypeAlias(assoc_ty) + if assoc_ty.name(sema.db).as_str() == arg_name => { override_location = Some(TypeLocation::AssocTypeEq); return None; - } - }, - hir::AssocItem::Const(const_) => { - if const_.name(sema.db)?.as_str() == arg_name { + }, + hir::AssocItem::Const(const_) + if const_.name(sema.db)?.as_str() == arg_name => { override_location = Some(TypeLocation::AssocConstEq); return None; - } - }, + }, _ => (), } } 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 69459a4b72dac..8b63fb9beadb6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1241,18 +1241,17 @@ impl<'a> FindUsages<'a> { }; sink(file_id, reference) } - Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => { - if self.include_self_kw_refs == def_to_ty(self.sema, &def) { - let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); - let reference = FileReference { - range, - name: FileReferenceNode::NameRef(name_ref.clone()), - category: ReferenceCategory::new(self.sema, &def, name_ref), - }; - sink(file_id, reference) - } else { - false - } + Some(NameRefClass::Definition(def, _)) + if self.include_self_kw_refs.is_some() + && self.include_self_kw_refs == def_to_ty(self.sema, &def) => + { + let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); + let reference = FileReference { + range, + name: FileReferenceNode::NameRef(name_ref.clone()), + category: ReferenceCategory::new(self.sema, &def, name_ref), + }; + sink(file_id, reference) } Some(NameRefClass::FieldShorthand { local_ref: local, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs index 8f25833fffb8d..6cc3334196d22 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs @@ -125,7 +125,7 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec), ()> { // if the expression consists of a single number, like "0" or "12", it can refer to // format args in the order they are specified. // see: https://doc.rust-lang.org/std/fmt/#positional-parameters - if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) { + if trimmed.chars().all(|c| c.is_ascii_digit()) { output.push_str(trimmed); } else if matches!(state, State::Expr) { extracted_expressions.push(Arg::Expr(trimmed.into())); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index e30b21c139fad..11ba815dab2ec 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -216,7 +216,12 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) { preorder.skip_subtree(); cb(ty); } - Some(ty) => { + Some(ty) => + { + #[expect( + clippy::collapsible_match, + reason = "it won't compile due to exhaustiveness" + )] if cb(ty) { preorder.skip_subtree(); } diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index 375e42cc833ee..965190b27df42 100644 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -487,7 +487,7 @@ mod tests { "The amount of folds is different than the expected amount" ); - for (fold, (range, attr, collapsed_text)) in folds.iter().zip(ranges.into_iter()) { + for (fold, (range, attr, collapsed_text)) in folds.iter().zip(ranges) { assert_eq!(fold.range.start(), range.start(), "mismatched start of folding ranges"); assert_eq!(fold.range.end(), range.end(), "mismatched end of folding ranges"); 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 4982b60f1dc8e..b12b296d34976 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 @@ -240,17 +240,14 @@ fn hints_( is_trivial = false; true } - ast::Type::PathType(t) => { + ast::Type::PathType(t) if t.path() .and_then(|it| it.segment()) .and_then(|it| it.parenthesized_arg_list()) - .is_some() - { - is_trivial = false; - true - } else { - false - } + .is_some() => + { + is_trivial = false; + true } _ => false, }) @@ -339,17 +336,14 @@ fn hints_( is_trivial = false; true } - ast::Type::PathType(t) => { + ast::Type::PathType(t) if t.path() .and_then(|it| it.segment()) .and_then(|it| it.parenthesized_arg_list()) - .is_some() - { - is_trivial = false; - true - } else { - false - } + .is_some() => + { + is_trivial = false; + true } _ => false, }) diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 3b472390d2f43..60750608a5b49 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -606,14 +606,14 @@ fn has_test_function_or_multiple_test_submodules( return true; } } - hir::ModuleDef::Module(submodule) => { + hir::ModuleDef::Module(submodule) if has_test_function_or_multiple_test_submodules( sema, &submodule, consider_exported_main, - ) { - number_of_test_submodules += 1; - } + ) => + { + number_of_test_submodules += 1; } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 0e101ab235f54..6823736d12730 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -307,12 +307,12 @@ fn highlight_name_ref( h |= HlMod::Consuming; } // highlight unsafe traits as unsafe only in their implementations - Definition::Trait(trait_) if trait_.is_unsafe(db) => { - if ast::Impl::for_trait_name_ref(&name_ref) - .is_some_and(|impl_| impl_.unsafe_token().is_some()) - { - h |= HlMod::Unsafe; - } + Definition::Trait(trait_) + if trait_.is_unsafe(db) + && ast::Impl::for_trait_name_ref(&name_ref) + .is_some_and(|impl_| impl_.unsafe_token().is_some()) => + { + h |= HlMod::Unsafe; } Definition::Function(_) => { let is_unsafe = name_ref diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 68bf78e037c0c..839df181597ba 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -884,7 +884,7 @@ mod tests { let fsc = builder.build(); let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2, 3] }; let mut vc = src.source_root_parent_map().into_iter().collect::>(); - vc.sort_by(|x, y| x.0.0.cmp(&y.0.0)); + vc.sort_by_key(|x| x.0.0); assert_eq!(vc, vec![(SourceRootId(2), SourceRootId(1)), (SourceRootId(3), SourceRootId(1))]) } @@ -899,7 +899,7 @@ mod tests { let fsc = builder.build(); let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] }; let mut vc = src.source_root_parent_map().into_iter().collect::>(); - vc.sort_by(|x, y| x.0.0.cmp(&y.0.0)); + vc.sort_by_key(|x| x.0.0); assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),]) } @@ -914,7 +914,7 @@ mod tests { let fsc = builder.build(); let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] }; let mut vc = src.source_root_parent_map().into_iter().collect::>(); - vc.sort_by(|x, y| x.0.0.cmp(&y.0.0)); + vc.sort_by_key(|x| x.0.0); assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),]) } @@ -930,7 +930,7 @@ mod tests { let fsc = builder.build(); let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] }; let mut vc = src.source_root_parent_map().into_iter().collect::>(); - vc.sort_by(|x, y| x.0.0.cmp(&y.0.0)); + vc.sort_by_key(|x| x.0.0); assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0)),]) } @@ -946,7 +946,7 @@ mod tests { let fsc = builder.build(); let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] }; let mut vc = src.source_root_parent_map().into_iter().collect::>(); - vc.sort_by(|x, y| x.0.0.cmp(&y.0.0)); + vc.sort_by_key(|x| x.0.0); assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0)),]) } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index e8a9ddde69ea3..0623e7ea19ab0 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -250,7 +250,8 @@ fn opt_visibility_inner(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { // test pub_parens_typepath // struct B(pub (super::A)); // struct B(pub (crate::A,)); - T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { + T![crate] | T![self] | T![super] | T![ident] | T![')'] + if p.nth(2) != T![:] // If we are in a tuple struct, then the parens following `pub` // might be an tuple field, not part of the visibility. So in that // case we don't want to consume an identifier. @@ -259,14 +260,14 @@ fn opt_visibility_inner(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { // struct MyStruct(pub (u32, u32)); // struct MyStruct(pub (u32)); // struct MyStruct(pub ()); - if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { - let m = p.start(); - p.bump(T!['(']); - paths::vis_path(p); - p.expect(T![')']); - m.complete(p, VISIBILITY_INNER); - return true; - } + && !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) => + { + let m = p.start(); + p.bump(T!['(']); + paths::vis_path(p); + p.expect(T![')']); + m.complete(p, VISIBILITY_INNER); + return true; } // test crate_visibility_in // pub(in super::A) struct S; 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 cd8944aa6170a..248de70f0e71d 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 @@ -495,6 +495,10 @@ impl<'a, T: SpanTransformer> Writer<'a, '_, T, tt::iter::TtIt } } + #[expect( + clippy::explicit_counter_loop, + reason = "it looks better the current way since we use `first_tt` before the loop" + )] fn subtree(&mut self, idx: usize, n_tt: usize, subtree: tt::iter::TtIter<'a>) { let mut first_tt = self.token_tree.len(); self.token_tree.resize(first_tt + n_tt, !0); diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index ab45917a5663b..37cfcd554524a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -160,7 +160,7 @@ env.RA_TEST_NOT_AN_OBJECT = "value" ("RA_TEST_UNSET", None), ] .iter() - .map(|(k, v)| (k.to_string(), v.map(ToString::to_string))) + .map(|(k, v)| (k.to_string(), v.map(str::to_owned))) .collect(); let env = cargo_config_env(&Some(config), &extra_env); assert_eq!(env.get("RA_TEST_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); 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 a8c3d062d041c..f5b3658ea90ca 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 @@ -144,12 +144,11 @@ impl fmt::Debug for Event { }; match self { - Event::Lsp(lsp_server::Message::Notification(not)) => { - if notification_is::(not) - || notification_is::(not) - { - return debug_non_verbose(not, f); - } + Event::Lsp(lsp_server::Message::Notification(not)) + if (notification_is::(not) + || notification_is::(not)) => + { + return debug_non_verbose(not, f); } Event::Task(Task::Response(resp)) => { return f diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 157285d1b6917..46ea4daba82ea 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -514,10 +514,11 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { let indent = IndentLevel::from_node(node); match l.next_sibling_or_token() { - Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { - if ws.next_sibling_or_token()?.into_token()? == r { - ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}"))); - } + Some(ws) + if ws.kind() == SyntaxKind::WHITESPACE + && ws.next_sibling_or_token()?.into_token()? == r => + { + ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}"))); } Some(ws) if ws.kind() == T!['}'] => { ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); 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 b2bd10b354e9d..edd063ffd4617 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -591,8 +591,8 @@ mod tests { assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); assert!( edit.annotations - .iter() - .flat_map(|(_, elements)| elements) + .values() + .flatten() .all(|element| element.ancestors().any(|it| &it == edit.new_root())) ) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 300d0f02ea778..28e8ceed708f3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -301,10 +301,11 @@ fn normalize_ws_between_braces(editor: &SyntaxEditor, node: &SyntaxNode) -> Opti let indent = IndentLevel::from_node(node); match l.next_sibling_or_token() { - Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { - if ws.next_sibling_or_token()?.into_token()? == r { - editor.replace(ws, make.whitespace(&format!("\n{indent}"))); - } + Some(ws) + if ws.kind() == SyntaxKind::WHITESPACE + && ws.next_sibling_or_token()?.into_token()? == r => + { + editor.replace(ws, make.whitespace(&format!("\n{indent}"))); } Some(ws) if ws.kind() == T!['}'] => { editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}"))); diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index 05528505f280e..122cf4887ec39 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -100,6 +100,10 @@ fn check_cargo_toml(path: &Path, text: String) { if !text.contains("path=") { continue; } + #[expect( + clippy::collapsible_match, + reason = "this changes meaning, as `dev-dependencies` includes `dependencies`" + )] match section { Some(s) if s.contains("dev-dependencies") => { if text.contains("version") { From 86c3c777d4b367c87b4c5ed86a9a0382a8168133 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 17 Apr 2026 08:15:10 +0300 Subject: [PATCH 52/98] Recognize `#[std::prelude::vX::test]` (not just `#[core::prelude::vX::test]` --- src/tools/rust-analyzer/crates/hir-def/src/attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f5f483d6af1a9..92d4928893865 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -200,7 +200,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| it.text() == "core") + && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std")) }); if is_test { attr_flags.insert(AttrFlags::IS_TEST); From 1ff215221bb722b81e3201658f8c0e77fbe9a38e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 7 Feb 2026 13:55:11 +0100 Subject: [PATCH 53/98] Bump salsa --- src/tools/rust-analyzer/Cargo.lock | 12 ++--- src/tools/rust-analyzer/Cargo.toml | 4 +- .../crates/hir-ty/src/tests/regression.rs | 1 + src/tools/rust-analyzer/crates/ide/src/lib.rs | 13 ++++- .../rust-analyzer/crates/span/src/hygiene.rs | 51 +++++++++---------- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 88f5c33cb2724..5534fb81a828c 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2453,9 +2453,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc" +checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" dependencies = [ "boxcar", "crossbeam-queue", @@ -2478,15 +2478,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3" +checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" [[package]] name = "salsa-macros" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1" +checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0ae65922d0072..0d00297e570cd 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -135,13 +135,13 @@ rayon = "1.10.0" rowan = "=0.15.18" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.25.2", default-features = false, features = [ +salsa = { version = "0.26", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", ] } -salsa-macros = "0.25.2" +salsa-macros = "0.26" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } 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 a559ad9b91d68..92b1b14fa0699 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 @@ -2363,6 +2363,7 @@ fn test() { } "#, expect![[r#" + 46..49 'Foo': Foo 93..97 'self': Foo 108..125 '{ ... }': usize 118..119 'N': usize diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 8a6ef6418063b..9f8374f625978 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -64,6 +64,7 @@ use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; use ide_db::base_db::relevant_crates; +use ide_db::base_db::salsa::Durability; use ide_db::line_index; use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ @@ -203,10 +204,18 @@ impl AnalysisHost { self.db.per_query_memory_usage() } pub fn trigger_cancellation(&mut self) { - self.db.trigger_cancellation(); + // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation + // the revision bump there is a reset marker for clearing fixpoint poisoning. + // That is `trigger_cancellation` is currently bugged wrt to cancellation. + // self.db.trigger_cancellation(); + self.db.synthetic_write(Durability::LOW); } pub fn trigger_garbage_collection(&mut self) { - self.db.trigger_lru_eviction(); + // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation + // the revision bump there is a reset marker for clearing fixpoint poisoning. + // That is `trigger_lru_eviction` is currently bugged wrt to cancellation. + // self.db.trigger_lru_eviction(); + self.db.synthetic_write(Durability::LOW); // SAFETY: `trigger_lru_eviction` triggers cancellation, so all running queries were canceled. unsafe { hir::collect_ty_garbage() }; } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index fe05ef9465181..0a81cef52ec5a 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,25 +81,24 @@ const _: () = { #[derive(Hash)] struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); - impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike> - for SyntaxContextData + impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike> for SyntaxContextData where - Option: zalsa_::interned::HashEqLike, - Transparency: zalsa_::interned::HashEqLike, - Edition: zalsa_::interned::HashEqLike, - SyntaxContext: zalsa_::interned::HashEqLike, + Option: zalsa_::HashEqLike, + Transparency: zalsa_::HashEqLike, + Edition: zalsa_::HashEqLike, + SyntaxContext: zalsa_::HashEqLike, { fn hash(&self, h: &mut H) { - zalsa_::interned::HashEqLike::::hash(&self.outer_expn, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.outer_transparency, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.edition, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.parent, &mut *h); + zalsa_::HashEqLike::::hash(&self.outer_expn, &mut *h); + zalsa_::HashEqLike::::hash(&self.outer_transparency, &mut *h); + zalsa_::HashEqLike::::hash(&self.edition, &mut *h); + zalsa_::HashEqLike::::hash(&self.parent, &mut *h); } fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { - zalsa_::interned::HashEqLike::::eq(&self.outer_expn, &data.0) - && zalsa_::interned::HashEqLike::::eq(&self.outer_transparency, &data.1) - && zalsa_::interned::HashEqLike::::eq(&self.edition, &data.2) - && zalsa_::interned::HashEqLike::::eq(&self.parent, &data.3) + zalsa_::HashEqLike::::eq(&self.outer_expn, &data.0) + && zalsa_::HashEqLike::::eq(&self.outer_transparency, &data.1) + && zalsa_::HashEqLike::::eq(&self.edition, &data.2) + && zalsa_::HashEqLike::::eq(&self.parent, &data.3) } } impl zalsa_struct_::Configuration for SyntaxContext { @@ -203,10 +202,10 @@ const _: () = { impl<'db> SyntaxContext { pub fn new< Db, - T0: zalsa_::interned::Lookup> + std::hash::Hash, - T1: zalsa_::interned::Lookup + std::hash::Hash, - T2: zalsa_::interned::Lookup + std::hash::Hash, - T3: zalsa_::interned::Lookup + std::hash::Hash, + T0: zalsa_::Lookup> + std::hash::Hash, + T1: zalsa_::Lookup + std::hash::Hash, + T2: zalsa_::Lookup + std::hash::Hash, + T3: zalsa_::Lookup + std::hash::Hash, >( db: &'db Db, outer_expn: T0, @@ -218,10 +217,10 @@ const _: () = { ) -> Self where Db: ?Sized + salsa::Database, - Option: zalsa_::interned::HashEqLike, - Transparency: zalsa_::interned::HashEqLike, - Edition: zalsa_::interned::HashEqLike, - SyntaxContext: zalsa_::interned::HashEqLike, + Option: zalsa_::HashEqLike, + Transparency: zalsa_::HashEqLike, + Edition: zalsa_::HashEqLike, + SyntaxContext: zalsa_::HashEqLike, { let (zalsa, zalsa_local) = db.zalsas(); @@ -236,10 +235,10 @@ const _: () = { std::marker::PhantomData, ), |id, data| SyntaxContextData { - outer_expn: zalsa_::interned::Lookup::into_owned(data.0), - outer_transparency: zalsa_::interned::Lookup::into_owned(data.1), - edition: zalsa_::interned::Lookup::into_owned(data.2), - parent: zalsa_::interned::Lookup::into_owned(data.3), + outer_expn: zalsa_::Lookup::into_owned(data.0), + outer_transparency: zalsa_::Lookup::into_owned(data.1), + edition: zalsa_::Lookup::into_owned(data.2), + parent: zalsa_::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, From 1b660518052e62ac603f7c6c441ca62572c248d2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 15:29:45 +0800 Subject: [PATCH 54/98] fix: add parentheses on record expr for replace_let_with_if_let Example --- ```rust fn main() { $0let x = Foo { x }; } ``` **Before this PR** ```rust fn main() { if let x = Foo { } } ``` **After this PR** ```rust fn main() { if let x = (Foo { x }) { } } ``` --- .../src/handlers/replace_let_with_if_let.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index d36f7f1f066af..1c5805cb046ce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -88,6 +88,11 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> fn let_expr_needs_paren(expr: &ast::Expr) -> bool { let make = SyntaxFactory::without_mappings(); let fake_expr_let = make.expr_let(make.tuple_pat(None).into(), make.expr_unit()); + let fake_if = make.expr_if(fake_expr_let.into(), make.expr_empty_block(), None); + let Some(ast::Expr::LetExpr(fake_expr_let)) = fake_if.condition() else { + stdx::never!(); + return false; + }; let Some(fake_expr) = fake_expr_let.expr() else { stdx::never!(); return false; @@ -182,6 +187,24 @@ fn main() { ) } + #[test] + fn replace_let_record_expr() { + check_assist( + replace_let_with_if_let, + r" +fn main() { + $0let x = Foo { x }; +} + ", + r" +fn main() { + if let x = (Foo { x }) { + } +} + ", + ) + } + #[test] fn replace_let_else() { check_assist( From fa6376e63f75ff0d0235893be57840e8ba5fe06e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 15:59:46 +0800 Subject: [PATCH 55/98] internal: remove redundant editor.make() calls --- .../src/handlers/add_turbo_fish.rs | 1 - .../src/handlers/convert_bool_then.rs | 3 +- .../src/handlers/convert_let_else_to_match.rs | 14 +++----- .../src/handlers/convert_match_to_let_else.rs | 4 +-- .../convert_tuple_return_type_to_struct.rs | 4 +-- .../src/handlers/convert_while_to_loop.rs | 3 +- .../handlers/destructure_struct_binding.rs | 7 ++-- .../src/handlers/desugar_try_expr.rs | 33 +++++++------------ .../src/handlers/expand_rest_pattern.rs | 16 ++++----- .../src/handlers/generate_delegate_methods.rs | 9 ++--- .../src/handlers/generate_deref.rs | 6 ++-- .../src/handlers/generate_getter_or_setter.rs | 5 +-- .../ide-assists/src/handlers/generate_impl.rs | 4 +-- .../ide-assists/src/handlers/generate_new.rs | 8 ++--- .../generate_single_field_struct_from.rs | 3 +- .../src/handlers/replace_if_let_with_match.rs | 6 ++-- .../src/handlers/replace_let_with_if_let.rs | 7 ++-- .../src/handlers/unmerge_imports.rs | 4 +-- 18 files changed, 46 insertions(+), 91 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 490a91d594939..dcd2124f7bebc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -103,7 +103,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ); } - let make = editor.make(); let placeholder_ty = make.ty_placeholder(); if let Some(pat) = let_stmt.pat() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index cb2e7d6d100ba..a2a71bcba6baa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -215,8 +215,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> ast::Expr::ParenExpr(expr) => expr.expr().unwrap_or(receiver), _ => receiver, }; - let if_expr = editor - .make() + let if_expr = make .expr_if( cond, closure_body, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 885750b1aabe9..1ae12390eedbf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -181,15 +181,11 @@ fn remove_mut_and_collect_idents( }) }) .collect::>>()?; - editor - .make() - .record_pat_with_fields( - p.path()?, - editor - .make() - .record_pat_field_list(fields, p.record_pat_field_list()?.rest_pat()), - ) - .into() + make.record_pat_with_fields( + p.path()?, + make.record_pat_field_list(fields, p.record_pat_field_list()?.rest_pat()), + ) + .into() } ast::Pat::RefPat(p) => { let inner = p.pat()?; 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 2b658248171a0..bc49acc1ef356 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 @@ -137,9 +137,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(), - editor - .make() - .record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) + make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) .syntax(), ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index a99425cef92ae..0af0cbc32a988 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -149,9 +149,7 @@ fn replace_usages( for tuple_pat in tuple_pats { editor.replace( tuple_pat.syntax(), - editor - .make() - .tuple_struct_pat(make.path_from_text(struct_name), tuple_pat.fields()) + make.tuple_struct_pat(make.path_from_text(struct_name), tuple_pat.fields()) .syntax(), ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index bbfac8f095f57..793e7465c11ae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -55,8 +55,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) let make = editor.make(); let while_indent_level = IndentLevel::from_node(while_expr.syntax()); - let break_block = editor - .make() + let break_block = make .block_expr( iter::once(make.expr_stmt(make.expr_break(None, None).into()).into()), None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index f23957e647e46..9ffce445d1a1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -152,9 +152,8 @@ impl StructEditData { // If the binding is nested inside a record, we need to wrap the new // destructured pattern in a non-shorthand record field if self.need_record_field_name { - let new_pat = editor - .make() - .record_pat_field(make.name_ref(&self.name.to_string()), new_pat); + let new_pat = + make.record_pat_field(make.name_ref(&self.name.to_string()), new_pat); editor.replace(pat.syntax(), new_pat.syntax()) } else { editor.replace(pat.syntax(), new_pat.syntax()) @@ -294,7 +293,7 @@ fn destructure_pat( // Use shorthand syntax if possible if old_name == new_name { make.record_pat_field_shorthand( - editor.make().ident_pat(is_ref, is_mut, make.name(old_name)).into(), + make.ident_pat(is_ref, is_mut, make.name(old_name)).into(), ) } else { make.record_pat_field( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index 399b2aa69be88..fc894f0fe9a0f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -69,8 +69,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let make = editor.make(); let sad_pat = match try_enum { TryEnum::Option => make.path_pat(make.ident_path("None")), - TryEnum::Result => editor - .make() + TryEnum::Result => make .tuple_struct_pat( make.ident_path("Err"), iter::once(make.path_pat(make.ident_path("err"))), @@ -90,8 +89,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let match_arm_list = make.match_arm_list([happy_arm, sad_arm]); - let expr_match = editor - .make() + let expr_match = make .expr_match(expr.clone(), match_arm_list) .indent(IndentLevel::from_node(try_expr.syntax())); @@ -121,23 +119,16 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op TryEnum::Result => make.ty_result(ty, make.ty_infer().into()).into(), }), expr, - editor - .make() - .block_expr( - iter::once( - editor - .make() - .expr_stmt( - editor - .make() - .expr_return(Some(sad_expr(try_enum, make, fill_expr))) - .into(), - ) - .into(), - ), - None, - ) - .indent(indent_level), + make.block_expr( + iter::once( + make.expr_stmt( + make.expr_return(Some(sad_expr(try_enum, make, fill_expr))).into(), + ) + .into(), + ), + None, + ) + .indent(indent_level), ); editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index c75e4cede747b..dc4976e8c29d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -55,16 +55,12 @@ fn expand_record_rest_pattern( let make = editor.make(); let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| { make.record_pat_field_shorthand( - editor - .make() - .ident_pat( - false, - false, - editor - .make() - .name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()), - ) - .into(), + make.ident_pat( + false, + false, + make.name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()), + ) + .into(), ) })); let new_field_list = make.record_pat_field_list(new_fields, None); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index d63ef33debec0..9486aa6f01953 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -108,8 +108,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' |edit| { let editor = edit.make_editor(strukt.syntax()); let make = editor.make(); - let field = editor - .make() + let field = make .field_from_idents(["self", &field_name]) .expect("always be a valid expression"); // Create the function @@ -149,16 +148,14 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' .map(|v| convert_param_list_to_arg_list(v, make)) .unwrap_or_else(|| make.arg_list([])); - let tail_expr = - editor.make().expr_method_call(field, make.name_ref(&name), arg_list).into(); + let tail_expr = make.expr_method_call(field, make.name_ref(&name), arg_list).into(); let tail_expr_finished = if is_async { make.expr_await(tail_expr).into() } else { tail_expr }; let body = make.block_expr([], Some(tail_expr_finished)); let ret_type = method_source.ret_type(); - let f = editor - .make() + let f = make .fn_( None, vis, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 89666dc804768..a5bdf80ac725e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -151,8 +151,7 @@ fn generate_edit( make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), false); let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), false))); - let fn_ = editor - .make() + let fn_ = make .fn_( [], None, @@ -175,8 +174,7 @@ fn generate_edit( make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), true); let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name); let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), true))); - let fn_ = editor - .make() + let fn_ = make .fn_( [], None, 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 d9719aaaf2892..b884581041f45 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 @@ -463,10 +463,7 @@ fn build_source_change( None, ty_params, ty_args, - editor - .make() - .ty_path(make.ident_path(&assist_info.strukt.name().unwrap().to_string())) - .into(), + make.ty_path(make.ident_path(&assist_info.strukt.name().unwrap().to_string())).into(), None, Some(make.assoc_item_list(items)), ); 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 e0f01b58c2c2a..c5a46f6981f59 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 @@ -173,9 +173,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> ); let trait_gen_args = trait_.generic_param_list().map(|list| { - editor - .make() - .generic_arg_list(list.generic_params().map(|_| holder_arg.clone()), false) + make.generic_arg_list(list.generic_params().map(|_| holder_arg.clone()), false) }); let make_impl_ = |body| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index ea6f0186d18b5..520709adc5e29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -138,8 +138,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let ret_type = make.ret_type(make.ty_path(make.ident_path("Self")).into()); - let fn_ = editor - .make() + let fn_ = make .fn_( [], strukt.visibility(), @@ -165,10 +164,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option editor.insert_all( Position::after(l_curly), vec![ - editor - .make() - .whitespace(&format!("\n{}", impl_def.indent_level() + 1)) - .into(), + make.whitespace(&format!("\n{}", impl_def.indent_level() + 1)).into(), fn_.syntax().clone().into(), make.whitespace("\n").into(), ], 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 7160400a969c6..10c009a2ea440 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 @@ -98,8 +98,7 @@ pub(crate) fn generate_single_field_struct_from( make_adt_constructor(names.as_deref(), constructors, &main_field_name, make); let body = make.block_expr([], Some(constructor)); - let fn_ = editor - .make() + let fn_ = make .fn_( [], None, 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 3e8afd8936746..20cd647e3b60f 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 @@ -140,8 +140,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` - let block_expr = editor - .make() + let block_expr = make .block_expr([], Some(match_expr.dedent(indent).indent(IndentLevel(1)))) .indent(indent); block_expr.into() @@ -301,8 +300,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let else_expr = else_expr.reset_indent(); let then_block = make_block_expr(then_expr); let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; - let if_let_expr = editor - .make() + let if_let_expr = make .expr_if( condition, then_block, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index d36f7f1f066af..8e028467d457e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -59,10 +59,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> .map(|it| it.happy_case()); match happy_variant { None => original_pat, - Some(var_name) => editor - .make() - .tuple_struct_pat(make.ident_path(var_name), [original_pat]) - .into(), + Some(var_name) => { + make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into() + } } }; let init_expr = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index 520b106bd962e..8875a7224685e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -55,9 +55,7 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt editor.insert_all( Position::after(use_.syntax()), vec![ - editor - .make() - .whitespace(&format!("\n{}", IndentLevel::from_node(use_.syntax()))) + make.whitespace(&format!("\n{}", IndentLevel::from_node(use_.syntax()))) .syntax_element(), new_use.syntax().syntax_element(), ], From 85f2d15011d5cc8be5970cf27219334e786bf2dc Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Fri, 17 Apr 2026 16:21:06 +0600 Subject: [PATCH 56/98] fix: return canonical short prefix from `Comment::prefix` `Comment::prefix` iterated `CommentKind::BY_PREFIX` forward, so `/**/` matched the `/**/` entry itself and returned the full 4-char string. `block_to_line` then computed `&text[4..text.len()-2]` = `&text[4..2]` and panicked. Delegate `Comment::prefix` to `CommentKind::prefix`, which already iterates in reverse and returns the canonical short form (`/*` for non-doc block comments). The `/**/` and `/***` entries in `BY_PREFIX` are still needed for `from_text` to classify them as non-doc blocks. fixes rust-lang/rust-analyzer#22071 --- .../src/handlers/convert_comment_block.rs | 15 +++++++++++++++ .../crates/syntax/src/ast/token_ext.rs | 6 +----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs index 0d36a5ddb304c..f242fe831447a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -381,6 +381,21 @@ fn main() { ); } + #[test] + fn empty_block_to_line() { + check_assist( + convert_comment_block, + r#" +/**/$0 +fn main() {} +"#, + r#" + +fn main() {} +"#, + ); + } + #[test] fn end_of_line_block_to_line() { check_assist_not_applicable( 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 83ab87c1c687e..29b3b0930adab 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 @@ -32,11 +32,7 @@ impl ast::Comment { } pub fn prefix(&self) -> &'static str { - let &(prefix, _kind) = CommentKind::BY_PREFIX - .iter() - .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix)) - .unwrap(); - prefix + self.kind().prefix() } /// Returns the textual content of a doc comment node as a single string with prefix and suffix From ab704f43bb8fdea492d7ece0eb45cfdd60a52fc4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 18:38:58 +0800 Subject: [PATCH 57/98] internal: support RUSTFMT_TOOLCHAIN for xtask codegen --- src/tools/rust-analyzer/xtask/src/codegen.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index bc7eb88f3a848..482b31e306cab 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -130,16 +130,17 @@ impl fmt::Display for Location { fn reformat(text: String) -> String { let sh = Shell::new().unwrap(); let rustfmt_toml = project_root().join("rustfmt.toml"); - let version = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap_or_default(); + let toolchain = &std::env::var("RUSTFMT_TOOLCHAIN").unwrap_or("stable".to_owned()); + let version = cmd!(sh, "rustup run {toolchain} rustfmt --version").read().unwrap_or_default(); // First try explicitly requesting the stable channel via rustup in case nightly is being used by default, // then plain rustfmt in case rustup isn't being used to manage the compiler (e.g. when using Nix). - let mut stdout = if !version.contains("stable") { + let mut stdout = if !version.contains(toolchain) { let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default(); - if !version.contains("stable") { + if !version.contains(toolchain) { panic!( - "Failed to run rustfmt from toolchain 'stable'. \ - Please run `rustup component add rustfmt --toolchain stable` to install it.", + "Failed to run rustfmt from toolchain '{toolchain}'. \ + Please run `rustup component add rustfmt --toolchain {toolchain}` to install it.", ); } else { cmd!(sh, "rustfmt --config-path {rustfmt_toml} --config fn_single_line=true") @@ -150,7 +151,7 @@ fn reformat(text: String) -> String { } else { cmd!( sh, - "rustup run stable rustfmt --config-path {rustfmt_toml} --config fn_single_line=true" + "rustup run {toolchain} rustfmt --config-path {rustfmt_toml} --config fn_single_line=true" ) .stdin(text) .read() From 5642dfb8875f9d64e9cfae46f1dbdadc11dac3da Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 17:21:49 +0800 Subject: [PATCH 58/98] fix: adjust name of extract_type_alias Example --- **Before this PR** ```rust struct Type; type $0Type = (u8, u8, u8); struct S { field: Type } ``` **After this PR** ```rust struct Type; type $0Type1 = (u8, u8, u8); struct S { field: Type1 } ``` --- .../src/handlers/extract_type_alias.rs | 49 +++++++++++++++++-- .../ide-db/src/syntax_helpers/suggest_name.rs | 14 ++++++ 2 files changed, 58 insertions(+), 5 deletions(-) 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 23573dc3388d3..eda35eba45c9a 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 @@ -1,6 +1,6 @@ use either::Either; use hir::HirDisplay; -use ide_db::syntax_helpers::node_ext::walk_ty; +use ide_db::syntax_helpers::{node_ext::walk_ty, suggest_name::NameGenerator}; use syntax::{ ast::{self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor, @@ -40,9 +40,10 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> ); let target = ty.syntax().text_range(); + let scope = ctx.sema.scope(ty.syntax())?; let resolved_ty = ctx.sema.resolve_type(&ty)?; let resolved_ty = if !resolved_ty.contains_unknown() { - let module = ctx.sema.scope(ty.syntax())?.module(); + let module = scope.module(); resolved_ty.display_source_code(ctx.db(), module.into(), false).ok()? } else { ty.to_string() @@ -57,6 +58,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> let make = editor.make(); let resolved_ty = make.ty(&resolved_ty); + let name = &NameGenerator::new_from_scope_non_locals(Some(scope)).suggest_name("Type"); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -75,15 +77,15 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> // Replace original type with the alias let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); let new_ty = if let Some(ty_args) = ty_args { - make.generic_ty_path_segment(make.name_ref("Type"), ty_args) + make.generic_ty_path_segment(make.name_ref(name), ty_args) } else { - make.path_segment(make.name_ref("Type")) + make.path_segment(make.name_ref(name)) }; editor.replace(ty.syntax(), new_ty.syntax()); // Insert new alias let ty_alias = - make.ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None))); + make.ty_alias(None, name, generic_params, None, None, Some((resolved_ty, None))); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() @@ -447,4 +449,41 @@ fn main() { "#, ) } + + #[test] + fn duplicate_names() { + check_assist( + extract_type_alias, + r" +struct Type; +struct S { + field: $0u8$0, +} + ", + r#" +struct Type; +type $0Type1 = u8; + +struct S { + field: Type1, +} + "#, + ); + + check_assist( + extract_type_alias, + r" +struct S { + field: $0u8$0, +} + ", + r#" +type $0Type1 = u8; + +struct S { + field: Type1, +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 3a785fbe80a0f..09e6115320664 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -123,6 +123,20 @@ impl NameGenerator { generator } + pub fn new_from_scope_non_locals(scope: Option>) -> Self { + let mut generator = Self::default(); + if let Some(scope) = scope { + scope.process_all_names(&mut |name, scope| { + if let hir::ScopeDef::Local(_) = scope { + return; + } + generator.insert(name.as_str()); + }); + } + + generator + } + /// Suggest a name without conflicts. If the name conflicts with existing names, /// it will try to resolve the conflict by adding a numeric suffix. pub fn suggest_name(&mut self, name: &str) -> SmolStr { From a0d8dd8f9dbbe574057b9dbc97719f1125d341df Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Fri, 17 Apr 2026 20:04:40 +0600 Subject: [PATCH 59/98] fix: wrap match scrutinee in parens when needed in replace_if_let_with_match `if (return) {}` caused a panic in `replace_if_let_with_match`: `let_and_guard` recursed through the `ParenExpr`, producing a bare `return` as the scrutinee. `make::expr_match` formatted `match return { ... }`, which the parser reinterpreted as `match (return { ... })`, consuming the arm list as the return value. The resulting match expr had no `MatchArmList`, so the `unwrap()` in `SyntaxFactory::expr_match` panicked. Mirror the fix pattern from rust-lang/rust-analyzer#22067: build a fake `match () { }` and ask whether the scrutinee needs parens in that position via `needs_parens_in_place_of`. Wrap when required. fixes rust-lang/rust-analyzer#22072 --- .../src/handlers/replace_if_let_with_match.rs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) 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 20cd647e3b60f..0badad7d0cbe6 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 @@ -132,6 +132,11 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' }; let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); let expr = scrutinee_to_be_expr.reset_indent(); + let expr = if match_scrutinee_needs_paren(&expr) { + make.expr_paren(expr).into() + } else { + expr + }; let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent); match_expr.into() }; @@ -419,6 +424,17 @@ fn let_and_guard(cond: &ast::Expr) -> (Option, Option) } } +fn match_scrutinee_needs_paren(expr: &ast::Expr) -> bool { + let make = SyntaxFactory::without_mappings(); + let fake_scrutinee = make.expr_unit(); + let fake_match = make.expr_match(fake_scrutinee, make.match_arm_list(std::iter::empty())); + let Some(fake_expr) = fake_match.expr() else { + stdx::never!(); + return false; + }; + expr.needs_parens_in_place_of(fake_match.syntax(), fake_expr.syntax()) +} + fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr { if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) && let Some(ast::Expr::BinExpr(left)) = expr.lhs() @@ -447,6 +463,26 @@ fn main() { ) } + #[test] + fn test_if_with_match_paren_jump_scrutinee() { + check_assist( + replace_if_let_with_match, + r#" +fn f() { + if $0(return) {} +} +"#, + r#" +fn f() { + match (return) { + true => {} + false => (), + } +} +"#, + ) + } + #[test] fn test_if_with_match_no_else() { check_assist( From 2dda8f6a40df32c506d43a278bc067758f920181 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 18 Apr 2026 19:00:43 +0800 Subject: [PATCH 60/98] Fix `make.expr_closure` invalid mappings --- .../syntax/src/ast/syntax_factory/constructors.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 421d13f0dc011..5688b8b1f54d3 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 @@ -1088,13 +1088,15 @@ impl SyntaxFactory { let ast = make::expr_closure(args, expr.clone()).clone_for_update(); if let Some(mut mapping) = self.mappings() { - let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone()); - builder.map_children( - input, - ast.param_list().unwrap().params().map(|param| param.syntax().clone()), - ); + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_node(expr.syntax().clone(), ast.body().unwrap().syntax().clone()); builder.finish(&mut mapping); + + let param_list = ast.param_list().unwrap(); + let mut params_builder = SyntaxMappingBuilder::new(param_list.syntax().clone()); + params_builder + .map_children(input, param_list.params().map(|param| param.syntax().clone())); + params_builder.finish(&mut mapping); } ast From cdc1d7c058268445f9bf77fc4a437161387d97da Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sat, 18 Apr 2026 20:40:30 +0800 Subject: [PATCH 61/98] internal: code review tweak --- .../rust-analyzer/crates/ide-db/src/search.rs | 67 ++++++++++--------- .../crates/ide/src/references.rs | 21 ++++-- 2 files changed, 51 insertions(+), 37 deletions(-) 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 8922eb65877b6..cb52a3f567e3a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -449,7 +449,7 @@ impl Definition { scope: None, include_self_kw_refs: None, search_self_mod: false, - excluded_categories: ReferenceCategory::empty(), + included_categories: ReferenceCategory::all(), exclude_library_files: false, } } @@ -467,8 +467,8 @@ pub struct FindUsages<'a> { include_self_kw_refs: Option>, /// whether to search for the `self` module search_self_mod: bool, - /// categories to exclude while collecting usages - excluded_categories: ReferenceCategory, + /// categories to include while collecting usages + included_categories: ReferenceCategory, /// whether to skip files from library source roots exclude_library_files: bool, } @@ -501,8 +501,8 @@ impl<'a> FindUsages<'a> { self } - pub fn set_excluded_categories(mut self, categories: ReferenceCategory) -> Self { - self.excluded_categories = categories; + pub fn set_included_categories(mut self, categories: ReferenceCategory) -> Self { + self.included_categories = categories; self } @@ -532,14 +532,21 @@ impl<'a> FindUsages<'a> { fn scope_files<'b>( db: &'b RootDatabase, scope: &'b SearchScope, + exclude_library_files: bool, ) -> impl Iterator, EditionedFileId, TextRange)> + 'b { - scope.entries.iter().map(|(&file_id, &search_range)| { - let text = db.file_text(file_id.file_id(db)).text(db); - let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text))); + scope + .entries + .iter() + .filter(move |(file_id, _)| { + !exclude_library_files || !is_library_file(db, file_id.file_id(db)) + }) + .map(|(&file_id, &search_range)| { + let text = db.file_text(file_id.file_id(db)).text(db); + let search_range = + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text))); - (text.clone(), file_id, search_range) - }) + (text.clone(), file_id, search_range) + }) } fn match_indices<'b>( @@ -665,6 +672,7 @@ impl<'a> FindUsages<'a> { fn collect_possible_aliases( sema: &Semantics<'_, RootDatabase>, container: Adt, + exclude_library_files: bool, ) -> Option<(FxHashSet, Vec>)> { fn insert_type_alias( db: &RootDatabase, @@ -698,9 +706,11 @@ impl<'a> FindUsages<'a> { }; let finder = Finder::new(current_to_process.as_bytes()); - for (file_text, file_id, search_range) in - FindUsages::scope_files(db, ¤t_to_process_search_scope) - { + for (file_text, file_id, search_range) in FindUsages::scope_files( + db, + ¤t_to_process_search_scope, + exclude_library_files, + ) { let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); for offset in FindUsages::match_indices(&file_text, &finder, search_range) { @@ -885,7 +895,7 @@ impl<'a> FindUsages<'a> { } let Some((container_possible_aliases, is_possibly_self)) = - collect_possible_aliases(self.sema, container) + collect_possible_aliases(self.sema, container, self.exclude_library_files) else { return false; }; @@ -897,16 +907,12 @@ impl<'a> FindUsages<'a> { let finder = Finder::new(name.as_bytes()); // The search for `Self` may return duplicate results with `ContainerName`, so deduplicate them. let mut self_positions = FxHashSet::default(); - let is_possibly_self = is_possibly_self.into_iter().filter(|position| { - !self.exclude_library_files - || !is_library_file(self.sema.db, position.file_id.file_id(self.sema.db)) - }); tracing::info_span!("Self_search").in_scope(|| { search( self, &finder, name, - is_possibly_self.map(|position| { + is_possibly_self.into_iter().map(|position| { (position.file_text(self.sema.db).clone(), position.file_id, position.range) }), |path, name_position| { @@ -926,7 +932,7 @@ impl<'a> FindUsages<'a> { self, &finder, name, - FindUsages::scope_files(self.sema.db, search_scope), + FindUsages::scope_files(self.sema.db, search_scope, self.exclude_library_files), |path, name_position| { has_any_name(path, |name| container_possible_aliases.contains(name)) && !self_positions.contains(&name_position) @@ -942,7 +948,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("FindUsages:search").entered(); let sema = self.sema; - let mut search_scope = { + let search_scope = { // FIXME: Is the trait scope needed for trait impl assoc items? let base = as_trait_assoc_def(sema.db, self.def).unwrap_or(self.def).search_scope(sema.db); @@ -951,11 +957,6 @@ impl<'a> FindUsages<'a> { Some(scope) => base.intersection(scope), } }; - if self.exclude_library_files { - search_scope - .entries - .retain(|&file_id, _| !is_library_file(sema.db, file_id.file_id(sema.db))); - } if search_scope.entries.is_empty() { return; } @@ -1010,7 +1011,9 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new(name); let include_self_kw_refs = self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self"))); - for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) { + for (text, file_id, search_range) in + Self::scope_files(sema.db, &search_scope, self.exclude_library_files) + { let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); // Search for occurrences of the items name @@ -1067,7 +1070,9 @@ impl<'a> FindUsages<'a> { let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); let finder = &Finder::new("super"); - for (text, file_id, search_range) in Self::scope_files(sema.db, &scope) { + for (text, file_id, search_range) in + Self::scope_files(sema.db, &scope, self.exclude_library_files) + { self.sema.db.unwind_if_revision_cancelled(); let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); @@ -1324,9 +1329,9 @@ impl<'a> FindUsages<'a> { } fn is_excluded_name_ref(&self, name_ref: &ast::NameRef) -> bool { - (self.excluded_categories.contains(ReferenceCategory::TEST) + (!self.included_categories.contains(ReferenceCategory::TEST) && is_name_ref_in_test(self.sema, name_ref)) - || (self.excluded_categories.contains(ReferenceCategory::IMPORT) + || (!self.included_categories.contains(ReferenceCategory::IMPORT) && is_name_ref_in_import(name_ref)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index ea32f0226783b..6b60d9a621dde 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -131,17 +131,17 @@ pub(crate) fn find_all_refs( let exclude_library_refs = !is_library_file(sema.db, position.file_id); let make_searcher = |literal_search: bool| { move |def: Definition| { - let mut excluded_categories = ReferenceCategory::empty(); + let mut included_categories = ReferenceCategory::all(); if config.exclude_imports { - excluded_categories |= ReferenceCategory::IMPORT; + included_categories.remove(ReferenceCategory::IMPORT); } if config.exclude_tests { - excluded_categories |= ReferenceCategory::TEST; + included_categories.remove(ReferenceCategory::TEST); } let mut usages = def .usages(sema) .set_scope(config.search_scope.as_ref()) - .set_excluded_categories(excluded_categories) + .set_included_categories(included_categories) .set_exclude_library_files(exclude_library_refs) .include_self_refs() .all(); @@ -182,8 +182,7 @@ pub(crate) fn find_all_refs( is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)), nav, } - }) - .filter(|decl| !(exclude_library_refs && is_library_file(sema.db, decl.nav.file_id))); + }); ReferenceSearchResult { declaration, references } } }; @@ -582,6 +581,8 @@ pub fn also_calls_foo() { false, false, expect![[r#" + foo Function FileId(1) 0..15 7..10 + FileId(0) 9..12 import FileId(0) 31..34 "#]], @@ -598,6 +599,8 @@ fn main() { false, false, expect![[r#" + Some Variant FileId(1) 5999..6031 6024..6028 + FileId(0) 46..50 "#]], ); @@ -2244,6 +2247,8 @@ use proc_macros::identity; fn func() {} "#, expect![[r#" + identity Attribute FileId(1) 1..107 32..40 + FileId(0) 17..25 import FileId(0) 43..51 "#]], @@ -2273,6 +2278,8 @@ use proc_macros::mirror; mirror$0! {} "#, expect![[r#" + mirror ProcMacro FileId(1) 1..77 22..28 + FileId(0) 17..23 import FileId(0) 26..32 "#]], @@ -2291,6 +2298,8 @@ use proc_macros::DeriveIdentity; struct Foo; "#, expect![[r#" + derive_identity Derive FileId(2) 1..107 45..60 + FileId(0) 17..31 import FileId(0) 56..70 "#]], From 28141750d7e336eb3904616897ec51624235d345 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 16 Apr 2026 10:59:37 +0200 Subject: [PATCH 62/98] misc improvements --- .../crates/ide-completion/src/completions.rs | 112 ++++++++---------- .../crates/ide-completion/src/render.rs | 3 +- 2 files changed, 48 insertions(+), 67 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 460a0b139a1e4..2ed582598b729 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -234,17 +234,13 @@ impl Completions { Visible::Editable => true, Visible::No => return, }; - self.add( - render_path_resolution( - RenderContext::new(ctx) - .private_editable(is_private_editable) - .doc_aliases(doc_aliases), - path_ctx, - local_name, - resolution, - ) - .build(ctx.db), - ); + render_path_resolution( + RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), + path_ctx, + local_name, + resolution, + ) + .add_to(self, ctx.db); } pub(crate) fn add_pattern_resolution( @@ -259,15 +255,13 @@ impl Completions { Visible::Editable => true, Visible::No => return, }; - self.add( - render_pattern_resolution( - RenderContext::new(ctx).private_editable(is_private_editable), - pattern_ctx, - local_name, - resolution, - ) - .build(ctx.db), - ); + render_pattern_resolution( + RenderContext::new(ctx).private_editable(is_private_editable), + pattern_ctx, + local_name, + resolution, + ) + .add_to(self, ctx.db); } pub(crate) fn add_enum_variants( @@ -310,15 +304,13 @@ impl Completions { Visible::Editable => true, Visible::No => return, }; - self.add( - render_macro( - RenderContext::new(ctx).private_editable(is_private_editable), - path_ctx, - local_name, - mac, - ) - .build(ctx.db), - ); + render_macro( + RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, + local_name, + mac, + ) + .add_to(self, ctx.db); } pub(crate) fn add_function( @@ -334,17 +326,13 @@ impl Completions { Visible::No => return, }; let doc_aliases = ctx.doc_aliases(&func); - self.add( - render_fn( - RenderContext::new(ctx) - .private_editable(is_private_editable) - .doc_aliases(doc_aliases), - path_ctx, - local_name, - func, - ) - .build(ctx.db), - ); + render_fn( + RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), + path_ctx, + local_name, + func, + ) + .add_to(self, ctx.db); } pub(crate) fn add_method( @@ -361,18 +349,14 @@ impl Completions { Visible::No => return, }; let doc_aliases = ctx.doc_aliases(&func); - self.add( - render_method( - RenderContext::new(ctx) - .private_editable(is_private_editable) - .doc_aliases(doc_aliases), - dot_access, - receiver, - local_name, - func, - ) - .build(ctx.db), - ); + render_method( + RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), + dot_access, + receiver, + local_name, + func, + ) + .add_to(self, ctx.db); } pub(crate) fn add_method_with_import( @@ -388,19 +372,17 @@ impl Completions { Visible::No => return, }; let doc_aliases = ctx.doc_aliases(&func); - self.add( - render_method( - RenderContext::new(ctx) - .private_editable(is_private_editable) - .doc_aliases(doc_aliases) - .import_to_add(Some(import)), - dot_access, - None, - None, - func, - ) - .build(ctx.db), - ); + render_method( + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases) + .import_to_add(Some(import)), + dot_access, + None, + None, + func, + ) + .add_to(self, ctx.db); } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f14fd9221f454..3975e51d8b7ca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -111,8 +111,7 @@ impl<'a> RenderContext<'a> { is_assoc_deprecated || assoc .container_or_implemented_trait(db) - .map(|trait_| self.is_deprecated(trait_)) - .unwrap_or(false) + .is_some_and(|trait_| self.is_deprecated(trait_)) } // FIXME: remove this From 3310c64da7caf38e4748728aae7e159fa335a730 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 16 Apr 2026 10:58:33 +0200 Subject: [PATCH 63/98] add tests Note: the following def kinds were already handled correctly (deprecated=true): - Variant - Function - Macro --- .../crates/ide-completion/src/render.rs | 310 +++++++++++++++++- 1 file changed, 307 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 3975e51d8b7ca..a855898e76ac1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1560,6 +1560,31 @@ fn main() { let _: m::Spam = S$0 } check( r#" #[deprecated] +mod something_deprecated {} + +fn main() { som$0 } +"#, + SymbolKind::Module, + expect![[r#" + [ + CompletionItem { + label: "something_deprecated", + detail_left: None, + detail_right: None, + source_range: 55..58, + delete: 55..58, + insert: "something_deprecated", + kind: SymbolKind( + Module, + ), + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] fn something_deprecated() {} fn main() { som$0 } @@ -1602,10 +1627,289 @@ fn main() { som$0 } "#]], ); + check( + r#" +#[deprecated] +struct A; + +fn main() { A$0 } +"#, + SymbolKind::Struct, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: Some( + "A", + ), + source_range: 37..38, + delete: 37..38, + insert: "A", + kind: SymbolKind( + Struct, + ), + detail: "A", + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +enum A {} + +fn main() { A$0 } +"#, + SymbolKind::Enum, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: Some( + "A", + ), + source_range: 37..38, + delete: 37..38, + insert: "A", + kind: SymbolKind( + Enum, + ), + detail: "A", + }, + ] + "#]], + ); + + check( + r#" +enum A { + Okay, + #[deprecated] + Old, +} + +fn main() { A::$0 } +"#, + SymbolKind::Variant, + expect![[r#" + [ + CompletionItem { + label: "Okay", + detail_left: None, + detail_right: Some( + "Okay", + ), + source_range: 64..64, + delete: 64..64, + insert: "Okay$0", + kind: SymbolKind( + Variant, + ), + detail: "Okay", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: false, + has_self_param: false, + return_type: DirectConstructor, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + }, + trigger_call_info: true, + }, + CompletionItem { + label: "Old", + detail_left: None, + detail_right: Some( + "Old", + ), + source_range: 64..64, + delete: 64..64, + insert: "Old$0", + kind: SymbolKind( + Variant, + ), + detail: "Old", + deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: false, + has_self_param: false, + return_type: DirectConstructor, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + }, + trigger_call_info: true, + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +const A: i32 = 0; + +fn main() { A$0 } +"#, + SymbolKind::Const, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 45..46, + delete: 45..46, + insert: "A", + kind: SymbolKind( + Const, + ), + detail: "i32", + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +static A: i32 = 0; + +fn main() { A$0 } +"#, + SymbolKind::Static, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 46..47, + delete: 46..47, + insert: "A", + kind: SymbolKind( + Static, + ), + detail: "i32", + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +trait A {} + +impl A$0 +"#, + SymbolKind::Trait, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: None, + source_range: 31..32, + delete: 31..32, + insert: "A", + kind: SymbolKind( + Trait, + ), + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +type A = i32; + +fn main() { A$0 } +"#, + SymbolKind::TypeAlias, + expect![[r#" + [ + CompletionItem { + label: "A", + detail_left: None, + detail_right: None, + source_range: 41..42, + delete: 41..42, + insert: "A", + kind: SymbolKind( + TypeAlias, + ), + }, + ] + "#]], + ); + + check( + r#" +#[deprecated] +macro_rules! a { _ => {}} + +fn main() { a$0 } +"#, + SymbolKind::Macro, + expect![[r#" + [ + CompletionItem { + label: "a!(…)", + detail_left: None, + detail_right: Some( + "macro_rules! a", + ), + source_range: 53..54, + delete: 53..54, + insert: "a!($0)", + kind: SymbolKind( + Macro, + ), + lookup: "a!", + detail: "macro_rules! a", + deprecated: true, + }, + ] + "#]], + ); + check( r#" struct A { #[deprecated] the_field: u32 } -fn foo() { A { the$0 } } + +fn main() { A { the$0 } } "#, SymbolKind::Field, expect![[r#" @@ -1616,8 +1920,8 @@ fn foo() { A { the$0 } } detail_right: Some( "u32", ), - source_range: 57..60, - delete: 57..60, + source_range: 59..62, + delete: 59..62, insert: "the_field", kind: SymbolKind( Field, From 02f0edc195ae8541451e9d59614d1b994d6bb22e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 16 Apr 2026 10:59:37 +0200 Subject: [PATCH 64/98] fix: also call `is_deprecated` on `ModuleDef` --- src/tools/rust-analyzer/crates/hir/src/attrs.rs | 6 +++++- src/tools/rust-analyzer/crates/hir/src/lib.rs | 17 +++++++++++++++++ .../crates/ide-completion/src/render.rs | 9 ++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index bec91032b9a9e..223103b6e5d26 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -38,7 +38,11 @@ pub enum AttrsOwner { Field(FieldId), LifetimeParam(LifetimeParamId), TypeOrConstParam(TypeOrConstParamId), - /// Things that do not have attributes. Used for builtin derives. + /// Things that do not have attributes. + /// + /// Used for: + /// - builtin derives + /// - builtin types (as those do not have attributes) Dummy, } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 27516ed80b278..1ae6643294a51 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -556,6 +556,23 @@ impl HasCrate for ModuleDef { } } +impl HasAttrs for ModuleDef { + fn attr_id(self, db: &dyn HirDatabase) -> attrs::AttrsOwner { + match self { + ModuleDef::Module(it) => it.attr_id(db), + ModuleDef::Function(it) => it.attr_id(db), + ModuleDef::Adt(it) => it.attr_id(db), + ModuleDef::EnumVariant(it) => it.attr_id(db), + ModuleDef::Const(it) => it.attr_id(db), + ModuleDef::Static(it) => it.attr_id(db), + ModuleDef::Trait(it) => it.attr_id(db), + ModuleDef::TypeAlias(it) => it.attr_id(db), + ModuleDef::Macro(it) => it.attr_id(db), + ModuleDef::BuiltinType(_) => attrs::AttrsOwner::Dummy, + } + } +} + impl HasVisibility for ModuleDef { fn visibility(&self, db: &dyn HirDatabase) -> Visibility { match *self { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index a855898e76ac1..5a34782235bb4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -576,7 +576,7 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option, resolution: ScopeDef) -> bool { match resolution { - ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it), + ScopeDef::ModuleDef(it) => ctx.is_deprecated(it) || ctx.is_deprecated_assoc_item(it), ScopeDef::GenericParam(it) => ctx.is_deprecated(it), ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it), _ => false, @@ -1577,6 +1577,7 @@ fn main() { som$0 } kind: SymbolKind( Module, ), + deprecated: true, }, ] "#]], @@ -1650,6 +1651,7 @@ fn main() { A$0 } Struct, ), detail: "A", + deprecated: true, }, ] "#]], @@ -1678,6 +1680,7 @@ fn main() { A$0 } Enum, ), detail: "A", + deprecated: true, }, ] "#]], @@ -1792,6 +1795,7 @@ fn main() { A$0 } Const, ), detail: "i32", + deprecated: true, }, ] "#]], @@ -1820,6 +1824,7 @@ fn main() { A$0 } Static, ), detail: "i32", + deprecated: true, }, ] "#]], @@ -1845,6 +1850,7 @@ impl A$0 kind: SymbolKind( Trait, ), + deprecated: true, }, ] "#]], @@ -1870,6 +1876,7 @@ fn main() { A$0 } kind: SymbolKind( TypeAlias, ), + deprecated: true, }, ] "#]], From 0b9410f728d18f2bca12d73a3545f17b36cf6458 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 17 Apr 2026 23:57:53 +0200 Subject: [PATCH 65/98] refactor: merge `RenderContext::is_deprecated` and `RenderContext::is_deprecated_assoc_item` --- .../crates/ide-completion/src/render.rs | 57 +++++++++++-------- .../ide-completion/src/render/const_.rs | 2 +- .../ide-completion/src/render/function.rs | 2 +- .../ide-completion/src/render/literal.rs | 8 ++- .../ide-completion/src/render/macro_.rs | 2 +- .../ide-completion/src/render/pattern.rs | 4 +- .../ide-completion/src/render/type_alias.rs | 2 +- .../src/render/union_literal.rs | 2 +- 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 5a34782235bb4..a636c0603ba52 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -92,26 +92,32 @@ impl<'a> RenderContext<'a> { && self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL) } - fn is_deprecated(&self, def: impl HasAttrs) -> bool { - def.attrs(self.db()).is_deprecated() - } - - fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool { + /// Whether `def` is deprecated. + /// + /// This can happen for two reasons: + /// - the def is marked with `#[deprecated]` + /// - the def is an assoc item whose trait is deprecated + /// + /// In order to be able to check for the latter, we'd ideally want to `try_as_dyn<_, dyn AsAssocItem>(def)` + /// (see [`try_as_dyn`][]), but that function is currently unstable. Therefore, we employ a hack instead: + /// if `def` can be an assoc item, it should be passed to this method as follows: + /// ```ignore + /// self.is_deprecated(def, Some(def)) + /// ``` + /// otherwise, it should be passed as: + /// ```ignore + /// self.is_deprecated(def, None) + /// ``` + /// + /// [`try_as_dyn`]: https://doc.rust-lang.org/std/any/fn.try_as_dyn.html + fn is_deprecated(&self, def: impl HasAttrs, def_as_assoc_item: Option) -> bool { let db = self.db(); - let assoc = match as_assoc_item.as_assoc_item(db) { - Some(assoc) => assoc, - None => return false, - }; - - let is_assoc_deprecated = match assoc { - hir::AssocItem::Function(it) => self.is_deprecated(it), - hir::AssocItem::Const(it) => self.is_deprecated(it), - hir::AssocItem::TypeAlias(it) => self.is_deprecated(it), - }; - is_assoc_deprecated - || assoc - .container_or_implemented_trait(db) - .is_some_and(|trait_| self.is_deprecated(trait_)) + def.attrs(db).is_deprecated() + || def_as_assoc_item + .and_then(|assoc| assoc.container_or_implemented_trait(db)) + .is_some_and(|trait_| { + self.is_deprecated(trait_, None /* traits can't be assoc items */) + }) } // FIXME: remove this @@ -128,7 +134,7 @@ pub(crate) fn render_field( ty: &hir::Type<'_>, ) -> CompletionItem { let db = ctx.db(); - let is_deprecated = ctx.is_deprecated(field); + let is_deprecated = ctx.is_deprecated(field, None /* fields can't be assoc items */); let name = field.name(db); let (name, escaped_name) = (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); @@ -575,10 +581,15 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option, resolution: ScopeDef) -> bool { + let db = ctx.db(); match resolution { - ScopeDef::ModuleDef(it) => ctx.is_deprecated(it) || ctx.is_deprecated_assoc_item(it), - ScopeDef::GenericParam(it) => ctx.is_deprecated(it), - ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it), + ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)), + ScopeDef::GenericParam(it) => { + ctx.is_deprecated(it, None /* generic params can't be assoc items */) + } + ScopeDef::AdtSelfType(it) => { + ctx.is_deprecated(it, None /* `Self` can't be an assoc item */) + } _ => false, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 707a8aed4fb9e..134a77a8991e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -21,7 +21,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition); item.set_documentation(ctx.docs(const_)) - .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_)) + .set_deprecated(ctx.is_deprecated(const_, const_.as_assoc_item(db))) .detail(detail) .set_relevance(ctx.completion_relevance()); 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 dfa30841e7db1..18151cffcd391 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 @@ -147,7 +147,7 @@ fn render( detail(ctx.completion, func) }; item.set_documentation(ctx.docs(func)) - .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) + .set_deprecated(ctx.is_deprecated(func, func.as_assoc_item(db))) .detail(detail) .lookup_by(name.as_str().to_smolstr()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 6e49af980aeaa..b7de3da468dde 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -189,8 +189,12 @@ impl Variant { fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool { match self { - Variant::Struct(it) => ctx.is_deprecated(it), - Variant::EnumVariant(it) => ctx.is_deprecated(it), + Variant::Struct(it) => { + ctx.is_deprecated(it, None /* structs can't be assoc items */) + } + Variant::EnumVariant(it) => { + ctx.is_deprecated(it, None /* enum variants can't be assoc items */) + } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 8cdeb8abbff77..ff4cf9a75b60e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -64,7 +64,7 @@ fn render( label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); - item.set_deprecated(ctx.is_deprecated(macro_)) + item.set_deprecated(ctx.is_deprecated(macro_, None /* macros can't be assoc items */)) .detail(macro_.display(completion.db, completion.display_target).to_string()) .set_documentation(docs) .set_relevance(ctx.completion_relevance()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index fb35d7b9b6714..022e97e4f7600 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -126,7 +126,9 @@ fn build_completion( ctx.completion.edition, ); item.set_documentation(ctx.docs(def)) - .set_deprecated(ctx.is_deprecated(def)) + .set_deprecated( + ctx.is_deprecated(def, None /* the two current `def` arguments to this function, `Struct` and `EnumVariant`, both can't be assoc items */), + ) .detail(&pat) .lookup_by(lookup) .set_relevance(relevance); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 3fc0f369e5ada..2b79ca2deb693 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -47,7 +47,7 @@ fn render( ctx.completion.edition, ); item.set_documentation(ctx.docs(type_alias)) - .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias)) + .set_deprecated(ctx.is_deprecated(type_alias, type_alias.as_assoc_item(db))) .detail(detail) .set_relevance(ctx.completion_relevance()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index 23f0d4e06f2c8..7164c94fde946 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -95,7 +95,7 @@ pub(crate) fn render_union_literal( ); item.set_documentation(ctx.docs(un)) - .set_deprecated(ctx.is_deprecated(un)) + .set_deprecated(ctx.is_deprecated(un, None /* unions can't be assoc items */)) .detail(detail) .set_relevance(ctx.completion_relevance()); From 8463b33a014c7c7ff4b006a00f8021253da7abb2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 16 Apr 2026 10:59:37 +0200 Subject: [PATCH 66/98] implement custom `Debug` for `MiniCore` When the contents correspond to the default, just output `MiniCore("")` instead of the whole thing. Helps reduce the length of the debug output in the common case. --- src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 99af1c32d896b..6b72a3033990b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -385,7 +385,7 @@ pub enum Severity { Allow, } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] pub struct MiniCore<'a>(&'a str); impl<'a> MiniCore<'a> { @@ -400,6 +400,15 @@ impl<'a> MiniCore<'a> { } } +impl std::fmt::Debug for MiniCore<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("MiniCore") + // don't print the whole contents if they correspond to the default + .field(if self.0 == test_utils::MiniCore::RAW_SOURCE { &"" } else { &self.0 }) + .finish() + } +} + impl<'a> Default for MiniCore<'a> { #[inline] fn default() -> Self { From 7d13144973710c2c06ae23b59892c8350149715f Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:01:21 +0000 Subject: [PATCH 67/98] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 206 +++++++++++++----- 1 file changed, 155 insertions(+), 51 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index be595ec0f608a..a67b0bc5a60ad 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -285,6 +285,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, + Lint { + label: "deprecated_llvm_intrinsic", + description: r##"detects uses of deprecated LLVM intrinsics"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, Lint { label: "deprecated_safe_2024", description: r##"detects unsafe functions being used as safe functions"##, @@ -425,6 +432,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, + Lint { + label: "float_literal_f32_fallback", + description: r##"detects unsuffixed floating point literals whose type fallback to `f32`"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, Lint { label: "for_loops_over_fallibles", description: r##"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"##, @@ -1681,7 +1695,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "varargs_without_pattern", description: r##"detects usage of `...` arguments without a pattern in non-foreign items"##, - default_severity: Severity::Warning, + default_severity: Severity::Error, warn_since: None, deny_since: None, }, @@ -1715,7 +1729,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "future_incompatible", - description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, + description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, float-literal-f32-fallback, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1813,7 +1827,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "future_incompatible", - description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, + description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, float-literal-f32-fallback, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##, default_severity: Severity::Allow, warn_since: None, deny_since: None, @@ -1831,6 +1845,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ "conflicting_repr_hints", "const_evaluatable_unchecked", "elided_lifetimes_in_associated_constant", + "float_literal_f32_fallback", "forbidden_lint_groups", "ill_formed_attribute_input", "invalid_macro_export_arguments", @@ -2485,6 +2500,22 @@ The tracking issue for this feature is: [#40180] [#40180]: https://github.com/rust-lang/rust/issues/40180 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "abort_immediate", + description: r##"# `abort_immediate` + + + +The tracking issue for this feature is: [#154601] + +[#154601]: https://github.com/rust-lang/rust/issues/154601 + ------------------------ "##, default_severity: Severity::Allow, @@ -4615,6 +4646,47 @@ Allows checking whether or not the backend correctly supports unstable float typ This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "cfg_target_object_format", + description: r##"# `cfg_target_object_format` + +The tracking issue for this feature is: [#152586] + +[#152586]: https://github.com/rust-lang/rust/issues/152586 + +------------------------ + +The `cfg_target_object_format` feature makes it possible to execute different code +depending on the current target's object file format. + +## Examples + +```rust +#![feature(cfg_target_object_format)] + +#[cfg(target_object_format = "elf")] +fn a() { + // ... +} + +#[cfg(target_object_format = "mach-o")] +fn a() { + // ... +} + +fn b() { + if cfg!(target_object_format = "wasm") { + // ... + } else { + // ... + } +} +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -7714,6 +7786,22 @@ The tracking issue for this feature is: [#116909] --- Enable the `f16` type for IEEE 16-bit floating numbers (half precision). +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "f32_from_f16", + description: r##"# `f32_from_f16` + + + +The tracking issue for this feature is: [#154005] + +[#154005]: https://github.com/rust-lang/rust/issues/154005 + +------------------------ "##, default_severity: Severity::Allow, warn_since: None, @@ -8017,6 +8105,22 @@ The tracking issue for this feature is: [#91079] This feature is internal to the Rust compiler and is not intended for general use. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "fma4_target_feature", + description: r##"# `fma4_target_feature` + +fma4 target feature on x86. + +The tracking issue for this feature is: [#155233] + +[#155233]: https://github.com/rust-lang/rust/issues/155233 + ------------------------ "##, default_severity: Severity::Allow, @@ -8898,22 +9002,6 @@ The tracking issue for this feature is: [#134821] [#134821]: https://github.com/rust-lang/rust/issues/134821 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "int_lowest_highest_one", - description: r##"# `int_lowest_highest_one` - - - -The tracking issue for this feature is: [#145203] - -[#145203]: https://github.com/rust-lang/rust/issues/145203 - ------------------------ "##, default_severity: Severity::Allow, @@ -9261,22 +9349,6 @@ The tracking issue for this feature is: [#111192] [#111192]: https://github.com/rust-lang/rust/issues/111192 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "isolate_most_least_significant_one", - description: r##"# `isolate_most_least_significant_one` - - - -The tracking issue for this feature is: [#136909] - -[#136909]: https://github.com/rust-lang/rust/issues/136909 - ------------------------ "##, default_severity: Severity::Allow, @@ -14884,6 +14956,38 @@ The tracking issue for this feature is: [#109929] [#109929]: https://github.com/rust-lang/rust/issues/109929 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "transmute_neo", + description: r##"# `transmute_neo` + + + +The tracking issue for this feature is: [#155079] + +[#155079]: https://github.com/rust-lang/rust/issues/155079 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "transmute_prefix", + description: r##"# `transmute_prefix` + + + +The tracking issue for this feature is: [#155079] + +[#155079]: https://github.com/rust-lang/rust/issues/155079 + ------------------------ "##, default_severity: Severity::Allow, @@ -15181,6 +15285,22 @@ The tracking issue for this feature is: [#63178] [#63178]: https://github.com/rust-lang/rust/issues/63178 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "try_from_int_error_kind", + description: r##"# `try_from_int_error_kind` + + + +The tracking issue for this feature is: [#153978] + +[#153978]: https://github.com/rust-lang/rust/issues/153978 + ------------------------ "##, default_severity: Severity::Allow, @@ -15543,22 +15663,6 @@ The tracking issue for this feature is: [#100499] [#100499]: https://github.com/rust-lang/rust/issues/100499 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "uint_bit_width", - description: r##"# `uint_bit_width` - - - -The tracking issue for this feature is: [#142326] - -[#142326]: https://github.com/rust-lang/rust/issues/142326 - ------------------------ "##, default_severity: Severity::Allow, From 0bb544a470dee81e47777525d30d5212734ea476 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 15 Apr 2026 17:41:20 +0300 Subject: [PATCH 68/98] Support non-body expression store for `upvars_mentioned()` --- .../crates/hir-ty/src/infer/coerce.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/upvars.rs | 99 ++++++++++++++++--- 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index f92503003b397..4f62c6d965263 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1718,9 +1718,6 @@ fn coerce<'db>( fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool { let InternedClosure(owner, expr) = closure.loc(db); - let Some(body_owner) = owner.as_def_with_body() else { - return false; - }; - upvars_mentioned(db, body_owner) + upvars_mentioned(db, owner) .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs index 489895fe3cb7d..289f72d888cf7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs @@ -1,8 +1,8 @@ //! A simple query to collect tall locals (upvars) a closure use. use hir_def::{ - DefWithBodyId, - expr_store::{Body, path::Path}, + DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, + expr_store::{ExpressionStore, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, resolver::{HasResolver, Resolver, ValueNs}, }; @@ -36,18 +36,89 @@ impl Upvars { pub fn is_empty(&self) -> bool { self.0.is_empty() } + + #[inline] + pub fn as_ref(&self) -> UpvarsRef<'_> { + UpvarsRef(&self.0) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +// Kept sorted. +pub struct UpvarsRef<'db>(&'db [BindingId]); + +impl UpvarsRef<'_> { + #[inline] + pub fn contains(self, local: BindingId) -> bool { + self.0.binary_search(&local).is_ok() + } + + #[inline] + pub fn iter(self) -> impl ExactSizeIterator { + self.0.iter().copied() + } + + #[inline] + pub fn is_empty(self) -> bool { + self.0.is_empty() + } + + #[inline] + pub const fn empty() -> Self { + UpvarsRef(&[]) + } } /// Returns a map from `Expr::Closure` to its upvars. -#[salsa::tracked(returns(as_deref))] pub fn upvars_mentioned( db: &dyn HirDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, +) -> Option<&FxHashMap> { + return match owner { + ExpressionStoreOwnerId::Signature(owner) => signature_upvars_mentioned(db, owner), + ExpressionStoreOwnerId::Body(owner) => body_upvars_mentioned(db, owner), + ExpressionStoreOwnerId::VariantFields(owner) => variant_fields_upvars_mentioned(db, owner), + }; + + #[salsa::tracked(returns(as_deref))] + pub fn signature_upvars_mentioned( + db: &dyn HirDatabase, + owner: GenericDefId, + ) -> Option>> { + upvars_mentioned_impl(db, owner.into()) + } + + #[salsa::tracked(returns(as_deref))] + pub fn body_upvars_mentioned( + db: &dyn HirDatabase, + owner: DefWithBodyId, + ) -> Option>> { + upvars_mentioned_impl(db, owner.into()) + } + + #[salsa::tracked(returns(as_deref))] + pub fn variant_fields_upvars_mentioned( + db: &dyn HirDatabase, + owner: VariantId, + ) -> Option>> { + upvars_mentioned_impl(db, owner.into()) + } +} + +pub fn upvars_mentioned_impl( + db: &dyn HirDatabase, + owner: ExpressionStoreOwnerId, ) -> Option>> { - let body = Body::of(db, owner); + let store = ExpressionStore::of(db, owner); + if store.const_expr_origins().is_empty() { + // Save constructing a Resolver. + return None; + } let mut resolver = owner.resolver(db); let mut result = FxHashMap::default(); - handle_expr_outside_closure(db, &mut resolver, owner, body, body.root_expr(), &mut result); + for root_expr in store.expr_roots() { + handle_expr_outside_closure(db, &mut resolver, owner, store, root_expr, &mut result); + } return if result.is_empty() { None } else { @@ -58,8 +129,8 @@ pub fn upvars_mentioned( fn handle_expr_outside_closure<'db>( db: &'db dyn HirDatabase, resolver: &mut Resolver<'db>, - owner: DefWithBodyId, - body: &Body, + owner: ExpressionStoreOwnerId, + body: &ExpressionStore, expr: ExprId, closures_map: &mut FxHashMap, ) { @@ -89,8 +160,8 @@ pub fn upvars_mentioned( fn handle_expr_inside_closure<'db>( db: &'db dyn HirDatabase, resolver: &mut Resolver<'db>, - owner: DefWithBodyId, - body: &Body, + owner: ExpressionStoreOwnerId, + body: &ExpressionStore, current_closure: ExprId, expr: ExprId, upvars: &mut FxHashSet, @@ -170,8 +241,8 @@ pub fn upvars_mentioned( fn resolve_maybe_upvar<'db>( db: &'db dyn HirDatabase, resolver: &mut Resolver<'db>, - owner: DefWithBodyId, - body: &Body, + owner: ExpressionStoreOwnerId, + body: &ExpressionStore, current_closure: ExprId, expr: ExprId, id: ExprOrPatId, @@ -198,7 +269,7 @@ fn resolve_maybe_upvar<'db>( #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use hir_def::{ModuleDefId, expr_store::Body, nameres::crate_def_map}; + use hir_def::{DefWithBodyId, ModuleDefId, expr_store::Body, nameres::crate_def_map}; use itertools::Itertools; use span::Edition; use test_fixture::WithFixture; @@ -220,7 +291,7 @@ mod tests { .exactly_one() .unwrap_or_else(|_| panic!("expected one function")); let (body, source_map) = Body::with_source_map(&db, func.into()); - let Some(upvars) = upvars_mentioned(&db, func.into()) else { + let Some(upvars) = upvars_mentioned(&db, DefWithBodyId::from(func).into()) else { expectation.assert_eq(""); return; }; From 7c732dbd9808f819a7a3e930bd02d025390310bf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 15 Apr 2026 17:48:54 +0300 Subject: [PATCH 69/98] Support `self` upvar in `upvars_mentioned()` --- .../rust-analyzer/crates/hir-ty/src/upvars.rs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs index 289f72d888cf7..48f3c803d8322 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs @@ -250,8 +250,9 @@ fn resolve_maybe_upvar<'db>( path: &Path, ) { if let Path::BarePath(mod_path) = path - && matches!(mod_path.kind, PathKind::Plain) - && mod_path.segments().len() == 1 + && matches!(mod_path.kind, PathKind::Plain | PathKind::SELF) + // `self` is length zero. + && mod_path.segments().len() <= 1 { // Could be a variable. let guard = resolver.update_to_inner_scope(db, owner, expr); @@ -269,7 +270,9 @@ fn resolve_maybe_upvar<'db>( #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use hir_def::{DefWithBodyId, ModuleDefId, expr_store::Body, nameres::crate_def_map}; + use hir_def::{ + AssocItemId, DefWithBodyId, ModuleDefId, expr_store::Body, nameres::crate_def_map, + }; use itertools::Itertools; use span::Edition; use test_fixture::WithFixture; @@ -288,6 +291,14 @@ mod tests { ModuleDefId::FunctionId(func) => Some(func), _ => None, }) + .chain(def_map.modules().flat_map(|(_, module)| { + module.scope.impls().flat_map(|impl_| &*impl_.impl_items(&db).items).filter_map( + |&(_, item)| match item { + AssocItemId::FunctionId(it) => Some(it), + _ => None, + }, + ) + })) .exactly_one() .unwrap_or_else(|_| panic!("expected one function")); let (body, source_map) = Body::with_source_map(&db, func.into()); @@ -387,4 +398,19 @@ fn foo() { 49..110: a, b"#]], ); } + + #[test] + fn self_upvar() { + check( + r#" +struct Foo(i32); +impl Foo { + fn foo(&self) { + || self.0; + } +} + "#, + expect!["56..65: self"], + ); + } } From 79963ace70227a5bd4b1f420db65f94a430a5f74 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 4 Mar 2026 05:54:57 +0200 Subject: [PATCH 70/98] Allow crate authors to declare that their trait prefers to be imported `as _` For example for extension traits. Provide an attribute for that. It'll affect flyimport and the autoimport quickfix, as explained in the code. --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 5 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 13 ++ .../ide-assists/src/handlers/auto_import.rs | 144 +++++++++++++----- .../crates/ide-completion/src/item.rs | 23 ++- .../crates/ide-completion/src/lib.rs | 24 +-- .../ide-completion/src/tests/flyimport.rs | 35 +++++ src/tools/rust-analyzer/crates/ide/src/lib.rs | 5 +- .../rust-analyzer/src/handlers/request.rs | 13 +- .../crates/rust-analyzer/src/lsp.rs | 8 +- .../crates/rust-analyzer/src/lsp/ext.rs | 1 + .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +- .../book/src/contributing/lsp-extensions.md | 2 +- 12 files changed, 219 insertions(+), 59 deletions(-) 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 92d4928893865..5cf5a9b6be847 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -258,6 +258,9 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< Some(second_segment) => match &*first_segment { "rust_analyzer" => match &*second_segment { "skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP), + "prefer_underscore_import" => { + attr_flags.insert(AttrFlags::PREFER_UNDERSCORE_IMPORT) + } _ => {} }, _ => {} @@ -330,6 +333,8 @@ bitflags::bitflags! { const MACRO_STYLE_BRACES = 1 << 46; const MACRO_STYLE_BRACKETS = 1 << 47; const MACRO_STYLE_PARENTHESES = 1 << 48; + + const PREFER_UNDERSCORE_IMPORT = 1 << 49; } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 27516ed80b278..cd3391b77132a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3319,6 +3319,19 @@ impl Trait { pub fn complete(self, db: &dyn HirDatabase) -> Complete { Complete::extract(true, self.attrs(db).attrs) } + + // Feature: Prefer Underscore Import Attribute + // Crate authors can declare that their trait prefers to be imported `as _`. This can be used + // for example for extension traits. To do that, a trait has to include the attribute + // `#[rust_analyzer::prefer_underscore_import]` + // + // When a trait includes this attribute, flyimport will import it `as _`, and the quickfix + // to import it will prefer to import it `as _` (but allow to import it normally as well). + // + // Malformed attributes will be ignored without warnings. + pub fn prefer_underscore_import(self, db: &dyn HirDatabase) -> bool { + AttrFlags::query(db, self.id.into()).contains(AttrFlags::PREFER_UNDERSCORE_IMPORT) + } } impl HasVisibility for Trait { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index adeb191719fb7..f9d618790c6e8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -6,7 +6,7 @@ use ide_db::{ active_parameter::ActiveParameter, helpers::mod_path_to_ast, imports::{ - import_assets::{ImportAssets, ImportCandidate, LocatedImport}, + import_assets::{ImportAssets, ImportCandidate, LocatedImport, TraitImportCandidate}, insert_use::{ImportScope, insert_use, insert_use_as_alias}, }, }; @@ -123,44 +123,48 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let (assist_id, import_name) = (AssistId::quick_fix("auto_import"), import_path.display(ctx.db(), edition)); - acc.add_group( - &group_label, - assist_id, - format!("Import `{import_name}`"), - range, - |builder| { + let add_normal_import = |acc: &mut Assists, label| { + acc.add_group(&group_label, assist_id, label, range, |builder| { let scope = builder.make_import_scope_mut(scope.clone()); insert_use(&scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use); - }, - ); - - match import_assets.import_candidate() { - ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) => { - let is_method = - matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_)); - let type_ = if is_method { "method" } else { "item" }; - let group_label = GroupLabel(format!( - "Import a trait for {} {} by alias", - type_, - name.assoc_item_name.text() - )); - acc.add_group( - &group_label, - assist_id, - format!("Import `{import_name} as _`"), - range, - |builder| { - let scope = builder.make_import_scope_mut(scope.clone()); - insert_use_as_alias( - &scope, - mod_path_to_ast(&import_path, edition), - &ctx.config.insert_use, - edition, - ); - }, + }) + }; + let add_underscore_import = |acc: &mut Assists, name: &TraitImportCandidate<'_>, label| { + let is_method = + matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_)); + let type_ = if is_method { "method" } else { "item" }; + let group_label = GroupLabel(format!( + "Import a trait for {} {} by alias", + type_, + name.assoc_item_name.text() + )); + acc.add_group(&group_label, assist_id, label, range, |builder| { + let scope = builder.make_import_scope_mut(scope.clone()); + insert_use_as_alias( + &scope, + mod_path_to_ast(&import_path, edition), + &ctx.config.insert_use, + edition, ); - } - _ => {} + }); + }; + + if let ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) = + import_assets.import_candidate() + { + if let hir::ItemInNs::Types(hir::ModuleDef::Trait(trait_to_import)) = + import.item_to_import + && trait_to_import.prefer_underscore_import(ctx.db()) + { + // Flip the order of the suggestions and show a preference for `as _` in the name. + add_underscore_import(acc, name, format!("Import `{import_name}`")); + add_normal_import(acc, format!("Import `{import_name}` without `as _`")); + } else { + add_normal_import(acc, format!("Import `{import_name}`")); + add_underscore_import(acc, name, format!("Import `{import_name} as _`")); + } + } else { + add_normal_import(acc, format!("Import `{import_name}`")); } } Some(()) @@ -1957,4 +1961,72 @@ fn main() { "#, ); } + + #[test] + fn prefer_underscore_import() { + check_assist_by_label( + auto_import, + r#" +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.b$0ar(); +} + "#, + r#" +use foo::Ext as _; + +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.bar(); +} + "#, + "Import `foo::Ext`", + ); + check_assist_by_label( + auto_import, + r#" +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.b$0ar(); +} + "#, + r#" +use foo::Ext; + +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.bar(); +} + "#, + "Import `foo::Ext` without `as _`", + ); + } } 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 62211a808ca6f..6abf4f632aa8a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -84,7 +84,15 @@ pub struct CompletionItem { pub ref_match: Option<(CompletionItemRefMode, TextSize)>, /// The import data to add to completion's edits. - pub import_to_add: SmallVec<[String; 1]>, + pub import_to_add: SmallVec<[CompletionItemImport; 1]>, +} + +#[derive(Clone, UpmapFromRaFixture)] +pub struct CompletionItemImport { + /// The path to import. + pub path: String, + /// Whether to import `as _`. + pub as_underscore: bool, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -585,7 +593,18 @@ impl Builder { let import_to_add = self .imports_to_add .into_iter() - .map(|import| import.import_path.display(db, self.edition).to_string()) + .map(|import| { + let path = import.import_path.display(db, self.edition).to_string(); + let as_underscore = + if let hir::ItemInNs::Types(hir::ModuleDef::Trait(trait_to_import)) = + import.item_to_import + { + trait_to_import.prefer_underscore_import(db) + } else { + false + }; + CompletionItemImport { path, as_underscore } + }) .collect(); CompletionItem { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 3867e65ae57e4..3df511a5ad0f3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -36,8 +36,8 @@ use crate::{ pub use crate::{ config::{AutoImportExclusionType, CallableSnippets, CompletionConfig}, item::{ - CompletionItem, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, - CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, + CompletionItem, CompletionItemImport, CompletionItemKind, CompletionItemRefMode, + CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, CompletionRelevanceTypeMatch, }, snippet::{Snippet, SnippetScope}, @@ -280,7 +280,7 @@ pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, - imports: impl IntoIterator, + imports: impl IntoIterator, ) -> Option> { let _p = tracing::info_span!("resolve_completion_edits").entered(); let sema = hir::Semantics::new(db); @@ -299,12 +299,18 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - imports.into_iter().for_each(|full_import_path| { - insert_use::insert_use( - &new_ast, - make::path_from_text_with_edition(&full_import_path, current_edition), - &config.insert_use, - ); + imports.into_iter().for_each(|import| { + let full_path = make::path_from_text_with_edition(&import.path, current_edition); + if import.as_underscore { + insert_use::insert_use_as_alias( + &new_ast, + full_path, + &config.insert_use, + current_edition, + ); + } else { + insert_use::insert_use(&new_ast, full_path, &config.insert_use); + } }); diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 5391e6c9ce6e5..60ae077d01426 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -2057,3 +2057,38 @@ fn main() { "#, ); } + +#[test] +fn prefer_underscore_import() { + check_edit( + "bar", + r#" +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.bar$0 +} + "#, + r#" +use foo::Ext as _; + +mod foo { + #[rust_analyzer::prefer_underscore_import] + pub trait Ext { + fn bar(&self) {} + } + impl Ext for T {} +} + +fn baz() { + 1.bar();$0 +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 8a6ef6418063b..442dc6cfbfb2a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -127,7 +127,8 @@ pub use ide_assists::{ }; pub use ide_completion::{ CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem, - CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, SnippetScope, + CompletionItemImport, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, + SnippetScope, }; pub use ide_db::{ FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind, @@ -769,7 +770,7 @@ impl Analysis { &self, config: &CompletionConfig<'_>, position: FilePosition, - imports: impl IntoIterator + std::panic::UnwindSafe, + imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { Ok(self .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))? diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 4193bd25b210f..0c1c067ffa919 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -7,10 +7,10 @@ use anyhow::Context; use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ - AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition, - FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData, - InlayFieldsToResolve, Query, RangeInfo, Runnable, RunnableKind, SingleResolve, SourceChange, - TextEdit, + AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, + CompletionItemImport, FilePosition, FileRange, FileStructureConfig, FindAllRefsConfig, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, Runnable, RunnableKind, + SingleResolve, SourceChange, TextEdit, }; use ide_db::{FxHashMap, SymbolKind}; use itertools::Itertools; @@ -1233,7 +1233,10 @@ pub(crate) fn handle_completion_resolve( .resolve_completion_edits( &forced_resolve_completions_config, position, - resolve_data.imports.into_iter().map(|import| import.full_import_path), + resolve_data.imports.into_iter().map(|import| CompletionItemImport { + path: import.full_import_path, + as_underscore: import.as_underscore, + }), )? .into_iter() .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs index c7a5a95e66bbf..a6a35dadd9919 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs @@ -3,7 +3,7 @@ use core::fmt; use hir::Mutability; -use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance}; +use ide::{CompletionItem, CompletionItemImport, CompletionItemRefMode, CompletionRelevance}; use tenthash::TentHash; pub mod ext; @@ -136,8 +136,10 @@ pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: boo hasher.update(item.import_to_add.len().to_ne_bytes()); for import_path in &item.import_to_add { - hasher.update(import_path.len().to_ne_bytes()); - hasher.update(import_path); + let CompletionItemImport { path, as_underscore } = import_path; + hasher.update(path.len().to_ne_bytes()); + hasher.update(path); + hasher.update([u8::from(*as_underscore)]); } hasher.finalize() diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index e6493eefef17a..5d0d9209de2f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -858,6 +858,7 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, + pub as_underscore: bool, } #[derive(Debug, Deserialize, Default)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 5fa95252e7cbf..d857f23b6703c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -413,7 +413,10 @@ fn completion_item( item.import_to_add .clone() .into_iter() - .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) + .map(|import| lsp_ext::CompletionImport { + full_import_path: import.path, + as_underscore: import.as_underscore, + }) .collect() } else { Vec::new() diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index 22c1784ac293c..28fb979b01b37 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -1,5 +1,5 @@