aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock88
-rw-r--r--crates/assists/src/handlers/auto_import.rs3
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs3
-rw-r--r--crates/assists/src/handlers/ignore_test.rs103
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs71
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs28
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs613
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs20
-rw-r--r--crates/assists/src/utils.rs21
-rw-r--r--crates/assists/src/utils/import_assets.rs34
-rw-r--r--crates/assists/src/utils/insert_use.rs16
-rw-r--r--crates/base_db/src/input.rs20
-rw-r--r--crates/cfg/src/lib.rs6
-rw-r--r--crates/completion/Cargo.toml1
-rw-r--r--crates/completion/src/completions.rs8
-rw-r--r--crates/completion/src/completions/pattern.rs57
-rw-r--r--crates/completion/src/completions/unqualified_path.rs134
-rw-r--r--crates/completion/src/config.rs6
-rw-r--r--crates/completion/src/context.rs3
-rw-r--r--crates/completion/src/item.rs59
-rw-r--r--crates/completion/src/lib.rs7
-rw-r--r--crates/completion/src/render.rs58
-rw-r--r--crates/completion/src/render/enum_variant.rs10
-rw-r--r--crates/completion/src/render/function.rs12
-rw-r--r--crates/completion/src/render/macro_.rs12
-rw-r--r--crates/flycheck/Cargo.toml2
-rw-r--r--crates/hir/src/code_model.rs14
-rw-r--r--crates/hir/src/db.rs2
-rw-r--r--crates/hir/src/diagnostics.rs4
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/import_map.rs82
-rw-r--r--crates/hir_def/src/nameres.rs9
-rw-r--r--crates/hir_def/src/nameres/collector.rs6
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs6
-rw-r--r--crates/hir_expand/src/db.rs123
-rw-r--r--crates/hir_expand/src/diagnostics.rs2
-rw-r--r--crates/hir_expand/src/lib.rs4
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs248
-rw-r--r--crates/hir_ty/src/infer/pat.rs46
-rw-r--r--crates/hir_ty/src/tests/patterns.rs95
-rw-r--r--crates/ide/src/diagnostics.rs40
-rw-r--r--crates/ide/src/fn_references.rs5
-rw-r--r--crates/ide/src/references/rename.rs11
-rw-r--r--crates/ide/src/runnables.rs19
-rw-r--r--crates/ide/src/status.rs10
-rw-r--r--crates/ide_db/src/apply_change.rs4
-rw-r--r--crates/ide_db/src/imports_locator.rs67
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/mbe/src/lib.rs48
-rw-r--r--crates/mbe/src/mbe_expander.rs15
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs9
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs18
-rw-r--r--crates/parser/src/grammar/items.rs10
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/project_model/src/sysroot.rs2
-rw-r--r--crates/project_model/src/workspace.rs608
-rw-r--r--crates/rust-analyzer/Cargo.toml4
-rw-r--r--crates/rust-analyzer/src/caps.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt19
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs39
-rw-r--r--crates/rust-analyzer/src/document.rs6
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs16
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop.rs31
-rw-r--r--crates/rust-analyzer/src/reload.rs6
-rw-r--r--crates/rust-analyzer/src/to_proto.rs39
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs4
-rw-r--r--crates/syntax/test_data/parser/ok/0068_item_modifiers.rast16
-rw-r--r--crates/syntax/test_data/parser/ok/0068_item_modifiers.rs2
-rw-r--r--crates/tt/src/lib.rs20
-rw-r--r--docs/dev/lsp-extensions.md4
-rw-r--r--docs/dev/syntax.md4
-rw-r--r--editors/code/package-lock.json22
-rw-r--r--editors/code/package.json12
-rw-r--r--editors/code/rust.tmGrammar.json2
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--xtask/tests/tidy.rs1
88 files changed, 2207 insertions, 1079 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 715a80978..051d9e734 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -81,9 +81,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
81 81
82[[package]] 82[[package]]
83name = "backtrace" 83name = "backtrace"
84version = "0.3.54" 84version = "0.3.55"
85source = "registry+https://github.com/rust-lang/crates.io-index" 85source = "registry+https://github.com/rust-lang/crates.io-index"
86checksum = "2baad346b2d4e94a24347adeee9c7a93f412ee94b9cc26e5b59dea23848e9f28" 86checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
87dependencies = [ 87dependencies = [
88 "addr2line", 88 "addr2line",
89 "cfg-if 1.0.0", 89 "cfg-if 1.0.0",
@@ -139,9 +139,9 @@ dependencies = [
139 139
140[[package]] 140[[package]]
141name = "cc" 141name = "cc"
142version = "1.0.62" 142version = "1.0.65"
143source = "registry+https://github.com/rust-lang/crates.io-index" 143source = "registry+https://github.com/rust-lang/crates.io-index"
144checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" 144checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
145 145
146[[package]] 146[[package]]
147name = "cfg" 147name = "cfg"
@@ -242,9 +242,9 @@ dependencies = [
242 242
243[[package]] 243[[package]]
244name = "cmake" 244name = "cmake"
245version = "0.1.44" 245version = "0.1.45"
246source = "registry+https://github.com/rust-lang/crates.io-index" 246source = "registry+https://github.com/rust-lang/crates.io-index"
247checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" 247checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
248dependencies = [ 248dependencies = [
249 "cc", 249 "cc",
250] 250]
@@ -255,6 +255,7 @@ version = "0.0.0"
255dependencies = [ 255dependencies = [
256 "assists", 256 "assists",
257 "base_db", 257 "base_db",
258 "either",
258 "expect-test", 259 "expect-test",
259 "hir", 260 "hir",
260 "ide_db", 261 "ide_db",
@@ -380,9 +381,9 @@ dependencies = [
380 381
381[[package]] 382[[package]]
382name = "env_logger" 383name = "env_logger"
383version = "0.8.1" 384version = "0.8.2"
384source = "registry+https://github.com/rust-lang/crates.io-index" 385source = "registry+https://github.com/rust-lang/crates.io-index"
385checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" 386checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
386dependencies = [ 387dependencies = [
387 "log", 388 "log",
388] 389]
@@ -735,9 +736,9 @@ dependencies = [
735 736
736[[package]] 737[[package]]
737name = "instant" 738name = "instant"
738version = "0.1.8" 739version = "0.1.9"
739source = "registry+https://github.com/rust-lang/crates.io-index" 740source = "registry+https://github.com/rust-lang/crates.io-index"
740checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" 741checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
741dependencies = [ 742dependencies = [
742 "cfg-if 1.0.0", 743 "cfg-if 1.0.0",
743] 744]
@@ -821,9 +822,9 @@ dependencies = [
821 822
822[[package]] 823[[package]]
823name = "lock_api" 824name = "lock_api"
824version = "0.4.1" 825version = "0.4.2"
825source = "registry+https://github.com/rust-lang/crates.io-index" 826source = "registry+https://github.com/rust-lang/crates.io-index"
826checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 827checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
827dependencies = [ 828dependencies = [
828 "scopeguard", 829 "scopeguard",
829] 830]
@@ -852,9 +853,9 @@ dependencies = [
852 853
853[[package]] 854[[package]]
854name = "lsp-server" 855name = "lsp-server"
855version = "0.4.1" 856version = "0.5.0"
856source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
857checksum = "9c85acaf36c53bf15da2b8b35afeea56747707261f59eb0b77229081dd72b04e" 858checksum = "69b18dfe0e4a380b872aa79d8e0ee6c3d7a9682466e84b83ad807c88b3545f79"
858dependencies = [ 859dependencies = [
859 "crossbeam-channel 0.5.0", 860 "crossbeam-channel 0.5.0",
860 "log", 861 "log",
@@ -864,9 +865,9 @@ dependencies = [
864 865
865[[package]] 866[[package]]
866name = "lsp-types" 867name = "lsp-types"
867version = "0.83.0" 868version = "0.84.0"
868source = "registry+https://github.com/rust-lang/crates.io-index" 869source = "registry+https://github.com/rust-lang/crates.io-index"
869checksum = "25e0bd4b95038f2c23bda332ba0ca684e8dda765db1f9bdb63dc4c3e01f3b456" 870checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d"
870dependencies = [ 871dependencies = [
871 "base64", 872 "base64",
872 "bitflags", 873 "bitflags",
@@ -1065,21 +1066,21 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
1065 1066
1066[[package]] 1067[[package]]
1067name = "once_cell" 1068name = "once_cell"
1068version = "1.5.1" 1069version = "1.5.2"
1069source = "registry+https://github.com/rust-lang/crates.io-index" 1070source = "registry+https://github.com/rust-lang/crates.io-index"
1070checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596" 1071checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
1071 1072
1072[[package]] 1073[[package]]
1073name = "oorandom" 1074name = "oorandom"
1074version = "11.1.2" 1075version = "11.1.3"
1075source = "registry+https://github.com/rust-lang/crates.io-index" 1076source = "registry+https://github.com/rust-lang/crates.io-index"
1076checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" 1077checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
1077 1078
1078[[package]] 1079[[package]]
1079name = "parking_lot" 1080name = "parking_lot"
1080version = "0.11.0" 1081version = "0.11.1"
1081source = "registry+https://github.com/rust-lang/crates.io-index" 1082source = "registry+https://github.com/rust-lang/crates.io-index"
1082checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 1083checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
1083dependencies = [ 1084dependencies = [
1084 "instant", 1085 "instant",
1085 "lock_api", 1086 "lock_api",
@@ -1164,9 +1165,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
1164 1165
1165[[package]] 1166[[package]]
1166name = "pin-project-lite" 1167name = "pin-project-lite"
1167version = "0.1.11" 1168version = "0.2.0"
1168source = "registry+https://github.com/rust-lang/crates.io-index" 1169source = "registry+https://github.com/rust-lang/crates.io-index"
1169checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 1170checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
1170 1171
1171[[package]] 1172[[package]]
1172name = "plain" 1173name = "plain"
@@ -1593,9 +1594,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1593 1594
1594[[package]] 1595[[package]]
1595name = "smallvec" 1596name = "smallvec"
1596version = "1.4.2" 1597version = "1.5.0"
1597source = "registry+https://github.com/rust-lang/crates.io-index" 1598source = "registry+https://github.com/rust-lang/crates.io-index"
1598checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 1599checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85"
1599 1600
1600[[package]] 1601[[package]]
1601name = "smol_str" 1602name = "smol_str"
@@ -1626,9 +1627,9 @@ version = "0.0.0"
1626 1627
1627[[package]] 1628[[package]]
1628name = "syn" 1629name = "syn"
1629version = "1.0.48" 1630version = "1.0.51"
1630source = "registry+https://github.com/rust-lang/crates.io-index" 1631source = "registry+https://github.com/rust-lang/crates.io-index"
1631checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" 1632checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223"
1632dependencies = [ 1633dependencies = [
1633 "proc-macro2", 1634 "proc-macro2",
1634 "quote", 1635 "quote",
@@ -1671,9 +1672,9 @@ dependencies = [
1671 1672
1672[[package]] 1673[[package]]
1673name = "termcolor" 1674name = "termcolor"
1674version = "1.1.0" 1675version = "1.1.2"
1675source = "registry+https://github.com/rust-lang/crates.io-index" 1676source = "registry+https://github.com/rust-lang/crates.io-index"
1676checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 1677checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
1677dependencies = [ 1678dependencies = [
1678 "winapi-util", 1679 "winapi-util",
1679] 1680]
@@ -1739,9 +1740,18 @@ dependencies = [
1739 1740
1740[[package]] 1741[[package]]
1741name = "tinyvec" 1742name = "tinyvec"
1742version = "0.3.4" 1743version = "1.1.0"
1744source = "registry+https://github.com/rust-lang/crates.io-index"
1745checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
1746dependencies = [
1747 "tinyvec_macros",
1748]
1749
1750[[package]]
1751name = "tinyvec_macros"
1752version = "0.1.0"
1743source = "registry+https://github.com/rust-lang/crates.io-index" 1753source = "registry+https://github.com/rust-lang/crates.io-index"
1744checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" 1754checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1745 1755
1746[[package]] 1756[[package]]
1747name = "toolchain" 1757name = "toolchain"
@@ -1752,11 +1762,11 @@ dependencies = [
1752 1762
1753[[package]] 1763[[package]]
1754name = "tracing" 1764name = "tracing"
1755version = "0.1.21" 1765version = "0.1.22"
1756source = "registry+https://github.com/rust-lang/crates.io-index" 1766source = "registry+https://github.com/rust-lang/crates.io-index"
1757checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" 1767checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
1758dependencies = [ 1768dependencies = [
1759 "cfg-if 0.1.10", 1769 "cfg-if 1.0.0",
1760 "pin-project-lite", 1770 "pin-project-lite",
1761 "tracing-attributes", 1771 "tracing-attributes",
1762 "tracing-core", 1772 "tracing-core",
@@ -1879,18 +1889,18 @@ dependencies = [
1879 1889
1880[[package]] 1890[[package]]
1881name = "unicode-normalization" 1891name = "unicode-normalization"
1882version = "0.1.13" 1892version = "0.1.16"
1883source = "registry+https://github.com/rust-lang/crates.io-index" 1893source = "registry+https://github.com/rust-lang/crates.io-index"
1884checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 1894checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
1885dependencies = [ 1895dependencies = [
1886 "tinyvec", 1896 "tinyvec",
1887] 1897]
1888 1898
1889[[package]] 1899[[package]]
1890name = "unicode-segmentation" 1900name = "unicode-segmentation"
1891version = "1.6.0" 1901version = "1.7.0"
1892source = "registry+https://github.com/rust-lang/crates.io-index" 1902source = "registry+https://github.com/rust-lang/crates.io-index"
1893checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 1903checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae"
1894 1904
1895[[package]] 1905[[package]]
1896name = "unicode-xid" 1906name = "unicode-xid"
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 37dd61266..d665837a2 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
98 98
99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
100 let group = import_group_message(import_assets.import_candidate()); 100 let group = import_group_message(import_assets.import_candidate());
101 let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; 101 let scope =
102 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
102 for (import, _) in proposed_imports { 103 for (import, _) in proposed_imports {
103 acc.add_group( 104 acc.add_group(
104 &group, 105 &group,
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 067afabf2..cac77c49b 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -143,8 +143,7 @@ fn insert_import(
143 if let Some(mut mod_path) = mod_path { 143 if let Some(mut mod_path) = mod_path {
144 mod_path.segments.pop(); 144 mod_path.segments.pop();
145 mod_path.segments.push(variant_hir_name.clone()); 145 mod_path.segments.push(variant_hir_name.clone());
146 let scope = ImportScope::find_insert_use_container(scope_node, ctx)?; 146 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
147
148 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 147 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
149 } 148 }
150 Some(()) 149 Some(())
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
new file mode 100644
index 000000000..5096a0005
--- /dev/null
+++ b/crates/assists/src/handlers/ignore_test.rs
@@ -0,0 +1,103 @@
1use syntax::{
2 ast::{self, AttrsOwner},
3 AstNode, AstToken,
4};
5
6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
7
8// Assist: ignore_test
9//
10// Adds `#[ignore]` attribute to the test.
11//
12// ```
13// <|>#[test]
14// fn arithmetics {
15// assert_eq!(2 + 2, 5);
16// }
17// ```
18// ->
19// ```
20// #[test]
21// #[ignore]
22// fn arithmetics {
23// assert_eq!(2 + 2, 5);
24// }
25// ```
26pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let attr: ast::Attr = ctx.find_node_at_offset()?;
28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29 let attr = test_related_attribute(&func)?;
30
31 match has_ignore_attribute(&func) {
32 None => acc.add(
33 AssistId("ignore_test", AssistKind::None),
34 "Ignore this test",
35 attr.syntax().text_range(),
36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ),
38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None),
40 "Re-enable this test",
41 ignore_attr.syntax().text_range(),
42 |builder| {
43 builder.delete(ignore_attr.syntax().text_range());
44 let whitespace = ignore_attr
45 .syntax()
46 .next_sibling_or_token()
47 .and_then(|x| x.into_token())
48 .and_then(ast::Whitespace::cast);
49 if let Some(whitespace) = whitespace {
50 builder.delete(whitespace.syntax().text_range());
51 }
52 },
53 ),
54 }
55}
56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| {
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65}
66
67#[cfg(test)]
68mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist;
71
72 #[test]
73 fn test_base_case() {
74 check_assist(
75 ignore_test,
76 r#"
77 #[test<|>]
78 fn test() {}
79 "#,
80 r#"
81 #[test]
82 #[ignore]
83 fn test() {}
84 "#,
85 )
86 }
87
88 #[test]
89 fn test_unignore() {
90 check_assist(
91 ignore_test,
92 r#"
93 #[test<|>]
94 #[ignore]
95 fn test() {}
96 "#,
97 r#"
98 #[test]
99 fn test() {}
100 "#,
101 )
102 }
103}
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index 9731344b8..eae6367c1 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -49,12 +49,29 @@ fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
49 macro_text_with_brackets.len() - TextSize::of(')'), 49 macro_text_with_brackets.len() - TextSize::of(')'),
50 )); 50 ));
51 51
52 let is_leaf = macro_call.syntax().next_sibling().is_none(); 52 Some(
53 Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) { 53 if !is_leaf_or_control_flow_expr(macro_call)
54 format!("({})", macro_text_in_brackets) 54 && needs_parentheses_around_macro_contents(contents)
55 } else { 55 {
56 macro_text_in_brackets.to_string() 56 format!("({})", macro_text_in_brackets)
57 }) 57 } else {
58 macro_text_in_brackets.to_string()
59 },
60 )
61}
62
63fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool {
64 macro_call.syntax().next_sibling().is_none()
65 || match macro_call.syntax().parent() {
66 Some(parent) => match_ast! {
67 match parent {
68 ast::Condition(_it) => true,
69 ast::MatchExpr(_it) => true,
70 _ => false,
71 }
72 },
73 None => false,
74 }
58} 75}
59 76
60/// Verifies that the given macro_call actually matches the given name 77/// Verifies that the given macro_call actually matches the given name
@@ -361,4 +378,44 @@ fn main() {
361 r#"let res = (foo..=bar).foo();"#, 378 r#"let res = (foo..=bar).foo();"#,
362 ); 379 );
363 } 380 }
381
382 #[test]
383 fn test_remove_dbg_followed_by_block() {
384 check_assist(
385 remove_dbg,
386 r#"fn foo() {
387 if <|>dbg!(x || y) {}
388}"#,
389 r#"fn foo() {
390 if x || y {}
391}"#,
392 );
393 check_assist(
394 remove_dbg,
395 r#"fn foo() {
396 while let foo = <|>dbg!(&x) {}
397}"#,
398 r#"fn foo() {
399 while let foo = &x {}
400}"#,
401 );
402 check_assist(
403 remove_dbg,
404 r#"fn foo() {
405 if let foo = <|>dbg!(&x) {}
406}"#,
407 r#"fn foo() {
408 if let foo = &x {}
409}"#,
410 );
411 check_assist(
412 remove_dbg,
413 r#"fn foo() {
414 match <|>dbg!(&x) {}
415}"#,
416 r#"fn foo() {
417 match &x {}
418}"#,
419 );
420 }
364} 421}
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 82625516c..453a6cebf 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -62,19 +62,21 @@ pub(crate) fn replace_derive_with_manual_impl(
62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
63 let current_crate = current_module.krate(); 63 let current_crate = current_module.krate();
64 64
65 let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) 65 let found_traits =
66 .into_iter() 66 imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
67 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 67 .filter_map(
68 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 68 |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
69 _ => None, 69 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
70 }) 70 _ => None,
71 .flat_map(|trait_| { 71 },
72 current_module 72 )
73 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 73 .flat_map(|trait_| {
74 .as_ref() 74 current_module
75 .map(mod_path_to_ast) 75 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
76 .zip(Some(trait_)) 76 .as_ref()
77 }); 77 .map(mod_path_to_ast)
78 .zip(Some(trait_))
79 });
78 80
79 let mut no_traits_found = true; 81 let mut no_traits_found = true;
80 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 82 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index d7e1d9580..a66db9ae3 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
34 } 34 }
35 35
36 let target = path.syntax().text_range(); 36 let target = path.syntax().text_range();
37 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 37 let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
38 let syntax = scope.as_syntax_node(); 38 let syntax = scope.as_syntax_node();
39 acc.add( 39 acc.add(
40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), 40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
index 36ef871b9..676db7137 100644
--- a/crates/assists/src/handlers/unwrap_block.rs
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -3,7 +3,7 @@ use syntax::{
3 self, 3 self,
4 edit::{AstNodeEdit, IndentLevel}, 4 edit::{AstNodeEdit, IndentLevel},
5 }, 5 },
6 AstNode, TextRange, T, 6 AstNode, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; 9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
@@ -31,11 +31,21 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
31 31
32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; 32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; 33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
34 let target = block.syntax().text_range();
34 let mut parent = block.syntax().parent()?; 35 let mut parent = block.syntax().parent()?;
35 if ast::MatchArm::can_cast(parent.kind()) { 36 if ast::MatchArm::can_cast(parent.kind()) {
36 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
37 } 38 }
38 39
40 if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) {
41 return acc.add(assist_id, assist_label, target, |builder| {
42 builder.replace(
43 block.syntax().text_range(),
44 update_expr_string(block.to_string(), &[' ', '{', '\n']),
45 );
46 });
47 }
48
39 let parent = ast::Expr::cast(parent)?; 49 let parent = ast::Expr::cast(parent)?;
40 50
41 match parent.clone() { 51 match parent.clone() {
@@ -48,7 +58,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
48 // For `else if` blocks 58 // For `else if` blocks
49 let ancestor_then_branch = ancestor.then_branch()?; 59 let ancestor_then_branch = ancestor.then_branch()?;
50 60
51 let target = then_branch.syntax().text_range();
52 return acc.add(assist_id, assist_label, target, |edit| { 61 return acc.add(assist_id, assist_label, target, |edit| {
53 let range_to_del_else_if = TextRange::new( 62 let range_to_del_else_if = TextRange::new(
54 ancestor_then_branch.syntax().text_range().end(), 63 ancestor_then_branch.syntax().text_range().end(),
@@ -68,7 +77,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
68 }); 77 });
69 } 78 }
70 } else { 79 } else {
71 let target = block.syntax().text_range();
72 return acc.add(assist_id, assist_label, target, |edit| { 80 return acc.add(assist_id, assist_label, target, |edit| {
73 let range_to_del = TextRange::new( 81 let range_to_del = TextRange::new(
74 then_branch.syntax().text_range().end(), 82 then_branch.syntax().text_range().end(),
@@ -84,7 +92,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
84 }; 92 };
85 93
86 let unwrapped = unwrap_trivial_block(block); 94 let unwrapped = unwrap_trivial_block(block);
87 let target = unwrapped.syntax().text_range();
88 acc.add(assist_id, assist_label, target, |builder| { 95 acc.add(assist_id, assist_label, target, |builder| {
89 builder.replace( 96 builder.replace(
90 parent.syntax().text_range(), 97 parent.syntax().text_range(),
@@ -112,31 +119,89 @@ mod tests {
112 use super::*; 119 use super::*;
113 120
114 #[test] 121 #[test]
122 fn unwrap_tail_expr_block() {
123 check_assist(
124 unwrap_block,
125 r#"
126fn main() {
127 <|>{
128 92
129 }
130}
131"#,
132 r#"
133fn main() {
134 92
135}
136"#,
137 )
138 }
139
140 #[test]
141 fn unwrap_stmt_expr_block() {
142 check_assist(
143 unwrap_block,
144 r#"
145fn main() {
146 <|>{
147 92;
148 }
149 ()
150}
151"#,
152 r#"
153fn main() {
154 92;
155 ()
156}
157"#,
158 );
159 // Pedantically, we should add an `;` here...
160 check_assist(
161 unwrap_block,
162 r#"
163fn main() {
164 <|>{
165 92
166 }
167 ()
168}
169"#,
170 r#"
171fn main() {
172 92
173 ()
174}
175"#,
176 );
177 }
178
179 #[test]
115 fn simple_if() { 180 fn simple_if() {
116 check_assist( 181 check_assist(
117 unwrap_block, 182 unwrap_block,
118 r#" 183 r#"
119 fn main() { 184fn main() {
120 bar(); 185 bar();
121 if true {<|> 186 if true {<|>
122 foo(); 187 foo();
123 188
124 //comment 189 //comment
125 bar(); 190 bar();
126 } else { 191 } else {
127 println!("bar"); 192 println!("bar");
128 } 193 }
129 } 194}
130 "#, 195"#,
131 r#" 196 r#"
132 fn main() { 197fn main() {
133 bar(); 198 bar();
134 foo(); 199 foo();
135 200
136 //comment 201 //comment
137 bar(); 202 bar();
138 } 203}
139 "#, 204"#,
140 ); 205 );
141 } 206 }
142 207
@@ -145,30 +210,30 @@ mod tests {
145 check_assist( 210 check_assist(
146 unwrap_block, 211 unwrap_block,
147 r#" 212 r#"
148 fn main() { 213fn main() {
149 bar(); 214 bar();
150 if true { 215 if true {
151 foo(); 216 foo();
152 217
153 //comment 218 //comment
154 bar(); 219 bar();
155 } else {<|> 220 } else {<|>
156 println!("bar"); 221 println!("bar");
157 } 222 }
158 } 223}
159 "#, 224"#,
160 r#" 225 r#"
161 fn main() { 226fn main() {
162 bar(); 227 bar();
163 if true { 228 if true {
164 foo(); 229 foo();
165 230
166 //comment 231 //comment
167 bar(); 232 bar();
168 } 233 }
169 println!("bar"); 234 println!("bar");
170 } 235}
171 "#, 236"#,
172 ); 237 );
173 } 238 }
174 239
@@ -177,32 +242,32 @@ mod tests {
177 check_assist( 242 check_assist(
178 unwrap_block, 243 unwrap_block,
179 r#" 244 r#"
180 fn main() { 245fn main() {
181 //bar(); 246 //bar();
182 if true { 247 if true {
183 println!("true"); 248 println!("true");
184 249
185 //comment 250 //comment
186 //bar(); 251 //bar();
187 } else if false {<|> 252 } else if false {<|>
188 println!("bar"); 253 println!("bar");
189 } else { 254 } else {
190 println!("foo"); 255 println!("foo");
191 } 256 }
192 } 257}
193 "#, 258"#,
194 r#" 259 r#"
195 fn main() { 260fn main() {
196 //bar(); 261 //bar();
197 if true { 262 if true {
198 println!("true"); 263 println!("true");
199 264
200 //comment 265 //comment
201 //bar(); 266 //bar();
202 } 267 }
203 println!("bar"); 268 println!("bar");
204 } 269}
205 "#, 270"#,
206 ); 271 );
207 } 272 }
208 273
@@ -211,34 +276,34 @@ mod tests {
211 check_assist( 276 check_assist(
212 unwrap_block, 277 unwrap_block,
213 r#" 278 r#"
214 fn main() { 279fn main() {
215 //bar(); 280 //bar();
216 if true { 281 if true {
217 println!("true"); 282 println!("true");
218 283
219 //comment 284 //comment
220 //bar(); 285 //bar();
221 } else if false { 286 } else if false {
222 println!("bar"); 287 println!("bar");
223 } else if true {<|> 288 } else if true {<|>
224 println!("foo"); 289 println!("foo");
225 } 290 }
226 } 291}
227 "#, 292"#,
228 r#" 293 r#"
229 fn main() { 294fn main() {
230 //bar(); 295 //bar();
231 if true { 296 if true {
232 println!("true"); 297 println!("true");
233 298
234 //comment 299 //comment
235 //bar(); 300 //bar();
236 } else if false { 301 } else if false {
237 println!("bar"); 302 println!("bar");
238 } 303 }
239 println!("foo"); 304 println!("foo");
240 } 305}
241 "#, 306"#,
242 ); 307 );
243 } 308 }
244 309
@@ -247,38 +312,38 @@ mod tests {
247 check_assist( 312 check_assist(
248 unwrap_block, 313 unwrap_block,
249 r#" 314 r#"
250 fn main() { 315fn main() {
251 //bar(); 316 //bar();
252 if true { 317 if true {
253 println!("true"); 318 println!("true");
254 319
255 //comment 320 //comment
256 //bar(); 321 //bar();
257 } else if false { 322 } else if false {
258 println!("bar"); 323 println!("bar");
259 } else if true { 324 } else if true {
260 println!("foo"); 325 println!("foo");
261 } else {<|> 326 } else {<|>
262 println!("else"); 327 println!("else");
263 } 328 }
264 } 329}
265 "#, 330"#,
266 r#" 331 r#"
267 fn main() { 332fn main() {
268 //bar(); 333 //bar();
269 if true { 334 if true {
270 println!("true"); 335 println!("true");
271 336
272 //comment 337 //comment
273 //bar(); 338 //bar();
274 } else if false { 339 } else if false {
275 println!("bar"); 340 println!("bar");
276 } else if true { 341 } else if true {
277 println!("foo"); 342 println!("foo");
278 } 343 }
279 println!("else"); 344 println!("else");
280 } 345}
281 "#, 346"#,
282 ); 347 );
283 } 348 }
284 349
@@ -287,36 +352,36 @@ mod tests {
287 check_assist( 352 check_assist(
288 unwrap_block, 353 unwrap_block,
289 r#" 354 r#"
290 fn main() { 355fn main() {
291 //bar(); 356 //bar();
292 if true { 357 if true {
293 println!("true"); 358 println!("true");
294 359
295 //comment 360 //comment
296 //bar(); 361 //bar();
297 } else if false { 362 } else if false {
298 println!("bar"); 363 println!("bar");
299 } else if true {<|> 364 } else if true {<|>
300 println!("foo"); 365 println!("foo");
301 } else { 366 } else {
302 println!("else"); 367 println!("else");
303 } 368 }
304 } 369}
305 "#, 370"#,
306 r#" 371 r#"
307 fn main() { 372fn main() {
308 //bar(); 373 //bar();
309 if true { 374 if true {
310 println!("true"); 375 println!("true");
311 376
312 //comment 377 //comment
313 //bar(); 378 //bar();
314 } else if false { 379 } else if false {
315 println!("bar"); 380 println!("bar");
316 } 381 }
317 println!("foo"); 382 println!("foo");
318 } 383}
319 "#, 384"#,
320 ); 385 );
321 } 386 }
322 387
@@ -325,18 +390,18 @@ mod tests {
325 check_assist_not_applicable( 390 check_assist_not_applicable(
326 unwrap_block, 391 unwrap_block,
327 r#" 392 r#"
328 fn main() { 393fn main() {
329 bar();<|> 394 bar();<|>
330 if true { 395 if true {
331 foo(); 396 foo();
332 397
333 //comment 398 //comment
334 bar(); 399 bar();
335 } else { 400 } else {
336 println!("bar"); 401 println!("bar");
337 } 402 }
338 } 403}
339 "#, 404"#,
340 ); 405 );
341 } 406 }
342 407
@@ -345,31 +410,31 @@ mod tests {
345 check_assist( 410 check_assist(
346 unwrap_block, 411 unwrap_block,
347 r#" 412 r#"
348 fn main() { 413fn main() {
349 for i in 0..5 {<|> 414 for i in 0..5 {<|>
350 if true { 415 if true {
351 foo(); 416 foo();
352 417
353 //comment 418 //comment
354 bar(); 419 bar();
355 } else { 420 } else {
356 println!("bar"); 421 println!("bar");
357 } 422 }
358 } 423 }
359 } 424}
360 "#, 425"#,
361 r#" 426 r#"
362 fn main() { 427fn main() {
363 if true { 428 if true {
364 foo(); 429 foo();
365 430
366 //comment 431 //comment
367 bar(); 432 bar();
368 } else { 433 } else {
369 println!("bar"); 434 println!("bar");
370 } 435 }
371 } 436}
372 "#, 437"#,
373 ); 438 );
374 } 439 }
375 440
@@ -378,29 +443,29 @@ mod tests {
378 check_assist( 443 check_assist(
379 unwrap_block, 444 unwrap_block,
380 r#" 445 r#"
381 fn main() { 446fn main() {
382 for i in 0..5 { 447 for i in 0..5 {
383 if true {<|> 448 if true {<|>
384 foo(); 449 foo();
385 450
386 //comment 451 //comment
387 bar(); 452 bar();
388 } else { 453 } else {
389 println!("bar"); 454 println!("bar");
390 } 455 }
391 } 456 }
392 } 457}
393 "#, 458"#,
394 r#" 459 r#"
395 fn main() { 460fn main() {
396 for i in 0..5 { 461 for i in 0..5 {
397 foo(); 462 foo();
398 463
399 //comment 464 //comment
400 bar(); 465 bar();
401 } 466 }
402 } 467}
403 "#, 468"#,
404 ); 469 );
405 } 470 }
406 471
@@ -409,31 +474,31 @@ mod tests {
409 check_assist( 474 check_assist(
410 unwrap_block, 475 unwrap_block,
411 r#" 476 r#"
412 fn main() { 477fn main() {
413 loop {<|> 478 loop {<|>
414 if true { 479 if true {
415 foo(); 480 foo();
416 481
417 //comment 482 //comment
418 bar(); 483 bar();
419 } else { 484 } else {
420 println!("bar"); 485 println!("bar");
421 } 486 }
422 } 487 }
423 } 488}
424 "#, 489"#,
425 r#" 490 r#"
426 fn main() { 491fn main() {
427 if true { 492 if true {
428 foo(); 493 foo();
429 494
430 //comment 495 //comment
431 bar(); 496 bar();
432 } else { 497 } else {
433 println!("bar"); 498 println!("bar");
434 } 499 }
435 } 500}
436 "#, 501"#,
437 ); 502 );
438 } 503 }
439 504
@@ -442,31 +507,31 @@ mod tests {
442 check_assist( 507 check_assist(
443 unwrap_block, 508 unwrap_block,
444 r#" 509 r#"
445 fn main() { 510fn main() {
446 while true {<|> 511 while true {<|>
447 if true { 512 if true {
448 foo(); 513 foo();
449 514
450 //comment 515 //comment
451 bar(); 516 bar();
452 } else { 517 } else {
453 println!("bar"); 518 println!("bar");
454 } 519 }
455 } 520 }
456 } 521}
457 "#, 522"#,
458 r#" 523 r#"
459 fn main() { 524fn main() {
460 if true { 525 if true {
461 foo(); 526 foo();
462 527
463 //comment 528 //comment
464 bar(); 529 bar();
465 } else { 530 } else {
466 println!("bar"); 531 println!("bar");
467 } 532 }
468 } 533}
469 "#, 534"#,
470 ); 535 );
471 } 536 }
472 537
@@ -499,19 +564,19 @@ fn main() {
499 check_assist_not_applicable( 564 check_assist_not_applicable(
500 unwrap_block, 565 unwrap_block,
501 r#" 566 r#"
502 fn main() { 567fn main() {
503 while true { 568 while true {
504 if true { 569 if true {
505 foo();<|> 570 foo();<|>
506 571
507 //comment 572 //comment
508 bar(); 573 bar();
509 } else { 574 } else {
510 println!("bar"); 575 println!("bar");
511 } 576 }
512 } 577 }
513 } 578}
514 "#, 579"#,
515 ); 580 );
516 } 581 }
517} 582}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index e8d81b33d..17e9312db 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -141,6 +141,7 @@ mod handlers {
141 mod generate_function; 141 mod generate_function;
142 mod generate_impl; 142 mod generate_impl;
143 mod generate_new; 143 mod generate_new;
144 mod ignore_test;
144 mod infer_function_return_type; 145 mod infer_function_return_type;
145 mod inline_local_variable; 146 mod inline_local_variable;
146 mod introduce_named_lifetime; 147 mod introduce_named_lifetime;
@@ -189,6 +190,7 @@ mod handlers {
189 generate_function::generate_function, 190 generate_function::generate_function,
190 generate_impl::generate_impl, 191 generate_impl::generate_impl,
191 generate_new::generate_new, 192 generate_new::generate_new,
193 ignore_test::ignore_test,
192 infer_function_return_type::infer_function_return_type, 194 infer_function_return_type::infer_function_return_type,
193 inline_local_variable::inline_local_variable, 195 inline_local_variable::inline_local_variable,
194 introduce_named_lifetime::introduce_named_lifetime, 196 introduce_named_lifetime::introduce_named_lifetime,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index dbf4f21aa..5a9d1a01b 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -474,6 +474,26 @@ impl<T: Clone> Ctx<T> {
474} 474}
475 475
476#[test] 476#[test]
477fn doctest_ignore_test() {
478 check_doc_test(
479 "ignore_test",
480 r#####"
481<|>#[test]
482fn arithmetics {
483 assert_eq!(2 + 2, 5);
484}
485"#####,
486 r#####"
487#[test]
488#[ignore]
489fn arithmetics {
490 assert_eq!(2 + 2, 5);
491}
492"#####,
493 )
494}
495
496#[test]
477fn doctest_infer_function_return_type() { 497fn doctest_infer_function_return_type() {
478 check_doc_test( 498 check_doc_test(
479 "infer_function_return_type", 499 "infer_function_return_type",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7bd338e99..66c0cdd5f 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -9,6 +9,7 @@ use ide_db::RootDatabase;
9use itertools::Itertools; 9use itertools::Itertools;
10use syntax::{ 10use syntax::{
11 ast::edit::AstNodeEdit, 11 ast::edit::AstNodeEdit,
12 ast::AttrsOwner,
12 ast::NameOwner, 13 ast::NameOwner,
13 ast::{self, edit, make, ArgListOwner}, 14 ast::{self, edit, make, ArgListOwner},
14 AstNode, Direction, 15 AstNode, Direction,
@@ -21,8 +22,7 @@ use crate::{
21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 22 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
22}; 23};
23 24
24pub use insert_use::MergeBehaviour; 25pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
25pub(crate) use insert_use::{insert_use, ImportScope};
26 26
27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
28 let mut segments = Vec::new(); 28 let mut segments = Vec::new();
@@ -82,6 +82,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
82 None 82 None
83} 83}
84 84
85/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
86/// `#[test_case(...)]`, `#[tokio::test]` and similar.
87/// Also a regular `#[test]` annotation is supported.
88///
89/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
90/// but it's better than not to have the runnables for the tests at all.
91pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
92 fn_def.attrs().find_map(|attr| {
93 let path = attr.path()?;
94 if path.syntax().text().to_string().contains("test") {
95 Some(attr)
96 } else {
97 None
98 }
99 })
100}
101
85#[derive(Copy, Clone, PartialEq)] 102#[derive(Copy, Clone, PartialEq)]
86pub enum DefaultMethods { 103pub enum DefaultMethods {
87 Only, 104 Only,
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index f47edbb76..ff5c0e78e 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -179,21 +179,25 @@ impl ImportAssets {
179 } 179 }
180 }; 180 };
181 181
182 let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) 182 let mut res =
183 .into_iter() 183 imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query())
184 .filter_map(filter) 184 .filter_map(filter)
185 .filter_map(|candidate| { 185 .filter_map(|candidate| {
186 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 186 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
187 if let Some(prefix_kind) = prefixed { 187 if let Some(prefix_kind) = prefixed {
188 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) 188 self.module_with_name_to_import.find_use_path_prefixed(
189 } else { 189 db,
190 self.module_with_name_to_import.find_use_path(db, item) 190 item,
191 } 191 prefix_kind,
192 .map(|path| (path, item)) 192 )
193 }) 193 } else {
194 .filter(|(use_path, _)| !use_path.segments.is_empty()) 194 self.module_with_name_to_import.find_use_path(db, item)
195 .take(20) 195 }
196 .collect::<Vec<_>>(); 196 .map(|path| (path, item))
197 })
198 .filter(|(use_path, _)| use_path.len() > 1)
199 .take(20)
200 .collect::<Vec<_>>();
197 res.sort_by_key(|(path, _)| path.clone()); 201 res.sort_by_key(|(path, _)| path.clone());
198 res 202 res
199 } 203 }
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index af3fc96b6..423782a0e 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,6 +1,8 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::{cmp::Ordering, iter::successors}; 2use std::{cmp::Ordering, iter::successors};
3 3
4use hir::Semantics;
5use ide_db::RootDatabase;
4use itertools::{EitherOrBoth, Itertools}; 6use itertools::{EitherOrBoth, Itertools};
5use syntax::{ 7use syntax::{
6 algo::SyntaxRewriter, 8 algo::SyntaxRewriter,
@@ -13,8 +15,8 @@ use syntax::{
13}; 15};
14use test_utils::mark; 16use test_utils::mark;
15 17
16#[derive(Debug)] 18#[derive(Debug, Clone)]
17pub(crate) enum ImportScope { 19pub enum ImportScope {
18 File(ast::SourceFile), 20 File(ast::SourceFile),
19 Module(ast::ItemList), 21 Module(ast::ItemList),
20} 22}
@@ -31,14 +33,14 @@ impl ImportScope {
31 } 33 }
32 34
33 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. 35 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
34 pub(crate) fn find_insert_use_container( 36 pub fn find_insert_use_container(
35 position: &SyntaxNode, 37 position: &SyntaxNode,
36 ctx: &crate::assist_context::AssistContext, 38 sema: &Semantics<'_, RootDatabase>,
37 ) -> Option<Self> { 39 ) -> Option<Self> {
38 ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from) 40 sema.ancestors_with_macros(position.clone()).find_map(Self::from)
39 } 41 }
40 42
41 pub(crate) fn as_syntax_node(&self) -> &SyntaxNode { 43 pub fn as_syntax_node(&self) -> &SyntaxNode {
42 match self { 44 match self {
43 ImportScope::File(file) => file.syntax(), 45 ImportScope::File(file) => file.syntax(),
44 ImportScope::Module(item_list) => item_list.syntax(), 46 ImportScope::Module(item_list) => item_list.syntax(),
@@ -88,7 +90,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
88} 90}
89 91
90/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 92/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
91pub(crate) fn insert_use<'a>( 93pub fn insert_use<'a>(
92 scope: &ImportScope, 94 scope: &ImportScope,
93 path: ast::Path, 95 path: ast::Path,
94 merge: Option<MergeBehaviour>, 96 merge: Option<MergeBehaviour>,
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 31907ed98..98ba372ad 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -225,7 +225,10 @@ impl CrateGraph {
225 to: CrateId, 225 to: CrateId,
226 ) -> Result<(), CyclicDependenciesError> { 226 ) -> Result<(), CyclicDependenciesError> {
227 if self.dfs_find(from, to, &mut FxHashSet::default()) { 227 if self.dfs_find(from, to, &mut FxHashSet::default()) {
228 return Err(CyclicDependenciesError); 228 return Err(CyclicDependenciesError {
229 from: (from, self[from].display_name.clone()),
230 to: (to, self[to].display_name.clone()),
231 });
229 } 232 }
230 self.arena.get_mut(&from).unwrap().add_dep(name, to); 233 self.arena.get_mut(&from).unwrap().add_dep(name, to);
231 Ok(()) 234 Ok(())
@@ -421,7 +424,20 @@ impl fmt::Display for ParseEditionError {
421impl std::error::Error for ParseEditionError {} 424impl std::error::Error for ParseEditionError {}
422 425
423#[derive(Debug)] 426#[derive(Debug)]
424pub struct CyclicDependenciesError; 427pub struct CyclicDependenciesError {
428 from: (CrateId, Option<CrateDisplayName>),
429 to: (CrateId, Option<CrateDisplayName>),
430}
431
432impl fmt::Display for CyclicDependenciesError {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name {
435 Some(it) => format!("{}({:?})", it, id),
436 None => format!("{:?}", id),
437 };
438 write!(f, "cyclic deps: {} -> {}", render(&self.from), render(&self.to))
439 }
440}
425 441
426#[cfg(test)] 442#[cfg(test)]
427mod tests { 443mod tests {
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index d0e08cf5f..d88ecf8b0 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -41,12 +41,6 @@ impl CfgOptions {
41 self.enabled.insert(CfgAtom::KeyValue { key, value }); 41 self.enabled.insert(CfgAtom::KeyValue { key, value });
42 } 42 }
43 43
44 pub fn append(&mut self, other: &CfgOptions) {
45 for atom in &other.enabled {
46 self.enabled.insert(atom.clone());
47 }
48 }
49
50 pub fn apply_diff(&mut self, diff: CfgDiff) { 44 pub fn apply_diff(&mut self, diff: CfgDiff) {
51 for atom in diff.enable { 45 for atom in diff.enable {
52 self.enabled.insert(atom); 46 self.enabled.insert(atom);
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index 3015ec9e0..e7df9d955 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -13,6 +13,7 @@ doctest = false
13itertools = "0.9.0" 13itertools = "0.9.0"
14log = "0.4.8" 14log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1"
16 17
17assists = { path = "../assists", version = "0.0.0" } 18assists = { path = "../assists", version = "0.0.0" }
18stdx = { path = "../stdx", version = "0.0.0" } 19stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 75dbb1a23..9b7d6c580 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -90,7 +90,7 @@ impl Completions {
90 Some(it) => it, 90 Some(it) => it,
91 None => return, 91 None => return,
92 }; 92 };
93 if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) { 93 if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) {
94 self.add(item); 94 self.add(item);
95 } 95 }
96 } 96 }
@@ -101,7 +101,7 @@ impl Completions {
101 func: hir::Function, 101 func: hir::Function,
102 local_name: Option<String>, 102 local_name: Option<String>,
103 ) { 103 ) {
104 let item = render_fn(RenderContext::new(ctx), local_name, func); 104 let item = render_fn(RenderContext::new(ctx), None, local_name, func);
105 self.add(item) 105 self.add(item)
106 } 106 }
107 107
@@ -123,7 +123,7 @@ impl Completions {
123 variant: hir::EnumVariant, 123 variant: hir::EnumVariant,
124 path: ModPath, 124 path: ModPath,
125 ) { 125 ) {
126 let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path)); 126 let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path));
127 self.add(item); 127 self.add(item);
128 } 128 }
129 129
@@ -133,7 +133,7 @@ impl Completions {
133 variant: hir::EnumVariant, 133 variant: hir::EnumVariant,
134 local_name: Option<String>, 134 local_name: Option<String>,
135 ) { 135 ) {
136 let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None); 136 let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None);
137 self.add(item); 137 self.add(item);
138 } 138 }
139} 139}
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs
index 7ab7f09fe..4f63ff0ef 100644
--- a/crates/completion/src/completions/pattern.rs
+++ b/crates/completion/src/completions/pattern.rs
@@ -4,7 +4,7 @@ use crate::{CompletionContext, Completions};
4 4
5/// Completes constats and paths in patterns. 5/// Completes constats and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding_or_const { 7 if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) {
8 return; 8 return;
9 } 9 }
10 if ctx.record_pat_syntax.is_some() { 10 if ctx.record_pat_syntax.is_some() {
@@ -14,20 +14,27 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
14 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports 15 // suggest variants + auto-imports
16 ctx.scope.process_all_names(&mut |name, res| { 16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res { 17 let add_resolution = match &res {
18 hir::ScopeDef::ModuleDef(def) => match def { 18 hir::ScopeDef::ModuleDef(def) => {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 19 if ctx.is_irrefutable_let_pat_binding {
20 | hir::ModuleDef::Adt(hir::Adt::Struct(..)) 20 matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_)))
21 | hir::ModuleDef::EnumVariant(..) 21 } else {
22 | hir::ModuleDef::Const(..) 22 matches!(
23 | hir::ModuleDef::Module(..) => (), 23 def,
24 _ => return, 24 hir::ModuleDef::Adt(hir::Adt::Enum(..))
25 }, 25 | hir::ModuleDef::Adt(hir::Adt::Struct(..))
26 hir::ScopeDef::MacroDef(_) => (), 26 | hir::ModuleDef::EnumVariant(..)
27 _ => return, 27 | hir::ModuleDef::Const(..)
28 | hir::ModuleDef::Module(..)
29 )
30 }
31 }
32 hir::ScopeDef::MacroDef(_) => true,
33 _ => false,
28 }; 34 };
29 35 if add_resolution {
30 acc.add_resolution(ctx, name.to_string(), &res) 36 acc.add_resolution(ctx, name.to_string(), &res);
37 }
31 }); 38 });
32} 39}
33 40
@@ -85,4 +92,26 @@ fn foo() {
85 "#]], 92 "#]],
86 ); 93 );
87 } 94 }
95
96 #[test]
97 fn completes_in_irrefutable_let() {
98 check(
99 r#"
100enum E { X }
101use self::E::X;
102const Z: E = E::X;
103mod m {}
104
105static FOO: E = E::X;
106struct Bar { f: u32 }
107
108fn foo() {
109 let <|>
110}
111"#,
112 expect![[r#"
113 st Bar
114 "#]],
115 );
116 }
88} 117}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 7df58e1da..3bd776905 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -1,10 +1,16 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use assists::utils::ImportScope;
4use either::Either;
3use hir::{Adt, ModuleDef, ScopeDef, Type}; 5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use ide_db::imports_locator;
4use syntax::AstNode; 7use syntax::AstNode;
5use test_utils::mark; 8use test_utils::mark;
6 9
7use crate::{CompletionContext, Completions}; 10use crate::{
11 render::{render_resolution_with_import, RenderContext},
12 CompletionContext, Completions,
13};
8 14
9pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 15pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 16 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -37,6 +43,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
37 } 43 }
38 acc.add_resolution(ctx, name.to_string(), &res) 44 acc.add_resolution(ctx, name.to_string(), &res)
39 }); 45 });
46
47 if ctx.config.enable_experimental_completions {
48 fuzzy_completion(acc, ctx).unwrap_or_default()
49 }
40} 50}
41 51
42fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 52fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
@@ -63,6 +73,47 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
63 } 73 }
64} 74}
65 75
76fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
77 let _p = profile::span("fuzzy_completion");
78 let current_module = ctx.scope.module()?;
79 let anchor = ctx.name_ref_syntax.as_ref()?;
80 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
81
82 let potential_import_name = ctx.token.to_string();
83
84 let possible_imports = imports_locator::find_similar_imports(
85 &ctx.sema,
86 ctx.krate?,
87 &potential_import_name,
88 50,
89 true,
90 )
91 .filter_map(|import_candidate| {
92 Some(match import_candidate {
93 Either::Left(module_def) => {
94 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
95 }
96 Either::Right(macro_def) => {
97 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
98 }
99 })
100 })
101 .filter(|(mod_path, _)| mod_path.len() > 1)
102 .take(20)
103 .filter_map(|(import_path, definition)| {
104 render_resolution_with_import(
105 RenderContext::new(ctx),
106 import_path.clone(),
107 import_scope.clone(),
108 ctx.config.merge,
109 &definition,
110 )
111 });
112
113 acc.add_all(possible_imports);
114 Some(())
115}
116
66#[cfg(test)] 117#[cfg(test)]
67mod tests { 118mod tests {
68 use expect_test::{expect, Expect}; 119 use expect_test::{expect, Expect};
@@ -676,4 +727,85 @@ impl My<|>
676 "#]], 727 "#]],
677 ) 728 )
678 } 729 }
730
731 #[test]
732 fn function_fuzzy_completion() {
733 check_edit(
734 "stdin",
735 r#"
736//- /lib.rs crate:dep
737pub mod io {
738 pub fn stdin() {}
739};
740
741//- /main.rs crate:main deps:dep
742fn main() {
743 stdi<|>
744}
745"#,
746 r#"
747use dep::io::stdin;
748
749fn main() {
750 stdin()$0
751}
752"#,
753 );
754 }
755
756 #[test]
757 fn macro_fuzzy_completion() {
758 check_edit(
759 "macro_with_curlies!",
760 r#"
761//- /lib.rs crate:dep
762/// Please call me as macro_with_curlies! {}
763#[macro_export]
764macro_rules! macro_with_curlies {
765 () => {}
766}
767
768//- /main.rs crate:main deps:dep
769fn main() {
770 curli<|>
771}
772"#,
773 r#"
774use dep::macro_with_curlies;
775
776fn main() {
777 macro_with_curlies! {$0}
778}
779"#,
780 );
781 }
782
783 #[test]
784 fn struct_fuzzy_completion() {
785 check_edit(
786 "ThirdStruct",
787 r#"
788//- /lib.rs crate:dep
789pub struct FirstStruct;
790pub mod some_module {
791 pub struct SecondStruct;
792 pub struct ThirdStruct;
793}
794
795//- /main.rs crate:main deps:dep
796use dep::{FirstStruct, some_module::SecondStruct};
797
798fn main() {
799 this<|>
800}
801"#,
802 r#"
803use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
804
805fn main() {
806 ThirdStruct
807}
808"#,
809 );
810 }
679} 811}
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 71b49ace8..f50735372 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,12 +4,16 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use assists::utils::MergeBehaviour;
8
7#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig { 10pub struct CompletionConfig {
9 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_experimental_completions: bool,
10 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
16 pub merge: Option<MergeBehaviour>,
13} 17}
14 18
15impl CompletionConfig { 19impl CompletionConfig {
@@ -27,9 +31,11 @@ impl Default for CompletionConfig {
27 fn default() -> Self { 31 fn default() -> Self {
28 CompletionConfig { 32 CompletionConfig {
29 enable_postfix_completions: true, 33 enable_postfix_completions: true,
34 enable_experimental_completions: true,
30 add_call_parenthesis: true, 35 add_call_parenthesis: true,
31 add_call_argument_snippets: true, 36 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }), 37 snippet_cap: Some(SnippetCap { _private: () }),
38 merge: Some(MergeBehaviour::Full),
33 } 39 }
34 } 40 }
35} 41}
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index bf70ee478..5cd11cf77 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -51,6 +51,7 @@ pub(crate) struct CompletionContext<'a> {
51 /// If a name-binding or reference to a const in a pattern. 51 /// If a name-binding or reference to a const in a pattern.
52 /// Irrefutable patterns (like let) are excluded. 52 /// Irrefutable patterns (like let) are excluded.
53 pub(super) is_pat_binding_or_const: bool, 53 pub(super) is_pat_binding_or_const: bool,
54 pub(super) is_irrefutable_let_pat_binding: bool,
54 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 55 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
55 pub(super) is_trivial_path: bool, 56 pub(super) is_trivial_path: bool,
56 /// If not a trivial path, the prefix (qualifier). 57 /// If not a trivial path, the prefix (qualifier).
@@ -146,6 +147,7 @@ impl<'a> CompletionContext<'a> {
146 active_parameter: ActiveParameter::at(db, position), 147 active_parameter: ActiveParameter::at(db, position),
147 is_param: false, 148 is_param: false,
148 is_pat_binding_or_const: false, 149 is_pat_binding_or_const: false,
150 is_irrefutable_let_pat_binding: false,
149 is_trivial_path: false, 151 is_trivial_path: false,
150 path_qual: None, 152 path_qual: None,
151 after_if: false, 153 after_if: false,
@@ -330,6 +332,7 @@ impl<'a> CompletionContext<'a> {
330 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) 332 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
331 { 333 {
332 self.is_pat_binding_or_const = false; 334 self.is_pat_binding_or_const = false;
335 self.is_irrefutable_let_pat_binding = true;
333 } 336 }
334 } 337 }
335 } 338 }
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 6d1d085f4..b13c3f376 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -2,8 +2,9 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, Mutability}; 5use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
6use syntax::TextRange; 6use hir::{Documentation, ModPath, Mutability};
7use syntax::{algo, TextRange};
7use text_edit::TextEdit; 8use text_edit::TextEdit;
8 9
9use crate::config::SnippetCap; 10use crate::config::SnippetCap;
@@ -31,6 +32,7 @@ pub struct CompletionItem {
31 /// 32 ///
32 /// Typically, replaces `source_range` with new identifier. 33 /// Typically, replaces `source_range` with new identifier.
33 text_edit: TextEdit, 34 text_edit: TextEdit,
35
34 insert_text_format: InsertTextFormat, 36 insert_text_format: InsertTextFormat,
35 37
36 /// What item (struct, function, etc) are we completing. 38 /// What item (struct, function, etc) are we completing.
@@ -199,8 +201,10 @@ impl CompletionItem {
199 trigger_call_info: None, 201 trigger_call_info: None,
200 score: None, 202 score: None,
201 ref_match: None, 203 ref_match: None,
204 import_data: None,
202 } 205 }
203 } 206 }
207
204 /// What user sees in pop-up in the UI. 208 /// What user sees in pop-up in the UI.
205 pub fn label(&self) -> &str { 209 pub fn label(&self) -> &str {
206 &self.label 210 &self.label
@@ -257,6 +261,7 @@ impl CompletionItem {
257pub(crate) struct Builder { 261pub(crate) struct Builder {
258 source_range: TextRange, 262 source_range: TextRange,
259 completion_kind: CompletionKind, 263 completion_kind: CompletionKind,
264 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
260 label: String, 265 label: String,
261 insert_text: Option<String>, 266 insert_text: Option<String>,
262 insert_text_format: InsertTextFormat, 267 insert_text_format: InsertTextFormat,
@@ -273,23 +278,50 @@ pub(crate) struct Builder {
273 278
274impl Builder { 279impl Builder {
275 pub(crate) fn build(self) -> CompletionItem { 280 pub(crate) fn build(self) -> CompletionItem {
276 let label = self.label; 281 let mut label = self.label;
277 let text_edit = match self.text_edit { 282 let mut lookup = self.lookup;
283 let mut insert_text = self.insert_text;
284 let mut text_edits = TextEdit::builder();
285
286 if let Some((import_path, import_scope, merge_behaviour)) = self.import_data {
287 let import = mod_path_to_ast(&import_path);
288 let mut import_path_without_last_segment = import_path;
289 let _ = import_path_without_last_segment.segments.pop();
290
291 if !import_path_without_last_segment.segments.is_empty() {
292 if lookup.is_none() {
293 lookup = Some(label.clone());
294 }
295 if insert_text.is_none() {
296 insert_text = Some(label.clone());
297 }
298 label = format!("{}::{}", import_path_without_last_segment, label);
299 }
300
301 let rewriter = insert_use(&import_scope, import, merge_behaviour);
302 if let Some(old_ast) = rewriter.rewrite_root() {
303 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
304 }
305 }
306
307 let original_edit = match self.text_edit {
278 Some(it) => it, 308 Some(it) => it,
279 None => TextEdit::replace( 309 None => {
280 self.source_range, 310 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
281 self.insert_text.unwrap_or_else(|| label.clone()), 311 }
282 ),
283 }; 312 };
284 313
314 let mut resulting_edit = text_edits.finish();
315 resulting_edit.union(original_edit).expect("Failed to unite text edits");
316
285 CompletionItem { 317 CompletionItem {
286 source_range: self.source_range, 318 source_range: self.source_range,
287 label, 319 label,
288 insert_text_format: self.insert_text_format, 320 insert_text_format: self.insert_text_format,
289 text_edit, 321 text_edit: resulting_edit,
290 detail: self.detail, 322 detail: self.detail,
291 documentation: self.documentation, 323 documentation: self.documentation,
292 lookup: self.lookup, 324 lookup,
293 kind: self.kind, 325 kind: self.kind,
294 completion_kind: self.completion_kind, 326 completion_kind: self.completion_kind,
295 deprecated: self.deprecated.unwrap_or(false), 327 deprecated: self.deprecated.unwrap_or(false),
@@ -358,6 +390,13 @@ impl Builder {
358 self.trigger_call_info = Some(true); 390 self.trigger_call_info = Some(true);
359 self 391 self
360 } 392 }
393 pub(crate) fn import_data(
394 mut self,
395 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
396 ) -> Builder {
397 self.import_data = import_data;
398 self
399 }
361 pub(crate) fn set_ref_match( 400 pub(crate) fn set_ref_match(
362 mut self, 401 mut self,
363 ref_match: Option<(Mutability, CompletionScore)>, 402 ref_match: Option<(Mutability, CompletionScore)>,
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index cb6e0554e..aecc1378b 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -67,6 +67,13 @@ pub use crate::{
67// fn test_name() {} 67// fn test_name() {}
68// } 68// }
69// ``` 69// ```
70//
71// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting.
72// This flag enables or disables:
73//
74// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input
75//
76// Experimental completions might cause issues with performance and completion list look.
70 77
71/// Main entry point for completion. We run completion as a two-phase process. 78/// Main entry point for completion. We run completion as a two-phase process.
72/// 79///
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1fa02c375..bce02f577 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -9,7 +9,8 @@ pub(crate) mod type_alias;
9 9
10mod builder_ext; 10mod builder_ext;
11 11
12use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; 12use assists::utils::{ImportScope, MergeBehaviour};
13use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
13use ide_db::RootDatabase; 14use ide_db::RootDatabase;
14use syntax::TextRange; 15use syntax::TextRange;
15use test_utils::mark; 16use test_utils::mark;
@@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>(
42 local_name: String, 43 local_name: String,
43 resolution: &ScopeDef, 44 resolution: &ScopeDef,
44) -> Option<CompletionItem> { 45) -> Option<CompletionItem> {
45 Render::new(ctx).render_resolution(local_name, resolution) 46 Render::new(ctx).render_resolution(local_name, None, resolution)
47}
48
49pub(crate) fn render_resolution_with_import<'a>(
50 ctx: RenderContext<'a>,
51 import: ModPath,
52 import_scope: ImportScope,
53 merge_behaviour: Option<MergeBehaviour>,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> {
56 let local_name = import.segments.last()?.to_string();
57 Render::new(ctx).render_resolution(
58 local_name,
59 Some((import, import_scope, merge_behaviour)),
60 resolution,
61 )
46} 62}
47 63
48/// Interface for data and methods required for items rendering. 64/// Interface for data and methods required for items rendering.
@@ -131,8 +147,10 @@ impl<'a> Render<'a> {
131 fn render_resolution( 147 fn render_resolution(
132 self, 148 self,
133 local_name: String, 149 local_name: String,
150 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
134 resolution: &ScopeDef, 151 resolution: &ScopeDef,
135 ) -> Option<CompletionItem> { 152 ) -> Option<CompletionItem> {
153 let _p = profile::span("render_resolution");
136 use hir::ModuleDef::*; 154 use hir::ModuleDef::*;
137 155
138 let completion_kind = match resolution { 156 let completion_kind = match resolution {
@@ -142,15 +160,15 @@ impl<'a> Render<'a> {
142 160
143 let kind = match resolution { 161 let kind = match resolution {
144 ScopeDef::ModuleDef(Function(func)) => { 162 ScopeDef::ModuleDef(Function(func)) => {
145 let item = render_fn(self.ctx, Some(local_name), *func); 163 let item = render_fn(self.ctx, import_data, Some(local_name), *func);
146 return Some(item); 164 return Some(item);
147 } 165 }
148 ScopeDef::ModuleDef(EnumVariant(var)) => { 166 ScopeDef::ModuleDef(EnumVariant(var)) => {
149 let item = render_enum_variant(self.ctx, Some(local_name), *var, None); 167 let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None);
150 return Some(item); 168 return Some(item);
151 } 169 }
152 ScopeDef::MacroDef(mac) => { 170 ScopeDef::MacroDef(mac) => {
153 let item = render_macro(self.ctx, local_name, *mac); 171 let item = render_macro(self.ctx, import_data, local_name, *mac);
154 return item; 172 return item;
155 } 173 }
156 174
@@ -175,6 +193,7 @@ impl<'a> Render<'a> {
175 local_name, 193 local_name,
176 ) 194 )
177 .kind(CompletionItemKind::UnresolvedReference) 195 .kind(CompletionItemKind::UnresolvedReference)
196 .import_data(import_data)
178 .build(); 197 .build();
179 return Some(item); 198 return Some(item);
180 } 199 }
@@ -227,7 +246,12 @@ impl<'a> Render<'a> {
227 } 246 }
228 } 247 }
229 248
230 let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); 249 let item = item
250 .kind(kind)
251 .import_data(import_data)
252 .set_documentation(docs)
253 .set_ref_match(ref_match)
254 .build();
231 Some(item) 255 Some(item)
232 } 256 }
233 257
@@ -426,6 +450,28 @@ fn main() { let _: m::Spam = S<|> }
426 kind: Module, 450 kind: Module,
427 }, 451 },
428 CompletionItem { 452 CompletionItem {
453 label: "m::Spam",
454 source_range: 75..76,
455 text_edit: TextEdit {
456 indels: [
457 Indel {
458 insert: "use m::Spam;",
459 delete: 0..0,
460 },
461 Indel {
462 insert: "\n\n",
463 delete: 0..0,
464 },
465 Indel {
466 insert: "Spam",
467 delete: 75..76,
468 },
469 ],
470 },
471 kind: Enum,
472 lookup: "Spam",
473 },
474 CompletionItem {
429 label: "m::Spam::Foo", 475 label: "m::Spam::Foo",
430 source_range: 75..76, 476 source_range: 75..76,
431 delete: 75..76, 477 delete: 75..76,
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index fd412ed0e..6070e9b1d 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -1,5 +1,6 @@
1//! Renderer for `enum` variants. 1//! Renderer for `enum` variants.
2 2
3use assists::utils::{ImportScope, MergeBehaviour};
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 4use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use itertools::Itertools; 5use itertools::Itertools;
5use test_utils::mark; 6use test_utils::mark;
@@ -11,11 +12,12 @@ use crate::{
11 12
12pub(crate) fn render_enum_variant<'a>( 13pub(crate) fn render_enum_variant<'a>(
13 ctx: RenderContext<'a>, 14 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
14 local_name: Option<String>, 16 local_name: Option<String>,
15 variant: hir::EnumVariant, 17 variant: hir::EnumVariant,
16 path: Option<ModPath>, 18 path: Option<ModPath>,
17) -> CompletionItem { 19) -> CompletionItem {
18 EnumVariantRender::new(ctx, local_name, variant, path).render() 20 EnumVariantRender::new(ctx, local_name, variant, path).render(import_data)
19} 21}
20 22
21#[derive(Debug)] 23#[derive(Debug)]
@@ -60,7 +62,10 @@ impl<'a> EnumVariantRender<'a> {
60 } 62 }
61 } 63 }
62 64
63 fn render(self) -> CompletionItem { 65 fn render(
66 self,
67 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
68 ) -> CompletionItem {
64 let mut builder = CompletionItem::new( 69 let mut builder = CompletionItem::new(
65 CompletionKind::Reference, 70 CompletionKind::Reference,
66 self.ctx.source_range(), 71 self.ctx.source_range(),
@@ -69,6 +74,7 @@ impl<'a> EnumVariantRender<'a> {
69 .kind(CompletionItemKind::EnumVariant) 74 .kind(CompletionItemKind::EnumVariant)
70 .set_documentation(self.variant.docs(self.ctx.db())) 75 .set_documentation(self.variant.docs(self.ctx.db()))
71 .set_deprecated(self.ctx.is_deprecated(self.variant)) 76 .set_deprecated(self.ctx.is_deprecated(self.variant))
77 .import_data(import_data)
72 .detail(self.detail()); 78 .detail(self.detail());
73 79
74 if self.variant_kind == StructKind::Tuple { 80 if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 4fa6eafd7..9dd5cd18c 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -1,6 +1,7 @@
1//! Renderer for function calls. 1//! Renderer for function calls.
2 2
3use hir::{HasSource, Type}; 3use assists::utils::{ImportScope, MergeBehaviour};
4use hir::{HasSource, ModPath, Type};
4use syntax::{ast::Fn, display::function_declaration}; 5use syntax::{ast::Fn, display::function_declaration};
5 6
6use crate::{ 7use crate::{
@@ -10,10 +11,11 @@ use crate::{
10 11
11pub(crate) fn render_fn<'a>( 12pub(crate) fn render_fn<'a>(
12 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
13 local_name: Option<String>, 15 local_name: Option<String>,
14 fn_: hir::Function, 16 fn_: hir::Function,
15) -> CompletionItem { 17) -> CompletionItem {
16 FunctionRender::new(ctx, local_name, fn_).render() 18 FunctionRender::new(ctx, local_name, fn_).render(import_data)
17} 19}
18 20
19#[derive(Debug)] 21#[derive(Debug)]
@@ -36,7 +38,10 @@ impl<'a> FunctionRender<'a> {
36 FunctionRender { ctx, name, fn_, ast_node } 38 FunctionRender { ctx, name, fn_, ast_node }
37 } 39 }
38 40
39 fn render(self) -> CompletionItem { 41 fn render(
42 self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> CompletionItem {
40 let params = self.params(); 45 let params = self.params();
41 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 46 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
42 .kind(self.kind()) 47 .kind(self.kind())
@@ -44,6 +49,7 @@ impl<'a> FunctionRender<'a> {
44 .set_deprecated(self.ctx.is_deprecated(self.fn_)) 49 .set_deprecated(self.ctx.is_deprecated(self.fn_))
45 .detail(self.detail()) 50 .detail(self.detail())
46 .add_call_parens(self.ctx.completion, self.name, params) 51 .add_call_parens(self.ctx.completion, self.name, params)
52 .import_data(import_data)
47 .build() 53 .build()
48 } 54 }
49 55
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index 96be59cc3..fead59e41 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -1,6 +1,7 @@
1//! Renderer for macro invocations. 1//! Renderer for macro invocations.
2 2
3use hir::{Documentation, HasSource}; 3use assists::utils::{ImportScope, MergeBehaviour};
4use hir::{Documentation, HasSource, ModPath};
4use syntax::display::macro_label; 5use syntax::display::macro_label;
5use test_utils::mark; 6use test_utils::mark;
6 7
@@ -11,10 +12,11 @@ use crate::{
11 12
12pub(crate) fn render_macro<'a>( 13pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>, 14 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
14 name: String, 16 name: String,
15 macro_: hir::MacroDef, 17 macro_: hir::MacroDef,
16) -> Option<CompletionItem> { 18) -> Option<CompletionItem> {
17 MacroRender::new(ctx, name, macro_).render() 19 MacroRender::new(ctx, name, macro_).render(import_data)
18} 20}
19 21
20#[derive(Debug)] 22#[derive(Debug)]
@@ -36,7 +38,10 @@ impl<'a> MacroRender<'a> {
36 MacroRender { ctx, name, macro_, docs, bra, ket } 38 MacroRender { ctx, name, macro_, docs, bra, ket }
37 } 39 }
38 40
39 fn render(&self) -> Option<CompletionItem> { 41 fn render(
42 &self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> Option<CompletionItem> {
40 // FIXME: Currently proc-macro do not have ast-node, 45 // FIXME: Currently proc-macro do not have ast-node,
41 // such that it does not have source 46 // such that it does not have source
42 if self.macro_.is_proc_macro() { 47 if self.macro_.is_proc_macro() {
@@ -48,6 +53,7 @@ impl<'a> MacroRender<'a> {
48 .kind(CompletionItemKind::Macro) 53 .kind(CompletionItemKind::Macro)
49 .set_documentation(self.docs.clone()) 54 .set_documentation(self.docs.clone())
50 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 55 .set_deprecated(self.ctx.is_deprecated(self.macro_))
56 .import_data(import_data)
51 .detail(self.detail()); 57 .detail(self.detail());
52 58
53 let needs_bang = self.needs_bang(); 59 let needs_bang = self.needs_bang();
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 44499bc79..3d9436d69 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "0.12.0" 15cargo_metadata = "=0.12.0"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 30a5e4580..f06b5cd9f 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -110,15 +110,9 @@ impl Crate {
110 pub fn query_external_importables( 110 pub fn query_external_importables(
111 self, 111 self,
112 db: &dyn DefDatabase, 112 db: &dyn DefDatabase,
113 query: &str, 113 query: import_map::Query,
114 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 114 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
115 import_map::search_dependencies( 115 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
116 db,
117 self.into(),
118 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
119 )
120 .into_iter()
121 .map(|item| match item {
122 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), 116 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
123 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), 117 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
124 }) 118 })
@@ -1426,11 +1420,11 @@ impl Type {
1426 pub fn normalize_trait_assoc_type( 1420 pub fn normalize_trait_assoc_type(
1427 &self, 1421 &self,
1428 db: &dyn HirDatabase, 1422 db: &dyn HirDatabase,
1429 r#trait: Trait, 1423 trait_: Trait,
1430 args: &[Type], 1424 args: &[Type],
1431 alias: TypeAlias, 1425 alias: TypeAlias,
1432 ) -> Option<Type> { 1426 ) -> Option<Type> {
1433 let subst = Substs::build_for_def(db, r#trait.id) 1427 let subst = Substs::build_for_def(db, trait_.id)
1434 .push(self.ty.value.clone()) 1428 .push(self.ty.value.clone())
1435 .fill(args.iter().map(|t| t.ty.value.clone())) 1429 .fill(args.iter().map(|t| t.ty.value.clone()))
1436 .build(); 1430 .build();
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index 07333c453..8c767b249 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -11,7 +11,7 @@ pub use hir_def::db::{
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
14 MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, 14 MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery,
15}; 15};
16pub use hir_ty::db::*; 16pub use hir_ty::db::*;
17 17
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index c18c1c587..d9ad8db6f 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,6 +1,8 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; 2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
3pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; 3pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5};
4pub use hir_ty::diagnostics::{ 6pub use hir_ty::diagnostics::{
5 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, 7 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
6 NoSuchField, 8 NoSuchField,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 0d184379f..93bdb4472 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -49,6 +49,7 @@ pub use hir_def::{
49 builtin_type::BuiltinType, 49 builtin_type::BuiltinType,
50 docs::Documentation, 50 docs::Documentation,
51 find_path::PrefixKind, 51 find_path::PrefixKind,
52 import_map,
52 item_scope::ItemInNs, 53 item_scope::ItemInNs,
53 nameres::ModuleSource, 54 nameres::ModuleSource,
54 path::{ModPath, PathKind}, 55 path::{ModPath, PathKind},
@@ -56,8 +57,8 @@ pub use hir_def::{
56 visibility::Visibility, 57 visibility::Visibility,
57}; 58};
58pub use hir_expand::{ 59pub use hir_expand::{
59 name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, 60 name::known, name::AsName, name::Name, ExpandResult, HirFileId, InFile, MacroCallId,
60 /* FIXME */ MacroDefId, MacroFile, Origin, 61 MacroCallLoc, /* FIXME */ MacroDefId, MacroFile, Origin,
61}; 62};
62pub use hir_ty::display::HirDisplay; 63pub use hir_ty::display::HirDisplay;
63 64
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 1e24f29a8..c0f108848 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -7,7 +7,7 @@ use fst::{self, Streamer};
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashMap, FxHasher}; 10use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
11use smallvec::SmallVec; 11use smallvec::SmallVec;
12use syntax::SmolStr; 12use syntax::SmolStr;
13 13
@@ -225,6 +225,19 @@ fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo))
225 lhs_str.cmp(&rhs_str) 225 lhs_str.cmp(&rhs_str)
226} 226}
227 227
228#[derive(Debug, Eq, PartialEq, Hash)]
229pub enum ImportKind {
230 Module,
231 Function,
232 Adt,
233 EnumVariant,
234 Const,
235 Static,
236 Trait,
237 TypeAlias,
238 BuiltinType,
239}
240
228#[derive(Debug)] 241#[derive(Debug)]
229pub struct Query { 242pub struct Query {
230 query: String, 243 query: String,
@@ -232,6 +245,7 @@ pub struct Query {
232 anchor_end: bool, 245 anchor_end: bool,
233 case_sensitive: bool, 246 case_sensitive: bool,
234 limit: usize, 247 limit: usize,
248 exclude_import_kinds: FxHashSet<ImportKind>,
235} 249}
236 250
237impl Query { 251impl Query {
@@ -242,6 +256,7 @@ impl Query {
242 anchor_end: false, 256 anchor_end: false,
243 case_sensitive: false, 257 case_sensitive: false,
244 limit: usize::max_value(), 258 limit: usize::max_value(),
259 exclude_import_kinds: FxHashSet::default(),
245 } 260 }
246 } 261 }
247 262
@@ -260,6 +275,12 @@ impl Query {
260 pub fn case_sensitive(self) -> Self { 275 pub fn case_sensitive(self) -> Self {
261 Self { case_sensitive: true, ..self } 276 Self { case_sensitive: true, ..self }
262 } 277 }
278
279 /// Do not include imports of the specified kind in the search results.
280 pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self {
281 self.exclude_import_kinds.insert(import_kind);
282 self
283 }
263} 284}
264 285
265/// Searches dependencies of `krate` for an importable path matching `query`. 286/// Searches dependencies of `krate` for an importable path matching `query`.
@@ -303,10 +324,17 @@ pub fn search_dependencies<'a>(
303 324
304 // Add the items from this `ModPath` group. Those are all subsequent items in 325 // Add the items from this `ModPath` group. Those are all subsequent items in
305 // `importables` whose paths match `path`. 326 // `importables` whose paths match `path`.
306 let iter = importables.iter().copied().take_while(|item| { 327 let iter = importables
307 let item_path = &import_map.map[item].path; 328 .iter()
308 fst_path(item_path) == fst_path(path) 329 .copied()
309 }); 330 .take_while(|item| {
331 let item_path = &import_map.map[item].path;
332 fst_path(item_path) == fst_path(path)
333 })
334 .filter(|&item| match item_import_kind(item) {
335 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
336 None => true,
337 });
310 338
311 if query.case_sensitive { 339 if query.case_sensitive {
312 // FIXME: This does not do a subsequence match. 340 // FIXME: This does not do a subsequence match.
@@ -341,6 +369,20 @@ pub fn search_dependencies<'a>(
341 res 369 res
342} 370}
343 371
372fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
373 Some(match item.as_module_def_id()? {
374 ModuleDefId::ModuleId(_) => ImportKind::Module,
375 ModuleDefId::FunctionId(_) => ImportKind::Function,
376 ModuleDefId::AdtId(_) => ImportKind::Adt,
377 ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant,
378 ModuleDefId::ConstId(_) => ImportKind::Const,
379 ModuleDefId::StaticId(_) => ImportKind::Static,
380 ModuleDefId::TraitId(_) => ImportKind::Trait,
381 ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
382 ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
383 })
384}
385
344#[cfg(test)] 386#[cfg(test)]
345mod tests { 387mod tests {
346 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 388 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
@@ -758,4 +800,34 @@ mod tests {
758 "#]], 800 "#]],
759 ); 801 );
760 } 802 }
803
804 #[test]
805 fn search_exclusions() {
806 let ra_fixture = r#"
807 //- /main.rs crate:main deps:dep
808 //- /dep.rs crate:dep
809
810 pub struct fmt;
811 pub struct FMT;
812 "#;
813
814 check_search(
815 ra_fixture,
816 "main",
817 Query::new("FMT"),
818 expect![[r#"
819 dep::fmt (t)
820 dep::fmt (v)
821 dep::FMT (t)
822 dep::FMT (v)
823 "#]],
824 );
825
826 check_search(
827 ra_fixture,
828 "main",
829 Query::new("FMT").exclude_import_kind(ImportKind::Adt),
830 expect![[r#""#]],
831 );
832 }
761} 833}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index eb41d324e..202a7dcb6 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -287,7 +287,7 @@ mod diagnostics {
287 use hir_expand::diagnostics::DiagnosticSink; 287 use hir_expand::diagnostics::DiagnosticSink;
288 use hir_expand::hygiene::Hygiene; 288 use hir_expand::hygiene::Hygiene;
289 use hir_expand::InFile; 289 use hir_expand::InFile;
290 use syntax::{ast, AstPtr, SyntaxNodePtr}; 290 use syntax::{ast, AstPtr};
291 291
292 use crate::path::ModPath; 292 use crate::path::ModPath;
293 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; 293 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -300,7 +300,7 @@ mod diagnostics {
300 300
301 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 301 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
302 302
303 UnconfiguredCode { ast: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, 303 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
304 } 304 }
305 305
306 #[derive(Debug, PartialEq, Eq)] 306 #[derive(Debug, PartialEq, Eq)]
@@ -341,7 +341,7 @@ mod diagnostics {
341 341
342 pub(super) fn unconfigured_code( 342 pub(super) fn unconfigured_code(
343 container: LocalModuleId, 343 container: LocalModuleId,
344 ast: InFile<SyntaxNodePtr>, 344 ast: AstId<ast::Item>,
345 cfg: CfgExpr, 345 cfg: CfgExpr,
346 opts: CfgOptions, 346 opts: CfgOptions,
347 ) -> Self { 347 ) -> Self {
@@ -399,9 +399,10 @@ mod diagnostics {
399 } 399 }
400 400
401 DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { 401 DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
402 let item = ast.to_node(db.upcast());
402 sink.push(InactiveCode { 403 sink.push(InactiveCode {
403 file: ast.file_id, 404 file: ast.file_id,
404 node: ast.value.clone(), 405 node: AstPtr::new(&item).into(),
405 cfg: cfg.clone(), 406 cfg: cfg.clone(),
406 opts: opts.clone(), 407 opts: opts.clone(),
407 }); 408 });
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 386287518..5ed9073e0 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1336,13 +1336,11 @@ impl ModCollector<'_, '_> {
1336 1336
1337 fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { 1337 fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
1338 let ast_id = item.ast_id(self.item_tree); 1338 let ast_id = item.ast_id(self.item_tree);
1339 let id_map = self.def_collector.db.ast_id_map(self.file_id);
1340 let syntax_ptr = id_map.get(ast_id).syntax_node_ptr();
1341 1339
1342 let ast_node = InFile::new(self.file_id, syntax_ptr); 1340 let ast_id = InFile::new(self.file_id, ast_id);
1343 self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( 1341 self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
1344 self.module_id, 1342 self.module_id,
1345 ast_node, 1343 ast_id,
1346 cfg.clone(), 1344 cfg.clone(),
1347 self.def_collector.cfg_options.clone(), 1345 self.def_collector.cfg_options.clone(),
1348 )); 1346 ));
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index cfbc62cc4..8981fa7c9 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -38,6 +38,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
38 fn foo() -> i32 { 38 fn foo() -> i32 {
39 1 + 1 39 1 + 1
40 } 40 }
41
42 #[cfg(never)]
43 fn no() {}
41 //- /foo/mod.rs 44 //- /foo/mod.rs
42 pub mod bar; 45 pub mod bar;
43 46
@@ -53,6 +56,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
53 use E::*; 56 use E::*;
54 57
55 fn foo() -> i32 { 92 } 58 fn foo() -> i32 { 92 }
59
60 #[cfg(never)]
61 fn no() {}
56 ", 62 ",
57 ); 63 );
58} 64}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ade57ac1b..46ebdbc74 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -75,9 +75,11 @@ pub trait AstDatabase: SourceDatabase {
75 #[salsa::transparent] 75 #[salsa::transparent]
76 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; 76 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
77 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; 77 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
78 fn parse_macro(&self, macro_file: MacroFile) 78 fn parse_macro_expansion(
79 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; 79 &self,
80 fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); 80 macro_file: MacroFile,
81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
81 83
82 #[salsa::interned] 84 #[salsa::interned]
83 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 85 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
@@ -102,23 +104,20 @@ pub fn expand_hypothetical(
102 let token_id = tmap_1.token_by_range(range)?; 104 let token_id = tmap_1.token_by_range(range)?;
103 let macro_def = expander(db, actual_macro_call)?; 105 let macro_def = expander(db, actual_macro_call)?;
104 let (node, tmap_2) = 106 let (node, tmap_2) =
105 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; 107 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?;
106 let token_id = macro_def.0.map_id_down(token_id); 108 let token_id = macro_def.0.map_id_down(token_id);
107 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; 109 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
108 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; 110 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?;
109 Some((node.syntax_node(), token)) 111 Some((node.syntax_node(), token))
110} 112}
111 113
112pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 114fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
113 let map = 115 let map =
114 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); 116 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
115 Arc::new(map) 117 Arc::new(map)
116} 118}
117 119
118pub(crate) fn macro_def( 120fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
119 db: &dyn AstDatabase,
120 id: MacroDefId,
121) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
122 match id.kind { 121 match id.kind {
123 MacroDefKind::Declarative => { 122 MacroDefKind::Declarative => {
124 let macro_call = id.ast_id?.to_node(db); 123 let macro_call = id.ast_id?.to_node(db);
@@ -149,7 +148,7 @@ pub(crate) fn macro_def(
149 } 148 }
150} 149}
151 150
152pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { 151fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
153 let id = match id { 152 let id = match id {
154 MacroCallId::LazyMacro(id) => id, 153 MacroCallId::LazyMacro(id) => id,
155 MacroCallId::EagerMacro(_id) => { 154 MacroCallId::EagerMacro(_id) => {
@@ -162,19 +161,13 @@ pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<Gr
162 Some(arg.green().clone()) 161 Some(arg.green().clone())
163} 162}
164 163
165pub(crate) fn macro_arg( 164fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
166 db: &dyn AstDatabase,
167 id: MacroCallId,
168) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
169 let arg = db.macro_arg_text(id)?; 165 let arg = db.macro_arg_text(id)?;
170 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; 166 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?;
171 Some(Arc::new((tt, tmap))) 167 Some(Arc::new((tt, tmap)))
172} 168}
173 169
174pub(crate) fn macro_expand( 170fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> {
175 db: &dyn AstDatabase,
176 id: MacroCallId,
177) -> (Option<Arc<tt::Subtree>>, Option<String>) {
178 macro_expand_with_arg(db, id, None) 171 macro_expand_with_arg(db, id, None)
179} 172}
180 173
@@ -195,17 +188,19 @@ fn macro_expand_with_arg(
195 db: &dyn AstDatabase, 188 db: &dyn AstDatabase,
196 id: MacroCallId, 189 id: MacroCallId,
197 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 190 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
198) -> (Option<Arc<tt::Subtree>>, Option<String>) { 191) -> ExpandResult<Option<Arc<tt::Subtree>>> {
199 let lazy_id = match id { 192 let lazy_id = match id {
200 MacroCallId::LazyMacro(id) => id, 193 MacroCallId::LazyMacro(id) => id,
201 MacroCallId::EagerMacro(id) => { 194 MacroCallId::EagerMacro(id) => {
202 if arg.is_some() { 195 if arg.is_some() {
203 return ( 196 return ExpandResult::str_err(
204 None, 197 "hypothetical macro expansion not implemented for eager macro".to_owned(),
205 Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
206 ); 198 );
207 } else { 199 } else {
208 return (Some(db.lookup_intern_eager_expansion(id).subtree), None); 200 return ExpandResult {
201 value: Some(db.lookup_intern_eager_expansion(id).subtree),
202 err: None,
203 };
209 } 204 }
210 } 205 }
211 }; 206 };
@@ -213,23 +208,27 @@ fn macro_expand_with_arg(
213 let loc = db.lookup_intern_macro(lazy_id); 208 let loc = db.lookup_intern_macro(lazy_id);
214 let macro_arg = match arg.or_else(|| db.macro_arg(id)) { 209 let macro_arg = match arg.or_else(|| db.macro_arg(id)) {
215 Some(it) => it, 210 Some(it) => it,
216 None => return (None, Some("Fail to args in to tt::TokenTree".into())), 211 None => return ExpandResult::str_err("Fail to args in to tt::TokenTree".into()),
217 }; 212 };
218 213
219 let macro_rules = match db.macro_def(loc.def) { 214 let macro_rules = match db.macro_def(loc.def) {
220 Some(it) => it, 215 Some(it) => it,
221 None => return (None, Some("Fail to find macro definition".into())), 216 None => return ExpandResult::str_err("Fail to find macro definition".into()),
222 }; 217 };
223 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0); 218 let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
224 // Set a hard limit for the expanded tt 219 // Set a hard limit for the expanded tt
225 let count = tt.count(); 220 let count = tt.count();
226 if count > 262144 { 221 if count > 262144 {
227 return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); 222 return ExpandResult::str_err(format!(
223 "Total tokens count exceed limit : count = {}",
224 count
225 ));
228 } 226 }
229 (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) 227
228 ExpandResult { value: Some(Arc::new(tt)), err }
230} 229}
231 230
232pub(crate) fn expand_proc_macro( 231fn expand_proc_macro(
233 db: &dyn AstDatabase, 232 db: &dyn AstDatabase,
234 id: MacroCallId, 233 id: MacroCallId,
235) -> Result<tt::Subtree, mbe::ExpandError> { 234) -> Result<tt::Subtree, mbe::ExpandError> {
@@ -256,36 +255,36 @@ pub(crate) fn expand_proc_macro(
256 expander.expand(db, lazy_id, &macro_arg.0) 255 expander.expand(db, lazy_id, &macro_arg.0)
257} 256}
258 257
259pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { 258fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
260 match file_id.0 { 259 match file_id.0 {
261 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), 260 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
262 HirFileIdRepr::MacroFile(macro_file) => { 261 HirFileIdRepr::MacroFile(macro_file) => {
263 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) 262 db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
264 } 263 }
265 } 264 }
266} 265}
267 266
268pub(crate) fn parse_macro( 267fn parse_macro_expansion(
269 db: &dyn AstDatabase, 268 db: &dyn AstDatabase,
270 macro_file: MacroFile, 269 macro_file: MacroFile,
271) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 270) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
272 parse_macro_with_arg(db, macro_file, None) 271 parse_macro_with_arg(db, macro_file, None)
273} 272}
274 273
275pub fn parse_macro_with_arg( 274fn parse_macro_with_arg(
276 db: &dyn AstDatabase, 275 db: &dyn AstDatabase,
277 macro_file: MacroFile, 276 macro_file: MacroFile,
278 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 277 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
279) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 278) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
280 let _p = profile::span("parse_macro_query"); 279 let _p = profile::span("parse_macro_query");
281 280
282 let macro_call_id = macro_file.macro_call_id; 281 let macro_call_id = macro_file.macro_call_id;
283 let (tt, err) = if let Some(arg) = arg { 282 let result = if let Some(arg) = arg {
284 macro_expand_with_arg(db, macro_call_id, Some(arg)) 283 macro_expand_with_arg(db, macro_call_id, Some(arg))
285 } else { 284 } else {
286 db.macro_expand(macro_call_id) 285 db.macro_expand(macro_call_id)
287 }; 286 };
288 if let Some(err) = &err { 287 if let Some(err) = &result.err {
289 // Note: 288 // Note:
290 // The final goal we would like to make all parse_macro success, 289 // The final goal we would like to make all parse_macro success,
291 // such that the following log will not call anyway. 290 // such that the following log will not call anyway.
@@ -303,40 +302,50 @@ pub fn parse_macro_with_arg(
303 .join("\n"); 302 .join("\n");
304 303
305 log::warn!( 304 log::warn!(
306 "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", 305 "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
307 err, 306 err,
308 node.value, 307 node.value,
309 parents 308 parents
310 ); 309 );
311 } 310 }
312 _ => { 311 _ => {
313 log::warn!("fail on macro_parse: (reason: {})", err); 312 log::warn!("fail on macro_parse: (reason: {:?})", err);
314 } 313 }
315 } 314 }
315 }
316 let tt = match result.value {
317 Some(tt) => tt,
318 None => return ExpandResult { value: None, err: result.err },
316 }; 319 };
317 let tt = tt?;
318 320
319 let fragment_kind = to_fragment_kind(db, macro_call_id); 321 let fragment_kind = to_fragment_kind(db, macro_call_id);
320 322
321 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; 323 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
324 Ok(it) => it,
325 Err(err) => {
326 return ExpandResult::only_err(err);
327 }
328 };
322 329
323 if err.is_none() { 330 match result.err {
324 Some((parse, Arc::new(rev_token_map))) 331 Some(err) => {
325 } else { 332 // Safety check for recursive identity macro.
326 // FIXME: 333 let node = parse.syntax_node();
327 // In future, we should propagate the actual error with recovery information 334 let file: HirFileId = macro_file.into();
328 // instead of ignore the error here. 335 let call_node = match file.call_node(db) {
329 336 Some(it) => it,
330 // Safe check for recurisve identity macro 337 None => {
331 let node = parse.syntax_node(); 338 return ExpandResult::only_err(err);
332 let file: HirFileId = macro_file.into(); 339 }
333 let call_node = file.call_node(db)?; 340 };
334 341
335 if !diff(&node, &call_node.value).is_empty() { 342 if !diff(&node, &call_node.value).is_empty() {
336 Some((parse, Arc::new(rev_token_map))) 343 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
337 } else { 344 } else {
338 None 345 return ExpandResult::only_err(err);
346 }
339 } 347 }
348 None => ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None },
340 } 349 }
341} 350}
342 351
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs
index 78ccc212c..1043c6aeb 100644
--- a/crates/hir_expand/src/diagnostics.rs
+++ b/crates/hir_expand/src/diagnostics.rs
@@ -20,7 +20,7 @@ use syntax::SyntaxNodePtr;
20 20
21use crate::InFile; 21use crate::InFile;
22 22
23#[derive(Copy, Clone, PartialEq)] 23#[derive(Copy, Clone, Debug, PartialEq)]
24pub struct DiagnosticCode(pub &'static str); 24pub struct DiagnosticCode(pub &'static str);
25 25
26impl DiagnosticCode { 26impl DiagnosticCode {
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 17f1178ed..d5ba691b7 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -15,6 +15,8 @@ pub mod proc_macro;
15pub mod quote; 15pub mod quote;
16pub mod eager; 16pub mod eager;
17 17
18pub use mbe::{ExpandError, ExpandResult};
19
18use std::hash::Hash; 20use std::hash::Hash;
19use std::sync::Arc; 21use std::sync::Arc;
20 22
@@ -144,7 +146,7 @@ impl HirFileId {
144 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; 146 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
145 147
146 let macro_def = db.macro_def(loc.def)?; 148 let macro_def = db.macro_def(loc.def)?;
147 let (parse, exp_map) = db.parse_macro(macro_file)?; 149 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
148 let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 150 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
149 151
150 Some(ExpansionInfo { 152 Some(ExpansionInfo {
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 5bd03f2ac..62c329731 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -216,14 +216,14 @@
216//! U(P, p) := U(P, (r_1, p_2, .., p_n)) 216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
217//! || U(P, (r_2, p_2, .., p_n)) 217//! || U(P, (r_2, p_2, .., p_n))
218//! ``` 218//! ```
219use std::sync::Arc; 219use std::{iter, sync::Arc};
220 220
221use arena::Idx; 221use arena::Idx;
222use hir_def::{ 222use hir_def::{
223 adt::VariantData, 223 adt::VariantData,
224 body::Body, 224 body::Body,
225 expr::{Expr, Literal, Pat, PatId}, 225 expr::{Expr, Literal, Pat, PatId},
226 AdtId, EnumVariantId, VariantId, 226 AdtId, EnumVariantId, StructId, VariantId,
227}; 227};
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
@@ -366,16 +366,17 @@ impl PatStack {
366 366
367 let head_pat = head.as_pat(cx); 367 let head_pat = head.as_pat(cx);
368 let result = match (head_pat, constructor) { 368 let result = match (head_pat, constructor) {
369 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 369 (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => {
370 if ellipsis.is_some() { 370 if let Some(ellipsis) = ellipsis {
371 // If there are ellipsis here, we should add the correct number of 371 let (pre, post) = pat_ids.split_at(ellipsis);
372 // Pat::Wild patterns to `pat_ids`. We should be able to use the 372 let n_wild_pats = arity.saturating_sub(pat_ids.len());
373 // constructors arity for this, but at the time of writing we aren't 373 let pre_iter = pre.iter().map(Into::into);
374 // correctly calculating this arity when ellipsis are present. 374 let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats);
375 return Err(MatchCheckErr::NotImplemented); 375 let post_iter = post.iter().map(Into::into);
376 Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter)))
377 } else {
378 Some(self.replace_head_with(pat_ids.iter()))
376 } 379 }
377
378 Some(self.replace_head_with(pat_ids.iter()))
379 } 380 }
380 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { 381 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
381 match cx.body.exprs[lit_expr] { 382 match cx.body.exprs[lit_expr] {
@@ -390,21 +391,28 @@ impl PatStack {
390 } 391 }
391 } 392 }
392 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), 393 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
393 (Pat::Path(_), Constructor::Enum(constructor)) => { 394 (Pat::Path(_), constructor) => {
394 // unit enum variants become `Pat::Path` 395 // unit enum variants become `Pat::Path`
395 let pat_id = head.as_id().expect("we know this isn't a wild"); 396 let pat_id = head.as_id().expect("we know this isn't a wild");
396 if !enum_variant_matches(cx, pat_id, *constructor) { 397 let variant_id: VariantId = match constructor {
398 &Constructor::Enum(e) => e.into(),
399 &Constructor::Struct(s) => s.into(),
400 _ => return Err(MatchCheckErr::NotImplemented),
401 };
402 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
397 None 403 None
398 } else { 404 } else {
399 Some(self.to_tail()) 405 Some(self.to_tail())
400 } 406 }
401 } 407 }
402 ( 408 (Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, constructor) => {
403 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
404 Constructor::Enum(enum_constructor),
405 ) => {
406 let pat_id = head.as_id().expect("we know this isn't a wild"); 409 let pat_id = head.as_id().expect("we know this isn't a wild");
407 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 410 let variant_id: VariantId = match constructor {
411 &Constructor::Enum(e) => e.into(),
412 &Constructor::Struct(s) => s.into(),
413 _ => return Err(MatchCheckErr::MalformedMatchArm),
414 };
415 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
408 None 416 None
409 } else { 417 } else {
410 let constructor_arity = constructor.arity(cx)?; 418 let constructor_arity = constructor.arity(cx)?;
@@ -442,12 +450,22 @@ impl PatStack {
442 } 450 }
443 } 451 }
444 } 452 }
445 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { 453 (Pat::Record { args: ref arg_patterns, .. }, constructor) => {
446 let pat_id = head.as_id().expect("we know this isn't a wild"); 454 let pat_id = head.as_id().expect("we know this isn't a wild");
447 if !enum_variant_matches(cx, pat_id, *e) { 455 let (variant_id, variant_data) = match constructor {
456 &Constructor::Enum(e) => (
457 e.into(),
458 cx.db.enum_data(e.parent).variants[e.local_id].variant_data.clone(),
459 ),
460 &Constructor::Struct(s) => {
461 (s.into(), cx.db.struct_data(s).variant_data.clone())
462 }
463 _ => return Err(MatchCheckErr::MalformedMatchArm),
464 };
465 if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) {
448 None 466 None
449 } else { 467 } else {
450 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { 468 match variant_data.as_ref() {
451 VariantData::Record(struct_field_arena) => { 469 VariantData::Record(struct_field_arena) => {
452 // Here we treat any missing fields in the record as the wild pattern, as 470 // Here we treat any missing fields in the record as the wild pattern, as
453 // if the record has ellipsis. We want to do this here even if the 471 // if the record has ellipsis. We want to do this here even if the
@@ -726,6 +744,7 @@ enum Constructor {
726 Bool(bool), 744 Bool(bool),
727 Tuple { arity: usize }, 745 Tuple { arity: usize },
728 Enum(EnumVariantId), 746 Enum(EnumVariantId),
747 Struct(StructId),
729} 748}
730 749
731impl Constructor { 750impl Constructor {
@@ -740,6 +759,11 @@ impl Constructor {
740 VariantData::Unit => 0, 759 VariantData::Unit => 0,
741 } 760 }
742 } 761 }
762 &Constructor::Struct(s) => match cx.db.struct_data(s).variant_data.as_ref() {
763 VariantData::Tuple(struct_field_data) => struct_field_data.len(),
764 VariantData::Record(struct_field_data) => struct_field_data.len(),
765 VariantData::Unit => 0,
766 },
743 }; 767 };
744 768
745 Ok(arity) 769 Ok(arity)
@@ -748,7 +772,7 @@ impl Constructor {
748 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { 772 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> {
749 match self { 773 match self {
750 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], 774 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)],
751 Constructor::Tuple { .. } => vec![*self], 775 Constructor::Tuple { .. } | Constructor::Struct(_) => vec![*self],
752 Constructor::Enum(e) => cx 776 Constructor::Enum(e) => cx
753 .db 777 .db
754 .enum_data(e.parent) 778 .enum_data(e.parent)
@@ -767,10 +791,11 @@ impl Constructor {
767fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { 791fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
768 let res = match pat.as_pat(cx) { 792 let res = match pat.as_pat(cx) {
769 Pat::Wild => None, 793 Pat::Wild => None,
770 // FIXME somehow create the Tuple constructor with the proper arity. If there are 794 Pat::Tuple { .. } => {
771 // ellipsis, the arity is not equal to the number of patterns. 795 let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
772 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => { 796 Some(Constructor::Tuple {
773 Some(Constructor::Tuple { arity: pats.len() }) 797 arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(),
798 })
774 } 799 }
775 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { 800 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
776 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 801 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
@@ -784,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt
784 VariantId::EnumVariantId(enum_variant_id) => { 809 VariantId::EnumVariantId(enum_variant_id) => {
785 Some(Constructor::Enum(enum_variant_id)) 810 Some(Constructor::Enum(enum_variant_id))
786 } 811 }
812 VariantId::StructId(struct_id) => Some(Constructor::Struct(struct_id)),
787 _ => return Err(MatchCheckErr::NotImplemented), 813 _ => return Err(MatchCheckErr::NotImplemented),
788 } 814 }
789 } 815 }
@@ -828,13 +854,13 @@ fn all_constructors_covered(
828 854
829 false 855 false
830 }), 856 }),
857 &Constructor::Struct(s) => used_constructors.iter().any(|constructor| match constructor {
858 &Constructor::Struct(sid) => sid == s,
859 _ => false,
860 }),
831 } 861 }
832} 862}
833 863
834fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool {
835 Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id)
836}
837
838#[cfg(test)] 864#[cfg(test)]
839mod tests { 865mod tests {
840 use crate::diagnostics::tests::check_diagnostics; 866 use crate::diagnostics::tests::check_diagnostics;
@@ -846,8 +872,8 @@ mod tests {
846fn main() { 872fn main() {
847 match () { } 873 match () { }
848 //^^ Missing match arm 874 //^^ Missing match arm
849 match (()) { } 875 match (()) { }
850 //^^^^ Missing match arm 876 //^^^^ Missing match arm
851 877
852 match () { _ => (), } 878 match () { _ => (), }
853 match () { () => (), } 879 match () { () => (), }
@@ -1352,6 +1378,123 @@ fn main() {
1352 ); 1378 );
1353 } 1379 }
1354 1380
1381 #[test]
1382 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1383 check_diagnostics(
1384 r#"
1385fn main() {
1386 match (false, true, false) {
1387 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1388 (false, ..) => (),
1389 }
1390}"#,
1391 );
1392 }
1393
1394 #[test]
1395 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1396 check_diagnostics(
1397 r#"
1398fn main() {
1399 match (false, true, false) {
1400 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1401 (.., false) => (),
1402 }
1403}"#,
1404 );
1405 }
1406
1407 #[test]
1408 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
1409 check_diagnostics(
1410 r#"
1411fn main() {
1412 match (false, true, false) {
1413 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1414 (true, .., false) => (),
1415 }
1416}"#,
1417 );
1418 }
1419
1420 #[test]
1421 fn record_struct() {
1422 check_diagnostics(
1423 r#"struct Foo { a: bool }
1424fn main(f: Foo) {
1425 match f {}
1426 //^ Missing match arm
1427 match f { Foo { a: true } => () }
1428 //^ Missing match arm
1429 match &f { Foo { a: true } => () }
1430 //^^ Missing match arm
1431 match f { Foo { a: _ } => () }
1432 match f {
1433 Foo { a: true } => (),
1434 Foo { a: false } => (),
1435 }
1436 match &f {
1437 Foo { a: true } => (),
1438 Foo { a: false } => (),
1439 }
1440}
1441"#,
1442 );
1443 }
1444
1445 #[test]
1446 fn tuple_struct() {
1447 check_diagnostics(
1448 r#"struct Foo(bool);
1449fn main(f: Foo) {
1450 match f {}
1451 //^ Missing match arm
1452 match f { Foo(true) => () }
1453 //^ Missing match arm
1454 match f {
1455 Foo(true) => (),
1456 Foo(false) => (),
1457 }
1458}
1459"#,
1460 );
1461 }
1462
1463 #[test]
1464 fn unit_struct() {
1465 check_diagnostics(
1466 r#"struct Foo;
1467fn main(f: Foo) {
1468 match f {}
1469 //^ Missing match arm
1470 match f { Foo => () }
1471}
1472"#,
1473 );
1474 }
1475
1476 #[test]
1477 fn record_struct_ellipsis() {
1478 check_diagnostics(
1479 r#"struct Foo { foo: bool, bar: bool }
1480fn main(f: Foo) {
1481 match f { Foo { foo: true, .. } => () }
1482 //^ Missing match arm
1483 match f {
1484 //^ Missing match arm
1485 Foo { foo: true, .. } => (),
1486 Foo { bar: false, .. } => ()
1487 }
1488 match f { Foo { .. } => () }
1489 match f {
1490 Foo { foo: true, .. } => (),
1491 Foo { foo: false, .. } => ()
1492 }
1493}
1494"#,
1495 );
1496 }
1497
1355 mod false_negatives { 1498 mod false_negatives {
1356 //! The implementation of match checking here is a work in progress. As we roll this out, we 1499 //! The implementation of match checking here is a work in progress. As we roll this out, we
1357 //! prefer false negatives to false positives (ideally there would be no false positives). This 1500 //! prefer false negatives to false positives (ideally there would be no false positives). This
@@ -1393,46 +1536,5 @@ fn main() {
1393"#, 1536"#,
1394 ); 1537 );
1395 } 1538 }
1396
1397 #[test]
1398 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1399 // We don't currently handle tuple patterns with ellipsis.
1400 check_diagnostics(
1401 r#"
1402fn main() {
1403 match (false, true, false) {
1404 (false, ..) => (),
1405 }
1406}
1407"#,
1408 );
1409 }
1410
1411 #[test]
1412 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1413 // We don't currently handle tuple patterns with ellipsis.
1414 check_diagnostics(
1415 r#"
1416fn main() {
1417 match (false, true, false) {
1418 (.., false) => (),
1419 }
1420}
1421"#,
1422 );
1423 }
1424
1425 #[test]
1426 fn struct_missing_arm() {
1427 // We don't currently handle structs.
1428 check_diagnostics(
1429 r#"
1430struct Foo { a: bool }
1431fn main(f: Foo) {
1432 match f { Foo { a: true } => () }
1433}
1434"#,
1435 );
1436 }
1437 } 1539 }
1438} 1540}
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index cde2ab82b..b70ec55eb 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> {
23 expected: &Ty, 23 expected: &Ty,
24 default_bm: BindingMode, 24 default_bm: BindingMode,
25 id: PatId, 25 id: PatId,
26 ellipsis: Option<usize>,
26 ) -> Ty { 27 ) -> Ty {
27 let (ty, def) = self.resolve_variant(path); 28 let (ty, def) = self.resolve_variant(path);
28 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 29 let var_data = def.map(|it| variant_data(self.db.upcast(), it));
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> {
34 let substs = ty.substs().unwrap_or_else(Substs::empty); 35 let substs = ty.substs().unwrap_or_else(Substs::empty);
35 36
36 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 37 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
38 let (pre, post) = match ellipsis {
39 Some(idx) => subpats.split_at(idx),
40 None => (&subpats[..], &[][..]),
41 };
42 let post_idx_offset = field_tys.iter().count() - post.len();
37 43
38 for (i, &subpat) in subpats.iter().enumerate() { 44 let pre_iter = pre.iter().enumerate();
45 let post_iter = (post_idx_offset..).zip(post.iter());
46 for (i, &subpat) in pre_iter.chain(post_iter) {
39 let expected_ty = var_data 47 let expected_ty = var_data
40 .as_ref() 48 .as_ref()
41 .and_then(|d| d.field(&Name::new_tuple_field(i))) 49 .and_then(|d| d.field(&Name::new_tuple_field(i)))
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> {
111 let expected = expected; 119 let expected = expected;
112 120
113 let ty = match &body[pat] { 121 let ty = match &body[pat] {
114 Pat::Tuple { ref args, .. } => { 122 &Pat::Tuple { ref args, ellipsis } => {
115 let expectations = match expected.as_tuple() { 123 let expectations = match expected.as_tuple() {
116 Some(parameters) => &*parameters.0, 124 Some(parameters) => &*parameters.0,
117 _ => &[], 125 _ => &[],
118 }; 126 };
119 let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
120 127
121 let inner_tys = args 128 let (pre, post) = match ellipsis {
122 .iter() 129 Some(idx) => args.split_at(idx),
123 .zip(expectations_iter) 130 None => (&args[..], &[][..]),
124 .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) 131 };
125 .collect(); 132 let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
133 let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
134 let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
135
136 let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
137 inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
138 inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
139 inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
126 140
127 Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) 141 Ty::apply(
142 TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
143 Substs(inner_tys.into()),
144 )
128 } 145 }
129 Pat::Or(ref pats) => { 146 Pat::Or(ref pats) => {
130 if let Some((first_pat, rest)) = pats.split_first() { 147 if let Some((first_pat, rest)) = pats.split_first() {
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> {
150 let subty = self.infer_pat(*pat, expectation, default_bm); 167 let subty = self.infer_pat(*pat, expectation, default_bm);
151 Ty::apply_one(TypeCtor::Ref(*mutability), subty) 168 Ty::apply_one(TypeCtor::Ref(*mutability), subty)
152 } 169 }
153 Pat::TupleStruct { path: p, args: subpats, .. } => { 170 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
154 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 171 p.as_ref(),
155 } 172 subpats,
173 expected,
174 default_bm,
175 pat,
176 *ellipsis,
177 ),
156 Pat::Record { path: p, args: fields, ellipsis: _ } => { 178 Pat::Record { path: p, args: fields, ellipsis: _ } => {
157 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) 179 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
158 } 180 }
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 6a965ac4f..5a5f48fd0 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -679,3 +679,98 @@ fn box_pattern() {
679 "#]], 679 "#]],
680 ); 680 );
681} 681}
682
683#[test]
684fn tuple_ellipsis_pattern() {
685 check_infer(
686 r#"
687fn foo(tuple: (u8, i16, f32)) {
688 match tuple {
689 (.., b, c) => {},
690 (a, .., c) => {},
691 (a, b, ..) => {},
692 (a, b) => {/*too short*/}
693 (a, b, c, d) => {/*too long*/}
694 _ => {}
695 }
696}"#,
697 expect![[r#"
698 7..12 'tuple': (u8, i16, f32)
699 30..224 '{ ... } }': ()
700 36..222 'match ... }': ()
701 42..47 'tuple': (u8, i16, f32)
702 58..68 '(.., b, c)': (u8, i16, f32)
703 63..64 'b': i16
704 66..67 'c': f32
705 72..74 '{}': ()
706 84..94 '(a, .., c)': (u8, i16, f32)
707 85..86 'a': u8
708 92..93 'c': f32
709 98..100 '{}': ()
710 110..120 '(a, b, ..)': (u8, i16, f32)
711 111..112 'a': u8
712 114..115 'b': i16
713 124..126 '{}': ()
714 136..142 '(a, b)': (u8, i16, f32)
715 137..138 'a': u8
716 140..141 'b': i16
717 146..161 '{/*too short*/}': ()
718 170..182 '(a, b, c, d)': (u8, i16, f32, {unknown})
719 171..172 'a': u8
720 174..175 'b': i16
721 177..178 'c': f32
722 180..181 'd': {unknown}
723 186..200 '{/*too long*/}': ()
724 209..210 '_': (u8, i16, f32)
725 214..216 '{}': ()
726 "#]],
727 );
728}
729
730#[test]
731fn tuple_struct_ellipsis_pattern() {
732 check_infer(
733 r#"
734struct Tuple(u8, i16, f32);
735fn foo(tuple: Tuple) {
736 match tuple {
737 Tuple(.., b, c) => {},
738 Tuple(a, .., c) => {},
739 Tuple(a, b, ..) => {},
740 Tuple(a, b) => {/*too short*/}
741 Tuple(a, b, c, d) => {/*too long*/}
742 _ => {}
743 }
744}"#,
745 expect![[r#"
746 35..40 'tuple': Tuple
747 49..268 '{ ... } }': ()
748 55..266 'match ... }': ()
749 61..66 'tuple': Tuple
750 77..92 'Tuple(.., b, c)': Tuple
751 87..88 'b': i16
752 90..91 'c': f32
753 96..98 '{}': ()
754 108..123 'Tuple(a, .., c)': Tuple
755 114..115 'a': u8
756 121..122 'c': f32
757 127..129 '{}': ()
758 139..154 'Tuple(a, b, ..)': Tuple
759 145..146 'a': u8
760 148..149 'b': i16
761 158..160 '{}': ()
762 170..181 'Tuple(a, b)': Tuple
763 176..177 'a': u8
764 179..180 'b': i16
765 185..200 '{/*too short*/}': ()
766 209..226 'Tuple(... c, d)': Tuple
767 215..216 'a': u8
768 218..219 'b': i16
769 221..222 'c': f32
770 224..225 'd': {unknown}
771 230..244 '{/*too long*/}': ()
772 253..254 '_': Tuple
773 258..260 '{}': ()
774 "#]],
775 );
776}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 1c7f02763..3df73ed4f 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -10,7 +10,7 @@ mod field_shorthand;
10use std::cell::RefCell; 10use std::cell::RefCell;
11 11
12use hir::{ 12use hir::{
13 diagnostics::{Diagnostic as _, DiagnosticSinkBuilder}, 13 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
14 Semantics, 14 Semantics,
15}; 15};
16use ide_db::base_db::SourceDatabase; 16use ide_db::base_db::SourceDatabase;
@@ -35,15 +35,23 @@ pub struct Diagnostic {
35 pub severity: Severity, 35 pub severity: Severity,
36 pub fix: Option<Fix>, 36 pub fix: Option<Fix>,
37 pub unused: bool, 37 pub unused: bool,
38 pub code: Option<DiagnosticCode>,
38} 39}
39 40
40impl Diagnostic { 41impl Diagnostic {
41 fn error(range: TextRange, message: String) -> Self { 42 fn error(range: TextRange, message: String) -> Self {
42 Self { message, range, severity: Severity::Error, fix: None, unused: false } 43 Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
43 } 44 }
44 45
45 fn hint(range: TextRange, message: String) -> Self { 46 fn hint(range: TextRange, message: String) -> Self {
46 Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false } 47 Self {
48 message,
49 range,
50 severity: Severity::WeakWarning,
51 fix: None,
52 unused: false,
53 code: None,
54 }
47 } 55 }
48 56
49 fn with_fix(self, fix: Option<Fix>) -> Self { 57 fn with_fix(self, fix: Option<Fix>) -> Self {
@@ -53,6 +61,10 @@ impl Diagnostic {
53 fn with_unused(self, unused: bool) -> Self { 61 fn with_unused(self, unused: bool) -> Self {
54 Self { unused, ..self } 62 Self { unused, ..self }
55 } 63 }
64
65 fn with_code(self, code: Option<DiagnosticCode>) -> Self {
66 Self { code, ..self }
67 }
56} 68}
57 69
58#[derive(Debug)] 70#[derive(Debug)]
@@ -126,7 +138,8 @@ pub(crate) fn diagnostics(
126 // Override severity and mark as unused. 138 // Override severity and mark as unused.
127 res.borrow_mut().push( 139 res.borrow_mut().push(
128 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) 140 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
129 .with_unused(true), 141 .with_unused(true)
142 .with_code(Some(d.code())),
130 ); 143 );
131 }) 144 })
132 // Only collect experimental diagnostics when they're enabled. 145 // Only collect experimental diagnostics when they're enabled.
@@ -137,8 +150,10 @@ pub(crate) fn diagnostics(
137 let mut sink = sink_builder 150 let mut sink = sink_builder
138 // Diagnostics not handled above get no fix and default treatment. 151 // Diagnostics not handled above get no fix and default treatment.
139 .build(|d| { 152 .build(|d| {
140 res.borrow_mut() 153 res.borrow_mut().push(
141 .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())); 154 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
155 .with_code(Some(d.code())),
156 );
142 }); 157 });
143 158
144 if let Some(m) = sema.to_module_def(file_id) { 159 if let Some(m) = sema.to_module_def(file_id) {
@@ -149,11 +164,15 @@ pub(crate) fn diagnostics(
149} 164}
150 165
151fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 166fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
152 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) 167 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
168 .with_fix(d.fix(&sema))
169 .with_code(Some(d.code()))
153} 170}
154 171
155fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 172fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
156 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) 173 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
174 .with_fix(d.fix(&sema))
175 .with_code(Some(d.code()))
157} 176}
158 177
159fn check_unnecessary_braces_in_use_statement( 178fn check_unnecessary_braces_in_use_statement(
@@ -589,6 +608,11 @@ fn test_fn() {
589 }, 608 },
590 ), 609 ),
591 unused: false, 610 unused: false,
611 code: Some(
612 DiagnosticCode(
613 "unresolved-module",
614 ),
615 ),
592 }, 616 },
593 ] 617 ]
594 "#]], 618 "#]],
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs
index 459f201ed..5cbbe306e 100644
--- a/crates/ide/src/fn_references.rs
+++ b/crates/ide/src/fn_references.rs
@@ -1,11 +1,12 @@
1//! This module implements a methods and free functions search in the specified file. 1//! This module implements a methods and free functions search in the specified file.
2//! We have to skip tests, so cannot reuse file_structure module. 2//! We have to skip tests, so cannot reuse file_structure module.
3 3
4use assists::utils::test_related_attribute;
4use hir::Semantics; 5use hir::Semantics;
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
6use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; 7use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode};
7 8
8use crate::{runnables::has_test_related_attribute, FileId, FileRange}; 9use crate::{FileId, FileRange};
9 10
10pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { 11pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
11 let sema = Semantics::new(db); 12 let sema = Semantics::new(db);
@@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa
15 16
16fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { 17fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
17 ast::Fn::cast(item).and_then(|fn_def| { 18 ast::Fn::cast(item).and_then(|fn_def| {
18 if has_test_related_attribute(&fn_def) { 19 if test_related_attribute(&fn_def).is_some() {
19 None 20 None
20 } else { 21 } else {
21 fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) 22 fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index b8725693a..91c64bd4a 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,4 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{
3 convert::TryInto,
4 error::Error,
5 fmt::{self, Display},
6};
2 7
3use hir::{Module, ModuleDef, ModuleSource, Semantics}; 8use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ide_db::base_db::{FileRange, SourceDatabaseExt}; 9use ide_db::base_db::{FileRange, SourceDatabaseExt};
@@ -6,12 +11,6 @@ use ide_db::{
6 defs::{Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
7 RootDatabase, 12 RootDatabase,
8}; 13};
9
10use std::{
11 convert::TryInto,
12 error::Error,
13 fmt::{self, Display},
14};
15use syntax::{ 14use syntax::{
16 algo::find_node_at_offset, 15 algo::find_node_at_offset,
17 ast::{self, NameOwner}, 16 ast::{self, NameOwner},
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 2bd0e86e5..e15411777 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -1,5 +1,6 @@
1use std::fmt; 1use std::fmt;
2 2
3use assists::utils::test_related_attribute;
3use cfg::CfgExpr; 4use cfg::CfgExpr;
4use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 5use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
@@ -156,7 +157,7 @@ fn runnable_fn(
156 None => TestId::Name(name_string), 157 None => TestId::Name(name_string),
157 }; 158 };
158 159
159 if has_test_related_attribute(&fn_def) { 160 if test_related_attribute(&fn_def).is_some() {
160 let attr = TestAttr::from_fn(&fn_def); 161 let attr = TestAttr::from_fn(&fn_def);
161 RunnableKind::Test { test_id, attr } 162 RunnableKind::Test { test_id, attr }
162 } else if fn_def.has_atom_attr("bench") { 163 } else if fn_def.has_atom_attr("bench") {
@@ -235,20 +236,6 @@ impl TestAttr {
235 } 236 }
236} 237}
237 238
238/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
239/// `#[test_case(...)]`, `#[tokio::test]` and similar.
240/// Also a regular `#[test]` annotation is supported.
241///
242/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
243/// but it's better than not to have the runnables for the tests at all.
244pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
245 fn_def
246 .attrs()
247 .filter_map(|attr| attr.path())
248 .map(|path| path.syntax().to_string().to_lowercase())
249 .any(|attribute_text| attribute_text.contains("test"))
250}
251
252const RUSTDOC_FENCE: &str = "```"; 239const RUSTDOC_FENCE: &str = "```";
253const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = 240const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
254 &["", "rust", "should_panic", "edition2015", "edition2018"]; 241 &["", "rust", "should_panic", "edition2015", "edition2018"];
@@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
307 for item in item_list.items() { 294 for item in item_list.items() {
308 match item { 295 match item {
309 ast::Item::Fn(f) => { 296 ast::Item::Fn(f) => {
310 if has_test_related_attribute(&f) { 297 if test_related_attribute(&f).is_some() {
311 return true; 298 return true;
312 } 299 }
313 } 300 }
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 8e91c99d7..e10d7c3a4 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -1,6 +1,6 @@
1use std::{fmt, iter::FromIterator, sync::Arc}; 1use std::{fmt, iter::FromIterator, sync::Arc};
2 2
3use hir::MacroFile; 3use hir::{ExpandResult, MacroFile};
4use ide_db::base_db::{ 4use ide_db::base_db::{
5 salsa::debug::{DebugQueryTable, TableEntry}, 5 salsa::debug::{DebugQueryTable, TableEntry},
6 CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, 6 CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
@@ -19,7 +19,7 @@ fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
19 ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() 19 ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
20} 20}
21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
22 hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>() 22 hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
23} 23}
24 24
25// Feature: Status 25// Feature: Status
@@ -115,10 +115,12 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat
115 } 115 }
116} 116}
117 117
118impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats { 118impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
119 for SyntaxTreeStats
120{
119 fn from_iter<T>(iter: T) -> SyntaxTreeStats 121 fn from_iter<T>(iter: T) -> SyntaxTreeStats
120 where 122 where
121 T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>, 123 T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
122 { 124 {
123 let mut res = SyntaxTreeStats::default(); 125 let mut res = SyntaxTreeStats::default();
124 for entry in iter { 126 for entry in iter {
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index da16fa21d..987191fe3 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -76,7 +76,7 @@ impl RootDatabase {
76 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); 76 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
77 77
78 base_db::ParseQuery.in_db(self).sweep(sweep); 78 base_db::ParseQuery.in_db(self).sweep(sweep);
79 hir::db::ParseMacroQuery.in_db(self).sweep(sweep); 79 hir::db::ParseMacroExpansionQuery.in_db(self).sweep(sweep);
80 80
81 // Macros do take significant space, but less then the syntax trees 81 // Macros do take significant space, but less then the syntax trees
82 // self.query(hir::db::MacroDefQuery).sweep(sweep); 82 // self.query(hir::db::MacroDefQuery).sweep(sweep);
@@ -143,7 +143,7 @@ impl RootDatabase {
143 hir::db::AstIdMapQuery 143 hir::db::AstIdMapQuery
144 hir::db::MacroArgTextQuery 144 hir::db::MacroArgTextQuery
145 hir::db::MacroDefQuery 145 hir::db::MacroDefQuery
146 hir::db::ParseMacroQuery 146 hir::db::ParseMacroExpansionQuery
147 hir::db::MacroExpandQuery 147 hir::db::MacroExpandQuery
148 148
149 // DefDatabase 149 // DefDatabase
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index df74be00b..09046d3c3 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,36 +1,77 @@
1//! This module contains an import search funcionality that is provided to the assists module. 1//! This module contains an import search funcionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 5use syntax::{ast, AstNode, SyntaxKind::NAME};
6 6
7use crate::{ 7use crate::{
8 defs::{Definition, NameClass}, 8 defs::{Definition, NameClass},
9 symbol_index::{self, FileSymbol, Query}, 9 symbol_index::{self, FileSymbol},
10 RootDatabase, 10 RootDatabase,
11}; 11};
12use either::Either; 12use either::Either;
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14 14
15pub fn find_imports<'a>( 15pub fn find_exact_imports<'a>(
16 sema: &Semantics<'a, RootDatabase>, 16 sema: &Semantics<'a, RootDatabase>,
17 krate: Crate, 17 krate: Crate,
18 name_to_import: &str, 18 name_to_import: &str,
19) -> Vec<Either<ModuleDef, MacroDef>> { 19) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
20 let _p = profile::span("search_for_imports"); 20 let _p = profile::span("find_exact_imports");
21 find_imports(
22 sema,
23 krate,
24 {
25 let mut local_query = symbol_index::Query::new(name_to_import.to_string());
26 local_query.exact();
27 local_query.limit(40);
28 local_query
29 },
30 import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40),
31 )
32}
33
34pub fn find_similar_imports<'a>(
35 sema: &Semantics<'a, RootDatabase>,
36 krate: Crate,
37 name_to_import: &str,
38 limit: usize,
39 ignore_modules: bool,
40) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
41 let _p = profile::span("find_similar_imports");
42
43 let mut external_query = import_map::Query::new(name_to_import).limit(limit);
44 if ignore_modules {
45 external_query = external_query.exclude_import_kind(import_map::ImportKind::Module);
46 }
47
48 find_imports(
49 sema,
50 krate,
51 {
52 let mut local_query = symbol_index::Query::new(name_to_import.to_string());
53 local_query.limit(limit);
54 local_query
55 },
56 external_query,
57 )
58}
59
60fn find_imports<'a>(
61 sema: &Semantics<'a, RootDatabase>,
62 krate: Crate,
63 local_query: symbol_index::Query,
64 external_query: import_map::Query,
65) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
66 let _p = profile::span("find_similar_imports");
21 let db = sema.db; 67 let db = sema.db;
22 68
23 // Query dependencies first. 69 // Query dependencies first.
24 let mut candidates: FxHashSet<_> = 70 let mut candidates: FxHashSet<_> =
25 krate.query_external_importables(db, name_to_import).collect(); 71 krate.query_external_importables(db, external_query).collect();
26 72
27 // Query the local crate using the symbol index. 73 // Query the local crate using the symbol index.
28 let local_results = { 74 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
29 let mut query = Query::new(name_to_import.to_string());
30 query.exact();
31 query.limit(40);
32 symbol_index::crate_symbols(db, krate.into(), query)
33 };
34 75
35 candidates.extend( 76 candidates.extend(
36 local_results 77 local_results
@@ -43,7 +84,7 @@ pub fn find_imports<'a>(
43 }), 84 }),
44 ); 85 );
45 86
46 candidates.into_iter().collect() 87 candidates.into_iter()
47} 88}
48 89
49fn get_name_definition<'a>( 90fn get_name_definition<'a>(
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 38ebdbf79..05139a651 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -113,7 +113,7 @@ impl RootDatabase {
113 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { 113 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
114 let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); 114 let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
115 base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 115 base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
116 hir::db::ParseMacroQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 116 hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
117 hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); 117 hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
118 } 118 }
119} 119}
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index f854ca09a..2d0763c47 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -12,6 +12,8 @@ mod subtree_source;
12#[cfg(test)] 12#[cfg(test)]
13mod tests; 13mod tests;
14 14
15use std::fmt;
16
15pub use tt::{Delimiter, Punct}; 17pub use tt::{Delimiter, Punct};
16 18
17use crate::{ 19use crate::{
@@ -33,6 +35,7 @@ pub enum ExpandError {
33 ConversionError, 35 ConversionError,
34 InvalidRepeat, 36 InvalidRepeat,
35 ProcMacroError(tt::ExpansionError), 37 ProcMacroError(tt::ExpansionError),
38 Other(String),
36} 39}
37 40
38impl From<tt::ExpansionError> for ExpandError { 41impl From<tt::ExpansionError> for ExpandError {
@@ -41,6 +44,20 @@ impl From<tt::ExpansionError> for ExpandError {
41 } 44 }
42} 45}
43 46
47impl fmt::Display for ExpandError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 ExpandError::NoMatchingRule => f.write_str("no rule matches input tokens"),
51 ExpandError::UnexpectedToken => f.write_str("unexpected token in input"),
52 ExpandError::BindingError(e) => f.write_str(e),
53 ExpandError::ConversionError => f.write_str("could not convert tokens"),
54 ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"),
55 ExpandError::ProcMacroError(e) => e.fmt(f),
56 ExpandError::Other(e) => f.write_str(e),
57 }
58 }
59}
60
44pub use crate::syntax_bridge::{ 61pub use crate::syntax_bridge::{
45 ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, 62 ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node,
46 TokenMap, 63 TokenMap,
@@ -246,33 +263,42 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
246 Ok(()) 263 Ok(())
247} 264}
248 265
249#[derive(Debug)] 266#[derive(Debug, Clone, Eq, PartialEq)]
250pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); 267pub struct ExpandResult<T> {
268 pub value: T,
269 pub err: Option<ExpandError>,
270}
251 271
252impl<T> ExpandResult<T> { 272impl<T> ExpandResult<T> {
253 pub fn ok(t: T) -> ExpandResult<T> { 273 pub fn ok(value: T) -> Self {
254 ExpandResult(t, None) 274 Self { value, err: None }
275 }
276
277 pub fn only_err(err: ExpandError) -> Self
278 where
279 T: Default,
280 {
281 Self { value: Default::default(), err: Some(err) }
255 } 282 }
256 283
257 pub fn only_err(err: ExpandError) -> ExpandResult<T> 284 pub fn str_err(err: String) -> Self
258 where 285 where
259 T: Default, 286 T: Default,
260 { 287 {
261 ExpandResult(Default::default(), Some(err)) 288 Self::only_err(ExpandError::Other(err))
262 } 289 }
263 290
264 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> { 291 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> {
265 ExpandResult(f(self.0), self.1) 292 ExpandResult { value: f(self.value), err: self.err }
266 } 293 }
267 294
268 pub fn result(self) -> Result<T, ExpandError> { 295 pub fn result(self) -> Result<T, ExpandError> {
269 self.1.map(Err).unwrap_or(Ok(self.0)) 296 self.err.map(Err).unwrap_or(Ok(self.value))
270 } 297 }
271} 298}
272 299
273impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> { 300impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> {
274 fn from(result: Result<T, ExpandError>) -> ExpandResult<T> { 301 fn from(result: Result<T, ExpandError>) -> Self {
275 result 302 result.map_or_else(|e| Self::only_err(e), |it| Self::ok(it))
276 .map_or_else(|e| ExpandResult(Default::default(), Some(e)), |it| ExpandResult(it, None))
277 } 303 }
278} 304}
diff --git a/crates/mbe/src/mbe_expander.rs b/crates/mbe/src/mbe_expander.rs
index 1ad8b9f8a..97bce0536 100644
--- a/crates/mbe/src/mbe_expander.rs
+++ b/crates/mbe/src/mbe_expander.rs
@@ -28,10 +28,10 @@ fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::
28 // If we find a rule that applies without errors, we're done. 28 // If we find a rule that applies without errors, we're done.
29 // Unconditionally returning the transcription here makes the 29 // Unconditionally returning the transcription here makes the
30 // `test_repeat_bad_var` test fail. 30 // `test_repeat_bad_var` test fail.
31 let ExpandResult(res, transcribe_err) = 31 let ExpandResult { value, err: transcribe_err } =
32 transcriber::transcribe(&rule.rhs, &new_match.bindings); 32 transcriber::transcribe(&rule.rhs, &new_match.bindings);
33 if transcribe_err.is_none() { 33 if transcribe_err.is_none() {
34 return ExpandResult::ok(res); 34 return ExpandResult::ok(value);
35 } 35 }
36 } 36 }
37 // Use the rule if we matched more tokens, or had fewer errors 37 // Use the rule if we matched more tokens, or had fewer errors
@@ -47,11 +47,11 @@ fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::
47 } 47 }
48 if let Some((match_, rule)) = match_ { 48 if let Some((match_, rule)) = match_ {
49 // if we got here, there was no match without errors 49 // if we got here, there was no match without errors
50 let ExpandResult(result, transcribe_err) = 50 let ExpandResult { value, err: transcribe_err } =
51 transcriber::transcribe(&rule.rhs, &match_.bindings); 51 transcriber::transcribe(&rule.rhs, &match_.bindings);
52 ExpandResult(result, match_.err.or(transcribe_err)) 52 ExpandResult { value, err: match_.err.or(transcribe_err) }
53 } else { 53 } else {
54 ExpandResult(tt::Subtree::default(), Some(ExpandError::NoMatchingRule)) 54 ExpandResult::only_err(ExpandError::NoMatchingRule)
55 } 55 }
56} 56}
57 57
@@ -143,7 +143,10 @@ mod tests {
143 } 143 }
144 144
145 fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { 145 fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) {
146 assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err)); 146 assert_eq!(
147 expand_first(&create_rules(&format_macro(macro_body)), invocation).err,
148 Some(err)
149 );
147 } 150 }
148 151
149 fn format_macro(macro_body: &str) -> String { 152 fn format_macro(macro_body: &str) -> String {
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 39a8eefbd..3f8445897 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -158,7 +158,8 @@ fn match_subtree(
158 continue; 158 continue;
159 } 159 }
160 }; 160 };
161 let ExpandResult(matched, match_err) = match_meta_var(kind.as_str(), src); 161 let ExpandResult { value: matched, err: match_err } =
162 match_meta_var(kind.as_str(), src);
162 match matched { 163 match matched {
163 Some(fragment) => { 164 Some(fragment) => {
164 res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment)); 165 res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment));
@@ -342,17 +343,17 @@ impl<'a> TtIter<'a> {
342 token_trees: res.into_iter().cloned().collect(), 343 token_trees: res.into_iter().cloned().collect(),
343 })), 344 })),
344 }; 345 };
345 ExpandResult(res, err) 346 ExpandResult { value: res, err }
346 } 347 }
347 348
348 pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> { 349 pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> {
349 let mut fork = self.clone(); 350 let mut fork = self.clone();
350 match fork.expect_fragment(Visibility) { 351 match fork.expect_fragment(Visibility) {
351 ExpandResult(tt, None) => { 352 ExpandResult { value: tt, err: None } => {
352 *self = fork; 353 *self = fork;
353 tt 354 tt
354 } 355 }
355 ExpandResult(_, Some(_)) => None, 356 ExpandResult { value: _, err: Some(_) } => None,
356 } 357 }
357 } 358 }
358} 359}
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index c9525c5bf..616119ba9 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -93,17 +93,18 @@ fn expand_subtree(
93 match op { 93 match op {
94 Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), 94 Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()),
95 Op::TokenTree(tt::TokenTree::Subtree(tt)) => { 95 Op::TokenTree(tt::TokenTree::Subtree(tt)) => {
96 let ExpandResult(tt, e) = expand_subtree(ctx, tt, arena); 96 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena);
97 err = err.or(e); 97 err = err.or(e);
98 arena.push(tt.into()); 98 arena.push(tt.into());
99 } 99 }
100 Op::Var { name, kind: _ } => { 100 Op::Var { name, kind: _ } => {
101 let ExpandResult(fragment, e) = expand_var(ctx, name); 101 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name);
102 err = err.or(e); 102 err = err.or(e);
103 push_fragment(arena, fragment); 103 push_fragment(arena, fragment);
104 } 104 }
105 Op::Repeat { subtree, kind, separator } => { 105 Op::Repeat { subtree, kind, separator } => {
106 let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator, arena); 106 let ExpandResult { value: fragment, err: e } =
107 expand_repeat(ctx, subtree, kind, separator, arena);
107 err = err.or(e); 108 err = err.or(e);
108 push_fragment(arena, fragment) 109 push_fragment(arena, fragment)
109 } 110 }
@@ -111,7 +112,7 @@ fn expand_subtree(
111 } 112 }
112 // drain the elements added in this instance of expand_subtree 113 // drain the elements added in this instance of expand_subtree
113 let tts = arena.drain(start_elements..arena.len()).collect(); 114 let tts = arena.drain(start_elements..arena.len()).collect();
114 ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err) 115 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
115} 116}
116 117
117fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { 118fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
@@ -152,7 +153,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
152 ExpandResult::ok(Fragment::Tokens(tt)) 153 ExpandResult::ok(Fragment::Tokens(tt))
153 } else { 154 } else {
154 ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( 155 ctx.bindings.get(&v, &mut ctx.nesting).map_or_else(
155 |e| ExpandResult(Fragment::Tokens(tt::TokenTree::empty()), Some(e)), 156 |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
156 |b| ExpandResult::ok(b.clone()), 157 |b| ExpandResult::ok(b.clone()),
157 ) 158 )
158 } 159 }
@@ -174,7 +175,7 @@ fn expand_repeat(
174 let mut counter = 0; 175 let mut counter = 0;
175 176
176 loop { 177 loop {
177 let ExpandResult(mut t, e) = expand_subtree(ctx, template, arena); 178 let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, arena);
178 let nesting_state = ctx.nesting.last_mut().unwrap(); 179 let nesting_state = ctx.nesting.last_mut().unwrap();
179 if nesting_state.at_end || !nesting_state.hit { 180 if nesting_state.at_end || !nesting_state.hit {
180 break; 181 break;
@@ -234,7 +235,10 @@ fn expand_repeat(
234 let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); 235 let tt = tt::Subtree { delimiter: None, token_trees: buf }.into();
235 236
236 if RepeatKind::OneOrMore == kind && counter == 0 { 237 if RepeatKind::OneOrMore == kind && counter == 0 {
237 return ExpandResult(Fragment::Tokens(tt), Some(ExpandError::UnexpectedToken)); 238 return ExpandResult {
239 value: Fragment::Tokens(tt),
240 err: Some(ExpandError::UnexpectedToken),
241 };
238 } 242 }
239 ExpandResult::ok(Fragment::Tokens(tt)) 243 ExpandResult::ok(Fragment::Tokens(tt))
240} 244}
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 780bc470a..ad29b82f7 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -112,7 +112,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
112 has_mods = true; 112 has_mods = true;
113 } 113 }
114 114
115 if p.at(T![extern]) { 115 if p.at(T![extern]) && p.nth(1) != T!['{'] && (p.nth(1) != STRING || p.nth(2) != T!['{']) {
116 has_mods = true; 116 has_mods = true;
117 abi(p); 117 abi(p);
118 } 118 }
@@ -181,6 +181,14 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
181 T![type] => { 181 T![type] => {
182 type_alias(p, m); 182 type_alias(p, m);
183 } 183 }
184
185 // unsafe extern "C" {}
186 T![extern] => {
187 abi(p);
188 extern_item_list(p);
189 m.complete(p, EXTERN_BLOCK);
190 }
191
184 _ => { 192 _ => {
185 if !has_visibility && !has_mods { 193 if !has_visibility && !has_mods {
186 return Err(m); 194 return Err(m);
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 048b32186..729372968 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" } 20test_utils = { path = "../test_utils", version = "0.0.0" }
21 21
22[dev-dependencies] 22[dev-dependencies]
23cargo_metadata = "0.12.0" 23cargo_metadata = "=0.12.0"
24difference = "2.0.0" 24difference = "2.0.0"
25 25
26# used as proc macro test targets 26# used as proc macro test targets
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index 2d53bcbcc..e0c591603 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15cargo_metadata = "0.12.0" 15cargo_metadata = "=0.12.0"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index b0e8863f6..f0a43eaf6 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -37,7 +37,7 @@ impl Sysroot {
37 pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { 37 pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
38 // core is added as a dependency before std in order to 38 // core is added as a dependency before std in order to
39 // mimic rustcs dependency order 39 // mimic rustcs dependency order
40 vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?))) 40 ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
41 } 41 }
42 42
43 pub fn proc_macro(&self) -> Option<SysrootCrate> { 43 pub fn proc_macro(&self) -> Option<SysrootCrate> {
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 9ebb0a811..a71f96164 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,8 +12,8 @@ use proc_macro_api::ProcMacroClient;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13 13
14use crate::{ 14use crate::{
15 cargo_workspace, cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoWorkspace, ProjectJson, 15 cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig,
16 ProjectManifest, Sysroot, TargetKind, 16 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind,
17}; 17};
18 18
19/// `PackageRoot` describes a package root folder. 19/// `PackageRoot` describes a package root folder.
@@ -70,12 +70,8 @@ impl ProjectWorkspace {
70 format!("Failed to deserialize json file {}", project_json.display()) 70 format!("Failed to deserialize json file {}", project_json.display())
71 })?; 71 })?;
72 let project_location = project_json.parent().unwrap().to_path_buf(); 72 let project_location = project_json.parent().unwrap().to_path_buf();
73 let project = ProjectJson::new(&project_location, data); 73 let project_json = ProjectJson::new(&project_location, data);
74 let sysroot = match &project.sysroot_src { 74 ProjectWorkspace::load_inline(project_json)?
75 Some(path) => Some(Sysroot::load(path)?),
76 None => None,
77 };
78 ProjectWorkspace::Json { project, sysroot }
79 } 75 }
80 ProjectManifest::CargoToml(cargo_toml) => { 76 ProjectManifest::CargoToml(cargo_toml) => {
81 let cargo_version = utf8_stdout({ 77 let cargo_version = utf8_stdout({
@@ -150,43 +146,38 @@ impl ProjectWorkspace {
150 }) 146 })
151 })) 147 }))
152 .collect::<Vec<_>>(), 148 .collect::<Vec<_>>(),
153 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 149 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo
154 let roots = cargo 150 .packages()
155 .packages() 151 .map(|pkg| {
156 .map(|pkg| { 152 let is_member = cargo[pkg].is_member;
157 let is_member = cargo[pkg].is_member; 153 let pkg_root = cargo[pkg].root().to_path_buf();
158 let pkg_root = cargo[pkg].root().to_path_buf(); 154
159 155 let mut include = vec![pkg_root.clone()];
160 let mut include = vec![pkg_root.clone()]; 156 include.extend(cargo[pkg].out_dir.clone());
161 include.extend(cargo[pkg].out_dir.clone()); 157
162 158 let mut exclude = vec![pkg_root.join(".git")];
163 let mut exclude = vec![pkg_root.join(".git")]; 159 if is_member {
164 if is_member { 160 exclude.push(pkg_root.join("target"));
165 exclude.push(pkg_root.join("target")); 161 } else {
166 } else { 162 exclude.push(pkg_root.join("tests"));
167 exclude.push(pkg_root.join("tests")); 163 exclude.push(pkg_root.join("examples"));
168 exclude.push(pkg_root.join("examples")); 164 exclude.push(pkg_root.join("benches"));
169 exclude.push(pkg_root.join("benches")); 165 }
170 } 166 PackageRoot { is_member, include, exclude }
171 PackageRoot { is_member, include, exclude } 167 })
172 }) 168 .chain(sysroot.crates().map(|krate| PackageRoot {
173 .chain(sysroot.crates().map(|krate| PackageRoot { 169 is_member: false,
170 include: vec![sysroot[krate].root_dir().to_path_buf()],
171 exclude: Vec::new(),
172 }))
173 .chain(rustc.into_iter().flat_map(|rustc| {
174 rustc.packages().map(move |krate| PackageRoot {
174 is_member: false, 175 is_member: false,
175 include: vec![sysroot[krate].root_dir().to_path_buf()], 176 include: vec![rustc[krate].root().to_path_buf()],
176 exclude: Vec::new(), 177 exclude: Vec::new(),
177 })); 178 })
178 if let Some(rustc_packages) = rustc { 179 }))
179 roots 180 .collect(),
180 .chain(rustc_packages.packages().map(|krate| PackageRoot {
181 is_member: false,
182 include: vec![rustc_packages[krate].root().to_path_buf()],
183 exclude: Vec::new(),
184 }))
185 .collect()
186 } else {
187 roots.collect()
188 }
189 }
190 } 181 }
191 } 182 }
192 183
@@ -206,312 +197,280 @@ impl ProjectWorkspace {
206 proc_macro_client: &ProcMacroClient, 197 proc_macro_client: &ProcMacroClient,
207 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 198 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
208 ) -> CrateGraph { 199 ) -> CrateGraph {
209 let mut crate_graph = CrateGraph::default(); 200 let mut crate_graph = match self {
210 match self {
211 ProjectWorkspace::Json { project, sysroot } => { 201 ProjectWorkspace::Json { project, sysroot } => {
212 let sysroot_dps = sysroot 202 project_json_to_crate_graph(target, proc_macro_client, load, project, sysroot)
213 .as_ref()
214 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
215
216 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
217 let crates: FxHashMap<_, _> = project
218 .crates()
219 .filter_map(|(crate_id, krate)| {
220 let file_path = &krate.root_module;
221 let file_id = match load(&file_path) {
222 Some(id) => id,
223 None => {
224 log::error!("failed to load crate root {}", file_path.display());
225 return None;
226 }
227 };
228
229 let env = krate.env.clone().into_iter().collect();
230 let proc_macro = krate
231 .proc_macro_dylib_path
232 .clone()
233 .map(|it| proc_macro_client.by_dylib_path(&it));
234
235 let target = krate.target.as_deref().or(target);
236 let target_cfgs = cfg_cache
237 .entry(target)
238 .or_insert_with(|| get_rustc_cfg_options(target));
239
240 let mut cfg_options = CfgOptions::default();
241 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
242
243 Some((
244 crate_id,
245 crate_graph.add_crate_root(
246 file_id,
247 krate.edition,
248 krate.display_name.clone(),
249 cfg_options,
250 env,
251 proc_macro.unwrap_or_default(),
252 ),
253 ))
254 })
255 .collect();
256
257 for (from, krate) in project.crates() {
258 if let Some(&from) = crates.get(&from) {
259 if let Some((public_deps, _proc_macro)) = &sysroot_dps {
260 for (name, to) in public_deps.iter() {
261 if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) {
262 log::error!("cyclic dependency on {} for {:?}", name, from)
263 }
264 }
265 }
266
267 for dep in &krate.deps {
268 let to_crate_id = dep.crate_id;
269 if let Some(&to) = crates.get(&to_crate_id) {
270 if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
271 log::error!("cyclic dependency {:?} -> {:?}", from, to);
272 }
273 }
274 }
275 }
276 }
277 } 203 }
278 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 204 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
279 let (public_deps, libproc_macro) = 205 cargo_to_crate_graph(target, proc_macro_client, load, cargo, sysroot, rustc)
280 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 206 }
207 };
208 if crate_graph.patch_cfg_if() {
209 log::debug!("Patched std to depend on cfg-if")
210 } else {
211 log::debug!("Did not patch std to depend on cfg-if")
212 }
213 crate_graph
214 }
215}
216
217fn project_json_to_crate_graph(
218 target: Option<&str>,
219 proc_macro_client: &ProcMacroClient,
220 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
221 project: &ProjectJson,
222 sysroot: &Option<Sysroot>,
223) -> CrateGraph {
224 let mut crate_graph = CrateGraph::default();
225 let sysroot_deps = sysroot
226 .as_ref()
227 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
228
229 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
230 let crates: FxHashMap<CrateId, CrateId> = project
231 .crates()
232 .filter_map(|(crate_id, krate)| {
233 let file_path = &krate.root_module;
234 let file_id = load(&file_path)?;
235 Some((crate_id, krate, file_id))
236 })
237 .map(|(crate_id, krate, file_id)| {
238 let env = krate.env.clone().into_iter().collect();
239 let proc_macro =
240 krate.proc_macro_dylib_path.clone().map(|it| proc_macro_client.by_dylib_path(&it));
241
242 let target = krate.target.as_deref().or(target);
243 let target_cfgs =
244 cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target));
245
246 let mut cfg_options = CfgOptions::default();
247 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
248 (
249 crate_id,
250 crate_graph.add_crate_root(
251 file_id,
252 krate.edition,
253 krate.display_name.clone(),
254 cfg_options,
255 env,
256 proc_macro.unwrap_or_default(),
257 ),
258 )
259 })
260 .collect();
281 261
282 let mut cfg_options = CfgOptions::default(); 262 for (from, krate) in project.crates() {
283 cfg_options.extend(get_rustc_cfg_options(target)); 263 if let Some(&from) = crates.get(&from) {
264 if let Some((public_deps, _proc_macro)) = &sysroot_deps {
265 for (name, to) in public_deps.iter() {
266 add_dep(&mut crate_graph, from, name.clone(), *to)
267 }
268 }
284 269
285 let mut pkg_to_lib_crate = FxHashMap::default(); 270 for dep in &krate.deps {
271 if let Some(&to) = crates.get(&dep.crate_id) {
272 add_dep(&mut crate_graph, from, dep.name.clone(), to)
273 }
274 }
275 }
276 }
277 crate_graph
278}
286 279
287 // Add test cfg for non-sysroot crates 280fn cargo_to_crate_graph(
288 cfg_options.insert_atom("test".into()); 281 target: Option<&str>,
289 cfg_options.insert_atom("debug_assertions".into()); 282 proc_macro_client: &ProcMacroClient,
283 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
284 cargo: &CargoWorkspace,
285 sysroot: &Sysroot,
286 rustc: &Option<CargoWorkspace>,
287) -> CrateGraph {
288 let mut crate_graph = CrateGraph::default();
289 let (public_deps, libproc_macro) =
290 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
290 291
291 let mut pkg_crates = FxHashMap::default(); 292 let mut cfg_options = CfgOptions::default();
293 cfg_options.extend(get_rustc_cfg_options(target));
292 294
293 // Next, create crates for each package, target pair 295 let mut pkg_to_lib_crate = FxHashMap::default();
294 for pkg in cargo.packages() { 296
295 let mut lib_tgt = None; 297 // Add test cfg for non-sysroot crates
296 for &tgt in cargo[pkg].targets.iter() { 298 cfg_options.insert_atom("test".into());
297 if let Some(crate_id) = add_target_crate_root( 299 cfg_options.insert_atom("debug_assertions".into());
300
301 let mut pkg_crates = FxHashMap::default();
302
303 // Next, create crates for each package, target pair
304 for pkg in cargo.packages() {
305 let mut lib_tgt = None;
306 for &tgt in cargo[pkg].targets.iter() {
307 if let Some(file_id) = load(&cargo[tgt].root) {
308 let crate_id = add_target_crate_root(
309 &mut crate_graph,
310 &cargo[pkg],
311 &cfg_options,
312 proc_macro_client,
313 file_id,
314 );
315 if cargo[tgt].kind == TargetKind::Lib {
316 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
317 pkg_to_lib_crate.insert(pkg, crate_id);
318 }
319 if cargo[tgt].is_proc_macro {
320 if let Some(proc_macro) = libproc_macro {
321 add_dep(
298 &mut crate_graph, 322 &mut crate_graph,
299 &cargo[pkg], 323 crate_id,
300 &cargo[tgt], 324 CrateName::new("proc_macro").unwrap(),
301 &cfg_options, 325 proc_macro,
302 proc_macro_client, 326 );
303 load,
304 ) {
305 if cargo[tgt].kind == TargetKind::Lib {
306 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
307 pkg_to_lib_crate.insert(pkg, crate_id);
308 }
309 if cargo[tgt].is_proc_macro {
310 if let Some(proc_macro) = libproc_macro {
311 if let Err(_) = crate_graph.add_dep(
312 crate_id,
313 CrateName::new("proc_macro").unwrap(),
314 proc_macro,
315 ) {
316 log::error!(
317 "cyclic dependency on proc_macro for {}",
318 &cargo[pkg].name
319 )
320 }
321 }
322 }
323
324 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
325 }
326 } 327 }
328 }
327 329
328 // Set deps to the core, std and to the lib target of the current package 330 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
329 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 331 }
330 if let Some((to, name)) = lib_tgt.clone() { 332 }
331 // For root projects with dashes in their name, 333
332 // cargo metadata does not do any normalization, 334 // Set deps to the core, std and to the lib target of the current package
333 // so we do it ourselves currently 335 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
334 let name = CrateName::normalize_dashes(&name); 336 if let Some((to, name)) = lib_tgt.clone() {
335 if to != from && crate_graph.add_dep(from, name, to).is_err() { 337 if to != from {
336 log::error!( 338 // For root projects with dashes in their name,
337 "cyclic dependency between targets of {}", 339 // cargo metadata does not do any normalization,
338 &cargo[pkg].name 340 // so we do it ourselves currently
339 ) 341 let name = CrateName::normalize_dashes(&name);
340 } 342 add_dep(&mut crate_graph, from, name, to);
341 } 343 }
342 for (name, krate) in public_deps.iter() { 344 }
343 if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) { 345 for (name, krate) in public_deps.iter() {
344 log::error!( 346 add_dep(&mut crate_graph, from, name.clone(), *krate);
345 "cyclic dependency on {} for {}", 347 }
346 name, 348 }
347 &cargo[pkg].name 349 }
348 ) 350
349 } 351 // Now add a dep edge from all targets of upstream to the lib
350 } 352 // target of downstream.
351 } 353 for pkg in cargo.packages() {
354 for dep in cargo[pkg].dependencies.iter() {
355 let name = CrateName::new(&dep.name).unwrap();
356 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
357 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
358 add_dep(&mut crate_graph, from, name.clone(), to)
352 } 359 }
360 }
361 }
362 }
353 363
354 // Now add a dep edge from all targets of upstream to the lib 364 let mut rustc_pkg_crates = FxHashMap::default();
355 // target of downstream. 365
356 for pkg in cargo.packages() { 366 // If the user provided a path to rustc sources, we add all the rustc_private crates
357 for dep in cargo[pkg].dependencies.iter() { 367 // and create dependencies on them for the crates in the current workspace
358 let name = CrateName::new(&dep.name).unwrap(); 368 if let Some(rustc_workspace) = rustc {
359 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 369 for pkg in rustc_workspace.packages() {
360 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 370 for &tgt in rustc_workspace[pkg].targets.iter() {
361 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { 371 if rustc_workspace[tgt].kind != TargetKind::Lib {
362 log::error!( 372 continue;
363 "cyclic dependency {} -> {}", 373 }
364 &cargo[pkg].name, 374 // Exclude alloc / core / std
365 &cargo[dep.pkg].name 375 if rustc_workspace[tgt]
366 ) 376 .root
367 } 377 .components()
368 } 378 .any(|c| c == Component::Normal("library".as_ref()))
369 } 379 {
370 } 380 continue;
371 } 381 }
372 382
373 let mut rustc_pkg_crates = FxHashMap::default(); 383 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
374 384 let crate_id = add_target_crate_root(
375 // If the user provided a path to rustc sources, we add all the rustc_private crates 385 &mut crate_graph,
376 // and create dependencies on them for the crates in the current workspace 386 &rustc_workspace[pkg],
377 if let Some(rustc_workspace) = rustc { 387 &cfg_options,
378 for pkg in rustc_workspace.packages() { 388 proc_macro_client,
379 for &tgt in rustc_workspace[pkg].targets.iter() { 389 file_id,
380 if rustc_workspace[tgt].kind != TargetKind::Lib { 390 );
381 continue; 391 pkg_to_lib_crate.insert(pkg, crate_id);
382 } 392 // Add dependencies on the core / std / alloc for rustc
383 // Exclude alloc / core / std 393 for (name, krate) in public_deps.iter() {
384 if rustc_workspace[tgt] 394 add_dep(&mut crate_graph, crate_id, name.clone(), *krate);
385 .root
386 .components()
387 .any(|c| c == Component::Normal("library".as_ref()))
388 {
389 continue;
390 }
391
392 if let Some(crate_id) = add_target_crate_root(
393 &mut crate_graph,
394 &rustc_workspace[pkg],
395 &rustc_workspace[tgt],
396 &cfg_options,
397 proc_macro_client,
398 load,
399 ) {
400 pkg_to_lib_crate.insert(pkg, crate_id);
401 // Add dependencies on the core / std / alloc for rustc
402 for (name, krate) in public_deps.iter() {
403 if let Err(_) =
404 crate_graph.add_dep(crate_id, name.clone(), *krate)
405 {
406 log::error!(
407 "cyclic dependency on {} for {}",
408 name,
409 &cargo[pkg].name
410 )
411 }
412 }
413 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
414 }
415 }
416 } 395 }
417 // Now add a dep edge from all targets of upstream to the lib 396 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
418 // target of downstream. 397 }
419 for pkg in rustc_workspace.packages() { 398 }
420 for dep in rustc_workspace[pkg].dependencies.iter() { 399 }
421 let name = CrateName::new(&dep.name).unwrap(); 400 // Now add a dep edge from all targets of upstream to the lib
422 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 401 // target of downstream.
423 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 402 for pkg in rustc_workspace.packages() {
424 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { 403 for dep in rustc_workspace[pkg].dependencies.iter() {
425 log::error!( 404 let name = CrateName::new(&dep.name).unwrap();
426 "cyclic dependency {} -> {}", 405 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
427 &rustc_workspace[pkg].name, 406 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
428 &rustc_workspace[dep.pkg].name 407 add_dep(&mut crate_graph, from, name.clone(), to);
429 )
430 }
431 }
432 }
433 }
434 } 408 }
409 }
410 }
411 }
435 412
436 // Add dependencies for all the crates of the current workspace to rustc_private libraries 413 // Add dependencies for all the crates of the current workspace to rustc_private libraries
437 for dep in rustc_workspace.packages() { 414 for dep in rustc_workspace.packages() {
438 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 415 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
439 416
440 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 417 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
441 for pkg in cargo.packages() { 418 for pkg in cargo.packages() {
442 if !cargo[pkg].is_member { 419 if !cargo[pkg].is_member {
443 continue; 420 continue;
444 } 421 }
445 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 422 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
446 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { 423 add_dep(&mut crate_graph, from, name.clone(), to);
447 log::error!(
448 "cyclic dependency {} -> {}",
449 &cargo[pkg].name,
450 &rustc_workspace[dep].name
451 )
452 }
453 }
454 }
455 }
456 } 424 }
457 } 425 }
458 } 426 }
459 } 427 }
460 if crate_graph.patch_cfg_if() {
461 log::debug!("Patched std to depend on cfg-if")
462 } else {
463 log::debug!("Did not patch std to depend on cfg-if")
464 }
465 crate_graph
466 } 428 }
429 crate_graph
467} 430}
468 431
469fn add_target_crate_root( 432fn add_target_crate_root(
470 crate_graph: &mut CrateGraph, 433 crate_graph: &mut CrateGraph,
471 pkg: &cargo_workspace::PackageData, 434 pkg: &cargo_workspace::PackageData,
472 tgt: &cargo_workspace::TargetData,
473 cfg_options: &CfgOptions, 435 cfg_options: &CfgOptions,
474 proc_macro_client: &ProcMacroClient, 436 proc_macro_client: &ProcMacroClient,
475 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 437 file_id: FileId,
476) -> Option<CrateId> { 438) -> CrateId {
477 let root = tgt.root.as_path(); 439 let edition = pkg.edition;
478 if let Some(file_id) = load(root) { 440 let cfg_options = {
479 let edition = pkg.edition; 441 let mut opts = cfg_options.clone();
480 let cfg_options = { 442 for feature in pkg.features.iter() {
481 let mut opts = cfg_options.clone(); 443 opts.insert_key_value("feature".into(), feature.into());
482 for feature in pkg.features.iter() { 444 }
483 opts.insert_key_value("feature".into(), feature.into()); 445 opts.extend(pkg.cfgs.iter().cloned());
484 } 446 opts
485 opts.extend(pkg.cfgs.iter().cloned()); 447 };
486 opts 448 let mut env = Env::default();
487 }; 449 if let Some(out_dir) = &pkg.out_dir {
488 let mut env = Env::default(); 450 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
489 if let Some(out_dir) = &pkg.out_dir { 451 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
490 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 452 env.set("OUT_DIR", out_dir);
491 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
492 env.set("OUT_DIR", out_dir);
493 }
494 } 453 }
495 let proc_macro = pkg
496 .proc_macro_dylib_path
497 .as_ref()
498 .map(|it| proc_macro_client.by_dylib_path(&it))
499 .unwrap_or_default();
500
501 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
502 let crate_id = crate_graph.add_crate_root(
503 file_id,
504 edition,
505 Some(display_name),
506 cfg_options,
507 env,
508 proc_macro.clone(),
509 );
510
511 return Some(crate_id);
512 } 454 }
513 None 455 let proc_macro = pkg
456 .proc_macro_dylib_path
457 .as_ref()
458 .map(|it| proc_macro_client.by_dylib_path(&it))
459 .unwrap_or_default();
460
461 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
462 let crate_id = crate_graph.add_crate_root(
463 file_id,
464 edition,
465 Some(display_name),
466 cfg_options,
467 env,
468 proc_macro.clone(),
469 );
470
471 crate_id
514} 472}
473
515fn sysroot_to_crate_graph( 474fn sysroot_to_crate_graph(
516 crate_graph: &mut CrateGraph, 475 crate_graph: &mut CrateGraph,
517 sysroot: &Sysroot, 476 sysroot: &Sysroot,
@@ -520,19 +479,18 @@ fn sysroot_to_crate_graph(
520) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { 479) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
521 let mut cfg_options = CfgOptions::default(); 480 let mut cfg_options = CfgOptions::default();
522 cfg_options.extend(get_rustc_cfg_options(target)); 481 cfg_options.extend(get_rustc_cfg_options(target));
523 let sysroot_crates: FxHashMap<_, _> = sysroot 482 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
524 .crates() 483 .crates()
525 .filter_map(|krate| { 484 .filter_map(|krate| {
526 let file_id = load(&sysroot[krate].root)?; 485 let file_id = load(&sysroot[krate].root)?;
527 486
528 let env = Env::default(); 487 let env = Env::default();
529 let proc_macro = vec![]; 488 let proc_macro = vec![];
530 let name = CrateName::new(&sysroot[krate].name) 489 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
531 .expect("Sysroot crates' names do not contain dashes");
532 let crate_id = crate_graph.add_crate_root( 490 let crate_id = crate_graph.add_crate_root(
533 file_id, 491 file_id,
534 Edition::Edition2018, 492 Edition::Edition2018,
535 Some(name.into()), 493 Some(display_name),
536 cfg_options.clone(), 494 cfg_options.clone(),
537 env, 495 env,
538 proc_macro, 496 proc_macro,
@@ -545,9 +503,7 @@ fn sysroot_to_crate_graph(
545 for &to in sysroot[from].deps.iter() { 503 for &to in sysroot[from].deps.iter() {
546 let name = CrateName::new(&sysroot[to].name).unwrap(); 504 let name = CrateName::new(&sysroot[to].name).unwrap();
547 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { 505 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
548 if let Err(_) = crate_graph.add_dep(from, name, to) { 506 add_dep(crate_graph, from, name, to);
549 log::error!("cyclic dependency between sysroot crates")
550 }
551 } 507 }
552 } 508 }
553 } 509 }
@@ -588,3 +544,9 @@ fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
588 544
589 res 545 res
590} 546}
547
548fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
549 if let Err(err) = graph.add_dep(from, name, to) {
550 log::error!("{}", err)
551 }
552}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index d25c4bf83..436f5041b 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.83.0", features = ["proposed"] } 24lsp-types = { version = "0.84.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "11.1.2"
@@ -31,7 +31,7 @@ serde_json = "1.0.48"
31threadpool = "1.7.1" 31threadpool = "1.7.1"
32rayon = "1.5" 32rayon = "1.5"
33mimalloc = { version = "0.1.19", default-features = false, optional = true } 33mimalloc = { version = "0.1.19", default-features = false, optional = true }
34lsp-server = "0.4.0" 34lsp-server = "0.5.0"
35tracing = "0.1" 35tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
37tracing-tree = { version = "0.1.4" } 37tracing-tree = { version = "0.1.4" }
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 9a8870053..c7203451c 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -62,6 +62,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
62 prepare_provider: Some(true), 62 prepare_provider: Some(true),
63 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 63 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
64 })), 64 })),
65 on_type_rename_provider: None,
65 document_link_provider: None, 66 document_link_provider: None,
66 color_provider: None, 67 color_provider: None,
67 execute_command_provider: None, 68 execute_command_provider: None,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index d16796590..a334cdb11 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -184,6 +184,7 @@ impl Config {
184 }, 184 },
185 completion: CompletionConfig { 185 completion: CompletionConfig {
186 enable_postfix_completions: true, 186 enable_postfix_completions: true,
187 enable_experimental_completions: true,
187 add_call_parenthesis: true, 188 add_call_parenthesis: true,
188 add_call_argument_snippets: true, 189 add_call_argument_snippets: true,
189 ..CompletionConfig::default() 190 ..CompletionConfig::default()
@@ -294,10 +295,6 @@ impl Config {
294 max_length: data.inlayHints_maxLength, 295 max_length: data.inlayHints_maxLength,
295 }; 296 };
296 297
297 self.completion.enable_postfix_completions = data.completion_postfix_enable;
298 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
299 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
300
301 self.assist.insert_use.merge = match data.assist_importMergeBehaviour { 298 self.assist.insert_use.merge = match data.assist_importMergeBehaviour {
302 MergeBehaviourDef::None => None, 299 MergeBehaviourDef::None => None,
303 MergeBehaviourDef::Full => Some(MergeBehaviour::Full), 300 MergeBehaviourDef::Full => Some(MergeBehaviour::Full),
@@ -309,6 +306,12 @@ impl Config {
309 ImportPrefixDef::BySelf => PrefixKind::BySelf, 306 ImportPrefixDef::BySelf => PrefixKind::BySelf,
310 }; 307 };
311 308
309 self.completion.enable_postfix_completions = data.completion_postfix_enable;
310 self.completion.enable_experimental_completions = data.completion_enableExperimental;
311 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
312 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
313 self.completion.merge = self.assist.insert_use.merge;
314
312 self.call_info_full = data.callInfo_full; 315 self.call_info_full = data.callInfo_full;
313 316
314 self.lens = LensConfig { 317 self.lens = LensConfig {
@@ -505,6 +508,7 @@ config_data! {
505 completion_addCallArgumentSnippets: bool = true, 508 completion_addCallArgumentSnippets: bool = true,
506 completion_addCallParenthesis: bool = true, 509 completion_addCallParenthesis: bool = true,
507 completion_postfix_enable: bool = true, 510 completion_postfix_enable: bool = true,
511 completion_enableExperimental: bool = true,
508 512
509 diagnostics_enable: bool = true, 513 diagnostics_enable: bool = true,
510 diagnostics_enableExperimental: bool = true, 514 diagnostics_enableExperimental: bool = true,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 5b2e5187a..72f6c5725 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -27,7 +27,24 @@
27 "trivially_copy_pass_by_ref", 27 "trivially_copy_pass_by_ref",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "rust-lang.github.io",
37 ),
38 ),
39 port: None,
40 path: "/rust-clippy/master/index.html",
41 query: None,
42 fragment: Some(
43 "trivially_copy_pass_by_ref",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "clippy", 49 "clippy",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index 116f0ff73..eb4a6b597 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -27,7 +27,24 @@
27 "E0277", 27 "E0277",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "doc.rust-lang.org",
37 ),
38 ),
39 port: None,
40 path: "/error-index.html",
41 query: None,
42 fragment: Some(
43 "E0277",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "rustc", 49 "rustc",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
index 2cbf657e5..19f72196d 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
@@ -27,7 +27,24 @@
27 "E0053", 27 "E0053",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "doc.rust-lang.org",
37 ),
38 ),
39 port: None,
40 path: "/error-index.html",
41 query: None,
42 fragment: Some(
43 "E0053",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "rustc", 49 "rustc",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
index 1142dc2ac..15ac95d72 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
@@ -27,7 +27,24 @@
27 "E0308", 27 "E0308",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "doc.rust-lang.org",
37 ),
38 ),
39 port: None,
40 path: "/error-index.html",
41 query: None,
42 fragment: Some(
43 "E0308",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "rustc", 49 "rustc",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index 782c72dbd..b9650f3e4 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -27,7 +27,24 @@
27 "E0061", 27 "E0061",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "doc.rust-lang.org",
37 ),
38 ),
39 port: None,
40 path: "/error-index.html",
41 query: None,
42 fragment: Some(
43 "E0061",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "rustc", 49 "rustc",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index d3f27ab6a..c45f68a91 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -27,7 +27,24 @@
27 "let_and_return", 27 "let_and_return",
28 ), 28 ),
29 ), 29 ),
30 code_description: None, 30 code_description: Some(
31 CodeDescription {
32 href: Url {
33 scheme: "https",
34 host: Some(
35 Domain(
36 "rust-lang.github.io",
37 ),
38 ),
39 port: None,
40 path: "/rust-clippy/master/index.html",
41 query: None,
42 fragment: Some(
43 "let_and_return",
44 ),
45 },
46 },
47 ),
31 source: Some( 48 source: Some(
32 "clippy", 49 "clippy",
33 ), 50 ),
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 15145415b..324019614 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -55,8 +55,8 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo
55 55
56 // FIXME: this doesn't handle UTF16 offsets correctly 56 // FIXME: this doesn't handle UTF16 offsets correctly
57 let range = lsp_types::Range::new( 57 let range = lsp_types::Range::new(
58 lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), 58 lsp_types::Position::new(span.line_start as u32 - 1, span.column_start as u32 - 1),
59 lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), 59 lsp_types::Position::new(span.line_end as u32 - 1, span.column_end as u32 - 1),
60 ); 60 );
61 61
62 lsp_types::Location { uri, range } 62 lsp_types::Location { uri, range }
@@ -211,6 +211,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
211 } 211 }
212 } 212 }
213 213
214 let code_description = match source.as_str() {
215 "rustc" => rustc_code_description(code.as_deref()),
216 "clippy" => clippy_code_description(code.as_deref()),
217 _ => None,
218 };
219
214 primary_spans 220 primary_spans
215 .iter() 221 .iter()
216 .map(|primary_span| { 222 .map(|primary_span| {
@@ -248,7 +254,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
248 range: in_macro_location.range, 254 range: in_macro_location.range,
249 severity, 255 severity,
250 code: code.clone().map(lsp_types::NumberOrString::String), 256 code: code.clone().map(lsp_types::NumberOrString::String),
251 code_description: None, 257 code_description: code_description.clone(),
252 source: Some(source.clone()), 258 source: Some(source.clone()),
253 message: message.clone(), 259 message: message.clone(),
254 related_information: Some(information_for_additional_diagnostic), 260 related_information: Some(information_for_additional_diagnostic),
@@ -269,7 +275,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
269 range: location.range, 275 range: location.range,
270 severity, 276 severity,
271 code: code.clone().map(lsp_types::NumberOrString::String), 277 code: code.clone().map(lsp_types::NumberOrString::String),
272 code_description: None, 278 code_description: code_description.clone(),
273 source: Some(source.clone()), 279 source: Some(source.clone()),
274 message, 280 message,
275 related_information: if related_information.is_empty() { 281 related_information: if related_information.is_empty() {
@@ -292,6 +298,31 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
292 .collect() 298 .collect()
293} 299}
294 300
301fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
302 code.filter(|code| {
303 let mut chars = code.chars();
304 chars.next().map_or(false, |c| c == 'E')
305 && chars.by_ref().take(4).all(|c| c.is_ascii_digit())
306 && chars.next().is_none()
307 })
308 .and_then(|code| {
309 lsp_types::Url::parse(&format!("https://doc.rust-lang.org/error-index.html#{}", code))
310 .ok()
311 .map(|href| lsp_types::CodeDescription { href })
312 })
313}
314
315fn clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
316 code.and_then(|code| {
317 lsp_types::Url::parse(&format!(
318 "https://rust-lang.github.io/rust-clippy/master/index.html#{}",
319 code
320 ))
321 .ok()
322 .map(|href| lsp_types::CodeDescription { href })
323 })
324}
325
295#[cfg(test)] 326#[cfg(test)]
296#[cfg(not(windows))] 327#[cfg(not(windows))]
297mod tests { 328mod tests {
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs
index 04c7ee150..cf091510f 100644
--- a/crates/rust-analyzer/src/document.rs
+++ b/crates/rust-analyzer/src/document.rs
@@ -6,11 +6,11 @@
6/// client notifications. 6/// client notifications.
7#[derive(Debug, Clone)] 7#[derive(Debug, Clone)]
8pub(crate) struct DocumentData { 8pub(crate) struct DocumentData {
9 pub(crate) version: Option<i64>, 9 pub(crate) version: i32,
10} 10}
11 11
12impl DocumentData { 12impl DocumentData {
13 pub(crate) fn new(version: i64) -> Self { 13 pub(crate) fn new(version: i32) -> Self {
14 DocumentData { version: Some(version) } 14 DocumentData { version }
15 } 15 }
16} 16}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 63c70a09d..defe11c55 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -263,9 +263,9 @@ impl GlobalStateSnapshot {
263 self.vfs.read().1[&id] 263 self.vfs.read().1[&id]
264 } 264 }
265 265
266 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> { 266 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
267 let path = from_proto::vfs_path(&url).ok()?; 267 let path = from_proto::vfs_path(&url).ok()?;
268 self.mem_docs.get(&path)?.version 268 Some(self.mem_docs.get(&path)?.version)
269 } 269 }
270 270
271 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { 271 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 782797e85..1cf4139d2 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -18,7 +18,7 @@ use lsp_types::{
18 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 18 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
19 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, 19 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
20 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 20 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
21 HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams, 21 HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
22 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, 22 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
23 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, 23 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
24 SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, 24 SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
@@ -573,7 +573,8 @@ pub(crate) fn handle_completion(
573 .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item)) 573 .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item))
574 .collect(); 574 .collect();
575 575
576 Ok(Some(items.into())) 576 let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
577 Ok(Some(completion_list.into()))
577} 578}
578 579
579pub(crate) fn handle_folding_range( 580pub(crate) fn handle_folding_range(
@@ -1127,8 +1128,15 @@ pub(crate) fn publish_diagnostics(
1127 .map(|d| Diagnostic { 1128 .map(|d| Diagnostic {
1128 range: to_proto::range(&line_index, d.range), 1129 range: to_proto::range(&line_index, d.range),
1129 severity: Some(to_proto::diagnostic_severity(d.severity)), 1130 severity: Some(to_proto::diagnostic_severity(d.severity)),
1130 code: None, 1131 code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
1131 code_description: None, 1132 code_description: d.code.and_then(|code| {
1133 lsp_types::Url::parse(&format!(
1134 "https://rust-analyzer.github.io/manual.html#{}",
1135 code.as_str()
1136 ))
1137 .ok()
1138 .map(|href| lsp_types::CodeDescription { href })
1139 }),
1132 source: Some("rust-analyzer".to_string()), 1140 source: Some("rust-analyzer".to_string()),
1133 message: d.message, 1141 message: d.message,
1134 related_information: None, 1142 related_information: None,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index a5c65fa3e..93ac45415 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -302,7 +302,7 @@ pub enum SnippetDocumentChangeOperation {
302#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] 302#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
303#[serde(rename_all = "camelCase")] 303#[serde(rename_all = "camelCase")]
304pub struct SnippetTextDocumentEdit { 304pub struct SnippetTextDocumentEdit {
305 pub text_document: lsp_types::VersionedTextDocumentIdentifier, 305 pub text_document: lsp_types::OptionalVersionedTextDocumentIdentifier,
306 pub edits: Vec<SnippetTextEdit>, 306 pub edits: Vec<SnippetTextEdit>,
307} 307}
308 308
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 1d271a9d8..6427c7367 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -51,7 +51,7 @@ impl GlobalState {
51 } 51 }
52 let percentage = fraction.map(|f| { 52 let percentage = fraction.map(|f| {
53 assert!(0.0 <= f && f <= 1.0); 53 assert!(0.0 <= f && f <= 1.0);
54 f * 100.0 54 (f * 100.0) as u32
55 }); 55 });
56 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); 56 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
57 let work_done_progress = match state { 57 let work_done_progress = match state {
@@ -98,11 +98,11 @@ pub(crate) fn apply_document_changes(
98 // The VFS will normalize the end of lines to `\n`. 98 // The VFS will normalize the end of lines to `\n`.
99 enum IndexValid { 99 enum IndexValid {
100 All, 100 All,
101 UpToLineExclusive(u64), 101 UpToLineExclusive(u32),
102 } 102 }
103 103
104 impl IndexValid { 104 impl IndexValid {
105 fn covers(&self, line: u64) -> bool { 105 fn covers(&self, line: u32) -> bool {
106 match *self { 106 match *self {
107 IndexValid::UpToLineExclusive(to) => to > line, 107 IndexValid::UpToLineExclusive(to) => to > line,
108 _ => true, 108 _ => true,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 68a53bbcb..866d1d176 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -1,7 +1,7 @@
1//! The main loop of `rust-analyzer` responsible for dispatching LSP 1//! The main loop of `rust-analyzer` responsible for dispatching LSP
2//! requests/replies and notifications back to the client. 2//! requests/replies and notifications back to the client.
3use std::{ 3use std::{
4 env, fmt, panic, 4 env, fmt,
5 time::{Duration, Instant}, 5 time::{Duration, Instant},
6}; 6};
7 7
@@ -348,13 +348,7 @@ impl GlobalState {
348 } 348 }
349 349
350 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { 350 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) {
351 let subscriptions = self 351 self.update_file_notifications_on_threadpool();
352 .mem_docs
353 .keys()
354 .map(|path| self.vfs.read().0.file_id(&path).unwrap())
355 .collect::<Vec<_>>();
356
357 self.update_file_notifications_on_threadpool(subscriptions);
358 352
359 // Refresh semantic tokens if the client supports it. 353 // Refresh semantic tokens if the client supports it.
360 if self.config.semantic_tokens_refresh { 354 if self.config.semantic_tokens_refresh {
@@ -368,7 +362,7 @@ impl GlobalState {
368 let url = file_id_to_url(&self.vfs.read().0, file_id); 362 let url = file_id_to_url(&self.vfs.read().0, file_id);
369 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); 363 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
370 let version = from_proto::vfs_path(&url) 364 let version = from_proto::vfs_path(&url)
371 .map(|path| self.mem_docs.get(&path)?.version) 365 .map(|path| self.mem_docs.get(&path).map(|it| it.version))
372 .unwrap_or_default(); 366 .unwrap_or_default();
373 367
374 self.send_notification::<lsp_types::notification::PublishDiagnostics>( 368 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
@@ -498,12 +492,19 @@ impl GlobalState {
498 .write() 492 .write()
499 .0 493 .0
500 .set_file_contents(path, Some(params.text_document.text.into_bytes())); 494 .set_file_contents(path, Some(params.text_document.text.into_bytes()));
495 this.update_file_notifications_on_threadpool();
501 } 496 }
502 Ok(()) 497 Ok(())
503 })? 498 })?
504 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { 499 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
505 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 500 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
506 let doc = this.mem_docs.get_mut(&path).unwrap(); 501 let doc = match this.mem_docs.get_mut(&path) {
502 Some(doc) => doc,
503 None => {
504 log::error!("expected DidChangeTextDocument: {}", path);
505 return Ok(());
506 }
507 };
507 let vfs = &mut this.vfs.write().0; 508 let vfs = &mut this.vfs.write().0;
508 let file_id = vfs.file_id(&path).unwrap(); 509 let file_id = vfs.file_id(&path).unwrap();
509 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); 510 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
@@ -521,7 +522,7 @@ impl GlobalState {
521 let mut version = None; 522 let mut version = None;
522 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 523 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
523 match this.mem_docs.remove(&path) { 524 match this.mem_docs.remove(&path) {
524 Some(doc) => version = doc.version, 525 Some(doc) => version = Some(doc.version),
525 None => log::error!("orphan DidCloseTextDocument: {}", path), 526 None => log::error!("orphan DidCloseTextDocument: {}", path),
526 } 527 }
527 528
@@ -600,7 +601,13 @@ impl GlobalState {
600 .finish(); 601 .finish();
601 Ok(()) 602 Ok(())
602 } 603 }
603 fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) { 604 fn update_file_notifications_on_threadpool(&mut self) {
605 let subscriptions = self
606 .mem_docs
607 .keys()
608 .map(|path| self.vfs.read().0.file_id(&path).unwrap())
609 .collect::<Vec<_>>();
610
604 log::trace!("updating notifications for {:?}", subscriptions); 611 log::trace!("updating notifications for {:?}", subscriptions);
605 if self.config.publish_diagnostics { 612 if self.config.publish_diagnostics {
606 let snapshot = self.snapshot(); 613 let snapshot = self.snapshot();
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index fa6e09f42..001bf5949 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -203,7 +203,11 @@ impl GlobalState {
203 let contents = loader.handle.load_sync(path); 203 let contents = loader.handle.load_sync(path);
204 vfs.set_file_contents(vfs_path.clone(), contents); 204 vfs.set_file_contents(vfs_path.clone(), contents);
205 } 205 }
206 vfs.file_id(&vfs_path) 206 let res = vfs.file_id(&vfs_path);
207 if res.is_none() {
208 log::error!("failed to load {}", path.display())
209 }
210 res
207 }; 211 };
208 for ws in workspaces.iter() { 212 for ws in workspaces.iter() {
209 crate_graph.extend(ws.to_crate_graph( 213 crate_graph.extend(ws.to_crate_graph(
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 4bdf4bf0f..2052b800c 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -21,9 +21,7 @@ use crate::{
21 21
22pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 22pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
23 let line_col = line_index.line_col(offset); 23 let line_col = line_index.line_col(offset);
24 let line = u64::from(line_col.line); 24 lsp_types::Position::new(line_col.line, line_col.col_utf16)
25 let character = u64::from(line_col.col_utf16);
26 lsp_types::Position::new(line, character)
27} 25}
28 26
29pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { 27pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range {
@@ -278,9 +276,9 @@ pub(crate) fn signature_help(
278 label.push_str(", "); 276 label.push_str(", ");
279 } 277 }
280 first = false; 278 first = false;
281 let start = label.len() as u64; 279 let start = label.len() as u32;
282 label.push_str(param); 280 label.push_str(param);
283 let end = label.len() as u64; 281 let end = label.len() as u32;
284 params.push(lsp_types::ParameterInformation { 282 params.push(lsp_types::ParameterInformation {
285 label: lsp_types::ParameterLabel::LabelOffsets([start, end]), 283 label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
286 documentation: None, 284 documentation: None,
@@ -302,7 +300,7 @@ pub(crate) fn signature_help(
302 }) 300 })
303 }; 301 };
304 302
305 let active_parameter = call_info.active_parameter.map(|it| it as i64); 303 let active_parameter = call_info.active_parameter.map(|it| it as u32);
306 304
307 let signature = lsp_types::SignatureInformation { 305 let signature = lsp_types::SignatureInformation {
308 label, 306 label,
@@ -518,13 +516,13 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
518 lsp_types::Url::parse(&url).unwrap() 516 lsp_types::Url::parse(&url).unwrap()
519} 517}
520 518
521pub(crate) fn versioned_text_document_identifier( 519pub(crate) fn optional_versioned_text_document_identifier(
522 snap: &GlobalStateSnapshot, 520 snap: &GlobalStateSnapshot,
523 file_id: FileId, 521 file_id: FileId,
524) -> lsp_types::VersionedTextDocumentIdentifier { 522) -> lsp_types::OptionalVersionedTextDocumentIdentifier {
525 let url = url(snap, file_id); 523 let url = url(snap, file_id);
526 let version = snap.url_file_version(&url); 524 let version = snap.url_file_version(&url);
527 lsp_types::VersionedTextDocumentIdentifier { uri: url, version } 525 lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version }
528} 526}
529 527
530pub(crate) fn location( 528pub(crate) fn location(
@@ -613,7 +611,7 @@ pub(crate) fn snippet_text_document_edit(
613 is_snippet: bool, 611 is_snippet: bool,
614 source_file_edit: SourceFileEdit, 612 source_file_edit: SourceFileEdit,
615) -> Result<lsp_ext::SnippetTextDocumentEdit> { 613) -> Result<lsp_ext::SnippetTextDocumentEdit> {
616 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id); 614 let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id);
617 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; 615 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?;
618 let line_endings = snap.file_line_endings(source_file_edit.file_id); 616 let line_endings = snap.file_line_endings(source_file_edit.file_id);
619 let edits = source_file_edit 617 let edits = source_file_edit
@@ -631,12 +629,21 @@ pub(crate) fn resource_op(
631 match file_system_edit { 629 match file_system_edit {
632 FileSystemEdit::CreateFile { anchor, dst } => { 630 FileSystemEdit::CreateFile { anchor, dst } => {
633 let uri = snap.anchored_path(anchor, &dst); 631 let uri = snap.anchored_path(anchor, &dst);
634 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 632 lsp_types::ResourceOp::Create(lsp_types::CreateFile {
633 uri,
634 options: None,
635 annotation: None,
636 })
635 } 637 }
636 FileSystemEdit::MoveFile { src, anchor, dst } => { 638 FileSystemEdit::MoveFile { src, anchor, dst } => {
637 let old_uri = snap.file_id_to_url(src); 639 let old_uri = snap.file_id_to_url(src);
638 let new_uri = snap.anchored_path(anchor, &dst); 640 let new_uri = snap.anchored_path(anchor, &dst);
639 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 641 lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
642 old_uri,
643 new_uri,
644 options: None,
645 annotation: None,
646 })
640 } 647 }
641 } 648 }
642} 649}
@@ -686,9 +693,11 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
686 edits: edit 693 edits: edit
687 .edits 694 .edits
688 .into_iter() 695 .into_iter()
689 .map(|edit| lsp_types::TextEdit { 696 .map(|edit| {
690 range: edit.range, 697 lsp_types::OneOf::Left(lsp_types::TextEdit {
691 new_text: edit.new_text, 698 range: edit.range,
699 new_text: edit.new_text,
700 })
692 }) 701 })
693 .collect(), 702 .collect(),
694 }, 703 },
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index b210b98f0..456125789 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -108,7 +108,7 @@ pub(crate) fn project(fixture: &str) -> Server {
108} 108}
109 109
110pub(crate) struct Server { 110pub(crate) struct Server {
111 req_id: Cell<u64>, 111 req_id: Cell<i32>,
112 messages: RefCell<Vec<Message>>, 112 messages: RefCell<Vec<Message>>,
113 _thread: jod_thread::JoinHandle<()>, 113 _thread: jod_thread::JoinHandle<()>,
114 client: Connection, 114 client: Connection,
@@ -165,7 +165,7 @@ impl Server {
165 R::Params: Serialize, 165 R::Params: Serialize,
166 { 166 {
167 let id = self.req_id.get(); 167 let id = self.req_id.get();
168 self.req_id.set(id + 1); 168 self.req_id.set(id.wrapping_add(1));
169 169
170 let r = Request::new(id.into(), R::METHOD.to_string(), params); 170 let r = Request::new(id.into(), R::METHOD.to_string(), params);
171 self.send_request_(r) 171 self.send_request_(r)
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast
index 50a6d8ee9..87eebf185 100644
--- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast
+++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast
@@ -1,4 +1,4 @@
1[email protected]04 1[email protected]28
2 [email protected] 2 [email protected]
3 [email protected] "async" 3 [email protected] "async"
4 [email protected] " " 4 [email protected] " "
@@ -215,4 +215,16 @@ [email protected]
215 [email protected] 215 [email protected]
216 [email protected] "{" 216 [email protected] "{"
217 [email protected] "}" 217 [email protected] "}"
218 [email protected] "\n" 218 [email protected] "\n\n"
219 [email protected]
220 [email protected] "unsafe"
221 [email protected] " "
222 [email protected]
223 [email protected] "extern"
224 [email protected] " "
225 [email protected] "\"C++\""
226 [email protected] " "
227 [email protected]
228 [email protected] "{"
229 [email protected] "}"
230 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs
index 8d697c04b..6d27a082c 100644
--- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs
+++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs
@@ -14,3 +14,5 @@ unsafe auto trait T {}
14unsafe impl Foo {} 14unsafe impl Foo {}
15default impl Foo {} 15default impl Foo {}
16unsafe default impl Foo {} 16unsafe default impl Foo {}
17
18unsafe extern "C++" {}
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 20c3f5eab..6c1bf8d09 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -1,10 +1,7 @@
1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both 1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both
2//! input and output) of macros. It closely mirrors `proc_macro` crate's 2//! input and output) of macros. It closely mirrors `proc_macro` crate's
3//! `TokenTree`. 3//! `TokenTree`.
4use std::{ 4use std::{fmt, panic::RefUnwindSafe};
5 fmt::{self, Debug},
6 panic::RefUnwindSafe,
7};
8 5
9use stdx::impl_from; 6use stdx::impl_from;
10 7
@@ -139,7 +136,7 @@ fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize)
139 Ok(()) 136 Ok(())
140} 137}
141 138
142impl Debug for Subtree { 139impl fmt::Debug for Subtree {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 print_debug_subtree(f, self, 0) 141 print_debug_subtree(f, self, 0)
145 } 142 }
@@ -240,7 +237,18 @@ pub enum ExpansionError {
240 ExpansionError(String), 237 ExpansionError(String),
241} 238}
242 239
243pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { 240impl fmt::Display for ExpansionError {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 match self {
243 ExpansionError::IOError(e) => write!(f, "I/O error: {}", e),
244 ExpansionError::JsonError(e) => write!(f, "JSON decoding error: {}", e),
245 ExpansionError::Unknown(e) => e.fmt(f),
246 ExpansionError::ExpansionError(e) => write!(f, "proc macro returned error: {}", e),
247 }
248 }
249}
250
251pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
244 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) 252 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
245 -> Result<Subtree, ExpansionError>; 253 -> Result<Subtree, ExpansionError>;
246} 254}
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index db9727bee..8c01db07c 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
1<!--- 1<!---
2lsp_ext.rs hash: 9d5daed5b25dc4f6 2lsp_ext.rs hash: 203fdf79b21b5987
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
@@ -45,7 +45,7 @@ interface SnippetTextEdit extends TextEdit {
45 45
46```typescript 46```typescript
47export interface TextDocumentEdit { 47export interface TextDocumentEdit {
48 textDocument: VersionedTextDocumentIdentifier; 48 textDocument: OptionalVersionedTextDocumentIdentifier;
49 edits: (TextEdit | SnippetTextEdit)[]; 49 edits: (TextEdit | SnippetTextEdit)[];
50} 50}
51``` 51```
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index 2eb08b7ca..1edafab68 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -195,7 +195,7 @@ Modeling this with immutable trees is possible, but annoying.
195A function green tree is not super-convenient to use. 195A function green tree is not super-convenient to use.
196The biggest problem is accessing parents (there are no parent pointers!). 196The biggest problem is accessing parents (there are no parent pointers!).
197But there are also "identify" issues. 197But there are also "identify" issues.
198Let's say you want to write a code which builds a list of expressions in a file: `fn collect_exrepssions(file: GreenNode) -> HashSet<GreenNode>`. 198Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet<GreenNode>`.
199For the input like 199For the input like
200 200
201```rust 201```rust
@@ -236,7 +236,7 @@ impl SyntaxNode {
236 self.parent.clone() 236 self.parent.clone()
237 } 237 }
238 fn children(&self) -> impl Iterator<Item = SyntaxNode> { 238 fn children(&self) -> impl Iterator<Item = SyntaxNode> {
239 let mut offset = self.offset 239 let mut offset = self.offset;
240 self.green.children().map(|green_child| { 240 self.green.children().map(|green_child| {
241 let child_offset = offset; 241 let child_offset = offset;
242 offset += green_child.text_len; 242 offset += green_child.text_len;
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 83ef00058..a60d3668b 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -2414,27 +2414,27 @@
2414 "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug==" 2414 "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug=="
2415 }, 2415 },
2416 "vscode-languageclient": { 2416 "vscode-languageclient": {
2417 "version": "7.0.0-next.12", 2417 "version": "7.0.0-next.14",
2418 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.12.tgz", 2418 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.14.tgz",
2419 "integrity": "sha512-OrzvOvhS5o26C0KctTJC7hkwh3avCwkVhllzy42AqwpIUZ3p2aVqkSG2uVxaeodq8ThBb3TLgtg50vxyWs6FEg==", 2419 "integrity": "sha512-QUccfXK2F6AXXRFR8QJCaIz7N2BhJK6ok8E1aO8LHq2IBU33+5hTSJBXs7nEqrqZ/cY2VlDDbMWtMvCxz+/y1w==",
2420 "requires": { 2420 "requires": {
2421 "semver": "^6.3.0", 2421 "semver": "^6.3.0",
2422 "vscode-languageserver-protocol": "3.16.0-next.10" 2422 "vscode-languageserver-protocol": "3.16.0-next.11"
2423 } 2423 }
2424 }, 2424 },
2425 "vscode-languageserver-protocol": { 2425 "vscode-languageserver-protocol": {
2426 "version": "3.16.0-next.10", 2426 "version": "3.16.0-next.11",
2427 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.10.tgz", 2427 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.11.tgz",
2428 "integrity": "sha512-YRTctHUZvts0Z1xXKNYU0ha0o+Tlgtwr+6O8OmDquM086N8exiSKBMwMC+Ra1QtIE+1mfW43Wxsme2FnMkAS9A==", 2428 "integrity": "sha512-31FmupmSmfznuMuGp7qN6h3d/hKUbexbvcwTvrUE/igqRlzFU542s8MtGICx1ERbVuDOLGp96W2Z92qbUbmBPA==",
2429 "requires": { 2429 "requires": {
2430 "vscode-jsonrpc": "6.0.0-next.7", 2430 "vscode-jsonrpc": "6.0.0-next.7",
2431 "vscode-languageserver-types": "3.16.0-next.4" 2431 "vscode-languageserver-types": "3.16.0-next.5"
2432 } 2432 }
2433 }, 2433 },
2434 "vscode-languageserver-types": { 2434 "vscode-languageserver-types": {
2435 "version": "3.16.0-next.4", 2435 "version": "3.16.0-next.5",
2436 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.4.tgz", 2436 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.5.tgz",
2437 "integrity": "sha512-NlKJyGcET/ZBCCLBYIPaGo2c37R03bPYeWXozUtnjyye7+9dhlbMSODyoG2INcQf8zFmB4qhm2UOJjgYEgPCNA==" 2437 "integrity": "sha512-lf8Y1XXMtF1r2oDDAmJe+drizNXkybSRXAQQk5dPy2rYJsY9SPXYNO074L3THu9zNYepzV5fRJZUPo/V/TLBRQ=="
2438 }, 2438 },
2439 "vscode-test": { 2439 "vscode-test": {
2440 "version": "1.4.0", 2440 "version": "1.4.0",
diff --git a/editors/code/package.json b/editors/code/package.json
index 220d44abc..c3f1a0d8d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
21 "Programming Languages" 21 "Programming Languages"
22 ], 22 ],
23 "engines": { 23 "engines": {
24 "vscode": "^1.47.1" 24 "vscode": "^1.51.0"
25 }, 25 },
26 "enableProposedApi": true, 26 "enableProposedApi": true,
27 "scripts": { 27 "scripts": {
@@ -36,7 +36,7 @@
36 }, 36 },
37 "dependencies": { 37 "dependencies": {
38 "node-fetch": "^2.6.1", 38 "node-fetch": "^2.6.1",
39 "vscode-languageclient": "7.0.0-next.12" 39 "vscode-languageclient": "7.0.0-next.14"
40 }, 40 },
41 "devDependencies": { 41 "devDependencies": {
42 "@rollup/plugin-commonjs": "^13.0.2", 42 "@rollup/plugin-commonjs": "^13.0.2",
@@ -460,6 +460,11 @@
460 "default": true, 460 "default": true,
461 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." 461 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
462 }, 462 },
463 "rust-analyzer.completion.enableExperimental": {
464 "type": "boolean",
465 "default": true,
466 "markdownDescription": "Display additional completions with potential false positives and performance issues"
467 },
463 "rust-analyzer.callInfo.full": { 468 "rust-analyzer.callInfo.full": {
464 "type": "boolean", 469 "type": "boolean",
465 "default": true, 470 "default": true,
@@ -952,9 +957,6 @@
952 { 957 {
953 "language": "rust", 958 "language": "rust",
954 "scopes": { 959 "scopes": {
955 "macro": [
956 "entity.name.function.macro.rust"
957 ],
958 "attribute": [ 960 "attribute": [
959 "meta.attribute.rust" 961 "meta.attribute.rust"
960 ], 962 ],
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index cd4775d27..4759bb116 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -409,7 +409,7 @@
409 { 409 {
410 "comment": "booleans", 410 "comment": "booleans",
411 "name": "constant.language.bool.rust", 411 "name": "constant.language.bool.rust",
412 "match": "\\btrue|false\\b" 412 "match": "\\b(true|false)\\b"
413 } 413 }
414 ] 414 ]
415 }, 415 },
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index c9d032ead..63ab82dde 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -174,6 +174,8 @@ class ExperimentalFeatures implements lc.StaticFeature {
174 } 174 }
175 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 175 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
176 } 176 }
177 dispose(): void {
178 }
177} 179}
178 180
179function isCodeActionWithoutEditsAndCommands(value: any): boolean { 181function isCodeActionWithoutEditsAndCommands(value: any): boolean {
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 99652e76b..4c58aed59 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -168,6 +168,7 @@ ISC
168MIT 168MIT
169MIT / Apache-2.0 169MIT / Apache-2.0
170MIT OR Apache-2.0 170MIT OR Apache-2.0
171MIT OR Apache-2.0 OR Zlib
171MIT OR Zlib OR Apache-2.0 172MIT OR Zlib OR Apache-2.0
172MIT/Apache-2.0 173MIT/Apache-2.0
173Unlicense OR MIT 174Unlicense OR MIT