From dd24fd96a0c87a3d0db1fafc396a357075971ba4 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Tue, 8 Jul 2025 05:09:44 -0700 Subject: [PATCH 001/394] BE: make buck::Target.app_name an AppName Summary: As title. This makes it clearer when we work with a combination of `AppName`s and `TargetFullName`s. Reviewed By: robertoaloi Differential Revision: D77927019 fbshipit-source-id: affb557cda7ffc4be65f9d8cabb65150e05f5032 --- crates/project_model/src/buck.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 90c1f4f74f..1b4e6db5e2 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -300,11 +300,11 @@ pub struct BuckTarget { } impl BuckTarget { - fn name(&self) -> String { + fn name(&self) -> AppName { if let Some(name) = self.app_name.clone() { - name + AppName(name) } else { - self.name.clone() + AppName(self.name.clone()) } } } @@ -313,7 +313,7 @@ impl BuckTarget { pub struct Target { // full-name, like cell//path/to/target/... pub name: TargetFullName, - pub app_name: String, + pub app_name: AppName, pub dir: AbsPathBuf, pub src_files: Vec, pub include_files: Vec, @@ -772,14 +772,18 @@ fn targets_to_project_data_bxl( for target in targets.values() { target.include_files.iter().for_each(|inc: &AbsPathBuf| { let include_path = include_path_from_file(inc); - update_mapping_from_path(&target.app_name, include_path, &mut include_mapping); + update_mapping_from_path(&target.app_name.0, include_path, &mut include_mapping); }); if target.private_header { target.src_files.iter().for_each(|path: &AbsPathBuf| { if Some("hrl") == path.extension() { let include_path = include_path_from_file(path); - update_mapping_from_path(&target.app_name, include_path, &mut include_mapping); + update_mapping_from_path( + &target.app_name.0, + include_path, + &mut include_mapping, + ); } }); } @@ -836,7 +840,7 @@ fn targets_to_project_data_bxl( ), }; let project_app_data = ProjectAppData { - name: AppName(target.app_name.clone()), + name: target.app_name.clone(), dir: target.dir.clone(), ebin: None, extra_src_dirs: extra_src_dirs.into_iter().collect(), From 8f75584a2fcc3eaaa78630293fe5f4e8ff77a564 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Thu, 10 Jul 2025 02:15:03 -0700 Subject: [PATCH 002/394] Fix W0037 unspecific include for buck generated files Summary: Our current `elp.bxl` script returns a full path instead of a directory for the `includes` field for generated files. This breaks the reverse lookup for W0037, so the unspecific_include diagnostic is not reported. Add a check on the ELP side for this, and remove the file name if present. Reviewed By: TD5, robertoaloi Differential Revision: D77792503 fbshipit-source-id: 90d31106caca8828a07e82d82a4a4b93fd4114c4 --- crates/project_model/src/buck.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 1b4e6db5e2..975f836700 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -464,7 +464,10 @@ fn make_buck_target( } let mut include_files = vec![]; for include in &target.includes { - let inc = buck_path_to_abs_path(root, include).unwrap(); + let mut inc = buck_path_to_abs_path(root, include).unwrap(); + if inc.extension().is_some() { + inc.pop(); + } include_files.push(inc); } From d0cffb13998c3bc77c0c8337eca5eb74b6befc36 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Thu, 10 Jul 2025 05:37:15 -0700 Subject: [PATCH 003/394] Add failing test to show undefined macro duplicate actions Summary: Because ELP does not (yet) properly process conditional macros, a macro defined with conditional alternatives will show up multiple times. Add a test to demonstrate this case, which will be fixed in the next diff. Reviewed By: michalmuskala Differential Revision: D78087402 fbshipit-source-id: e450a3d8f1cbd08b3040a5b70f114bafd8227800 --- crates/ide/src/diagnostics/undefined_macro.rs | 36 +++++++++++++++++++ crates/ide/src/tests.rs | 32 +++++++++++------ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/ide/src/diagnostics/undefined_macro.rs b/crates/ide/src/diagnostics/undefined_macro.rs index 3136618da5..83519ac040 100644 --- a/crates/ide/src/diagnostics/undefined_macro.rs +++ b/crates/ide/src/diagnostics/undefined_macro.rs @@ -252,4 +252,40 @@ mod tests { "#]], ); } + + #[test] + #[should_panic( + expected = "Expecting one \"Add required include for 'assertEqual/2' (app_a)\", but multiple found in [(ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_b)\"), (ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_a)\"), (ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_a)\")]" + )] + fn undefined_macro_fix_duplicate_definitions() { + check_specific_fix( + "Add required include for 'assertEqual/2' (app_a)", + r#" + //- erlang_service + //- /main/src/main.erl app:main + -module(main). + + foo(X) -> ?assert~Equal(X,2). + %% ^^^^^^^^^^^^ 💡 error: undefined macro 'assertEqual/2' + + //- /app_a/include/inc.hrl app:app_a include_path:/app_a/include + + -ifdef(NOASSERT). + -define(assertEqual(A,B), ok). + -else. + -define(assertEqual(A,B), A =:= B). + -endif. + + //- /app_b/include/inc.hrl app:app_b include_path:/app_b/include + -define(assertEqual(A,B), A =:= B). + "#, + expect![[r#" + -module(main). + -include_lib("app_a/include/inc.hrl"). + + foo(X) -> ?assertEqual(X,2). + + "#]], + ); + } } diff --git a/crates/ide/src/tests.rs b/crates/ide/src/tests.rs index 467e70d8f9..386b7fa302 100644 --- a/crates/ide/src/tests.rs +++ b/crates/ide/src/tests.rs @@ -307,17 +307,29 @@ pub(crate) fn check_specific_fix_with_config_and_adhoc( let actual = convert_diagnostics_to_annotations(diagnostics.clone()); assert_eq!(expected, actual); - let fix: &Assist = if let Some(label) = assist_label { - if let Some(fix) = diagnostics + let fix: Assist = if let Some(label) = assist_label { + let fixes: Vec<_> = diagnostics .iter() - .filter_map(|d| { - d.fixes - .as_ref() - .and_then(|fixes| fixes.iter().find(|f| f.label == label)) - }) - .next() - { - fix + .flat_map(|d| d.fixes.clone().unwrap_or_default()) + .filter(|f| f.label == label) + .collect(); + if fixes.len() == 1 { + fixes.into_iter().next().unwrap() + } else if fixes.len() > 1 { + panic!( + "Expecting one \"{}\", but multiple found in {:?}", + label, + diagnostics + .iter() + .flat_map(|d| match d.fixes.as_ref() { + None => vec![], + Some(fixes) => fixes + .iter() + .map(|f| (d.code.clone(), f.label.clone())) + .collect_vec(), + }) + .collect_vec() + ); } else { panic!( "Expecting \"{}\", but not found in {:?}", From 6040dcbfe56624cf943ea7caf3af6c35620a016d Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Thu, 10 Jul 2025 05:37:15 -0700 Subject: [PATCH 004/394] fix duplicate undefined macro duplicate actions Summary: As title. Since we currently can have multiple macro definitions, we put candidates in a set to filter them out. Reviewed By: michalmuskala Differential Revision: D78087578 fbshipit-source-id: 084db136f7842185cd840c1d13a00cb75d2e8e00 --- crates/ide/src/diagnostics/undefined_macro.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/diagnostics/undefined_macro.rs b/crates/ide/src/diagnostics/undefined_macro.rs index 83519ac040..b4be2ac306 100644 --- a/crates/ide/src/diagnostics/undefined_macro.rs +++ b/crates/ide/src/diagnostics/undefined_macro.rs @@ -16,6 +16,7 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::path_for_file; use elp_ide_db::source_change::SourceChange; use elp_text_edit::TextEdit; +use fxhash::FxHashSet; use hir::Semantic; use lazy_static::lazy_static; use regex::Regex; @@ -36,7 +37,7 @@ pub(crate) fn add_assist( Some(macro_arity) => format!("{macro_name}/{macro_arity}"), None => macro_name.clone(), }; - let includes: Vec<_> = index + let includes: FxHashSet<_> = index .complete(¯o_name) .iter() .flat_map(|(_chars, defines)| { @@ -254,9 +255,6 @@ mod tests { } #[test] - #[should_panic( - expected = "Expecting one \"Add required include for 'assertEqual/2' (app_a)\", but multiple found in [(ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_b)\"), (ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_a)\"), (ErlangService(\"E1508\"), \"Add required include for 'assertEqual/2' (app_a)\")]" - )] fn undefined_macro_fix_duplicate_definitions() { check_specific_fix( "Add required include for 'assertEqual/2' (app_a)", From e78a8d99b37034a67d1deea80fca76a06460a7e0 Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Fri, 11 Jul 2025 01:35:47 -0700 Subject: [PATCH 005/394] Migrate to Salsa 0.19 Summary: Migrate ELP to Salsa 0.19, featuring a completely new API. Overall, the migration follows closely the one of [rust-analyzer](https://github.com/rust-lang/rust-analyzer/commit/74620e64ec24861a821ebb9e519461042905668f#diff-5828430af308cfce455219d40dac23f272baf89378ada6febee5d6a186ffe0fb) Major changes: - Bump `rust-analyzer` to 2025-03-17 - Use `ra_ap_query-group-macro` to emulate the old `query_group` macro. - `SourceDatabase` is split into two, `RootQueryDb` containing queries inside a `query_group` and `SourceDatabase` which deals with salsa inputs only. - All parameterized inputs (`FileText`, `SourceRoot`, ...) are extracted as structs, and a read/write API is provided according to Salsa 0.19 principles. This API was previously automatically derived by Salsa. - Cycle handling functions signatures are modified. - Create structs for all interned values, also following Salsa 0.19 guidelines. Reviewed By: alanz Differential Revision: D75066592 fbshipit-source-id: 233e626ca3c3c9004241e2064d2bcf7e2d0106ed --- Cargo.lock | 215 +++++------ Cargo.toml | 16 +- crates/base_db/Cargo.toml | 2 + crates/base_db/src/change.rs | 4 +- crates/base_db/src/fixture.rs | 6 +- crates/base_db/src/include.rs | 20 +- crates/base_db/src/input.rs | 4 +- crates/base_db/src/lib.rs | 351 ++++++++++++++---- crates/elp/src/bin/glean.rs | 24 +- crates/elp/src/bin/shell.rs | 1 - crates/elp/src/build/load.rs | 1 - crates/elp/src/server.rs | 2 +- crates/eqwalizer/Cargo.toml | 1 + crates/eqwalizer/src/analyses/mod.rs | 2 +- crates/eqwalizer/src/db.rs | 6 +- crates/eqwalizer/src/lib.rs | 6 +- crates/hir/Cargo.toml | 1 + crates/hir/src/body.rs | 6 +- crates/hir/src/body/scope.rs | 2 +- crates/hir/src/db.rs | 11 +- crates/hir/src/def_map.rs | 9 +- crates/hir/src/edoc.rs | 6 +- crates/hir/src/include.rs | 3 +- crates/hir/src/intern.rs | 47 +-- crates/hir/src/lib.rs | 4 +- crates/hir/src/macro_exp.rs | 11 +- crates/hir/src/module_data.rs | 50 +-- crates/hir/src/sema.rs | 5 +- crates/hir/src/test_db.rs | 94 ++++- crates/ide/src/codemod_helpers.rs | 2 +- crates/ide/src/diagnostics.rs | 2 +- .../eqwalizer_assists/expected_type.rs | 2 +- crates/ide/src/diagnostics/module_mismatch.rs | 4 +- crates/ide/src/fixture.rs | 2 +- crates/ide/src/lib.rs | 12 +- crates/ide/src/rename.rs | 2 +- crates/ide/src/tests.rs | 20 +- crates/ide_assists/src/assist_context.rs | 2 +- crates/ide_assists/src/tests.rs | 26 +- crates/ide_completion/src/ctx.rs | 2 +- crates/ide_completion/src/helpers.rs | 6 +- crates/ide_completion/src/macros.rs | 4 +- crates/ide_db/Cargo.toml | 1 + crates/ide_db/src/common_test.rs | 8 +- crates/ide_db/src/docs.rs | 19 +- crates/ide_db/src/eqwalizer.rs | 26 +- crates/ide_db/src/erl_ast.rs | 24 +- crates/ide_db/src/lib.rs | 156 +++++--- crates/ide_db/src/search.rs | 5 +- crates/ide_ssr/src/lib.rs | 10 +- crates/ide_ssr/src/tests.rs | 10 +- 51 files changed, 794 insertions(+), 461 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a03c78f3cc..d19a4aeafc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,12 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "boxcar" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bb12751a83493ef4b8da1120451a262554e216a247f14b48cb5e8fe7ed8bdf" + [[package]] name = "bpaf" version = "0.7.9" @@ -301,6 +307,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -348,6 +363,20 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "deranged" version = "0.4.0" @@ -456,7 +485,7 @@ dependencies = [ "fxhash", "hir", "include_dir", - "indexmap 2.9.0", + "indexmap", "indicatif", "itertools 0.10.5", "jod-thread", @@ -464,7 +493,7 @@ dependencies = [ "log", "lsp-server", "lsp-types", - "parking_lot 0.12.4", + "parking_lot", "paths", "profile", "range-set", @@ -494,6 +523,7 @@ name = "elp_base_db" version = "1.1.0" dependencies = [ "anyhow", + "dashmap", "dissimilar", "eetf", "either", @@ -505,6 +535,7 @@ dependencies = [ "log", "paths", "profile", + "ra_ap_query-group-macro", "regex", "salsa", "serde_json", @@ -525,7 +556,8 @@ dependencies = [ "fxhash", "itertools 0.10.5", "log", - "parking_lot 0.12.4", + "parking_lot", + "ra_ap_query-group-macro", "salsa", "serde", "serde_json", @@ -552,7 +584,7 @@ dependencies = [ "jod-thread", "lazy_static", "log", - "parking_lot 0.12.4", + "parking_lot", "regex", "stdx", "tempfile", @@ -582,7 +614,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "parking_lot 0.12.4", + "parking_lot", "profile", "rayon", "regex", @@ -649,14 +681,15 @@ dependencies = [ "expect-test", "fxhash", "hir", - "indexmap 2.9.0", + "indexmap", "lazy_static", "log", "memchr", "once_cell", - "parking_lot 0.12.4", + "parking_lot", "paths", "profile", + "ra_ap_query-group-macro", "regex", "rustc-hash 1.1.0", "serde", @@ -693,7 +726,7 @@ dependencies = [ "humantime", "lazy_static", "log", - "parking_lot 0.12.4", + "parking_lot", "regex", "serde", "serde_json", @@ -713,11 +746,11 @@ dependencies = [ "fxhash", "glob", "include_dir", - "indexmap 2.9.0", + "indexmap", "itertools 0.10.5", "lazy_static", "log", - "parking_lot 0.12.4", + "parking_lot", "paths", "regex", "semver", @@ -740,7 +773,7 @@ dependencies = [ "elp_text_edit", "expect-test", "fxhash", - "indexmap 2.9.0", + "indexmap", "itertools 0.10.5", "lazy_static", "log", @@ -975,12 +1008,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.5" @@ -997,16 +1024,18 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", ] [[package]] -name = "heck" -version = "0.3.3" +name = "hashlink" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "unicode-segmentation", + "hashbrown 0.15.3", ] [[package]] @@ -1015,6 +1044,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.5.1" @@ -1037,6 +1072,7 @@ dependencies = [ "lazy_static", "log", "profile", + "ra_ap_query-group-macro", "regex", "stdx", "tracing", @@ -1202,16 +1238,6 @@ dependencies = [ "quote", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.9.0" @@ -1220,6 +1246,7 @@ checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.3", + "serde", ] [[package]] @@ -1256,15 +1283,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1370,7 +1388,7 @@ dependencies = [ [[package]] name = "la-arena" version = "0.3.1" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" [[package]] name = "lazy_static" @@ -1416,7 +1434,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall", ] [[package]] @@ -1684,12 +1702,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - [[package]] name = "option-ext" version = "0.2.0" @@ -1705,17 +1717,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.4" @@ -1723,21 +1724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -1748,7 +1735,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -1762,7 +1749,7 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "paths" version = "0.0.0" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" dependencies = [ "camino", ] @@ -1799,7 +1786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.9.0", + "indexmap", ] [[package]] @@ -1874,7 +1861,7 @@ dependencies = [ [[package]] name = "profile" version = "0.0.0" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" dependencies = [ "cfg-if", "libc", @@ -1898,6 +1885,19 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "ra_ap_query-group-macro" +version = "0.0.270" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1a38f07b442e47a234cbe2e8fd1b8a41ff0cc5123cb1cf994c5ce20edb5bd6" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "salsa", + "syn 2.0.101", +] + [[package]] name = "radix_trie" version = "0.2.1" @@ -1938,15 +1938,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.12" @@ -2089,31 +2080,43 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.17.0-pre.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b223dccb46c32753144d0b51290da7230bb4aedcd8379d6b4c9a474c18bf17a" +checksum = "dd55c6549513b2a42884dae31e3d4f4ac8a6cc51062e68e24d162133889f327c" dependencies = [ - "crossbeam-utils", - "indexmap 1.9.3", - "lock_api", - "log", - "oorandom", - "parking_lot 0.11.2", - "rustc-hash 1.1.0", + "boxcar", + "crossbeam-queue", + "dashmap", + "hashbrown 0.15.3", + "hashlink", + "indexmap", + "parking_lot", + "portable-atomic", + "rayon", + "rustc-hash 2.1.1", + "salsa-macro-rules", "salsa-macros", "smallvec", + "tracing", ] [[package]] -name = "salsa-macros" -version = "0.17.0-pre.2" +name = "salsa-macro-rules" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6c2e352df550bf019da7b16164ed2f7fa107c39653d1311d1bba42d1582ff7" +checksum = "2619b4b451beab0a7e4364ff1e6f31950e7e418888fd9bf2f28889671563166a" + +[[package]] +name = "salsa-macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be57a99b3896e8d26850428a6874fb86849e2db874e1db3528e5cee4337d277" dependencies = [ - "heck 0.3.3", + "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", + "synstructure", ] [[package]] @@ -2245,7 +2248,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stdx" version = "0.0.0" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" dependencies = [ "crossbeam-channel", "itertools 0.12.1", @@ -2607,7 +2610,7 @@ dependencies = [ "ahash", "byteorder", "lazy_static", - "parking_lot 0.12.4", + "parking_lot", "serde", ] @@ -2632,11 +2635,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vfs" version = "0.0.0" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" dependencies = [ "crossbeam-channel", "fst", - "indexmap 2.9.0", + "indexmap", "nohash-hasher", "paths", "rustc-hash 2.1.1", @@ -2647,7 +2650,7 @@ dependencies = [ [[package]] name = "vfs-notify" version = "0.0.0" -source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-04#02862f5d52c30b476a5dca909a17aa4386d1fdc5" +source = "git+https://github.com/rust-lang/rust-analyzer?rev=2025-03-17#b0632f749e6abf0f82f71755d7eaca4884c1a808" dependencies = [ "crossbeam-channel", "notify", diff --git a/Cargo.toml b/Cargo.toml index 7ec49a5f83..533310e950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ codespan-reporting = "0.11.1" cov-mark = "2.0.0" criterion = "0.3.6" crossbeam-channel = "0.5.15" +dashmap = "6.1.0" dirs = "5.0" dissimilar = "1.0.9" triple_accel = "0.4.0" @@ -69,7 +70,7 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl" } jemallocator = { version = "0.5.4", package = "tikv-jemallocator" } jod-thread = "0.1.2" krates = "0.12.6" -la-arena = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } +la-arena = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } lazy_static = "1.5.0" log = "0.4.22" lsp-server = "0.7.6" @@ -80,10 +81,11 @@ num-derive = "0.4.2" num-traits = "0.2.19" once_cell = "1.19.0" parking_lot = "0.12.3" -paths = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } +paths = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } proc-macro2 = "1.0.86" -profile = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } +profile = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } quote = "1.0.36" +ra_ap_query-group-macro = "0.0.270" range-set = "0.0.10" rayon = "1.10.0" regex = "1.10.6" @@ -91,7 +93,7 @@ rowan = "0.15.15" rust-ini = "0.18" rustc-hash = "1.1.0" rustyline = "11.0.0" -salsa = "0.17.0-pre.2" +salsa = "0.19.0" semver = "1.0.23" serde = { version = "1.0.206", features = ["derive", "rc"] } serde_json = "1.0.124" @@ -99,7 +101,7 @@ serde_path_to_error = "0.1.16" serde_with = "1.14.0" smallvec = { version = "1.13.2", features = ["const_new", "union", "const_generics"] } smol_str = "0.1.24" -stdx = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } +stdx = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } strsim = { version = "0.10.0" } strum = "0.25.0" strum_macros = "0.25.3" @@ -116,7 +118,7 @@ tree-sitter = "0.23.2" tree-sitter-erlang = "0.14.0" # @oss-only url = "2.5.4" ustr = { version = "1.1.0", features = ["serde"] } -vfs = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } -vfs-notify = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-04" } +vfs = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } +vfs-notify = { git = "https://github.com/rust-lang/rust-analyzer", rev = "2025-03-17" } walkdir = "2.5.0" xshell = "0.2.6" diff --git a/crates/base_db/Cargo.toml b/crates/base_db/Cargo.toml index a8c0c0e834..07f3cbb81a 100644 --- a/crates/base_db/Cargo.toml +++ b/crates/base_db/Cargo.toml @@ -14,6 +14,7 @@ elp_project_model.workspace = true elp_syntax.workspace = true anyhow.workspace = true +dashmap.workspace = true dissimilar.workspace = true eetf.workspace = true either.workspace = true @@ -22,6 +23,7 @@ lazy_static.workspace = true log.workspace = true paths.workspace = true profile.workspace = true +ra_ap_query-group-macro.workspace = true regex.workspace = true salsa.workspace = true serde_json.workspace = true diff --git a/crates/base_db/src/change.rs b/crates/base_db/src/change.rs index 48758f9e17..6bb412a60d 100644 --- a/crates/base_db/src/change.rs +++ b/crates/base_db/src/change.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use vfs::AbsPathBuf; use vfs::FileId; -use crate::SourceDatabaseExt; +use crate::RootQueryDb; use crate::SourceRoot; use crate::SourceRootId; use crate::input::AppStructure; @@ -65,7 +65,7 @@ impl Change { pub fn apply( self, - db: &mut dyn SourceDatabaseExt, + db: &mut dyn RootQueryDb, resolve_file_id: &impl Fn(&AbsPathBuf) -> Option, ) -> Vec { let _p = tracing::info_span!("RootDatabase::apply_change").entered(); diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 7464f4f793..9e56edef89 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -47,12 +47,12 @@ use crate::FilePosition; use crate::FileRange; use crate::ProjectApps; use crate::ProjectId; -use crate::SourceDatabaseExt; +use crate::SourceDatabase; use crate::SourceRoot; use crate::change::Change; use crate::input::IncludeOtp; -pub trait WithFixture: Default + SourceDatabaseExt + 'static { +pub trait WithFixture: Default + SourceDatabase + 'static { #[track_caller] fn with_single_file(fixture: &str) -> (Self, FileId) { let (db, fixture) = Self::with_fixture(fixture); @@ -91,7 +91,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { } } -impl WithFixture for DB {} +impl WithFixture for DB {} #[derive(Clone, Debug)] pub struct ChangeFixture { diff --git a/crates/base_db/src/include.rs b/crates/base_db/src/include.rs index adad605cbf..c561a6abb7 100644 --- a/crates/base_db/src/include.rs +++ b/crates/base_db/src/include.rs @@ -16,21 +16,21 @@ use vfs::VfsPath; use crate::AppData; use crate::ProjectId; -use crate::SourceDatabase; +use crate::RootQueryDb; use crate::SourceRoot; pub struct IncludeCtx<'a> { - db: &'a dyn SourceDatabase, + db: &'a dyn RootQueryDb, source_root: Arc, pub file_id: FileId, } impl<'a> IncludeCtx<'a> { - pub fn new(db: &'a dyn SourceDatabase, file_id: FileId) -> Self { + pub fn new(db: &'a dyn RootQueryDb, file_id: FileId) -> Self { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nIncludeCtx::new: {:?}", file_id)); - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); + let source_root_id = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); Self { db, file_id, @@ -58,7 +58,7 @@ impl<'a> IncludeCtx<'a> { /// Called via salsa for inserting in the graph pub(crate) fn resolve_local_query( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, file_id: FileId, path: SmolStr, ) -> Option { @@ -77,12 +77,12 @@ impl<'a> IncludeCtx<'a> { /// Called via salsa for inserting in the graph pub(crate) fn resolve_remote_query( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, file_id: FileId, path: SmolStr, ) -> Option { let project_id = db.file_project_id(file_id)?; - let project_data = db.project_data(project_id); + let project_data = db.project_data(project_id).project_data(db); let include = if let Some(include_mapping) = &project_data.include_mapping { include_mapping .get(&path) @@ -104,7 +104,7 @@ impl<'a> IncludeCtx<'a> { } fn find_generated_include_lib( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, project_id: ProjectId, include_path: &str, target_app_data: &AppData, @@ -132,7 +132,7 @@ fn find_generated_include_lib( } pub fn generated_file_include_lib( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, file_id: FileId, included_file_id: FileId, include_path: VfsPath, diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index f86dae5f61..ef672ff5af 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs @@ -29,7 +29,7 @@ use vfs::VfsPath; use vfs::file_set::FileSet; use crate::AppDataIndex; -use crate::SourceDatabaseExt; +use crate::RootQueryDb; /// Files are grouped into source roots. A source root is a directory on the /// file systems which is watched for changes. Typically it corresponds to an OTP @@ -204,7 +204,7 @@ impl AppStructure { /// Set the salsa inputs according to this AppStructure pub fn apply( self, - db: &mut dyn SourceDatabaseExt, + db: &mut dyn RootQueryDb, resolve_file_id: &impl Fn(&AbsPathBuf) -> Option, ) -> FxHashMap { let mut app_index = AppDataIndex::default(); diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 3ec2f75408..7999ce6674 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -9,8 +9,11 @@ */ use std::borrow::Cow; +use std::hash::BuildHasherDefault; use std::sync::Arc; +use dashmap::DashMap; +use dashmap::Entry; use elp_project_model::AppName; use elp_project_model::buck::IncludeMapping; use elp_syntax::AstNode; @@ -20,6 +23,7 @@ use elp_syntax::TextRange; use elp_syntax::TextSize; use elp_syntax::ast::SourceFile; use fxhash::FxHashMap; +use fxhash::FxHasher; use lazy_static::lazy_static; mod change; @@ -61,6 +65,7 @@ pub use paths::RelPath; pub use paths::RelPathBuf; use regex::Regex; pub use salsa; +use salsa::Setter; pub use vfs::AnchoredPath; pub use vfs::AnchoredPathBuf; pub use vfs::ChangeKind; @@ -143,35 +148,213 @@ impl FileKind { } } -pub trait FileLoader { - /// Text of the file. - fn file_text(&self, file_id: FileId) -> Arc; +#[derive(Debug, Default, Clone)] +pub struct Files { + files: Arc>>, + source_roots: Arc>>, + file_source_roots: Arc>>, + app_datas: Arc>>, + source_app_datas: Arc>>, + project_datas: Arc>>, +} + +impl Files { + pub fn file_text(&self, file_id: FileId) -> FileText { + *self + .files + .get(&file_id) + .expect("Unable to fetch file; this is a bug") + } + + pub fn set_file_text(&self, db: &mut dyn SourceDatabase, file_id: FileId, text: Arc) { + let files = Arc::clone(&self.files); + match files.entry(file_id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_text(db).to(text); + } + Entry::Vacant(vacant) => { + let text = FileText::builder(text).new(db); + vacant.insert(text); + } + }; + } + + pub fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput { + let source_root = self + .source_roots + .get(&source_root_id) + .expect("Unable to fetch source root id; this is a bug"); + + *source_root + } + + pub fn set_source_root( + &self, + db: &mut dyn SourceDatabase, + source_root_id: SourceRootId, + source_root: Arc, + ) { + let source_roots = Arc::clone(&self.source_roots); + match source_roots.entry(source_root_id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_source_root(db).to(source_root); + } + Entry::Vacant(vacant) => { + let source_root = SourceRootInput::builder(source_root).new(db); + vacant.insert(source_root); + } + }; + } + + pub fn file_source_root(&self, id: FileId) -> FileSourceRootInput { + let file_source_root = self + .file_source_roots + .get(&id) + .expect("Unable to fetch FileSourceRootInput; this is a bug"); + *file_source_root + } + + pub fn set_file_source_root( + &self, + db: &mut dyn SourceDatabase, + id: FileId, + source_root_id: SourceRootId, + ) { + let file_source_roots = Arc::clone(&self.file_source_roots); + match file_source_roots.entry(id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_source_root_id(db).to(source_root_id); + } + Entry::Vacant(vacant) => { + let file_source_root = FileSourceRootInput::builder(source_root_id).new(db); + vacant.insert(file_source_root); + } + }; + } + + pub fn app_data(&self, app_data_id: AppDataId) -> AppDataInput { + let app_data = self + .app_datas + .get(&app_data_id) + .expect("Unable to fetch app data id; this is a bug"); + + *app_data + } + + pub fn set_app_data( + &self, + db: &mut dyn SourceDatabase, + app_data_id: AppDataId, + app_data: Option>, + ) { + let app_datas = Arc::clone(&self.app_datas); + match app_datas.entry(app_data_id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_app_data(db).to(app_data); + } + Entry::Vacant(vacant) => { + let app_data = AppDataInput::builder(app_data).new(db); + vacant.insert(app_data); + } + }; + } + + pub fn source_app_data(&self, source_root_id: SourceRootId) -> SourceAppDataInput { + let source_app_data = self + .source_app_datas + .get(&source_root_id) + .expect("Unable to fetch source app data; this is a bug"); + + *source_app_data + } + + pub fn set_source_app_data( + &self, + db: &mut dyn SourceDatabase, + source_root_id: SourceRootId, + app_data_id: AppDataId, + ) { + let source_app_datas = Arc::clone(&self.source_app_datas); + match source_app_datas.entry(source_root_id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_app_data_id(db).to(app_data_id); + } + Entry::Vacant(vacant) => { + let source_app_data = SourceAppDataInput::builder(app_data_id).new(db); + vacant.insert(source_app_data); + } + }; + } + + pub fn project_data(&self, project_id: ProjectId) -> ProjectDataInput { + let project_data = self + .project_datas + .get(&project_id) + .expect("Unable to fetch project data; this is a bug"); + + *project_data + } + + pub fn set_project_data( + &self, + db: &mut dyn SourceDatabase, + project_id: ProjectId, + project_data: Arc, + ) { + let project_datas = Arc::clone(&self.project_datas); + match project_datas.entry(project_id) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().set_project_data(db).to(project_data); + } + Entry::Vacant(vacant) => { + let project_data = ProjectDataInput::builder(project_data).new(db); + vacant.insert(project_data); + } + }; + } +} + +#[salsa::input] +pub struct FileText { + pub text: Arc, +} + +#[salsa::input] +pub struct FileSourceRootInput { + pub source_root_id: SourceRootId, +} + +#[salsa::input] +pub struct SourceRootInput { + pub source_root: Arc, +} + +#[salsa::input] +pub struct AppDataInput { + pub app_data: Option>, +} + +#[salsa::input] +pub struct SourceAppDataInput { + pub app_data_id: AppDataId, +} + +#[salsa::input] +pub struct ProjectDataInput { + pub project_data: Arc, } /// Database which stores all significant input facts: source code and project /// model. Everything else in ELP is derived from these queries. -#[salsa::query_group(SourceDatabaseStorage)] -pub trait SourceDatabase: FileLoader + salsa::Database { - /// Path to a file, relative to the root of its source root. - /// Source root of the file. - #[salsa::input] - fn file_source_root(&self, file_id: FileId) -> SourceRootId; - +#[ra_ap_query_group_macro::query_group] +pub trait RootQueryDb: SourceDatabase + salsa::Database { #[salsa::input] fn catch_all_source_root(&self) -> SourceRootId; - /// Contents of the source root. - #[salsa::input] - fn source_root(&self, id: SourceRootId) -> Arc; - /// The data for a given application. We can access this either /// from the `FileId` or by `SourceRootId`, so introduce an /// intermediate `AppDataId` to map from the two sources. fn app_data(&self, id: SourceRootId) -> Option>; - #[salsa::input] - fn app_data_by_id(&self, id: AppDataId) -> Option>; - #[salsa::input] - fn app_data_id(&self, id: SourceRootId) -> AppDataId; fn app_data_id_by_file(&self, id: FileId) -> Option; @@ -179,9 +362,6 @@ pub trait SourceDatabase: FileLoader + salsa::Database { fn mapped_include_file(&self, project_id: ProjectId, path: SmolStr) -> Option; - #[salsa::input] - fn project_data(&self, id: ProjectId) -> Arc; - fn file_app_data(&self, file_id: FileId) -> Option>; /// Returns a map from module name to FileId of the containing file. @@ -227,13 +407,43 @@ pub trait SourceDatabase: FileLoader + salsa::Database { fn resolve_remote(&self, file_id: FileId, path: SmolStr) -> Option; } -fn app_data(db: &dyn SourceDatabase, id: SourceRootId) -> Option> { - db.app_data_by_id(db.app_data_id(id)) +#[salsa::db] +pub trait SourceDatabase: salsa::Database { + fn file_text(&self, file_id: FileId) -> FileText; + + fn set_file_text(&mut self, file_id: FileId, text: Arc); + + /// Contents of the source root. + fn source_root(&self, id: SourceRootId) -> SourceRootInput; + + fn set_source_root(&mut self, source_root_id: SourceRootId, source_root: Arc); + + /// Source root of the file. + fn file_source_root(&self, id: FileId) -> FileSourceRootInput; + + fn set_file_source_root(&mut self, id: FileId, source_root_id: SourceRootId); + + fn app_data_by_id(&self, id: AppDataId) -> AppDataInput; + + fn set_app_data_by_id(&mut self, id: AppDataId, app_data: Option>); + + fn app_data_id(&self, id: SourceRootId) -> SourceAppDataInput; + + fn set_app_data_id(&mut self, id: SourceRootId, app_data_id: AppDataId); + + fn project_data(&self, id: ProjectId) -> ProjectDataInput; + + fn set_project_data(&mut self, id: ProjectId, project_data: Arc); } -fn file_app_data(db: &dyn SourceDatabase, file_id: FileId) -> Option> { +fn app_data(db: &dyn RootQueryDb, id: SourceRootId) -> Option> { + db.app_data_by_id(db.app_data_id(id).app_data_id(db)) + .app_data(db) +} + +fn file_app_data(db: &dyn RootQueryDb, file_id: FileId) -> Option> { let lookup = if let Some(id) = db.app_data_id_by_file(file_id) { - db.app_data_by_id(id) + db.app_data_by_id(id).app_data(db) } else { None }; @@ -242,16 +452,16 @@ fn file_app_data(db: &dyn SourceDatabase, file_id: FileId) -> Option Arc { +fn module_index(db: &dyn RootQueryDb, project_id: ProjectId) -> Arc { let mut builder = ModuleIndex::builder(); - let project_data = db.project_data(project_id); + let project_data = db.project_data(project_id).project_data(db); for &source_root_id in &project_data.source_roots { - let source_root = db.source_root(source_root_id); + let source_root = db.source_root(source_root_id).source_root(db); for file_id in source_root.iter() { if db.file_kind(file_id).is_module() { if let Some(app_data) = db.file_app_data(file_id) { @@ -304,9 +514,9 @@ impl IncludeFileIndex { } } -fn include_file_index(db: &dyn SourceDatabase, project_id: ProjectId) -> Arc { +fn include_file_index(db: &dyn RootQueryDb, project_id: ProjectId) -> Arc { let mut include_file_index = IncludeFileIndex::default(); - let project_data = db.project_data(project_id); + let project_data = db.project_data(project_id).project_data(db); build_include_file_index(db, &project_data, &mut include_file_index); project_data .otp_project_id @@ -315,7 +525,7 @@ fn include_file_index(db: &dyn SourceDatabase, project_id: ProjectId) -> Arc Arc, include_file_index: &mut IncludeFileIndex, ) { for &source_root_id in &project_data.source_roots { - let source_root = db.source_root(source_root_id); + let source_root = db.source_root(source_root_id).source_root(db); for file_id in source_root.iter() { if let Some(path) = source_root.path_for_file(&file_id) { if let Some((_name, Some("hrl"))) = path.name_and_extension() { @@ -345,17 +555,13 @@ fn build_include_file_index( } } -fn include_file_id( - db: &dyn SourceDatabase, - project_id: ProjectId, - path: VfsPath, -) -> Option { +fn include_file_id(db: &dyn RootQueryDb, project_id: ProjectId, path: VfsPath) -> Option { let include_file_index = db.include_file_index(project_id); include_file_index.path_to_file_id.get(&path).copied() } fn mapped_include_file( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, project_id: ProjectId, path: SmolStr, ) -> Option { @@ -372,12 +578,12 @@ pub struct AppDataIndex { pub map: FxHashMap, } -fn app_data_id_by_file(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn app_data_id_by_file(db: &dyn RootQueryDb, file_id: FileId) -> Option { let app_data_index = db.app_index(); app_data_index.map.get(&file_id).copied() } -pub fn set_app_data_id_by_file(db: &mut dyn SourceDatabase, id: FileId, app_data_id: AppDataId) { +pub fn set_app_data_id_by_file(db: &mut dyn RootQueryDb, id: FileId, app_data_id: AppDataId) { let mut app_data_index: Arc = db.app_index(); Arc::make_mut(&mut app_data_index) .map @@ -385,18 +591,18 @@ pub fn set_app_data_id_by_file(db: &mut dyn SourceDatabase, id: FileId, app_data db.set_app_index(app_data_index); } -fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse { - let text = db.file_text(file_id); +fn parse(db: &dyn RootQueryDb, file_id: FileId) -> Parse { + let text = db.file_text(file_id).text(db); SourceFile::parse_text(&text) } -pub fn path_for_file(db: &dyn SourceDatabase, file_id: FileId) -> Option { - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); +pub fn path_for_file(db: &dyn RootQueryDb, file_id: FileId) -> Option { + let source_root_id = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); source_root.path_for_file(&file_id).cloned() } -fn is_generated(db: &dyn SourceDatabase, file_id: FileId) -> bool { +fn is_generated(db: &dyn RootQueryDb, file_id: FileId) -> bool { lazy_static! { // We operate a byte level via a regex (as opposed to use .contains) // to avoid issues with UTF8 character boundaries. @@ -404,27 +610,27 @@ fn is_generated(db: &dyn SourceDatabase, file_id: FileId) -> bool { // The format macro is used to avoid marking the whole file as generated static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(&format!("{}generated", "@")).unwrap(); } - let contents = db.file_text(file_id); + let contents = db.file_text(file_id).text(db); RE.is_match(&contents.as_bytes()[0..(2001.min(contents.len()))]) } -fn is_erlang_config_file(db: &dyn SourceDatabase, file_id: FileId) -> bool { +fn is_erlang_config_file(db: &dyn RootQueryDb, file_id: FileId) -> bool { let parse = db.parse(file_id); parse.tree().is_erlang_config_file() } -fn is_otp(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn is_otp(db: &dyn RootQueryDb, file_id: FileId) -> Option { let app_data = db.file_app_data(file_id)?; let project_id = app_data.project_id; - Some(db.project_data(project_id).otp_project_id == Some(project_id)) + Some(db.project_data(project_id).project_data(db).otp_project_id == Some(project_id)) } -fn is_test_suite_or_test_helper(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn is_test_suite_or_test_helper(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nis_test_suite_or_test_helper: {:?}", file_id)); let app_data = db.file_app_data(file_id)?; - let root_id = db.file_source_root(file_id); - let root = db.source_root(root_id); + let root_id = db.file_source_root(file_id).source_root_id(db); + let root = db.source_root(root_id).source_root(db); let path = root.path_for_file(&file_id)?; if app_data.is_extra_src_file(path) { Some(true) @@ -433,28 +639,28 @@ fn is_test_suite_or_test_helper(db: &dyn SourceDatabase, file_id: FileId) -> Opt } } -fn file_app_type(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn file_app_type(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nfile_app_type: {:?}", file_id)); let app_data = db.file_app_data(file_id)?; Some(app_data.app_type) } -fn file_app_name(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn file_app_name(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nfile_app_name: {:?}", file_id)); let app_data = db.file_app_data(file_id)?; Some(app_data.name.clone()) } -fn file_project_id(db: &dyn SourceDatabase, file_id: FileId) -> Option { +fn file_project_id(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nfile_project_id: {:?}", file_id)); let app_data = db.file_app_data(file_id)?; Some(app_data.project_id) } -pub fn module_name(db: &dyn SourceDatabase, file_id: FileId) -> Option { +pub fn module_name(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nmodule_name: {:?}", file_id)); let app_data = db.file_app_data(file_id)?; @@ -473,11 +679,11 @@ static ref IGNORED_SOURCES: Vec = { }; } -fn file_kind(db: &dyn SourceDatabase, file_id: FileId) -> FileKind { +fn file_kind(db: &dyn RootQueryDb, file_id: FileId) -> FileKind { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nfile_kind: {:?}", file_id)); - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); + let source_root_id = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); let ignored_path = source_root .path_for_file(&file_id) .and_then(|path| path.as_path()) @@ -512,7 +718,7 @@ fn file_kind(db: &dyn SourceDatabase, file_id: FileId) -> FileKind { } /// When we get a range from the client, limit it to what is in the source file -fn clamp_range(db: &dyn SourceDatabase, file_id: FileId, range: TextRange) -> TextRange { +fn clamp_range(db: &dyn RootQueryDb, file_id: FileId, range: TextRange) -> TextRange { let source_file = db.parse(file_id).tree(); let file_range = source_file.syntax().text_range(); let start = file_range.start(); @@ -523,7 +729,7 @@ fn clamp_range(db: &dyn SourceDatabase, file_id: FileId, range: TextRange) -> Te ) } -fn clamp_offset(db: &dyn SourceDatabase, file_id: FileId, offset: TextSize) -> TextSize { +fn clamp_offset(db: &dyn RootQueryDb, file_id: FileId, offset: TextSize) -> TextSize { let source_file = db.parse(file_id).tree(); let file_range = source_file.syntax().text_range(); let start = file_range.start(); @@ -531,23 +737,6 @@ fn clamp_offset(db: &dyn SourceDatabase, file_id: FileId, offset: TextSize) -> T offset.clamp(start, end) } -/// We don't want to give HIR knowledge of source roots, hence we extract these -/// methods into a separate DB. -#[salsa::query_group(SourceDatabaseExtStorage)] -pub trait SourceDatabaseExt: SourceDatabase { - #[salsa::input] - fn file_text(&self, file_id: FileId) -> Arc; -} - -/// Silly workaround for cyclic deps between the traits -pub struct FileLoaderDelegate(pub T); - -impl FileLoader for FileLoaderDelegate<&'_ T> { - fn file_text(&self, file_id: FileId) -> Arc { - SourceDatabaseExt::file_text(self.0, file_id) - } -} - /// If the `input` string represents an atom, and needs quoting, quote /// it. pub fn to_quoted_string(input: &str) -> Cow { diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index 3eb58e0836..d7e3006bc8 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -28,8 +28,8 @@ use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::ModuleName; use elp_ide::elp_ide_db::elp_base_db::ProjectId; +use elp_ide::elp_ide_db::elp_base_db::RootQueryDb; use elp_ide::elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide::elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide::elp_ide_db::elp_base_db::VfsPath; use elp_ide::elp_ide_db::elp_base_db::module_name; use elp_ide::elp_ide_db::elp_base_db::path_for_file; @@ -793,8 +793,8 @@ impl GleanIndexer { let file_id = index .file_for_module(&ModuleName::new(module)) .expect("No module found"); - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); + let source_root_id = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); let path = source_root.path_for_file(&file_id).unwrap(); match Self::index_file( db, @@ -859,12 +859,12 @@ impl GleanIndexer { } fn project_files(db: &RootDatabase, project_id: ProjectId) -> Vec<(FileId, VfsPath)> { - let project_data = db.project_data(project_id); + let project_data = db.project_data(project_id).project_data(db); let mut files = vec![]; for &source_root_id in &project_data.source_roots { if let Some(app_data) = db.app_data(source_root_id) { if app_data.app_type == AppType::App { - let source_root = db.source_root(source_root_id); + let source_root = db.source_root(source_root_id).source_root(db); for file_id in source_root.iter() { if let Some(path) = source_root.path_for_file(&file_id) { files.push((file_id, path.clone())); @@ -926,7 +926,7 @@ impl GleanIndexer { let def = def_map.get_macros().get(&MacroName::new(name, x.key.arity)); if let Some(def) = def { let range = def.source(db).syntax().text_range(); - let text = &db.file_text(id)[range]; + let text = &db.file_text(id).text(db)[range]; let text = format!("```erlang\n{}\n```", text); let doc = match (&x.key.expansion, &x.key.ods_url) { (None, None) => text, @@ -990,7 +990,7 @@ impl GleanIndexer { project_id: ProjectId, prefix: Option<&String>, ) -> Option { - let project_data = db.project_data(project_id); + let project_data = db.project_data(project_id).project_data(db); let root = project_data.root_dir.as_path(); let file_path = path.as_path()?; let file_path = file_path.strip_prefix(root)?; @@ -1013,7 +1013,7 @@ impl GleanIndexer { line += 1; prev_offset = curr_offset; } - let content = db.file_text(file_id); + let content = db.file_text(file_id).text(db); if !content.ends_with('\n') { ends_with_new_line = false; let len = if content.len() as u32 >= prev_offset { @@ -1112,7 +1112,7 @@ impl GleanIndexer { for (ty, def) in def_map.get_types() { let range = def.source(db).syntax().text_range(); - let text = &db.file_text(file_id)[range]; + let text = &db.file_text(file_id).text(db)[range]; let text = format!("```erlang\n{}\n```", text); let span: Location = range.into(); @@ -1139,7 +1139,7 @@ impl GleanIndexer { for (rec, def) in def_map.get_records() { let range = def.source(db).syntax().text_range(); - let text = &db.file_text(file_id)[range]; + let text = &db.file_text(file_id).text(db)[range]; let text = format!("```erlang\n{}\n```", text); let span: Location = range.into(); @@ -2571,8 +2571,8 @@ mod tests { let db = host.raw_database(); for file_id in &fixture.files { let file_id = *file_id; - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); + let source_root_id = db.file_source_root(file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); let path = source_root.path_for_file(&file_id).unwrap(); let (name, ext) = path.name_and_extension().unwrap(); let name = format!("{}.{}", name, ext.unwrap()); diff --git a/crates/elp/src/bin/shell.rs b/crates/elp/src/bin/shell.rs index b00ce58fc1..98ef380570 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -25,7 +25,6 @@ use elp_eqwalizer::Mode; use elp_ide::elp_ide_db::elp_base_db::AbsPathBuf; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide::elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide::elp_ide_db::elp_base_db::SourceRoot; use elp_ide::elp_ide_db::elp_base_db::SourceRootId; use elp_ide::elp_ide_db::elp_base_db::VfsPath; diff --git a/crates/elp/src/build/load.rs b/crates/elp/src/build/load.rs index 115f6f56c6..3126a6f09f 100644 --- a/crates/elp/src/build/load.rs +++ b/crates/elp/src/build/load.rs @@ -26,7 +26,6 @@ use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::ProjectApps; use elp_ide::elp_ide_db::elp_base_db::ProjectId; use elp_ide::elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide::elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide::elp_ide_db::elp_base_db::SourceRoot; use elp_ide::elp_ide_db::elp_base_db::SourceRootId; use elp_ide::elp_ide_db::elp_base_db::Vfs; diff --git a/crates/elp/src/server.rs b/crates/elp/src/server.rs index a240e3159f..988529b585 100644 --- a/crates/elp/src/server.rs +++ b/crates/elp/src/server.rs @@ -46,8 +46,8 @@ use elp_ide::elp_ide_db::elp_base_db::FileSetConfig; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::ProjectApps; use elp_ide::elp_ide_db::elp_base_db::ProjectId; +use elp_ide::elp_ide_db::elp_base_db::RootQueryDb; use elp_ide::elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide::elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide::elp_ide_db::elp_base_db::SourceRoot; use elp_ide::elp_ide_db::elp_base_db::SourceRootId; use elp_ide::elp_ide_db::elp_base_db::Vfs; diff --git a/crates/eqwalizer/Cargo.toml b/crates/eqwalizer/Cargo.toml index 031c43242f..e1bd709181 100644 --- a/crates/eqwalizer/Cargo.toml +++ b/crates/eqwalizer/Cargo.toml @@ -17,6 +17,7 @@ fxhash.workspace = true itertools.workspace = true log.workspace = true parking_lot.workspace = true +ra_ap_query-group-macro.workspace = true salsa.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/eqwalizer/src/analyses/mod.rs b/crates/eqwalizer/src/analyses/mod.rs index b5ad028630..548d503d96 100644 --- a/crates/eqwalizer/src/analyses/mod.rs +++ b/crates/eqwalizer/src/analyses/mod.rs @@ -19,7 +19,7 @@ use crate::db::EqwalizerDiagnosticsDatabase; mod escape_hatches; mod overloaded_specs; -#[salsa::query_group(EqwalizerAnalysesDatabaseStorage)] +#[ra_ap_query_group_macro::query_group(EqwalizerAnalysesDatabaseStorage)] pub trait EqwalizerAnalysesDatabase: EqwalizerDiagnosticsDatabase { fn compute_eqwalizer_stats( &self, diff --git a/crates/eqwalizer/src/db.rs b/crates/eqwalizer/src/db.rs index b94659b5e0..316b697221 100644 --- a/crates/eqwalizer/src/db.rs +++ b/crates/eqwalizer/src/db.rs @@ -19,7 +19,7 @@ use elp_base_db::AppType; use elp_base_db::FileId; use elp_base_db::ModuleName; use elp_base_db::ProjectId; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_types_db::StringId; use elp_types_db::eqwalizer::AST; use elp_types_db::eqwalizer::Id; @@ -76,8 +76,8 @@ pub trait ELPDbApi { fn module_ipc_handle(&self, module: ModuleName) -> Option>>; } -#[salsa::query_group(EqwalizerDiagnosticsDatabaseStorage)] -pub trait EqwalizerDiagnosticsDatabase: EqwalizerErlASTStorage + SourceDatabase + ELPDbApi { +#[ra_ap_query_group_macro::query_group] +pub trait EqwalizerDiagnosticsDatabase: EqwalizerErlASTStorage + RootQueryDb + ELPDbApi { #[salsa::input] fn eqwalizer_config(&self) -> Arc; diff --git a/crates/eqwalizer/src/lib.rs b/crates/eqwalizer/src/lib.rs index 6e92e108e0..155878e822 100644 --- a/crates/eqwalizer/src/lib.rs +++ b/crates/eqwalizer/src/lib.rs @@ -267,14 +267,14 @@ fn do_typecheck( project_id: ProjectId, ) -> Result { // Never cache the results of this function - db.salsa_runtime().report_untracked_read(); + db.report_untracked_read(); let handle = Arc::new(Mutex::new( IpcHandle::from_command(&mut cmd) .with_context(|| format!("starting eqWAlizer process: {:?}", cmd))?, )); let mut diagnostics = EqwalizerDiagnostics::default(); loop { - db.unwind_if_cancelled(); + db.unwind_if_revision_cancelled(); let msg = handle.lock().receive()?; match msg { MsgFromEqWAlizer::EnteringModule { module } => { @@ -318,7 +318,7 @@ fn get_module_diagnostics( let mut handle = handle_mutex.lock(); handle.send(&MsgToEqWAlizer::ELPEnteringModule)?; loop { - db.unwind_if_cancelled(); + db.unwind_if_revision_cancelled(); match handle.receive()? { MsgFromEqWAlizer::GetAstBytes { module, format } => { log::debug!( diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 934619ca47..84d2445bea 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -20,6 +20,7 @@ la-arena.workspace = true lazy_static.workspace = true log.workspace = true profile.workspace = true +ra_ap_query-group-macro.workspace = true regex.workspace = true stdx.workspace = true tracing.workspace = true diff --git a/crates/hir/src/body.rs b/crates/hir/src/body.rs index 0a3e160708..6545326604 100644 --- a/crates/hir/src/body.rs +++ b/crates/hir/src/body.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::FileRange; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_syntax::AstNode; use elp_syntax::AstPtr; use elp_syntax::SourceFile; @@ -1112,7 +1112,7 @@ impl InFileAstPtr { None } } - pub fn to_ast(&self, db: &dyn SourceDatabase) -> T { + pub fn to_ast(&self, db: &dyn RootQueryDb) -> T { let parse = db.parse(self.file_id()); self.0.value.to_node(parse.tree().syntax()) } @@ -1211,7 +1211,7 @@ impl BodySourceMap { #[cfg(test)] mod local_tests { - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_base_db::fixture::WithFixture; use elp_syntax::AstNode; use elp_syntax::algo::find_node_at_offset; diff --git a/crates/hir/src/body/scope.rs b/crates/hir/src/body/scope.rs index dd1da23e94..4584b58759 100644 --- a/crates/hir/src/body/scope.rs +++ b/crates/hir/src/body/scope.rs @@ -759,7 +759,7 @@ fn add_exported_scopes( #[cfg(test)] mod tests { use elp_base_db::FileId; - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_base_db::assert_eq_text; use elp_base_db::extract_offset; use elp_base_db::fixture::WithFixture; diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 783b30ae7c..207695d99a 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use elp_base_db::FileId; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_base_db::Upcast; use elp_base_db::salsa; use elp_syntax::ast; @@ -50,17 +50,12 @@ use crate::edoc; use crate::edoc::EdocHeader; use crate::include; pub use crate::intern::InternDatabase; -pub use crate::intern::InternDatabaseStorage; use crate::macro_exp; use crate::macro_exp::MacroResolution; -#[salsa::query_group(DefDatabaseStorage)] +#[ra_ap_query_group_macro::query_group] pub trait DefDatabase: - InternDatabase - + Upcast - + SourceDatabase - + Upcast - + TypedSemantic + InternDatabase + Upcast + RootQueryDb + Upcast + TypedSemantic { #[salsa::invoke(FormList::file_form_list_query)] fn file_form_list(&self, file_id: FileId) -> Arc; diff --git a/crates/hir/src/def_map.rs b/crates/hir/src/def_map.rs index 6cb840f7f2..ea04a60a4f 100644 --- a/crates/hir/src/def_map.rs +++ b/crates/hir/src/def_map.rs @@ -22,6 +22,7 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::module_name; +use elp_base_db::salsa::Cycle; use elp_syntax::AstNode; use elp_syntax::ast; use elp_syntax::match_ast; @@ -45,6 +46,7 @@ use crate::PPDirective; use crate::RecordDef; use crate::TypeAliasDef; use crate::db::DefDatabase; +use crate::db::DefDatabaseData; use crate::form_list::DeprecatedAttribute; use crate::form_list::DeprecatedDesc; use crate::form_list::DeprecatedFa; @@ -376,10 +378,11 @@ impl DefMap { // Return just the local def map in such cases, not resolving nested includes at all pub(crate) fn recover_cycle( db: &dyn DefDatabase, - _cycle: &[String], - file_id: &FileId, + _cycle: &Cycle, + _data: DefDatabaseData, + file_id: FileId, ) -> Arc { - db.def_map_local(*file_id) + db.def_map_local(file_id) } pub fn get_by_function_id(&self, function_id: &InFile) -> Option<&FunctionDef> { diff --git a/crates/hir/src/edoc.rs b/crates/hir/src/edoc.rs index cd44d2f36b..50d3491d3d 100644 --- a/crates/hir/src/edoc.rs +++ b/crates/hir/src/edoc.rs @@ -35,7 +35,7 @@ use std::sync::Arc; use std::sync::LazyLock; use elp_base_db::FileId; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_syntax::AstNode; use elp_syntax::AstPtr; use elp_syntax::Direction; @@ -120,12 +120,12 @@ impl EdocHeader { )) } - pub fn prev_divider(&self, db: &dyn SourceDatabase) -> Option { + pub fn prev_divider(&self, db: &dyn RootQueryDb) -> Option { let first_comment = self.comments().next()?; divider(first_comment.to_ast(db).syntax(), Direction::Prev) } - pub fn next_divider(&self, db: &dyn SourceDatabase) -> Option { + pub fn next_divider(&self, db: &dyn RootQueryDb) -> Option { let last_comment = self.comments().last()?; divider(last_comment.to_ast(db).syntax(), Direction::Next) } diff --git a/crates/hir/src/include.rs b/crates/hir/src/include.rs index 673f12e1af..c4a07c8396 100644 --- a/crates/hir/src/include.rs +++ b/crates/hir/src/include.rs @@ -58,7 +58,8 @@ mod tests { .resolve_include(InFile::new(file_id, idx)) .unwrap_or_else(|| panic!("unresolved include: {:?}", include)); let resolved_path = db - .source_root(db.file_source_root(resolved)) + .source_root(db.file_source_root(resolved).source_root_id(&db)) + .source_root(&db) .path_for_file(&resolved) .unwrap() .clone(); diff --git a/crates/hir/src/intern.rs b/crates/hir/src/intern.rs index e116c1a30f..ad1e3edf52 100644 --- a/crates/hir/src/intern.rs +++ b/crates/hir/src/intern.rs @@ -11,11 +11,12 @@ use std::sync::Arc; use elp_base_db::salsa; +use elp_base_db::salsa::plumbing::AsId; use crate::Name; -#[salsa::query_group(InternDatabaseStorage)] -pub trait InternDatabase { +#[ra_ap_query_group_macro::query_group] +pub trait InternDatabase: salsa::Database { #[salsa::interned] fn atom(&self, name: Name) -> Atom; @@ -26,43 +27,19 @@ pub trait InternDatabase { fn ssr(&self, source: Arc) -> SsrSource; } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Atom(salsa::InternId); - -impl salsa::InternKey for Atom { - fn from_intern_id(v: salsa::InternId) -> Self { - Atom(v) - } - - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } +#[salsa::interned(no_lifetime)] +pub struct Atom { + pub name: Name, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Var(salsa::InternId); - -impl salsa::InternKey for Var { - fn from_intern_id(v: salsa::InternId) -> Self { - Var(v) - } - - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } +#[salsa::interned(no_lifetime)] +pub struct Var { + pub name: Name, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SsrSource(salsa::InternId); - -impl salsa::InternKey for SsrSource { - fn from_intern_id(v: salsa::InternId) -> Self { - SsrSource(v) - } - - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } +#[salsa::interned(no_lifetime)] +pub struct SsrSource { + pub source: Arc, } impl Atom { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7c54e55b54..48b9e8802e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -9,7 +9,7 @@ */ use elp_base_db::FileId; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_syntax::ast; mod body; @@ -195,7 +195,7 @@ impl InFile { self.with_value(&self.value) } - pub fn file_syntax(&self, db: &dyn SourceDatabase) -> ast::SourceFile { + pub fn file_syntax(&self, db: &dyn RootQueryDb) -> ast::SourceFile { db.parse(self.file_id).tree() } } diff --git a/crates/hir/src/macro_exp.rs b/crates/hir/src/macro_exp.rs index 849797509d..d52eab5be7 100644 --- a/crates/hir/src/macro_exp.rs +++ b/crates/hir/src/macro_exp.rs @@ -9,6 +9,7 @@ */ use elp_base_db::FileId; +use elp_base_db::salsa::Cycle; use elp_syntax::ast; use crate::Define; @@ -20,6 +21,7 @@ use crate::Name; use crate::PPDirective; use crate::body::SSR_SOURCE_FILE_ID; use crate::db::DefDatabase; +use crate::db::DefDatabaseData; use crate::form_list::FormListData; use crate::known; use crate::name::AsName; @@ -170,9 +172,10 @@ pub(crate) fn local_resolve_query( // This handles the case of headers accidentally forming cycles during macro resolution. pub(crate) fn recover_cycle( _db: &dyn DefDatabase, - _cycle: &[String], - _file_id: &FileId, - _name: &MacroName, + _cycle: &Cycle, + _data: DefDatabaseData, + _file_id: FileId, + _name: MacroName, ) -> MacroResolution { MacroResolution::Unresolved } @@ -301,7 +304,7 @@ pub fn macro_name(macro_call: &ast::MacroCallExpr) -> Option { #[cfg(test)] mod tests { use elp_base_db::FileRange; - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_base_db::fixture::ChangeFixture; use elp_base_db::fixture::WithFixture; use elp_syntax::AstNode; diff --git a/crates/hir/src/module_data.rs b/crates/hir/src/module_data.rs index 32820a1d5c..4d008f445d 100644 --- a/crates/hir/src/module_data.rs +++ b/crates/hir/src/module_data.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::FileKind; use elp_base_db::ModuleName; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_syntax::AstNode; use elp_syntax::AstPtr; use elp_syntax::SmolStr; @@ -65,18 +65,20 @@ pub struct File { } impl File { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::SourceFile { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::SourceFile { db.parse(self.file_id).tree() } - pub fn kind(&self, db: &dyn SourceDatabase) -> FileKind { + pub fn kind(&self, db: &dyn RootQueryDb) -> FileKind { db.file_kind(self.file_id) } - pub fn name(&self, db: &dyn SourceDatabase) -> SmolStr { + pub fn name(&self, db: &dyn RootQueryDb) -> SmolStr { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nFile::name: {:?}", self.file_id)); - let source_root = db.source_root(db.file_source_root(self.file_id)); + let source_root = db + .source_root(db.file_source_root(self.file_id).source_root_id(db)) + .source_root(db); if let Some((name, Some(ext))) = source_root .path_for_file(&self.file_id) .and_then(|path| path.name_and_extension()) @@ -125,7 +127,7 @@ pub struct FunctionClauseDef { } impl FunctionClauseDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::FunDecl { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::FunDecl { let source_file = self.file.source(db); self.function_clause.form_id.get(&source_file) } @@ -172,7 +174,7 @@ pub struct FunctionDef { } impl FunctionDef { - pub fn source(&self, db: &dyn SourceDatabase) -> Vec { + pub fn source(&self, db: &dyn RootQueryDb) -> Vec { let source_file = self.file.source(db); self.function_clauses .iter() @@ -180,11 +182,11 @@ impl FunctionDef { .collect() } - pub fn first_clause_name(&self, db: &dyn SourceDatabase) -> Option { + pub fn first_clause_name(&self, db: &dyn RootQueryDb) -> Option { self.source(db).first()?.name() } - pub fn range(&self, db: &dyn SourceDatabase) -> Option { + pub fn range(&self, db: &dyn RootQueryDb) -> Option { let sources = self.source(db); let start = sources.first()?; let end = sources.last()?; @@ -222,7 +224,7 @@ impl FunctionDef { ) } - pub fn spec_range(&self, db: &dyn SourceDatabase) -> Option { + pub fn spec_range(&self, db: &dyn RootQueryDb) -> Option { self.spec.as_ref().map(|spec| spec.range(db)) } @@ -266,7 +268,7 @@ impl FunctionDef { Some(res) } - pub fn arg_names(&self, db: &dyn SourceDatabase) -> Option> { + pub fn arg_names(&self, db: &dyn RootQueryDb) -> Option> { match &self.spec { Some(spec_def) => match spec_def.arg_names(db) { Some(arg_names_from_spec) => { @@ -383,17 +385,17 @@ pub struct SpecDef { } impl SpecDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::Spec { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::Spec { let source_file = self.file.source(db); self.spec.form_id.get(&source_file) } - pub fn range(&self, db: &dyn SourceDatabase) -> TextRange { + pub fn range(&self, db: &dyn RootQueryDb) -> TextRange { let source = self.source(db); source.syntax().text_range() } - pub fn arg_names(&self, db: &dyn SourceDatabase) -> Option> { + pub fn arg_names(&self, db: &dyn RootQueryDb) -> Option> { let spec = self.source(db); let first_sig = spec.sigs().next()?; Some( @@ -449,12 +451,12 @@ pub struct RecordDef { } impl RecordDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::RecordDecl { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::RecordDecl { let source_file = self.file.source(db); self.record.form_id.get(&source_file) } - pub fn range(&self, db: &dyn SourceDatabase) -> TextRange { + pub fn range(&self, db: &dyn RootQueryDb) -> TextRange { let source = self.source(db); source.syntax().text_range() } @@ -514,7 +516,7 @@ pub struct RecordFieldDef { } impl RecordFieldDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::RecordField { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::RecordField { let record = self.record.source(db); record.fields().nth(self.field.idx as usize).unwrap() } @@ -534,7 +536,7 @@ pub enum TypeAliasSource { } impl TypeAliasDef { - pub fn source(&self, db: &dyn SourceDatabase) -> TypeAliasSource { + pub fn source(&self, db: &dyn RootQueryDb) -> TypeAliasSource { let source_file = self.file.source(db); match self.type_alias { TypeAlias::Opaque { form_id, .. } => TypeAliasSource::Opaque(form_id.get(&source_file)), @@ -555,14 +557,14 @@ impl TypeAliasDef { } } - pub fn range(&self, db: &dyn SourceDatabase) -> Option { + pub fn range(&self, db: &dyn RootQueryDb) -> Option { let source = self.source(db); Some(source.syntax().text_range()) } /// This information is used for completion. /// We deliberately return nothing for an opaque type - pub fn map_expr_for_completion(&self, db: &dyn SourceDatabase) -> Option { + pub fn map_expr_for_completion(&self, db: &dyn RootQueryDb) -> Option { let source = self.source(db); match source { TypeAliasSource::Regular(alias) => match alias.ty()? { @@ -604,7 +606,7 @@ pub struct CallbackDef { } impl CallbackDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::Callback { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::Callback { let source_file = self.file.source(db); self.callback.form_id.get(&source_file) } @@ -617,7 +619,7 @@ pub struct DefineDef { } impl DefineDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::PpDefine { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::PpDefine { let source_file = self.file.source(db); self.define.form_id.get(&source_file) } @@ -633,7 +635,7 @@ pub struct VarDef { } impl VarDef { - pub fn source(&self, db: &dyn SourceDatabase) -> ast::Var { + pub fn source(&self, db: &dyn RootQueryDb) -> ast::Var { let source_file = self.file.source(db); self.var.to_node(source_file.syntax()) } @@ -649,7 +651,7 @@ fn is_in_otp(file_id: FileId, db: &dyn DefDatabase) -> bool { match db.file_app_data(file_id) { Some(app_data) => { let project_id = app_data.project_id; - db.project_data(project_id).otp_project_id == Some(project_id) + db.project_data(project_id).project_data(db).otp_project_id == Some(project_id) } None => false, } diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index b433946852..86f5a68839 100644 --- a/crates/hir/src/sema.rs +++ b/crates/hir/src/sema.rs @@ -97,7 +97,6 @@ use crate::fold::MacroStrategy; use crate::fold::ParenStrategy; use crate::fold::Strategy; pub use crate::intern::InternDatabase; -pub use crate::intern::InternDatabaseStorage; use crate::resolver::Resolution; use crate::resolver::Resolver; @@ -561,7 +560,7 @@ impl Semantic<'_> { } pub fn vardef_source(&self, def: &VarDef) -> ast::Var { - def.source(self.db.upcast()) + def.source(self.db) } pub fn range_for_any(&self, body: &Body, expr_id: &AnyExprId) -> Option { @@ -1648,7 +1647,7 @@ impl Index for InFunctionClauseBody<'_, T> { #[cfg(test)] mod tests { use elp_base_db::ProjectId; - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_base_db::fixture::WithFixture; use elp_syntax::AstNode; use elp_syntax::algo::find_node_at_offset; diff --git a/crates/hir/src/test_db.rs b/crates/hir/src/test_db.rs index d71bdaa9ef..c13f46006c 100644 --- a/crates/hir/src/test_db.rs +++ b/crates/hir/src/test_db.rs @@ -14,11 +14,23 @@ use std::fmt; use std::panic; use std::sync::Arc; +use elp_base_db::AppData; +use elp_base_db::AppDataId; +use elp_base_db::AppDataInput; use elp_base_db::FileId; -use elp_base_db::FileLoader; -use elp_base_db::FileLoaderDelegate; use elp_base_db::FileRange; +use elp_base_db::FileSourceRootInput; +use elp_base_db::FileText; +use elp_base_db::Files; +use elp_base_db::ProjectData; +use elp_base_db::ProjectDataInput; +use elp_base_db::ProjectId; +use elp_base_db::RootQueryDb; +use elp_base_db::SourceAppDataInput; use elp_base_db::SourceDatabase; +use elp_base_db::SourceRoot; +use elp_base_db::SourceRootId; +use elp_base_db::SourceRootInput; use elp_base_db::Upcast; use elp_base_db::salsa; use elp_types_db::TypedSemantic; @@ -26,19 +38,15 @@ use elp_types_db::eqwalizer; use crate::db::InternDatabase; -#[salsa::database( - elp_base_db::SourceDatabaseExtStorage, - elp_base_db::SourceDatabaseStorage, - crate::db::DefDatabaseStorage, - crate::db::InternDatabaseStorage -)] -#[derive(Default)] +#[salsa::db] +#[derive(Default, Clone)] pub(crate) struct TestDB { storage: salsa::Storage, + files: Arc, } -impl Upcast for TestDB { - fn upcast(&self) -> &(dyn SourceDatabase + 'static) { +impl Upcast for TestDB { + fn upcast(&self) -> &(dyn RootQueryDb + 'static) { self } } @@ -49,7 +57,10 @@ impl Upcast for TestDB { } } -impl salsa::Database for TestDB {} +#[salsa::db] +impl salsa::Database for TestDB { + fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {} +} impl fmt::Debug for TestDB { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -57,14 +68,65 @@ impl fmt::Debug for TestDB { } } -impl panic::RefUnwindSafe for TestDB {} +#[salsa::db] +impl SourceDatabase for TestDB { + fn file_text(&self, file_id: FileId) -> FileText { + self.files.file_text(file_id) + } -impl FileLoader for TestDB { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) + fn set_file_text(&mut self, file_id: FileId, text: Arc) { + let files = self.files.clone(); + files.set_file_text(self, file_id, text); + } + + fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput { + self.files.source_root(source_root_id) + } + + fn set_source_root(&mut self, source_root_id: SourceRootId, source_root: Arc) { + let files = self.files.clone(); + files.set_source_root(self, source_root_id, source_root); + } + + fn file_source_root(&self, id: FileId) -> FileSourceRootInput { + self.files.file_source_root(id) + } + + fn set_file_source_root(&mut self, id: FileId, source_root_id: SourceRootId) { + let files = self.files.clone(); + files.set_file_source_root(self, id, source_root_id); + } + + fn app_data_by_id(&self, id: AppDataId) -> AppDataInput { + self.files.app_data(id) + } + + fn set_app_data_by_id(&mut self, id: AppDataId, app_data: Option>) { + let files = self.files.clone(); + files.set_app_data(self, id, app_data) + } + + fn app_data_id(&self, source_root_id: SourceRootId) -> SourceAppDataInput { + self.files.source_app_data(source_root_id) + } + + fn set_app_data_id(&mut self, id: SourceRootId, app_data_id: AppDataId) { + let files = self.files.clone(); + files.set_source_app_data(self, id, app_data_id) + } + + fn project_data(&self, project_id: ProjectId) -> ProjectDataInput { + self.files.project_data(project_id) + } + + fn set_project_data(&mut self, id: ProjectId, project_data: Arc) { + let files = self.files.clone(); + files.set_project_data(self, id, project_data) } } +impl panic::RefUnwindSafe for TestDB {} + impl TypedSemantic for TestDB { fn eqwalizer_diagnostics( &self, diff --git a/crates/ide/src/codemod_helpers.rs b/crates/ide/src/codemod_helpers.rs index e531b6264c..bd5c4fd16d 100644 --- a/crates/ide/src/codemod_helpers.rs +++ b/crates/ide/src/codemod_helpers.rs @@ -602,7 +602,7 @@ pub(crate) fn find_call_in_function( mod tests { use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FileId; - use elp_ide_db::elp_base_db::SourceDatabase; + use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::elp_base_db::fixture::WithFixture; use elp_project_model::otp::otp_supported_by_eqwalizer; use elp_syntax::algo::find_node_at_offset; diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index a9832e8843..56a5a83932 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -37,6 +37,7 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileKind; use elp_ide_db::elp_base_db::FileRange; use elp_ide_db::elp_base_db::ProjectId; +use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::erlang_service::DiagnosticLocation; use elp_ide_db::erlang_service::ParseError; use elp_ide_db::metadata::Kind; @@ -73,7 +74,6 @@ use serde::Deserialize; use serde::Serialize; use crate::RootDatabase; -use crate::SourceDatabase; use crate::common_test; mod application_env; diff --git a/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs b/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs index 31096f0112..ad7c5f4513 100644 --- a/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs +++ b/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs @@ -68,7 +68,7 @@ pub fn expected_type( if let [atom @ Type::AtomLitType(AtomLitType { .. }), other] = &arg_tys[..] { if other == other2 { // Add wrapping tuple to return - let file_text = sema.db.file_text(file_id); + let file_text = sema.db.file_text(file_id).text(sema.db); let current = &file_text[d.range.start().into()..d.range.end().into()]; let replacement = format!("{{{atom}, {current}}}"); let edit = TextEdit::replace(d.range, replacement.clone()); diff --git a/crates/ide/src/diagnostics/module_mismatch.rs b/crates/ide/src/diagnostics/module_mismatch.rs index 3e0bb0231b..32a138507c 100644 --- a/crates/ide/src/diagnostics/module_mismatch.rs +++ b/crates/ide/src/diagnostics/module_mismatch.rs @@ -34,8 +34,8 @@ pub(crate) fn module_mismatch( let module_name = ast::ModuleAttribute::cast(node.clone())?.name()?; // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nmodule_mismatch: {:?}", file_id)); - let root_id = sema.db.file_source_root(file_id); - let root = sema.db.source_root(root_id); + let root_id = sema.db.file_source_root(file_id).source_root_id(sema.db); + let root = sema.db.source_root(root_id).source_root(sema.db); let path = root.path_for_file(&file_id).unwrap(); let filename = path.name_and_extension().unwrap_or_default().0; let loc = module_name.syntax().text_range(); diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index 0f17fccf34..00e4ea859f 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs @@ -13,7 +13,7 @@ use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileRange; -use elp_ide_db::elp_base_db::SourceDatabase; +use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::elp_base_db::fixture::ChangeFixture; use elp_ide_db::elp_base_db::fixture::WithFixture; use elp_project_model::test_fixture::DiagnosticsEnabled; diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 63d2cbc9a2..9cee1a06b4 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -48,10 +48,9 @@ use elp_ide_db::elp_base_db::ModuleIndex; use elp_ide_db::elp_base_db::ModuleName; use elp_ide_db::elp_base_db::ProjectData; use elp_ide_db::elp_base_db::ProjectId; +use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide_db::elp_base_db::salsa; -use elp_ide_db::elp_base_db::salsa::ParallelDatabase; use elp_ide_db::eqwalizer::type_references; use elp_ide_db::erlang_service::ParseResult; use elp_ide_db::rename::RenameError; @@ -207,7 +206,7 @@ impl AnalysisHost { /// `Analysis` are canceled (most method return `Err(Canceled)`). #[derive(Debug)] pub struct Analysis { - db: salsa::Snapshot, + db: RootDatabase, } // As a general design guideline, `Analysis` API are intended to be independent @@ -384,7 +383,10 @@ impl Analysis { self.with_db(|db| { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nproject_data: {:?}", file_id)); - Some(db.project_data(db.file_app_data(file_id)?.project_id)) + Some( + db.project_data(db.file_app_data(file_id)?.project_id) + .project_data(db), + ) }) } @@ -429,7 +431,7 @@ impl Analysis { /// Returns the contents of a file pub fn file_text(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| db.file_text(file_id)) + self.with_db(|db| db.file_text(file_id).text(db)) } /// Returns the app_type for a file diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index b6c78ea2b3..820828120a 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1153,7 +1153,7 @@ mod tests { for indel in edit.1.into_iter() { text_edit_builder.replace(indel.delete, indel.insert); } - let mut result = sema.db.file_text(file_id).to_string(); + let mut result = sema.db.file_text(file_id).text(sema.db).to_string(); let edit = text_edit_builder.finish(); edit.apply(&mut result); let expected = analysis_after.file_text(file_id).unwrap().to_string(); diff --git a/crates/ide/src/tests.rs b/crates/ide/src/tests.rs index 386b7fa302..11db81329b 100644 --- a/crates/ide/src/tests.rs +++ b/crates/ide/src/tests.rs @@ -16,7 +16,7 @@ use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FilePosition; use elp_ide_db::elp_base_db::FileRange; -use elp_ide_db::elp_base_db::SourceDatabaseExt; +use elp_ide_db::elp_base_db::SourceDatabase; use elp_ide_db::elp_base_db::assert_eq_text; use elp_ide_db::elp_base_db::fixture::WithFixture; use elp_ide_db::elp_base_db::remove_annotations; @@ -109,7 +109,11 @@ pub(crate) fn check_filtered_ct_fix_with_config( let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = analysis.db.file_text(file_id).to_string(); + let mut actual = analysis + .db + .file_text(file_id) + .text(&analysis.db) + .to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut actual); @@ -226,7 +230,11 @@ pub(crate) fn check_nth_fix( let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = analysis.db.file_text(file_id).to_string(); + let mut actual = analysis + .db + .file_text(file_id) + .text(&analysis.db) + .to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut actual); @@ -353,7 +361,11 @@ pub(crate) fn check_specific_fix_with_config_and_adhoc( let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = analysis.db.file_text(file_id).to_string(); + let mut actual = analysis + .db + .file_text(file_id) + .text(&analysis.db) + .to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut actual); diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 1b4bcc5888..5739294f26 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs @@ -17,7 +17,7 @@ use elp_ide_db::assists::AssistUserInput; use elp_ide_db::assists::GroupLabel; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileRange; -use elp_ide_db::elp_base_db::SourceDatabase; +use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::source_change::SourceChangeBuilder; use elp_syntax::Direction; use elp_syntax::SourceFile; diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 42bb15ac23..91aa9018ea 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs @@ -19,8 +19,8 @@ use elp_ide_db::assists::AssistId; use elp_ide_db::assists::AssistKind; use elp_ide_db::assists::AssistUserInput; use elp_ide_db::assists::AssistUserInputType; +use elp_ide_db::elp_base_db::RootQueryDb; use elp_ide_db::elp_base_db::SourceDatabase; -use elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide_db::elp_base_db::fixture::WithFixture; use elp_ide_db::elp_base_db::remove_annotations; use elp_ide_db::helpers::SnippetCap; @@ -176,7 +176,7 @@ fn check( // Check that the fixture is syntactically valid if check_parse_error { // Check that we have a syntactically valid starting point - let text = sema.file_text(frange.file_id); + let text = sema.file_text(frange.file_id).text(sema); let parse = sema.parse(frange.file_id); let errors = parse.errors(); if !errors.is_empty() { @@ -246,11 +246,11 @@ fn check( let mut buf = String::new(); for (file_id, edit) in source_change.source_file_edits { - let mut text = db.file_text(file_id).as_ref().to_owned(); + let mut text = db.file_text(file_id).text(&db).as_ref().to_owned(); edit.apply(&mut text); if !skip_header { - let sr = db.file_source_root(file_id); - let sr = db.source_root(sr); + let sr = db.file_source_root(file_id).source_root_id(&db); + let sr = db.source_root(sr).source_root(&db); let path = sr.path_for_file(&file_id).unwrap(); format_to!(buf, "//- {}\n", path) } @@ -263,8 +263,8 @@ fn check( initial_contents, } = file_system_edit { - let sr = db.file_source_root(dst.anchor); - let sr = db.source_root(sr); + let sr = db.file_source_root(dst.anchor).source_root_id(&db); + let sr = db.source_root(sr).source_root(&db); let mut base = sr.path_for_file(&dst.anchor).unwrap().clone(); base.pop(); let created_file_path = format!("{}{}", base, &dst.path[1..]); @@ -754,7 +754,7 @@ fn add_compile_option_1() { let mut builder = SourceChangeBuilder::new(file_id); helpers::add_compile_option(&sema, file_id, "blah", None, &mut builder); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } @@ -790,7 +790,7 @@ fn add_to_suite_basic() { &mut builder, ); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } @@ -825,7 +825,7 @@ fn add_to_suite_existing_no_match() { let mut builder = SourceChangeBuilder::new(file_id); helpers::add_suite_0_option(&sema, file_id, "require", "foo", None, &mut builder); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } @@ -870,7 +870,7 @@ fn add_to_suite_existing_with_match() { &mut builder, ); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } @@ -916,7 +916,7 @@ fn add_to_suite_existing_with_comment() { &mut builder, ); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } @@ -963,7 +963,7 @@ fn add_to_suite_grouped_export() { &mut builder, ); let source_change = builder.finish(); - let mut changed = db.file_text(file_id).to_string(); + let mut changed = db.file_text(file_id).text(&db).to_string(); for edit in source_change.source_file_edits.values() { edit.apply(&mut changed); } diff --git a/crates/ide_completion/src/ctx.rs b/crates/ide_completion/src/ctx.rs index 20fbfb46fb..75265ac361 100644 --- a/crates/ide_completion/src/ctx.rs +++ b/crates/ide_completion/src/ctx.rs @@ -260,7 +260,7 @@ impl CtxKind { /// Tests of internals, delete when autocomplete is full-featured T126163525 #[cfg(test)] mod ctx_tests { - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FilePosition; use elp_ide_db::elp_base_db::fixture::WithFixture; diff --git a/crates/ide_completion/src/helpers.rs b/crates/ide_completion/src/helpers.rs index ed6b5f0cbf..7f02f03206 100644 --- a/crates/ide_completion/src/helpers.rs +++ b/crates/ide_completion/src/helpers.rs @@ -10,7 +10,7 @@ use elp_base_db::FileId; use elp_base_db::FilePosition; -use elp_base_db::SourceDatabase; +use elp_base_db::RootQueryDb; use elp_syntax::AstNode; use elp_syntax::SmolStr; use elp_syntax::SourceFile; @@ -134,7 +134,7 @@ pub(crate) fn should_include_args(next_token: &Option) -> bool { } } -fn function_arg_names(db: &dyn SourceDatabase, def: &FunctionDef) -> Option { +fn function_arg_names(db: &dyn RootQueryDb, def: &FunctionDef) -> Option { let param_names = def.arg_names(db); let res = param_names? .iter() @@ -149,7 +149,7 @@ fn function_arg_names(db: &dyn SourceDatabase, def: &FunctionDef) -> Option Arc; } @@ -78,7 +78,7 @@ impl CommonTestLoader for crate::RootDatabase { should_request_groups, file_abstract_forms: module_ast.ast.clone(), }; - match erlang_service.ct_info(module, request, || self.unwind_if_cancelled()) { + match erlang_service.ct_info(module, request, || self.unwind_if_revision_cancelled()) { Ok(result) => match result.all() { Ok(all) => match result.groups() { Ok(groups) => CommonTestInfo::Result { all, groups }, diff --git a/crates/ide_db/src/docs.rs b/crates/ide_db/src/docs.rs index dfe07a2916..397bbe6afd 100644 --- a/crates/ide_db/src/docs.rs +++ b/crates/ide_db/src/docs.rs @@ -15,8 +15,8 @@ use std::sync::Arc; use std::vec; use elp_base_db::FileId; +use elp_base_db::RootQueryDb; use elp_base_db::SourceDatabase; -use elp_base_db::SourceDatabaseExt; use elp_base_db::Upcast; use elp_base_db::salsa; use elp_erlang_service::DocDiagnostic; @@ -183,8 +183,8 @@ pub struct FileDoc { } // TODO Add an input so we know when to invalidate? -#[salsa::query_group(DocDatabaseStorage)] -pub trait DocDatabase: DefDatabase + SourceDatabase + DocLoader + Upcast { +#[ra_ap_query_group_macro::query_group] +pub trait DocDatabase: DefDatabase + RootQueryDb + DocLoader + Upcast { #[salsa::invoke(get_file_docs)] fn file_doc(&self, file_id: FileId) -> Arc; @@ -237,7 +237,7 @@ fn is_file_in_otp(db: &dyn DocDatabase, file_id: FileId) -> Option { let _ = stdx::panic_context::enter(format!("\nis_file_in_otp: {:?}", file_id)); if let Some(app_data) = db.file_app_data(file_id) { let project_id = app_data.project_id; - Some(db.project_data(project_id).otp_project_id == Some(project_id)) + Some(db.project_data(project_id).project_data(db).otp_project_id == Some(project_id)) } else { log::error!( "Unknown application - could not load app_data to determine whether file is on OTP" @@ -335,12 +335,12 @@ fn get_file_function_specs(def_db: &dyn DefDatabase, file_id: FileId) -> FxHashM impl DocLoader for crate::RootDatabase { fn load_doc_descriptions(&self, file_id: FileId, doc_origin: DocOrigin) -> FileDoc { - _ = SourceDatabaseExt::file_text(self, file_id); // Take dependency on the contents of the file we're getting docs for + _ = SourceDatabase::file_text(self, file_id); // Take dependency on the contents of the file we're getting docs for // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nload_doc_descriptions: {:?}", file_id)); - let root_id = self.file_source_root(file_id); - let root = self.source_root(root_id); - let src_db: &dyn SourceDatabase = self.upcast(); + let root_id = self.file_source_root(file_id).source_root_id(self); + let root = self.source_root(root_id).source_root(self); + let src_db: &dyn RootQueryDb = self.upcast(); let app_data = if let Some(app_data) = src_db.file_app_data(file_id) { app_data } else { @@ -373,7 +373,8 @@ impl DocLoader for crate::RootDatabase { ast: None, }, }; - let raw_doc = erlang_service.request_doc(doc_request, || src_db.unwind_if_cancelled()); + let raw_doc = + erlang_service.request_doc(doc_request, || src_db.unwind_if_revision_cancelled()); match raw_doc { Ok(d) => FileDoc { module_doc: Some(Doc { diff --git a/crates/ide_db/src/eqwalizer.rs b/crates/ide_db/src/eqwalizer.rs index 808e570ab3..3e13f50ba9 100644 --- a/crates/ide_db/src/eqwalizer.rs +++ b/crates/ide_db/src/eqwalizer.rs @@ -15,6 +15,7 @@ use elp_base_db::FileRange; use elp_base_db::FileSource; use elp_base_db::ModuleName; use elp_base_db::ProjectId; +use elp_base_db::RootQueryDb; use elp_base_db::SourceDatabase; use elp_base_db::VfsPath; use elp_base_db::salsa; @@ -52,8 +53,9 @@ impl EqwalizerLoader for crate::RootDatabase { None => { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\ntypecheck: {:?}", module)); - let source_root_id = self.file_source_root(module); - let source_root = self.source_root(source_root_id); + let source_root_id = + SourceDatabase::file_source_root(self, module).source_root_id(self); + let source_root = self.source_root(source_root_id).source_root(self); let path = source_root.path_for_file(&module); log::error!("Can't find module for path: {:?}", path); continue; @@ -64,11 +66,11 @@ impl EqwalizerLoader for crate::RootDatabase { } } -#[salsa::query_group(EqwalizerDatabaseStorage)] +#[ra_ap_query_group_macro::query_group] pub trait EqwalizerDatabase: EqwalizerDiagnosticsDatabase + EqwalizerAnalysesDatabase - + SourceDatabase + + RootQueryDb + EqwalizerLoader + ErlAstDatabase { @@ -175,7 +177,7 @@ fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId, include_tes return false; }; let project_id = app_data.project_id; - let project = db.project_data(project_id); + let project = db.project_data(project_id).project_data(db); let eqwalizer_config = &project.eqwalizer_config; let module_index = db.module_index(project_id); let is_src = module_index.file_source_for_file(file_id) == Some(FileSource::Src); @@ -240,17 +242,21 @@ fn find_path_in_project( project_id: ProjectId, path: &VfsPath, ) -> Option { - let project = db.project_data(project_id); + let project = db.project_data(project_id).project_data(db); let source_roots = &project.source_roots; let mut otp_source_roots = Vec::new(); if let Some(otp_project_id) = project.otp_project_id { - let otp_project = db.project_data(otp_project_id); + let otp_project = db.project_data(otp_project_id).project_data(db); otp_source_roots = otp_project.source_roots.clone(); } source_roots .iter() .chain(&otp_source_roots) - .find_map(|&source_root_id| db.source_root(source_root_id).file_for_path(path)) + .find_map(|&source_root_id| { + db.source_root(source_root_id) + .source_root(db) + .file_for_path(path) + }) } fn decl_location( @@ -263,8 +269,8 @@ fn decl_location( let module_file_id = module_index.file_for_module(&module)?; // Context for T171541590 let _ = stdx::panic_context::enter(format!("\ndecl_location: {:?}", module_file_id)); - let source_root_id = db.file_source_root(module_file_id); - let source_root = db.source_root(source_root_id); + let source_root_id = db.file_source_root(module_file_id).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); let decl_file_path = &source_root.path_for_file(&module_file_id)?; let file_id = find_path_in_project(db, project_id, decl_file_path)?; let range: elp_syntax::TextRange = { diff --git a/crates/ide_db/src/erl_ast.rs b/crates/ide_db/src/erl_ast.rs index ddcf597854..bdeb6fd516 100644 --- a/crates/ide_db/src/erl_ast.rs +++ b/crates/ide_db/src/erl_ast.rs @@ -13,9 +13,9 @@ use std::sync::Arc; use elp_base_db::AbsPath; use elp_base_db::FileId; -use elp_base_db::FileLoader; use elp_base_db::IncludeCtx; use elp_base_db::ProjectId; +use elp_base_db::RootQueryDb; use elp_base_db::SourceDatabase; use elp_base_db::path_for_file; use elp_base_db::salsa; @@ -61,7 +61,7 @@ impl AstLoader for crate::RootDatabase { CompileOption::ElpMetadata(elp_metadata), ]; let path: PathBuf = path.to_path_buf().into(); - let file_text = self.file_text(file_id); + let file_text = SourceDatabase::file_text(self, file_id).text(self); let req = ParseRequest { options, file_id, @@ -73,14 +73,14 @@ impl AstLoader for crate::RootDatabase { erlang_service.request_parse( req, - || self.unwind_if_cancelled(), + || self.unwind_if_revision_cancelled(), &move |file_id, include_type, path| resolve_include(self, file_id, include_type, path), ) } } fn resolve_include( - db: &dyn SourceDatabase, + db: &dyn RootQueryDb, file_id: FileId, include_type: IncludeType, path: &str, @@ -91,11 +91,15 @@ fn resolve_include( IncludeType::Doc => IncludeCtx::new(db, file_id).resolve_include_doc(path)?, }; let path = path_for_file(db, include_file_id).map(|vfs_path| vfs_path.to_string())?; - Some((path, include_file_id, db.file_text(include_file_id))) + Some(( + path, + include_file_id, + db.file_text(include_file_id).text(db), + )) } -#[salsa::query_group(ErlAstDatabaseStorage)] -pub trait ErlAstDatabase: SourceDatabase + AstLoader + LineIndexDatabase { +#[ra_ap_query_group_macro::query_group(ErlAstDatabaseStorage)] +pub trait ErlAstDatabase: RootQueryDb + AstLoader + LineIndexDatabase { fn module_ast(&self, file_id: FileId) -> Arc; fn elp_metadata(&self, file_id: FileId) -> Metadata; } @@ -103,8 +107,8 @@ pub trait ErlAstDatabase: SourceDatabase + AstLoader + LineIndexDatabase { fn module_ast(db: &dyn ErlAstDatabase, file_id: FileId) -> Arc { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nmodule_ast: {:?}", file_id)); - let root_id = db.file_source_root(file_id); - let root = db.source_root(root_id); + let root_id = db.file_source_root(file_id).source_root_id(db); + let root = db.source_root(root_id).source_root(db); let path = root.path_for_file(&file_id).unwrap().as_path().unwrap(); let metadata = db.elp_metadata(file_id); let app_data = if let Some(app_data) = db.file_app_data(file_id) { @@ -129,7 +133,7 @@ fn module_ast(db: &dyn ErlAstDatabase, file_id: FileId) -> Arc { fn elp_metadata(db: &dyn ErlAstDatabase, file_id: FileId) -> Metadata { let line_index = db.file_line_index(file_id); - let file_text = db.file_text(file_id); + let file_text = db.file_text(file_id).text(db); let source = db.parse(file_id); metadata::collect_metadata(&line_index, &file_text, &source) } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 7320907e34..a84795b6f5 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -15,13 +15,24 @@ use std::path::PathBuf; use std::sync::Arc; use elp_base_db::AbsPathBuf; +use elp_base_db::AppData; +use elp_base_db::AppDataId; +use elp_base_db::AppDataInput; use elp_base_db::FileId; -use elp_base_db::FileLoader; -use elp_base_db::FileLoaderDelegate; use elp_base_db::FilePosition; use elp_base_db::FileRange; +use elp_base_db::FileSourceRootInput; +use elp_base_db::FileText; +use elp_base_db::Files; +use elp_base_db::ProjectData; +use elp_base_db::ProjectDataInput; use elp_base_db::ProjectId; +use elp_base_db::RootQueryDb; +use elp_base_db::SourceAppDataInput; use elp_base_db::SourceDatabase; +use elp_base_db::SourceRoot; +use elp_base_db::SourceRootId; +use elp_base_db::SourceRootInput; use elp_base_db::Upcast; use elp_base_db::limit_logged_string; use elp_base_db::salsa; @@ -100,22 +111,11 @@ pub trait EqwalizerProgressReporter: Send + Sync + RefUnwindSafe { fn done_module(&mut self, module: &str); } -#[salsa::database( - LineIndexDatabaseStorage, - docs::DocDatabaseStorage, - elp_base_db::SourceDatabaseExtStorage, - elp_base_db::SourceDatabaseStorage, - eqwalizer::EqwalizerDatabaseStorage, - common_test::CommonTestDatabaseStorage, - elp_eqwalizer::db::EqwalizerDiagnosticsDatabaseStorage, - elp_eqwalizer::analyses::EqwalizerAnalysesDatabaseStorage, - erl_ast::ErlAstDatabaseStorage, - hir::db::InternDatabaseStorage, - hir::db::DefDatabaseStorage -)] +#[salsa::db] #[allow(clippy::type_complexity)] pub struct RootDatabase { storage: salsa::Storage, + files: Arc, erlang_services: Arc>>>, eqwalizer: Eqwalizer, eqwalizer_progress_reporter: EqwalizerProgressReporterBox, @@ -125,6 +125,7 @@ impl Default for RootDatabase { fn default() -> Self { let mut db = RootDatabase { storage: salsa::Storage::default(), + files: Arc::default(), erlang_services: Arc::default(), eqwalizer: Eqwalizer::default(), eqwalizer_progress_reporter: EqwalizerProgressReporterBox::default(), @@ -135,8 +136,23 @@ impl Default for RootDatabase { } } -impl Upcast for RootDatabase { - fn upcast(&self) -> &(dyn SourceDatabase + 'static) { +impl RefUnwindSafe for RootDatabase {} + +impl Clone for RootDatabase { + fn clone(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + erlang_services: self.erlang_services.clone(), + eqwalizer: self.eqwalizer.clone(), + eqwalizer_progress_reporter: self.eqwalizer_progress_reporter.clone(), + ipc_handles: self.ipc_handles.clone(), + } + } +} + +impl Upcast for RootDatabase { + fn upcast(&self) -> &(dyn RootQueryDb + 'static) { self } } @@ -153,37 +169,89 @@ impl Upcast for RootDatabase { } } -impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } -} - impl fmt::Debug for RootDatabase { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RootDatabase").finish_non_exhaustive() } } -impl salsa::Database for RootDatabase {} +#[salsa::db] +impl salsa::Database for RootDatabase { + fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {} +} -impl salsa::ParallelDatabase for RootDatabase { - fn snapshot(&self) -> salsa::Snapshot { - salsa::Snapshot::new(RootDatabase { - storage: self.storage.snapshot(), - erlang_services: self.erlang_services.clone(), - eqwalizer: self.eqwalizer.clone(), - eqwalizer_progress_reporter: self.eqwalizer_progress_reporter.clone(), - ipc_handles: self.ipc_handles.clone(), - }) +#[salsa::db] +impl SourceDatabase for RootDatabase { + fn file_text(&self, file_id: FileId) -> FileText { + self.files.file_text(file_id) + } + + fn set_file_text(&mut self, file_id: FileId, text: Arc) { + let files = self.files.clone(); + files.set_file_text(self, file_id, text); + } + + fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput { + self.files.source_root(source_root_id) + } + + fn set_source_root(&mut self, source_root_id: SourceRootId, source_root: Arc) { + let files = self.files.clone(); + files.set_source_root(self, source_root_id, source_root); + } + + fn file_source_root(&self, id: FileId) -> FileSourceRootInput { + self.files.file_source_root(id) + } + + fn set_file_source_root(&mut self, id: FileId, source_root_id: SourceRootId) { + let files = self.files.clone(); + files.set_file_source_root(self, id, source_root_id); + } + + fn app_data_by_id(&self, id: AppDataId) -> AppDataInput { + self.files.app_data(id) + } + + fn set_app_data_by_id(&mut self, id: AppDataId, app_data: Option>) { + let files = self.files.clone(); + files.set_app_data(self, id, app_data) + } + + fn app_data_id(&self, source_root_id: SourceRootId) -> SourceAppDataInput { + self.files.source_app_data(source_root_id) + } + + fn set_app_data_id(&mut self, id: SourceRootId, app_data_id: AppDataId) { + let files = self.files.clone(); + files.set_source_app_data(self, id, app_data_id) + } + + fn project_data(&self, project_id: ProjectId) -> ProjectDataInput { + self.files.project_data(project_id) + } + + fn set_project_data(&mut self, id: ProjectId, project_data: Arc) { + let files = self.files.clone(); + files.set_project_data(self, id, project_data) } } impl RootDatabase { + pub fn snapshot(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + erlang_services: self.erlang_services.clone(), + eqwalizer: self.eqwalizer.clone(), + eqwalizer_progress_reporter: self.eqwalizer_progress_reporter.clone(), + ipc_handles: self.ipc_handles.clone(), + } + } + pub fn request_cancellation(&mut self) { let _p = tracing::info_span!("RootDatabase::request_cancellation").entered(); - self.salsa_runtime_mut() - .synthetic_write(salsa::Durability::LOW); + self.synthetic_write(salsa::Durability::LOW); } pub fn clear_erlang_services(&mut self) { @@ -200,7 +268,7 @@ impl RootDatabase { .entry(project_id) .or_insert_with(|| { let conn = Connection::start().expect("failed to establish connection"); - let project_data = self.project_data(project_id); + let project_data = self.project_data(project_id).project_data(self); let path: Vec = project_data .deps_ebins .iter() @@ -217,7 +285,7 @@ impl RootDatabase { pub fn update_erlang_service_paths(&self) { for (&project_id, connection) in self.erlang_services.read().iter() { - let project_data = self.project_data(project_id); + let project_data = self.project_data(project_id).project_data(self); let paths = project_data .deps_ebins .iter() @@ -247,7 +315,7 @@ impl RootDatabase { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\nresolved_includes: {:?}", file_id)); let project_id = self.file_app_data(file_id)?.project_id; - let root_abs = &self.project_data(project_id).root_dir; + let root_abs = &self.project_data(project_id).project_data(self).root_dir; let form_list = self.file_form_list(file_id); let line_index = self.file_line_index(file_id); let includes: Vec<_> = form_list @@ -279,13 +347,13 @@ impl RootDatabase { } } -#[salsa::query_group(LineIndexDatabaseStorage)] -pub trait LineIndexDatabase: SourceDatabase { +#[ra_ap_query_group_macro::query_group(LineIndexDatabaseStorage)] +pub trait LineIndexDatabase: RootQueryDb { fn file_line_index(&self, file_id: FileId) -> Arc; } fn file_line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc { - let text = db.file_text(file_id); + let text = db.file_text(file_id).text(db); Arc::new(LineIndex::new(&text)) } @@ -315,8 +383,8 @@ impl Includes { ) -> Option { // Context for T171541590 let _ = stdx::panic_context::enter(format!("\napp_file_path: {:?}", file_id)); - let root_id = db.file_source_root(file_id); - let root = db.source_root(root_id); + let root_id = db.file_source_root(file_id).source_root_id(db); + let root = db.source_root(root_id).source_root(db); let path = root.path_for_file(&file_id)?; let app_data = db.file_app_data(file_id)?; @@ -417,7 +485,7 @@ impl TypedSemantic for RootDatabase { #[cfg(test)] mod tests { - use elp_base_db::SourceDatabase; + use elp_base_db::RootQueryDb; use elp_base_db::fixture::WithFixture; use elp_text_edit::TextRange; diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index cdac400688..7d792fbf06 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -117,9 +117,10 @@ impl SearchScope { pub fn project(db: &dyn DefDatabase, project_id: ProjectId) -> SearchScope { let mut entries = FxHashMap::default(); - for &source_root_id in &db.project_data(project_id).source_roots { + for &source_root_id in &db.project_data(project_id).project_data(db).source_roots { entries.extend( db.source_root(source_root_id) + .source_root(db) .iter() .map(|file_id| (file_id, None)), ) @@ -283,7 +284,7 @@ impl<'a> FindUsages<'a> { scope: &'a SearchScope, ) -> impl Iterator, FileId, TextRange)> + 'a { scope.entries.iter().map(move |(&file_id, &search_range)| { - let text = sema.db.file_text(file_id); + let text = sema.db.file_text(file_id).text(sema.db); let search_range = search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); diff --git a/crates/ide_ssr/src/lib.rs b/crates/ide_ssr/src/lib.rs index 3a04cd9037..af5307fa0b 100644 --- a/crates/ide_ssr/src/lib.rs +++ b/crates/ide_ssr/src/lib.rs @@ -61,7 +61,7 @@ use std::sync::Arc; use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileRange; -use elp_ide_db::elp_base_db::SourceDatabaseExt; +use elp_ide_db::elp_base_db::SourceDatabase; use elp_syntax::AstNode; use elp_syntax::SyntaxNode; use elp_syntax::TextRange; @@ -428,7 +428,7 @@ impl<'a> MatchFinder<'a> { pub fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec { let file = self.sema.parse(file_id); let mut res = Vec::new(); - let file_text = self.sema.db.file_text(file_id); + let file_text = self.sema.db.file_text(file_id).text(self.sema.db); let mut remaining_text = &*file_text; let mut base = 0; let len = snippet.len() as u32; @@ -530,7 +530,7 @@ impl SsrMatches { impl Match { pub fn matched_text(&self, db: &RootDatabase) -> String { - let file_text = db.file_text(self.range.file_id); + let file_text = SourceDatabase::file_text(db, self.range.file_id).text(db); file_text[self.range.range.start().into()..self.range.range.end().into()].to_string() } @@ -755,7 +755,7 @@ mod test { }, placeholders_by_var: { Var( - 0, + Id(3c00), ): { AnyExprId( Expr( @@ -841,7 +841,7 @@ mod test { }, placeholders_by_var: { Var( - 0, + Id(3c00), ): { AnyExprId( Expr( diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs index 108680853e..5b42e30ca1 100644 --- a/crates/ide_ssr/src/tests.rs +++ b/crates/ide_ssr/src/tests.rs @@ -1570,7 +1570,7 @@ fn ssr_predicates_on_match_expr_pat() { expect![[r#" Some( Var( - 3, + Id(3803), ), ) "#]] @@ -1584,7 +1584,7 @@ fn ssr_predicates_on_match_expr_pat() { expect![[r#" Some( Atom( - 1, + Id(6801), ), ) "#]] @@ -1596,7 +1596,7 @@ fn ssr_predicates_on_match_expr_pat() { expect![[r#" Some( Var( - 3, + Id(3803), ), ) "#]] @@ -1609,7 +1609,7 @@ fn ssr_predicates_on_match_expr_pat() { expect![[r#" Some( Var( - 3, + Id(3803), ), ) "#]] @@ -1623,7 +1623,7 @@ fn ssr_predicates_on_match_expr_pat() { expect![[r#" Some( Atom( - 2, + Id(6802), ), ) "#]] From babe31bb6f81572879cc8eb39655ce7ef829534e Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Mon, 14 Jul 2025 03:12:31 -0700 Subject: [PATCH 006/394] Keep original buck TargetFullName in AppData Summary: We use `AppData` as our general representation of an Erlang application in ELP, originally in the rebar3 sense. The ELP design standardises on this information, whatever the origin of the project data in an ELP Project. For a buck2 project, we use the last part of a buck2 target as the `AppData::name`. So the buck target `cell/path/to/app:app_name` would give rise to "app_name". In some circumstances involving test fixtures we cannot guarantee that the derived "app_name" is unique. This diff adds the original full buck target name to `AppData`, so this can be used as needed. It is used in a later diff, still unpublished. Reviewed By: TD5 Differential Revision: D78160505 fbshipit-source-id: 3b14d50709328b53079d648c5eb2ae6a86311d6d --- crates/base_db/src/fixture.rs | 4 ++++ crates/base_db/src/input.rs | 4 ++++ crates/project_model/src/buck.rs | 1 + crates/project_model/src/eqwalizer_support.rs | 1 + crates/project_model/src/json.rs | 1 + crates/project_model/src/lib.rs | 5 +++++ crates/project_model/src/no_manifest.rs | 1 + crates/project_model/src/rebar.rs | 1 + crates/project_model/src/test_fixture.rs | 1 + 9 files changed, 19 insertions(+) diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 9e56edef89..1c77eda18e 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -493,6 +493,7 @@ bar() -> ?FOO. name: AppName( "test-fixture", ), + buck_target_name: None, dir: AbsPathBuf( "/", ), @@ -531,6 +532,7 @@ bar() -> ?FOO. name: AppName( "comp", ), + buck_target_name: None, dir: AbsPathBuf( "/opt/lib/comp-1.3", ), @@ -580,6 +582,7 @@ bar() -> ?FOO. name: AppName( "foo-app", ), + buck_target_name: None, dir: AbsPathBuf( "/", ), @@ -753,6 +756,7 @@ foo() -> ?BAR. name: AppName( "test-fixture", ), + buck_target_name: None, dir: AbsPathBuf( "/extra", ), diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index ef672ff5af..75223bc3bd 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs @@ -20,6 +20,7 @@ use elp_project_model::EqwalizerConfig; use elp_project_model::Project; use elp_project_model::ProjectAppData; use elp_project_model::buck::IncludeMapping; +use elp_project_model::buck::TargetFullName; use fxhash::FxHashMap; use paths::RelPath; use paths::Utf8Path; @@ -124,6 +125,8 @@ pub struct ProjectData { pub struct AppData { pub project_id: ProjectId, pub name: AppName, + /// Target name if this application originates from a buck target + pub buck_target_name: Option, pub dir: AbsPathBuf, /// Include directories belonging to this app only. Used for /// include_lib resolution @@ -365,6 +368,7 @@ impl<'a> ProjectApps<'a> { let input_data = AppData { project_id, name: app.name.clone(), + buck_target_name: app.buck_target_name.clone(), dir: app.dir.clone(), include_dirs: app.include_dirs.clone(), include_path: app.include_path.clone(), diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 975f836700..35a40ce85e 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -844,6 +844,7 @@ fn targets_to_project_data_bxl( }; let project_app_data = ProjectAppData { name: target.app_name.clone(), + buck_target_name: Some(target.name.clone()), dir: target.dir.clone(), ebin: None, extra_src_dirs: extra_src_dirs.into_iter().collect(), diff --git a/crates/project_model/src/eqwalizer_support.rs b/crates/project_model/src/eqwalizer_support.rs index c9e34b3430..9a341a38ec 100644 --- a/crates/project_model/src/eqwalizer_support.rs +++ b/crates/project_model/src/eqwalizer_support.rs @@ -36,6 +36,7 @@ pub(crate) fn eqwalizer_suppport_data(otp_root: &AbsPath) -> ProjectAppData { ProjectAppData { name: AppName("eqwalizer_support".to_string()), + buck_target_name: None, dir: eqwalizer_support.clone(), include_dirs: vec![], abs_src_dirs: vec![eqwalizer_support.join("src")], diff --git a/crates/project_model/src/json.rs b/crates/project_model/src/json.rs index 41d2acfc0e..2861f2a621 100644 --- a/crates/project_model/src/json.rs +++ b/crates/project_model/src/json.rs @@ -92,6 +92,7 @@ impl JsonProjectAppData { }; Ok(ProjectAppData { name: AppName(self.name.clone()), + buck_target_name: None, dir, ebin, extra_src_dirs: self.extra_src_dirs.clone(), diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 746ae7b67e..cc8dc10c5a 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -30,6 +30,7 @@ use anyhow::bail; use buck::BuckConfig; use buck::BuckQueryConfig; use buck::IncludeMapping; +use buck::TargetFullName; use elp_log::timeit; use fxhash::FxHashMap; use fxhash::FxHashSet; @@ -832,6 +833,8 @@ pub enum AppType { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProjectAppData { pub name: AppName, + /// Target name if this application originates from a buck target + pub buck_target_name: Option, pub dir: AbsPathBuf, pub ebin: Option, pub extra_src_dirs: Vec, @@ -862,6 +865,7 @@ impl ProjectAppData { ) -> ProjectAppData { ProjectAppData { name, + buck_target_name: None, ebin: None, extra_src_dirs, include_dirs, @@ -886,6 +890,7 @@ impl ProjectAppData { let abs_src_dir = dir.join("src"); Self { name: AppName(name.to_string()), + buck_target_name: None, ebin: Some(dir.join("ebin")), extra_src_dirs: vec![], // This makes sure files in ./include are loaded into VFS diff --git a/crates/project_model/src/no_manifest.rs b/crates/project_model/src/no_manifest.rs index 23b00ae1c0..402d27d9c5 100644 --- a/crates/project_model/src/no_manifest.rs +++ b/crates/project_model/src/no_manifest.rs @@ -58,6 +58,7 @@ impl NoManifestConfig { pub fn to_project_app_data(&self, otp_root: &AbsPath) -> Vec { let mut data = ProjectAppData { name: self.name.clone(), + buck_target_name: None, dir: self.root_path.clone(), include_dirs: self.include_dirs.clone(), abs_src_dirs: self.abs_src_dirs.clone(), diff --git a/crates/project_model/src/rebar.rs b/crates/project_model/src/rebar.rs index 138e7e2469..14468e5fa8 100644 --- a/crates/project_model/src/rebar.rs +++ b/crates/project_model/src/rebar.rs @@ -187,6 +187,7 @@ impl RebarProject { .collect::>()?; Ok(ProjectAppData { name: AppName(into_string(map_pop(&mut term, "name")?)?), + buck_target_name: None, dir, ebin: map_pop(&mut term, "ebin") .ok() diff --git a/crates/project_model/src/test_fixture.rs b/crates/project_model/src/test_fixture.rs index 5b2b8e9e29..b1dbd93d6c 100644 --- a/crates/project_model/src/test_fixture.rs +++ b/crates/project_model/src/test_fixture.rs @@ -953,6 +953,7 @@ bar() -> ok. name: AppName( "test-fixture", ), + buck_target_name: None, dir: AbsPathBuf( "/", ), From 8d73b1dc45c99aba08e449d9b6fdfbb0e8c34a25 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Mon, 14 Jul 2025 09:36:14 -0700 Subject: [PATCH 007/394] Properly clear diagnostics when a file is closed Summary: When we process a `DidCloseTextDocument` we sent a `PublishDocuments` notification with empty diagnostics to the server, to clear its diagnostic store. But if at the time of closing the file we are calculating diagnostics for it, these will be sent when the diagnostic calculation ends. This diff ensures we clear all current diagnostics (except eqwalizer project ones, if enabled) when we close the file, and does not send diagnostics for unopened files. Reviewed By: TD5 Differential Revision: D78275536 fbshipit-source-id: f4d380e0022102dcb74151f027df4d5b0cdd58c5 --- crates/elp/src/server.rs | 43 ++++++++++++++---------- crates/ide/src/diagnostics_collection.rs | 14 ++++++++ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/crates/elp/src/server.rs b/crates/elp/src/server.rs index 988529b585..972824d529 100644 --- a/crates/elp/src/server.rs +++ b/crates/elp/src/server.rs @@ -566,16 +566,23 @@ impl Server { break; } }; + let (in_mem, version) = match convert::vfs_path(&url) { + Ok(path) => match self.mem_docs.read().get(&path).cloned() { + Some(d) => (true, Some(d.version)), + None => (false, None), + }, + Err(_) => (false, None), + }; + if !in_mem { + // Clear all but eqwalizer project diagnostics + Arc::make_mut(&mut self.diagnostics).clear(*file_id); + } let diagnostics = self .diagnostics .diagnostics_for(*file_id) .iter() .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) .collect(); - let version = convert::vfs_path(&url) - .map(|path| self.mem_docs.read().get(&path).cloned()) - .unwrap_or_default() - .map(|doc_info| doc_info.version); self.send_notification::( lsp_types::PublishDiagnosticsParams { @@ -762,20 +769,22 @@ impl Server { this.vfs_loader.handle.invalidate(path.to_path_buf()); } - // If project-wide diagnostics are enabled, ensure we don't lose the eqwalizer ones. - if this.config.eqwalizer().all { - let vfs = this.vfs.read(); - if let Some((file_id, _)) = vfs.file_id(&path) { + let vfs = this.vfs.read(); + if let Some((file_id, _)) = vfs.file_id(&path) { + // If project-wide diagnostics are enabled, ensure we don't lose the eqwalizer ones. + if this.config.eqwalizer().all { Arc::make_mut(&mut this.diagnostics) .move_eqwalizer_diagnostics_to_project_diagnostics(file_id); - if let Ok(line_index) = analysis.line_index(file_id) { - diagnostics = this - .diagnostics - .project_diagnostics_for(file_id) - .iter() - .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) - .collect() - } + } + Arc::make_mut(&mut this.diagnostics) + .clear(file_id); + if let Ok(line_index) = analysis.line_index(file_id) { + diagnostics = this + .diagnostics + .diagnostics_for(file_id) + .iter() + .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) + .collect() } } } @@ -952,7 +961,7 @@ impl Server { } if !opened { // This call will add the file to the changed_files, picked - // up in `process_changes`. + // up in `process_changes`, if it has changed. vfs.set_file_contents(path, contents); } } diff --git a/crates/ide/src/diagnostics_collection.rs b/crates/ide/src/diagnostics_collection.rs index 26b6098b2f..d944e223da 100644 --- a/crates/ide/src/diagnostics_collection.rs +++ b/crates/ide/src/diagnostics_collection.rs @@ -85,6 +85,20 @@ impl DiagnosticCollection { } } + /// Reset all diagnostics for a given file, by explicitly setting + /// them to an empty list. The LSP server (ELP) is responsible + /// for the diagnostic database for a file, so we need to + /// explicitly clear them when the file closes. This function + /// does not clear `eqwalizer_project` ones, as these are intended + /// to be valid regardless of whether the file is open or not. + pub fn clear(&mut self, file_id: FileId) { + self.set_native(file_id, LabeledDiagnostics::default()); + self.set_eqwalizer(file_id, vec![]); + self.set_edoc(file_id, vec![]); + self.set_ct(file_id, vec![]); + self.set_erlang_service(file_id, LabeledDiagnostics::default()); + } + pub fn diagnostics_for(&self, file_id: FileId) -> Vec { let empty_diags = LabeledDiagnostics::default(); let native = self.native.get(&file_id).unwrap_or(&empty_diags); From 748ecf93ecb3ba37aaa90fba8b5ffc57f318135b Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Tue, 15 Jul 2025 09:59:27 -0700 Subject: [PATCH 008/394] BE: Fix new clippy warnings from rust 1.88.0 Summary: We have moved to `cargo 1.88.0 (873a06493 2025-05-10)` internally. This has enabled a check for `uninlined_format_args`. Update the local clippy.toml to match this, and run ``` cargo clippy --workspace --tests --fix ``` to generate this diff This may be related to https://github.com/rust-lang/rust-clippy/issues/14694 Reviewed By: TD5 Differential Revision: D78345648 fbshipit-source-id: a218be720e34b06ed08c52b5f7db936940bc2c37 --- crates/base_db/src/fixture.rs | 4 +- crates/base_db/src/include.rs | 2 +- crates/base_db/src/lib.rs | 16 ++--- crates/base_db/src/test_utils.rs | 4 +- crates/elp/build.rs | 22 +++---- crates/elp/src/bin/config_stanza.rs | 2 +- crates/elp/src/bin/elp_parse_cli.rs | 17 +++-- crates/elp/src/bin/eqwalizer_cli.rs | 25 ++++---- crates/elp/src/bin/erlang_service_cli.rs | 2 +- crates/elp/src/bin/glean.rs | 30 ++++----- crates/elp/src/bin/lint_cli.rs | 13 ++-- crates/elp/src/bin/main.rs | 59 +++++++----------- crates/elp/src/bin/reporting.rs | 16 ++--- crates/elp/src/bin/shell.rs | 24 +++---- crates/elp/src/build/load.rs | 2 +- crates/elp/src/config.rs | 18 +++--- crates/elp/src/convert.rs | 2 +- crates/elp/src/handlers.rs | 6 +- crates/elp/src/reload.rs | 2 +- crates/elp/src/server.rs | 48 ++++++-------- crates/elp/src/server/dispatch.rs | 2 +- crates/elp/src/server/progress.rs | 4 +- crates/elp/src/server/setup.rs | 6 +- crates/elp/src/server/telemetry_manager.rs | 6 +- crates/elp/src/snapshot.rs | 17 +++-- crates/elp/tests/slow-tests/support.rs | 20 +++--- crates/elp_log/src/file.rs | 4 +- crates/eqwalizer/build.rs | 12 +--- crates/eqwalizer/src/ast/mod.rs | 18 +++--- crates/eqwalizer/src/ast/preprocess.rs | 2 +- crates/eqwalizer/src/ast/trans_valid.rs | 2 +- crates/eqwalizer/src/db.rs | 3 +- crates/eqwalizer/src/ipc.rs | 4 +- crates/eqwalizer/src/lib.rs | 35 ++++------- crates/erlang_service/build.rs | 9 +-- crates/erlang_service/src/common_test.rs | 2 +- crates/erlang_service/src/lib.rs | 46 +++++--------- crates/hir/src/body.rs | 2 +- crates/hir/src/body/pretty.rs | 62 +++++++++---------- crates/hir/src/body/tree_print.rs | 56 ++++++++--------- crates/hir/src/edoc.rs | 4 +- crates/hir/src/fold.rs | 8 +-- crates/hir/src/form_list/pretty.rs | 14 ++--- crates/hir/src/include.rs | 6 +- crates/hir/src/lib.rs | 7 +-- crates/hir/src/macro_exp.rs | 7 +-- crates/hir/src/module_data.rs | 8 +-- crates/hir/src/name.rs | 6 +- crates/hir/src/sema.rs | 4 +- crates/hir/src/sema/to_def.rs | 2 +- crates/ide/src/codemod_helpers.rs | 4 +- crates/ide/src/diagnostics.rs | 8 +-- .../ide/src/diagnostics/boolean_precedence.rs | 2 +- crates/ide/src/diagnostics/edoc.rs | 9 +-- .../expression_can_be_simplified.rs | 2 +- crates/ide/src/diagnostics/head_mismatch.rs | 4 +- .../diagnostics/map_insertion_to_syntax.rs | 4 +- .../ide/src/diagnostics/missing_separator.rs | 4 +- .../src/diagnostics/misspelled_attribute.rs | 8 +-- crates/ide/src/diagnostics/module_mismatch.rs | 2 +- .../nonstandard_integer_formatting.rs | 2 +- .../ide/src/diagnostics/record_tuple_match.rs | 2 +- crates/ide/src/diagnostics/replace_call.rs | 2 +- .../ide/src/diagnostics/undefined_function.rs | 4 +- .../ide/src/diagnostics/unspecific_include.rs | 4 +- .../src/diagnostics/unused_function_args.rs | 4 +- crates/ide/src/diagnostics/unused_include.rs | 4 +- crates/ide/src/doc_links/otp_links.rs | 5 +- crates/ide/src/document_symbols.rs | 2 +- crates/ide/src/fixture.rs | 2 +- crates/ide/src/handlers/goto_definition.rs | 2 +- crates/ide/src/handlers/references.rs | 5 +- crates/ide/src/lib.rs | 6 +- crates/ide/src/rename.rs | 6 +- crates/ide/src/runnables.rs | 6 +- crates/ide/src/tests.rs | 3 +- crates/ide_assists/src/handlers/add_doc.rs | 16 ++--- crates/ide_assists/src/handlers/add_format.rs | 2 +- crates/ide_assists/src/handlers/add_spec.rs | 6 +- .../src/handlers/extract_function.rs | 2 +- .../src/handlers/extract_variable.rs | 2 +- .../src/handlers/implement_behaviour.rs | 3 +- .../src/handlers/inline_function.rs | 5 +- crates/ide_assists/src/helpers.rs | 2 +- crates/ide_assists/src/tests.rs | 6 +- crates/ide_completion/src/helpers.rs | 4 +- crates/ide_completion/src/macros.rs | 2 +- crates/ide_completion/src/tests.rs | 2 +- crates/ide_db/src/apply_change.rs | 2 +- crates/ide_db/src/assists.rs | 2 +- crates/ide_db/src/common_test.rs | 2 +- crates/ide_db/src/diagnostic_code.rs | 5 +- crates/ide_db/src/docs.rs | 8 +-- crates/ide_db/src/eqwalizer.rs | 8 +-- crates/ide_db/src/erl_ast.rs | 2 +- crates/ide_db/src/lib.rs | 4 +- crates/ide_db/src/rename.rs | 6 +- crates/ide_db/src/search.rs | 2 +- crates/ide_ssr/src/lib.rs | 5 +- crates/project_model/src/buck.rs | 9 ++- crates/project_model/src/json.rs | 4 +- crates/project_model/src/lib.rs | 17 +++-- crates/project_model/src/otp.rs | 4 +- crates/project_model/src/test_fixture.rs | 28 ++++----- crates/syntax/src/algo.rs | 5 +- crates/syntax/src/ast/edit.rs | 2 +- crates/syntax/src/ast/node_ext.rs | 14 ++--- crates/syntax/src/ast/traits.rs | 4 +- crates/syntax/src/lib.rs | 10 +-- crates/syntax/src/ptr.rs | 2 +- crates/syntax/src/syntax_error.rs | 2 +- crates/types_db/src/eqwalizer/mod.rs | 2 +- crates/types_db/src/eqwalizer/types.rs | 6 +- xtask/src/codegen.rs | 7 +-- xtask/src/main.rs | 2 +- 115 files changed, 451 insertions(+), 563 deletions(-) diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 1c77eda18e..22e6c85c97 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -142,7 +142,7 @@ impl Builder { fn absolute_path(&self, path: String) -> String { if let Some(project_dir) = &self.project_dir { let project_dir_str = project_dir.path().as_os_str().to_str().unwrap(); - format!("{}/{}", project_dir_str, path) + format!("{project_dir_str}/{path}") } else { path } @@ -272,7 +272,7 @@ impl ChangeFixture { write!(tmp_file, "{}", &text).unwrap(); } - let json_config_file = format!("{}/build_info.json", project_dir_str); + let json_config_file = format!("{project_dir_str}/build_info.json"); let mut writer = File::create(&json_config_file).unwrap(); diff --git a/crates/base_db/src/include.rs b/crates/base_db/src/include.rs index c561a6abb7..458db14773 100644 --- a/crates/base_db/src/include.rs +++ b/crates/base_db/src/include.rs @@ -28,7 +28,7 @@ pub struct IncludeCtx<'a> { impl<'a> IncludeCtx<'a> { pub fn new(db: &'a dyn RootQueryDb, file_id: FileId) -> Self { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nIncludeCtx::new: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nIncludeCtx::new: {file_id:?}")); let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); Self { diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 7999ce6674..5f32cc358b 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -549,7 +549,7 @@ fn build_include_file_index( include_file_index.add(path, file_id); } } else { - log::warn!("No file path for {:?}", file_id); + log::warn!("No file path for {file_id:?}"); } } } @@ -627,7 +627,7 @@ fn is_otp(db: &dyn RootQueryDb, file_id: FileId) -> Option { fn is_test_suite_or_test_helper(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nis_test_suite_or_test_helper: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nis_test_suite_or_test_helper: {file_id:?}")); let app_data = db.file_app_data(file_id)?; let root_id = db.file_source_root(file_id).source_root_id(db); let root = db.source_root(root_id).source_root(db); @@ -641,28 +641,28 @@ fn is_test_suite_or_test_helper(db: &dyn RootQueryDb, file_id: FileId) -> Option fn file_app_type(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nfile_app_type: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nfile_app_type: {file_id:?}")); let app_data = db.file_app_data(file_id)?; Some(app_data.app_type) } fn file_app_name(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nfile_app_name: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nfile_app_name: {file_id:?}")); let app_data = db.file_app_data(file_id)?; Some(app_data.name.clone()) } fn file_project_id(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nfile_project_id: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nfile_project_id: {file_id:?}")); let app_data = db.file_app_data(file_id)?; Some(app_data.project_id) } pub fn module_name(db: &dyn RootQueryDb, file_id: FileId) -> Option { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nmodule_name: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nmodule_name: {file_id:?}")); let app_data = db.file_app_data(file_id)?; let module_index = db.module_index(app_data.project_id); module_index.module_for_file(file_id).cloned() @@ -681,7 +681,7 @@ static ref IGNORED_SOURCES: Vec = { fn file_kind(db: &dyn RootQueryDb, file_id: FileId) -> FileKind { // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nfile_kind: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nfile_kind: {file_id:?}")); let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); let ignored_path = source_root @@ -693,7 +693,7 @@ fn file_kind(db: &dyn RootQueryDb, file_id: FileId) -> FileKind { }) .unwrap_or(false); // Context for T171541590 - let _ = stdx::panic_context::enter(format!("\nfile_kind: {:?}", file_id)); + let _ = stdx::panic_context::enter(format!("\nfile_kind: {file_id:?}")); if ignored_path { // not part of the known project model, and on list of ignored // sources, do not process diff --git a/crates/base_db/src/test_utils.rs b/crates/base_db/src/test_utils.rs index fd7250d105..fa478eb385 100644 --- a/crates/base_db/src/test_utils.rs +++ b/crates/base_db/src/test_utils.rs @@ -46,8 +46,8 @@ pub fn format_diff(chunks: Vec) -> String { for chunk in chunks { let formatted = match chunk { dissimilar::Chunk::Equal(text) => text.into(), - dissimilar::Chunk::Delete(text) => format!("\x1b[41m{}\x1b[0m", text), - dissimilar::Chunk::Insert(text) => format!("\x1b[42m{}\x1b[0m", text), + dissimilar::Chunk::Delete(text) => format!("\x1b[41m{text}\x1b[0m"), + dissimilar::Chunk::Insert(text) => format!("\x1b[42m{text}\x1b[0m"), }; buf.push_str(&formatted); } diff --git a/crates/elp/build.rs b/crates/elp/build.rs index d55fabfa0b..be24e95092 100644 --- a/crates/elp/build.rs +++ b/crates/elp/build.rs @@ -33,7 +33,7 @@ fn main() { OffsetDateTime::from_unix_timestamp(timestamp).expect("parsing SOURCE_DATE_EPOCH") } Err(std::env::VarError::NotPresent) => OffsetDateTime::now_utc(), - Err(e) => panic!("Error getting SOURCE_DATE_EPOCH: {}", e), + Err(e) => panic!("Error getting SOURCE_DATE_EPOCH: {e}"), }; date.format(&date_format).expect("formatting date") } else { @@ -43,20 +43,14 @@ fn main() { let cargo_manifest_dir = env::var(CARGO_MANIFEST_DIR) .expect("CARGO_MANIFEST_DIR should be set automatically by cargo"); let eqwalizer_support_dir = match eqwalizer_dir { - Ok(eqwalizer_support_dir) => format!("{}/../eqwalizer_support", eqwalizer_support_dir), - Err(_) => format!( - "{}/../../../eqwalizer/eqwalizer_support", - cargo_manifest_dir - ), + Ok(eqwalizer_support_dir) => format!("{eqwalizer_support_dir}/../eqwalizer_support"), + Err(_) => format!("{cargo_manifest_dir}/../../../eqwalizer/eqwalizer_support"), }; println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed={}", SOURCE_DATE_EPOCH); - println!("cargo:rerun-if-env-changed={}", CI); - println!("cargo:rustc-env=BUILD_ID={}", build_id); - println!( - "cargo:rustc-env={}={}", - EQWALIZER_SUPPORT_DIR, eqwalizer_support_dir - ); - println!("cargo:rerun-if-env-changed={}", EQWALIZER_DIR); + println!("cargo:rerun-if-env-changed={SOURCE_DATE_EPOCH}"); + println!("cargo:rerun-if-env-changed={CI}"); + println!("cargo:rustc-env=BUILD_ID={build_id}"); + println!("cargo:rustc-env={EQWALIZER_SUPPORT_DIR}={eqwalizer_support_dir}"); + println!("cargo:rerun-if-env-changed={EQWALIZER_DIR}"); } diff --git a/crates/elp/src/bin/config_stanza.rs b/crates/elp/src/bin/config_stanza.rs index 97e76f1b4f..a0f0650c6b 100644 --- a/crates/elp/src/bin/config_stanza.rs +++ b/crates/elp/src/bin/config_stanza.rs @@ -16,5 +16,5 @@ use crate::args::ConfigStanza; pub fn config_stanza(_args: &ConfigStanza, cli: &mut dyn Cli) -> Result<()> { let schema = format!("{:#}", Config::json_schema()); - Ok(writeln!(cli, "{}", schema)?) + Ok(writeln!(cli, "{schema}")?) } diff --git a/crates/elp/src/bin/elp_parse_cli.rs b/crates/elp/src/bin/elp_parse_cli.rs index e8d36aa247..6d29ba38e5 100644 --- a/crates/elp/src/bin/elp_parse_cli.rs +++ b/crates/elp/src/bin/elp_parse_cli.rs @@ -87,7 +87,7 @@ pub fn parse_all( let (file_id, name) = match &args.module { Some(module) => { if args.is_format_normal() { - writeln!(cli, "module specified: {}", module)?; + writeln!(cli, "module specified: {module}")?; } let file_id = analysis.module_file_id(loaded.project_id, module)?; (file_id, analysis.module_name(file_id.unwrap())?) @@ -96,7 +96,7 @@ pub fn parse_all( None => match &args.file { Some(file_name) => { if args.is_format_normal() { - writeln!(cli, "file specified: {}", file_name)?; + writeln!(cli, "file specified: {file_name}")?; } let path_buf = Utf8PathBuf::from_path_buf(fs::canonicalize(file_name).unwrap()) .expect("UTF8 conversion failed"); @@ -127,7 +127,7 @@ pub fn parse_all( do_parse_one(&analysis, &loaded.vfs, &cfg, &args.to, file_id, &name)? .map_or(vec![], |x| vec![x]) } - (Some(file_id), _, _) => panic!("Could not get name from file_id for {:?}", file_id), + (Some(file_id), _, _) => panic!("Could not get name from file_id for {file_id:?}"), }; if args.dump_include_resolutions { @@ -208,8 +208,7 @@ fn print_diagnostic_json( cli, "{}", serde_json::to_string(&converted_diagnostic).unwrap_or_else(|err| panic!( - "print_diagnostics_json failed for '{:?}': {}", - converted_diagnostic, err + "print_diagnostics_json failed for '{converted_diagnostic:?}': {err}" )) )?; Ok(()) @@ -247,7 +246,7 @@ fn print_diagnostic( fn maybe_code_as_string(mc: Option) -> String { match mc { Some(ns) => match ns { - NumberOrString::Number(n) => format!("{}", n), + NumberOrString::Number(n) => format!("{n}"), NumberOrString::String(s) => s, }, None => "".to_string(), @@ -336,16 +335,16 @@ fn do_parse_one( .unwrap_or(LabeledDiagnostics::default()); if let Some(to) = to { - let to_path = to.join(format!("{}.diag", name)); + let to_path = to.join(format!("{name}.diag")); let mut output = File::create(to_path)?; for diagnostic in native.iter() { let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); - writeln!(output, "{:?}", diagnostic)?; + writeln!(output, "{diagnostic:?}")?; } for diagnostic in erlang_service.iter() { let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); - writeln!(output, "{:?}", diagnostic)?; + writeln!(output, "{diagnostic:?}")?; } } if !(native.is_empty() && erlang_service.is_empty()) { diff --git a/crates/elp/src/bin/eqwalizer_cli.rs b/crates/elp/src/bin/eqwalizer_cli.rs index 23b078c1de..e9159372be 100644 --- a/crates/elp/src/bin/eqwalizer_cli.rs +++ b/crates/elp/src/bin/eqwalizer_cli.rs @@ -100,11 +100,10 @@ pub fn do_eqwalize_module( for module in &args.modules { let suggest_name = Path::new(module).file_stem().and_then(|name| name.to_str()); let context_str = match suggest_name { - Some(name) if name != module => format!( - "Module {} not found. Did you mean elp eqwalize {}?", - module, name - ), - _ => format!("Module {} not found", module), + Some(name) if name != module => { + format!("Module {module} not found. Did you mean elp eqwalize {name}?") + } + _ => format!("Module {module} not found"), }; let file_id = analysis .module_file_id(loaded.project_id, module)? @@ -169,7 +168,7 @@ pub fn do_eqwalize_all( let module_index = analysis.module_index(loaded.project_id)?; let include_generated = args.include_generated; if include_generated { - write!(cli, "{}", DEPRECATED_INCLUDE_GENERATED)?; + write!(cli, "{DEPRECATED_INCLUDE_GENERATED}")?; } let pb = cli.progress(module_index.len_own() as u64, "Gathering modules"); let file_ids: Vec = module_index @@ -250,7 +249,7 @@ pub fn do_eqwalize_app( let module_index = analysis.module_index(loaded.project_id)?; let include_generated = args.include_generated; if include_generated { - write!(cli, "{}", DEPRECATED_INCLUDE_GENERATED)?; + write!(cli, "{DEPRECATED_INCLUDE_GENERATED}")?; } let file_ids: Vec = module_index .iter_own() @@ -307,7 +306,7 @@ pub fn eqwalize_target( let analysis = &loaded.analysis(); let include_generated = args.include_generated; if include_generated { - write!(cli, "{}", DEPRECATED_INCLUDE_GENERATED)?; + write!(cli, "{DEPRECATED_INCLUDE_GENERATED}")?; } let mut file_ids: Vec = Default::default(); let mut at_least_one_found = false; @@ -382,7 +381,7 @@ pub fn eqwalize_stats( let module_index = analysis.module_index(loaded.project_id)?; let include_generated = args.include_generated; if include_generated { - write!(cli, "{}", DEPRECATED_INCLUDE_GENERATED)?; + write!(cli, "{DEPRECATED_INCLUDE_GENERATED}")?; } let project_id = loaded.project_id; let pb = cli.progress(module_index.len_own() as u64, "Computing stats"); @@ -446,8 +445,7 @@ fn print_diagnostic_json( cli, "{}", serde_json::to_string(&converted_diagnostic).unwrap_or_else(|err| panic!( - "print_diagnostics_json failed for '{:?}': {}", - converted_diagnostic, err + "print_diagnostics_json failed for '{converted_diagnostic:?}': {err}" )) )?; Ok(()) @@ -504,7 +502,7 @@ fn eqwalize( let file_id = analysis .module_index(loaded.project_id)? .file_for_module(module.as_str()) - .with_context(|| format!("module {} not found", module))?; + .with_context(|| format!("module {module} not found"))?; reporter.write_eqwalizer_diagnostics(file_id, &diagnostics)?; if !diagnostics.is_empty() { has_errors = true; @@ -559,8 +557,7 @@ fn eqwalize( // The cached parse errors must be non-empty otherwise we wouldn't have `NoAst` assert!( !parse_diagnostics.is_empty(), - "Expecting erlang service diagnostics, but none found, for '{}'", - module + "Expecting erlang service diagnostics, but none found, for '{module}'" ); let parse_diagnostics: Vec<_> = parse_diagnostics .into_iter() diff --git a/crates/elp/src/bin/erlang_service_cli.rs b/crates/elp/src/bin/erlang_service_cli.rs index 06e596018b..0c1946b329 100644 --- a/crates/elp/src/bin/erlang_service_cli.rs +++ b/crates/elp/src/bin/erlang_service_cli.rs @@ -129,7 +129,7 @@ pub fn do_parse_one( let result = db.module_ast(file_id)?; if result.is_ok() { if let Some((name, to)) = to { - let to_path = to.join(format!("{}.etf", name)); + let to_path = to.join(format!("{name}.etf")); fs::write(to_path, &*result.ast)?; } Ok(vec![]) diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index d7e3006bc8..a99cde49bb 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -812,7 +812,7 @@ impl GleanIndexer { ); result } - None => panic!("Can't find module {}", module), + None => panic!("Can't find module {module}"), } } else { let iter = files @@ -838,7 +838,7 @@ impl GleanIndexer { .collect::>() .into_iter() .enumerate() - .map(|(id, facts)| (format!("{}.json", id), facts)) + .map(|(id, facts)| (format!("{id}.json"), facts)) .collect() } else { let mut result = FxHashMap::default(); @@ -927,12 +927,12 @@ impl GleanIndexer { if let Some(def) = def { let range = def.source(db).syntax().text_range(); let text = &db.file_text(id).text(db)[range]; - let text = format!("```erlang\n{}\n```", text); + let text = format!("```erlang\n{text}\n```"); let doc = match (&x.key.expansion, &x.key.ods_url) { (None, None) => text, - (None, Some(o)) => format!("[ODS]({})\n{}", o, text), - (Some(e), None) => format!("{}\n---\n\n{}", text, e), - (Some(e), Some(o)) => format!("[ODS]({})\n{}\n---\n\n{}", o, text, e), + (None, Some(o)) => format!("[ODS]({o})\n{text}"), + (Some(e), None) => format!("{text}\n---\n\n{e}"), + (Some(e), Some(o)) => format!("[ODS]({o})\n{text}\n---\n\n{e}"), }; let decl = Declaration::MacroDeclaration( MacroDecl { @@ -1113,7 +1113,7 @@ impl GleanIndexer { for (ty, def) in def_map.get_types() { let range = def.source(db).syntax().text_range(); let text = &db.file_text(file_id).text(db)[range]; - let text = format!("```erlang\n{}\n```", text); + let text = format!("```erlang\n{text}\n```"); let span: Location = range.into(); let decl = Declaration::TypeDeclaration( @@ -1140,7 +1140,7 @@ impl GleanIndexer { for (rec, def) in def_map.get_records() { let range = def.source(db).syntax().text_range(); let text = &db.file_text(file_id).text(db)[range]; - let text = format!("```erlang\n{}\n```", text); + let text = format!("```erlang\n{text}\n```"); let span: Location = range.into(); let decl = Declaration::RecordDeclaration( @@ -1165,7 +1165,7 @@ impl GleanIndexer { if let Some((name, Some("hrl"))) = path.name_and_extension() { declarations.push(Declaration::HeaderDeclaration( HeaderDecl { - name: format!("{}.hrl", name), + name: format!("{name}.hrl"), span: Location { start: 0, length: 1, @@ -1416,7 +1416,7 @@ impl GleanIndexer { let range: TextRange = range.clone().into(); let range: Location = range.into(); if let Some(name) = vars.get(&range) { - let text = format!("```erlang\n{} :: {}\n```", name, ty); + let text = format!("```erlang\n{name} :: {ty}\n```"); let decl = VarDecl { name: name.to_string(), doc: text, @@ -1463,7 +1463,7 @@ impl GleanIndexer { if let Some((name, Some("hrl"))) = path.name_and_extension() { let target = HeaderTarget { file_id: file.into(), - name: format!("{}.hrl", name), + name: format!("{name}.hrl"), }; let xref = XRef { source: range, @@ -1638,7 +1638,7 @@ impl GleanIndexer { if let ast::Expr::ExprMax(ExprMax::MacroCallExpr(macro_call)) = node { let (_, expansion) = sema.expand(InFile::new(source_file.file_id, ¯o_call))?; let expansion = expansion.trim(); - let expansion = format!("```erlang\n{}\n```", expansion); + let expansion = format!("```erlang\n{expansion}\n```"); return Some(expansion); } None @@ -1766,7 +1766,7 @@ impl GleanIndexer { fn path_to_module_name(path: &VfsPath) -> Option { match path.name_and_extension() { Some((name, Some("erl"))) => Some(name.to_string()), - Some((name, Some("hrl"))) => Some(format!("{}.hrl", name)), + Some((name, Some("hrl"))) => Some(format!("{name}.hrl")), _ => None, } } @@ -2642,7 +2642,7 @@ mod tests { if label.is_empty() { continue; } - let label = format!("{}/{}", file_name, label); + let label = format!("{file_name}/{label}"); let tuple = (range, label); let idx = annotations .iter() @@ -2791,7 +2791,7 @@ mod tests { .strip_suffix("\n```") .unwrap() .to_string(); - f.write_str(format!("var/{}", ttype).as_str()) + f.write_str(format!("var/{ttype}").as_str()) } Declaration::HeaderDeclaration(decl) => { f.write_str(format!("header/{}", decl.key.name).as_str()) diff --git a/crates/elp/src/bin/lint_cli.rs b/crates/elp/src/bin/lint_cli.rs index f32de37074..c08d6e065e 100644 --- a/crates/elp/src/bin/lint_cli.rs +++ b/crates/elp/src/bin/lint_cli.rs @@ -229,7 +229,7 @@ pub fn do_codemod( Some(module) => match analysis.module_file_id(loaded.project_id, module)? { Some(file_id) => { if args.is_format_normal() { - writeln!(cli, "module specified: {}", module)?; + writeln!(cli, "module specified: {module}")?; } (Some(file_id), analysis.module_name(file_id)?) } @@ -238,7 +238,7 @@ pub fn do_codemod( None => match &args.file { Some(file_name) => { if args.is_format_normal() { - writeln!(cli, "file specified: {}", file_name)?; + writeln!(cli, "file specified: {file_name}")?; } let path_buf = Utf8PathBuf::from_path_buf(fs::canonicalize(file_name).unwrap()) .expect("UTF8 conversion failed"); @@ -272,7 +272,7 @@ pub fn do_codemod( .map_or(vec![], |x| vec![x]) } (Some(file_id), _) => { - panic!("Could not get name from file_id for {:?}", file_id) + panic!("Could not get name from file_id for {file_id:?}") } }; @@ -365,7 +365,7 @@ pub fn do_codemod( match lints.apply_relevant_fixes(args.is_format_normal(), cli) { Ok(_) => {} Err(err) => { - writeln!(cli, "Apply fix failed: {:#}", err).ok(); + writeln!(cli, "Apply fix failed: {err:#}").ok(); } }; } @@ -424,8 +424,7 @@ fn print_diagnostic_json( cli, "{}", serde_json::to_string(&converted_diagnostic).unwrap_or_else(|err| panic!( - "print_diagnostics_json failed for '{:?}': {}", - converted_diagnostic, err + "print_diagnostics_json failed for '{converted_diagnostic:?}': {err}" )) )?; Ok(()) @@ -864,7 +863,7 @@ impl<'a> Lints<'a> { let mut output = File::create(to_path).ok()?; write!(output, "{file_text}").ok()?; } else if let Some(to) = &self.args.to { - let to_path = to.join(format!("{}.erl", name)); + let to_path = to.join(format!("{name}.erl")); let mut output = File::create(to_path).ok()?; write!(output, "{file_text}").ok()?; } else { diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 3a5c4daff0..e1eb2a3acc 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -72,7 +72,7 @@ fn main() { fn handle_res(result: Result<()>, stderr: &mut dyn Write) -> i32 { if let Err(err) = result { - writeln!(stderr, "{:#}", err).unwrap(); + writeln!(stderr, "{err:#}").unwrap(); 101 } else { 0 @@ -81,7 +81,7 @@ fn handle_res(result: Result<()>, stderr: &mut dyn Write) -> i32 { fn setup_static(args: &Args) { if let Err(err) = eqwalizer_support::setup_eqwalizer_support(&EQWALIZER_SUPPORT_DIR) { - log::warn!("Failed to setup eqwalizer_support: {}", err); + log::warn!("Failed to setup eqwalizer_support: {err}"); } if let Some(erl) = &args.erl { let path = fs::canonicalize(erl).expect("erl path should be valid"); @@ -123,13 +123,13 @@ fn try_main(cli: &mut dyn Cli, args: Args) -> Result<()> { args::Command::Lint(args) => lint_cli::run_lint_command(&args, cli, &query_config)?, args::Command::GenerateCompletions(args) => { let instructions = args::gen_completions(&args.shell); - writeln!(cli, "#Please run this:\n{}", instructions)? + writeln!(cli, "#Please run this:\n{instructions}")? } args::Command::Version(_) => writeln!(cli, "elp {}", elp::version())?, args::Command::Shell(args) => shell::run_shell(&args, cli, &query_config)?, args::Command::Help() => { let help = batteries::get_usage(args::args()); - writeln!(cli, "{}", help)? + writeln!(cli, "{help}")? } args::Command::Explain(args) => explain_cli::explain(&args, cli)?, args::Command::Glean(args) => glean::index(&args, cli, &query_config)?, @@ -169,7 +169,7 @@ fn setup_thread_pool() { .stack_size(THREAD_STACK_SIZE) .build_global() { - log::warn!("Failed to setup thread pool: {}", err); + log::warn!("Failed to setup thread pool: {err}"); } } @@ -282,7 +282,7 @@ mod tests { fn parse_all_complete(project: &str) -> Result { // Just check the command returns. - let project_path = format!("../../test_projects/{}", project); + let project_path = format!("../../test_projects/{project}"); let tmp = Builder::new().prefix("elp_parse_all_").tempdir().unwrap(); let (_stdout, _stderr, code) = elp(args_vec![ "parse-all", @@ -348,20 +348,20 @@ mod tests { Mode::Cli, &BUCK_QUERY_CONFIG, ) - .with_context(|| format!("Failed to load project at {}", str_path)) + .with_context(|| format!("Failed to load project at {str_path}")) .unwrap(); loaded .analysis_host .raw_database_mut() .set_eqwalizer_config(Arc::new(config)); build::compile_deps(&loaded, &cli) - .with_context(|| format!("Failed to compile deps for project {}", project)) + .with_context(|| format!("Failed to compile deps for project {project}")) .unwrap(); let analysis = loaded.analysis(); let module_index = analysis .module_index(loaded.project_id) - .with_context(|| format!("No module index for project {}", project)) + .with_context(|| format!("No module index for project {project}")) .unwrap(); let file_ids: Vec = module_index .iter_own() @@ -432,13 +432,10 @@ mod tests { } } EqwalizerDiagnostics::NoAst { module } => { - panic!( - "Could not run tests because module {} was not found", - module - ) + panic!("Could not run tests because module {module} was not found") } EqwalizerDiagnostics::Error(error) => { - panic!("Could not run tests: {}", error) + panic!("Could not run tests: {error}") } } } @@ -839,8 +836,7 @@ mod tests { ); assert!( stderr.is_empty(), - "expected stderr to be empty, got:\n{}", - stderr + "expected stderr to be empty, got:\n{stderr}" ); assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); @@ -893,8 +889,7 @@ mod tests { ); assert!( stderr.is_empty(), - "expected stderr to be empty, got:\n{}", - stderr + "expected stderr to be empty, got:\n{stderr}" ); assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); @@ -1042,8 +1037,7 @@ mod tests { ); assert!( stderr.is_empty(), - "expected stderr to be empty, got:\n{}", - stderr + "expected stderr to be empty, got:\n{stderr}" ); assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); @@ -2179,15 +2173,13 @@ mod tests { let (stdout, stderr, code) = elp(args); assert_eq!( code, expected_code, - "failed with unexpected exit code: got {} not {}\nstdout:\n{}\nstderr:\n{}", - code, expected_code, stdout, stderr + "failed with unexpected exit code: got {code} not {expected_code}\nstdout:\n{stdout}\nstderr:\n{stderr}" ); assert_normalised_file(expected, &stdout, path, false); if expected_code == 0 { assert!( stderr.is_empty(), - "expected stderr to be empty, got:\n{}", - stderr + "expected stderr to be empty, got:\n{stderr}" ) } } @@ -2203,16 +2195,14 @@ mod tests { let (stdout, stderr, code) = elp(args); assert_eq!( code, expected_code, - "failed with unexpected exit code: got {} not {}\nstdout:\n{}\nstderr:\n{}", - code, expected_code, stdout, stderr + "failed with unexpected exit code: got {code} not {expected_code}\nstdout:\n{stdout}\nstderr:\n{stderr}" ); let path = PathBuf::from(""); assert_normalised_file(expected, &stdout, path, false); if expected_code == 0 { assert!( stderr.is_empty(), - "expected stderr to be empty, got:\n{}", - stderr + "expected stderr to be empty, got:\n{stderr}" ) } } @@ -2232,8 +2222,7 @@ mod tests { let (stdout, stderr, code) = elp(args); assert_eq!( code, 101, - "Expected exit code 101, got: {}\nstdout:\n{}\nstderr:\n{}", - code, stdout, stderr + "Expected exit code 101, got: {code}\nstdout:\n{stdout}\nstderr:\n{stderr}" ); assert_normalised_file(expected, &stdout, path, false); } @@ -2255,8 +2244,7 @@ mod tests { let (stdout, stderr, code) = elp(args); assert_eq!( code, 101, - "Expected exit code 101, got: {}\nstdout:\n{}\nstderr:\n{}", - code, stdout, stderr + "Expected exit code 101, got: {code}\nstdout:\n{stdout}\nstderr:\n{stderr}" ); assert_normalised_file(expected, &stderr, path, normalise_urls); } @@ -2319,8 +2307,7 @@ mod tests { let (stdout, stderr, code) = elp(args); assert_eq!( code, expected_code, - "Expected exit code {expected_code}, got: {}\nstdout:\n{}\nstderr:\n{}", - code, stdout, stderr + "Expected exit code {expected_code}, got: {code}\nstdout:\n{stdout}\nstderr:\n{stderr}" ); if let Some(expected_stderr) = expected_stderr { expected_stderr.assert_eq(&stderr); @@ -2377,7 +2364,7 @@ mod tests { let project_path: PathBuf = path_str.clone().into(); args.push("--project".into()); if let Some(json_file) = json { - let full_file = format!("{}/{}", path_str, json_file); + let full_file = format!("{path_str}/{json_file}"); args.push(full_file.into()); } else { args.push(path_str.into()); @@ -2391,7 +2378,7 @@ mod tests { } fn project_path(project: &str) -> String { - format!("../../test_projects/{}", project) + format!("../../test_projects/{project}") } struct BackupFiles { diff --git a/crates/elp/src/bin/reporting.rs b/crates/elp/src/bin/reporting.rs index 7b447d2bfd..64fe8168c2 100644 --- a/crates/elp/src/bin/reporting.rs +++ b/crates/elp/src/bin/reporting.rs @@ -117,7 +117,7 @@ impl Reporter for PrettyReporter<'_> { let range: Range = diagnostic.range.start().into()..diagnostic.range.end().into(); let expr = match &diagnostic.expression { - Some(s) => format!("{}.\n", s), + Some(s) => format!("{s}.\n"), None => "".to_string(), }; @@ -127,7 +127,7 @@ impl Reporter for PrettyReporter<'_> { let mut labels = vec![msg_label]; if let Some(s) = &diagnostic.explanation { let explanation_label = - Label::secondary(reporting_id, range).with_message(format!("\n\n{}", s)); + Label::secondary(reporting_id, range).with_message(format!("\n\n{s}")); labels.push(explanation_label); }; let d: ReportingDiagnostic = ReportingDiagnostic::error() @@ -188,7 +188,7 @@ impl Reporter for PrettyReporter<'_> { let duration = self.start.elapsed().as_secs(); self.cli.set_color(&YELLOW_COLOR_SPEC)?; if count == total { - write!(self.cli, "eqWAlized {} module(s) in {}s", count, duration)?; + write!(self.cli, "eqWAlized {count} module(s) in {duration}s")?; } else { write!( self.cli, @@ -242,7 +242,7 @@ impl Reporter for JsonReporter<'_> { eqwalizer_enabled, ); let diagnostic = serde_json::to_string(&diagnostic)?; - writeln!(self.cli, "{}", diagnostic)?; + writeln!(self.cli, "{diagnostic}")?; } Ok(()) } @@ -260,7 +260,7 @@ impl Reporter for JsonReporter<'_> { None, ); let diagnostic = serde_json::to_string(&diagnostic)?; - writeln!(self.cli, "{}", diagnostic)?; + writeln!(self.cli, "{diagnostic}")?; } Ok(()) } @@ -283,7 +283,7 @@ impl Reporter for JsonReporter<'_> { None, ); let diagnostic = serde_json::to_string(&diagnostic)?; - writeln!(self.cli, "{}", diagnostic)?; + writeln!(self.cli, "{diagnostic}")?; Ok(()) } @@ -357,12 +357,12 @@ pub(crate) fn dump_stats(cli: &mut dyn Cli, list_modules: bool) { if list_modules { writeln!(cli, "--------------start of modules----------").ok(); stats.iter().sorted().for_each(|stat| { - writeln!(cli, "{}", stat).ok(); + writeln!(cli, "{stat}").ok(); }); } writeln!(cli, "{} modules processed", stats.len()).ok(); let mem_usage = MemoryUsage::now(); - writeln!(cli, "{}", mem_usage).ok(); + writeln!(cli, "{mem_usage}").ok(); } lazy_static! { diff --git a/crates/elp/src/bin/shell.rs b/crates/elp/src/bin/shell.rs index 98ef380570..3285b57fa1 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -102,14 +102,14 @@ enum ShellError { impl fmt::Display for ShellError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - ShellError::UnexpectedCommand(cmd) => write!(f, "Unexpected command {}", cmd), + ShellError::UnexpectedCommand(cmd) => write!(f, "Unexpected command {cmd}"), ShellError::UnexpectedOption(cmd, arg) => { - write!(f, "Unexpected option {} for command {}", arg, cmd) + write!(f, "Unexpected option {arg} for command {cmd}") } ShellError::UnexpectedArg(cmd, arg) => { - write!(f, "Unexpected arg {} for command {}", arg, cmd) + write!(f, "Unexpected arg {arg} for command {cmd}") } - ShellError::MissingArg(cmd) => write!(f, "Missing arg for command {}", cmd), + ShellError::MissingArg(cmd) => write!(f, "Missing arg for command {cmd}"), } } } @@ -321,7 +321,7 @@ fn update_changes( vfs.set_file_contents(vfs_path, None); } else { let contents = - fs::read(&path).unwrap_or_else(|_| panic!("Cannot read created file {:?}", path)); + fs::read(&path).unwrap_or_else(|_| panic!("Cannot read created file {path:?}")); vfs.set_file_contents(vfs_path, Some(contents)); } }); @@ -345,7 +345,7 @@ pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfi )?; let mut rl = rustyline::DefaultEditor::new()?; let mut last_read = watchman.get_clock()?; - write!(cli, "{}", WELCOME)?; + write!(cli, "{WELCOME}")?; loop { let readline = rl.readline("> "); match readline { @@ -367,21 +367,21 @@ pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfi last_read = update_changes(&mut loaded, &watchman, &last_read)?; match ShellCommand::parse(shell, line) { Ok(None) => (), - Ok(Some(ShellCommand::Help)) => write!(cli, "{}", HELP)?, + Ok(Some(ShellCommand::Help)) => write!(cli, "{HELP}")?, Ok(Some(ShellCommand::Quit)) => break, Ok(Some(ShellCommand::ShellEqwalize(eqwalize))) => { eqwalizer_cli::do_eqwalize_module(&eqwalize, &mut loaded, cli) - .or_else(|e| writeln!(cli, "Error: {}", e))?; + .or_else(|e| writeln!(cli, "Error: {e}"))?; } Ok(Some(ShellCommand::ShellEqwalizeApp(eqwalize_app))) => { eqwalizer_cli::do_eqwalize_app(&eqwalize_app, &mut loaded, cli) - .or_else(|e| writeln!(cli, "Error: {}", e))?; + .or_else(|e| writeln!(cli, "Error: {e}"))?; } Ok(Some(ShellCommand::ShellEqwalizeAll(eqwalize_all))) => { eqwalizer_cli::do_eqwalize_all(&eqwalize_all, &mut loaded, cli) - .or_else(|e| writeln!(cli, "Error: {}", e))?; + .or_else(|e| writeln!(cli, "Error: {e}"))?; } - Err(err) => write!(cli, "{}\n{}", err, HELP)?, + Err(err) => write!(cli, "{err}\n{HELP}")?, } } Err(ReadlineError::Interrupted) => { @@ -392,7 +392,7 @@ pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfi break; } Err(err) => { - writeln!(cli, "Error: {:?}", err)?; + writeln!(cli, "Error: {err:?}")?; break; } } diff --git a/crates/elp/src/build/load.rs b/crates/elp/src/build/load.rs index 3126a6f09f..a75349dce8 100644 --- a/crates/elp/src/build/load.rs +++ b/crates/elp/src/build/load.rs @@ -77,7 +77,7 @@ pub fn load_project_at( bail!("no projects") }; - log::info!("Discovered project: {:?}", manifest); + log::info!("Discovered project: {manifest:?}"); let pb = cli.spinner("Loading build info"); let project = Project::load( &manifest, diff --git a/crates/elp/src/config.rs b/crates/elp/src/config.rs index 598bea9bd1..a631721a94 100644 --- a/crates/elp/src/config.rs +++ b/crates/elp/src/config.rs @@ -164,7 +164,7 @@ impl Config { } pub fn update(&mut self, json: serde_json::Value) { - log::info!("updating config from JSON: {:#}", json); + log::info!("updating config from JSON: {json:#}"); if json.is_null() || json.as_object().is_some_and(|it| it.is_empty()) { return; } @@ -173,13 +173,13 @@ impl Config { } pub fn update_gks(&mut self, json: serde_json::Value) { - log::info!("updating gks from JSON: {:#}", json); + log::info!("updating gks from JSON: {json:#}"); if json.is_null() || json.as_object().is_some_and(|it| it.is_empty()) { return; } match from_json::("GKs", json) { Ok(val) => self.gks = val, - Err(err) => log::warn!("could not update GKs from JSON: {:#}", err), + Err(err) => log::warn!("could not update GKs from JSON: {err:#}"), } } @@ -465,7 +465,7 @@ fn schema( fn key(f: &str) -> &str { f.split_once('_').map_or(f, |x| x.0) } - assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); + assert!(key(f1) <= key(f2), "wrong field order: {f1:?} {f2:?}"); } let map = fields @@ -490,9 +490,7 @@ fn field_props( let doc = doc.trim_end_matches('\n'); assert!( doc.ends_with('.') && doc.starts_with(char::is_uppercase), - "bad docs for {}: {:?}", - field, - doc + "bad docs for {field}: {doc:?}" ); let mut map = serde_json::Map::default(); @@ -541,7 +539,7 @@ fn field_props( "type": ["null", "array"], "items": { "type": "string" }, }, - _ => panic!("{}: {}", ty, default), + _ => panic!("{ty}: {default}"), } map.into() @@ -551,14 +549,14 @@ fn doc_comment_to_string(doc: &[&str]) -> String { doc.iter() .map(|it| it.strip_prefix(' ').unwrap_or(it)) .fold(String::new(), |mut output, it| { - let _ = writeln!(output, "{}", it); + let _ = writeln!(output, "{it}"); output }) } pub fn config_schema_json() -> String { let s = Config::json_schema(); - let schema = format!("{:#}", s); + let schema = format!("{s:#}"); let mut schema = schema .trim_start_matches('{') .trim_end_matches('}') diff --git a/crates/elp/src/convert.rs b/crates/elp/src/convert.rs index a70aeadfb0..1f42e6f454 100644 --- a/crates/elp/src/convert.rs +++ b/crates/elp/src/convert.rs @@ -138,7 +138,7 @@ pub fn eqwalizer_to_arc_diagnostic( }; // formatting: https://fburl.com/max_wiki_link_to_phabricator_rich_text let explanation = match &d.explanation { - Some(s) => format!("```\n{}\n```", s), + Some(s) => format!("```\n{s}\n```"), None => "".to_string(), }; let link = format!("> [docs on `{}`]({})", d.code, d.uri); diff --git a/crates/elp/src/handlers.rs b/crates/elp/src/handlers.rs index a429066ce4..707d392069 100644 --- a/crates/elp/src/handlers.rs +++ b/crates/elp/src/handlers.rs @@ -214,7 +214,7 @@ fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> { let assist_kind: AssistKind = assist_kind_string.parse()?; let index: usize = match index_string.parse() { Ok(index) => index, - Err(e) => return Err(format!("Incorrect index string: {}", e)), + Err(e) => return Err(format!("Incorrect index string: {e}")), }; Ok(( index, @@ -549,7 +549,7 @@ pub(crate) fn handle_hover(snap: Snapshot, params: HoverParams) -> Result