aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock106
-rw-r--r--Cargo.toml2
-rw-r--r--crates/assists/src/assist_config.rs8
-rw-r--r--crates/assists/src/assist_context.rs41
-rw-r--r--crates/assists/src/handlers/auto_import.rs9
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs45
-rw-r--r--crates/assists/src/handlers/qualify_path.rs40
-rw-r--r--crates/assists/src/handlers/unmerge_use.rs231
-rw-r--r--crates/assists/src/lib.rs4
-rw-r--r--crates/assists/src/tests.rs49
-rw-r--r--crates/assists/src/tests/generated.rs14
-rw-r--r--crates/assists/src/utils.rs1
-rw-r--r--crates/completion/src/completions.rs1
-rw-r--r--crates/completion/src/completions/flyimport.rs291
-rw-r--r--crates/completion/src/completions/keyword.rs106
-rw-r--r--crates/completion/src/completions/unqualified_path.rs251
-rw-r--r--crates/completion/src/config.rs4
-rw-r--r--crates/completion/src/context.rs26
-rw-r--r--crates/completion/src/lib.rs5
-rw-r--r--crates/completion/src/render.rs15
-rw-r--r--crates/completion/src/test_utils.rs14
-rw-r--r--crates/hir/src/semantics.rs1
-rw-r--r--crates/hir/src/semantics/source_to_def.rs9
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/body/lower.rs4
-rw-r--r--crates/hir_def/src/expr.rs5
-rw-r--r--crates/hir_def/src/resolver.rs2
-rw-r--r--crates/hir_expand/Cargo.toml2
-rw-r--r--crates/hir_expand/src/ast_id_map.rs30
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/lib.rs8
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/infer/expr.rs7
-rw-r--r--crates/ide/src/diagnostics.rs21
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs7
-rw-r--r--crates/ide/src/diagnostics/fixes.rs26
-rw-r--r--crates/ide/src/display/navigation_target.rs23
-rw-r--r--crates/ide/src/extend_selection.rs6
-rw-r--r--crates/ide/src/goto_definition.rs35
-rw-r--r--crates/ide/src/hover.rs47
-rw-r--r--crates/ide/src/join_lines.rs4
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/references.rs119
-rw-r--r--crates/ide/src/references/rename.rs241
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs39
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html18
-rw-r--r--crates/ide/src/syntax_tree.rs4
-rw-r--r--crates/ide/src/typing.rs8
-rw-r--r--crates/ide_db/src/defs.rs45
-rw-r--r--crates/ide_db/src/helpers.rs1
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs (renamed from crates/assists/src/utils/import_assets.rs)50
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs18
-rw-r--r--crates/ide_db/src/imports_locator.rs6
-rw-r--r--crates/ide_db/src/search.rs2
-rw-r--r--crates/ide_db/src/source_change.rs56
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs16
-rw-r--r--crates/parser/src/grammar/paths.rs6
-rw-r--r--crates/parser/src/syntax_kind/generated.rs8
-rw-r--r--crates/profile/Cargo.toml2
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs5
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs8
-rw-r--r--crates/rust-analyzer/src/config.rs33
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt47
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt47
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt47
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt120
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs53
-rw-r--r--crates/rust-analyzer/src/handlers.rs14
-rw-r--r--crates/rust-analyzer/src/to_proto.rs24
-rw-r--r--crates/ssr/src/lib.rs24
-rw-r--r--crates/ssr/src/matching.rs4
-rw-r--r--crates/ssr/src/tests.rs3
-rw-r--r--crates/stdx/src/macros.rs2
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/algo.rs8
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs40
-rw-r--r--crates/syntax/src/ast/make.rs8
-rw-r--r--crates/syntax/src/ast/node_ext.rs22
-rw-r--r--crates/syntax/src/parsing/reparsing.rs5
-rw-r--r--crates/syntax/src/validation.rs2
-rw-r--r--crates/syntax/test_data/parser/err/0018_incomplete_fn.rast6
-rw-r--r--crates/syntax/test_data/parser/err/0035_use_recover.rast3
-rw-r--r--crates/syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast15
-rw-r--r--crates/syntax/test_data/parser/err/0041_illegal_super_keyword_location.rast15
-rw-r--r--crates/syntax/test_data/parser/err/0042_illegal_self_keyword_location.rast6
-rw-r--r--crates/syntax/test_data/parser/inline/err/0015_empty_segment.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0052_path_type.rast6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0067_crate_path.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rast28
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rs4
-rw-r--r--crates/syntax/test_data/parser/ok/0013_use_path_self_super.rast9
-rw-r--r--crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast3
-rw-r--r--crates/syntax/test_data/parser/ok/0034_crate_path_in_call.rast3
-rw-r--r--lib/arena/Cargo.toml10
-rw-r--r--lib/arena/src/lib.rs80
-rw-r--r--lib/arena/src/map.rs6
-rw-r--r--xtask/Cargo.toml2
-rw-r--r--xtask/src/ast_src.rs3
108 files changed, 1619 insertions, 1295 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c715e5e0b..76d64cb14 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,9 +26,9 @@ dependencies = [
26 26
27[[package]] 27[[package]]
28name = "anyhow" 28name = "anyhow"
29version = "1.0.38" 29version = "1.0.37"
30source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
31checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" 31checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86"
32 32
33[[package]] 33[[package]]
34name = "anymap" 34name = "anymap"
@@ -118,9 +118,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
118 118
119[[package]] 119[[package]]
120name = "byteorder" 120name = "byteorder"
121version = "1.4.2" 121version = "1.3.4"
122source = "registry+https://github.com/rust-lang/crates.io-index" 122source = "registry+https://github.com/rust-lang/crates.io-index"
123checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" 123checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
124 124
125[[package]] 125[[package]]
126name = "cargo-platform" 126name = "cargo-platform"
@@ -269,9 +269,9 @@ dependencies = [
269 269
270[[package]] 270[[package]]
271name = "const_fn" 271name = "const_fn"
272version = "0.4.5" 272version = "0.4.4"
273source = "registry+https://github.com/rust-lang/crates.io-index" 273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" 274checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
275 275
276[[package]] 276[[package]]
277name = "crc32fast" 277name = "crc32fast"
@@ -323,7 +323,7 @@ dependencies = [
323 "const_fn", 323 "const_fn",
324 "crossbeam-utils 0.8.1", 324 "crossbeam-utils 0.8.1",
325 "lazy_static", 325 "lazy_static",
326 "memoffset", 326 "memoffset 0.6.1",
327 "scopeguard", 327 "scopeguard",
328] 328]
329 329
@@ -552,7 +552,7 @@ dependencies = [
552 "hir_expand", 552 "hir_expand",
553 "indexmap", 553 "indexmap",
554 "itertools 0.10.0", 554 "itertools 0.10.0",
555 "la-arena 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 555 "la-arena",
556 "log", 556 "log",
557 "mbe", 557 "mbe",
558 "once_cell", 558 "once_cell",
@@ -571,7 +571,7 @@ version = "0.0.0"
571dependencies = [ 571dependencies = [
572 "base_db", 572 "base_db",
573 "either", 573 "either",
574 "la-arena 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 574 "la-arena",
575 "log", 575 "log",
576 "mbe", 576 "mbe",
577 "parser", 577 "parser",
@@ -596,7 +596,7 @@ dependencies = [
596 "hir_def", 596 "hir_def",
597 "hir_expand", 597 "hir_expand",
598 "itertools 0.10.0", 598 "itertools 0.10.0",
599 "la-arena 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 599 "la-arena",
600 "log", 600 "log",
601 "once_cell", 601 "once_cell",
602 "profile", 602 "profile",
@@ -769,13 +769,7 @@ dependencies = [
769 769
770[[package]] 770[[package]]
771name = "la-arena" 771name = "la-arena"
772version = "0.1.0" 772version = "0.1.1"
773
774[[package]]
775name = "la-arena"
776version = "0.1.0"
777source = "registry+https://github.com/rust-lang/crates.io-index"
778checksum = "b0385ab3b926cc05c78275d7ac6799c21fb964ada0a45cdaeaf1415d6a3dda39"
779 773
780[[package]] 774[[package]]
781name = "lazy_static" 775name = "lazy_static"
@@ -791,9 +785,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
791 785
792[[package]] 786[[package]]
793name = "libc" 787name = "libc"
794version = "0.2.82" 788version = "0.2.81"
795source = "registry+https://github.com/rust-lang/crates.io-index" 789source = "registry+https://github.com/rust-lang/crates.io-index"
796checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" 790checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
797 791
798[[package]] 792[[package]]
799name = "libloading" 793name = "libloading"
@@ -825,9 +819,9 @@ dependencies = [
825 819
826[[package]] 820[[package]]
827name = "log" 821name = "log"
828version = "0.4.13" 822version = "0.4.11"
829source = "registry+https://github.com/rust-lang/crates.io-index" 823source = "registry+https://github.com/rust-lang/crates.io-index"
830checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" 824checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
831dependencies = [ 825dependencies = [
832 "cfg-if 0.1.10", 826 "cfg-if 0.1.10",
833] 827]
@@ -910,6 +904,15 @@ dependencies = [
910 904
911[[package]] 905[[package]]
912name = "memoffset" 906name = "memoffset"
907version = "0.5.6"
908source = "registry+https://github.com/rust-lang/crates.io-index"
909checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
910dependencies = [
911 "autocfg",
912]
913
914[[package]]
915name = "memoffset"
913version = "0.6.1" 916version = "0.6.1"
914source = "registry+https://github.com/rust-lang/crates.io-index" 917source = "registry+https://github.com/rust-lang/crates.io-index"
915checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 918checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
@@ -1151,9 +1154,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
1151 1154
1152[[package]] 1155[[package]]
1153name = "pin-project-lite" 1156name = "pin-project-lite"
1154version = "0.2.4" 1157version = "0.2.0"
1155source = "registry+https://github.com/rust-lang/crates.io-index" 1158source = "registry+https://github.com/rust-lang/crates.io-index"
1156checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" 1159checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
1157 1160
1158[[package]] 1161[[package]]
1159name = "proc-macro2" 1162name = "proc-macro2"
@@ -1203,7 +1206,7 @@ name = "profile"
1203version = "0.0.0" 1206version = "0.0.0"
1204dependencies = [ 1207dependencies = [
1205 "cfg-if 1.0.0", 1208 "cfg-if 1.0.0",
1206 "la-arena 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1209 "la-arena",
1207 "libc", 1210 "libc",
1208 "once_cell", 1211 "once_cell",
1209 "perf-event", 1212 "perf-event",
@@ -1218,7 +1221,7 @@ dependencies = [
1218 "cargo_metadata", 1221 "cargo_metadata",
1219 "cfg", 1222 "cfg",
1220 "itertools 0.10.0", 1223 "itertools 0.10.0",
1221 "la-arena 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1224 "la-arena",
1222 "log", 1225 "log",
1223 "paths", 1226 "paths",
1224 "proc_macro_api", 1227 "proc_macro_api",
@@ -1291,9 +1294,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1291 1294
1292[[package]] 1295[[package]]
1293name = "regex" 1296name = "regex"
1294version = "1.4.3" 1297version = "1.4.2"
1295source = "registry+https://github.com/rust-lang/crates.io-index" 1298source = "registry+https://github.com/rust-lang/crates.io-index"
1296checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" 1299checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
1297dependencies = [ 1300dependencies = [
1298 "regex-syntax", 1301 "regex-syntax",
1299] 1302]
@@ -1310,20 +1313,20 @@ dependencies = [
1310 1313
1311[[package]] 1314[[package]]
1312name = "regex-syntax" 1315name = "regex-syntax"
1313version = "0.6.22" 1316version = "0.6.21"
1314source = "registry+https://github.com/rust-lang/crates.io-index" 1317source = "registry+https://github.com/rust-lang/crates.io-index"
1315checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" 1318checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
1316 1319
1317[[package]] 1320[[package]]
1318name = "rowan" 1321name = "rowan"
1319version = "0.10.0" 1322version = "0.10.3"
1320source = "registry+https://github.com/rust-lang/crates.io-index" 1323source = "registry+https://github.com/rust-lang/crates.io-index"
1321checksum = "1e081ed6eacce09e243b619ab90f069c27b0cff8a6d0eb8ad2ec935b65853798" 1324checksum = "d55d358c5fda3d5c4484f71a4808f5eeb39a0aff93bf3acebc680a6d15376f3c"
1322dependencies = [ 1325dependencies = [
1323 "rustc-hash", 1326 "rustc-hash",
1324 "smol_str", 1327 "smol_str",
1325 "text-size", 1328 "text-size",
1326 "thin-dst", 1329 "triomphe",
1327] 1330]
1328 1331
1329[[package]] 1332[[package]]
@@ -1541,9 +1544,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1541 1544
1542[[package]] 1545[[package]]
1543name = "smallvec" 1546name = "smallvec"
1544version = "1.6.1" 1547version = "1.6.0"
1545source = "registry+https://github.com/rust-lang/crates.io-index" 1548source = "registry+https://github.com/rust-lang/crates.io-index"
1546checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1549checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
1547 1550
1548[[package]] 1551[[package]]
1549name = "smol_str" 1552name = "smol_str"
@@ -1569,6 +1572,12 @@ dependencies = [
1569] 1572]
1570 1573
1571[[package]] 1574[[package]]
1575name = "stable_deref_trait"
1576version = "1.2.0"
1577source = "registry+https://github.com/rust-lang/crates.io-index"
1578checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1579
1580[[package]]
1572name = "stdx" 1581name = "stdx"
1573version = "0.0.0" 1582version = "0.0.0"
1574dependencies = [ 1583dependencies = [
@@ -1577,9 +1586,9 @@ dependencies = [
1577 1586
1578[[package]] 1587[[package]]
1579name = "syn" 1588name = "syn"
1580version = "1.0.58" 1589version = "1.0.57"
1581source = "registry+https://github.com/rust-lang/crates.io-index" 1590source = "registry+https://github.com/rust-lang/crates.io-index"
1582checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" 1591checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
1583dependencies = [ 1592dependencies = [
1584 "proc-macro2", 1593 "proc-macro2",
1585 "quote", 1594 "quote",
@@ -1655,16 +1664,10 @@ dependencies = [
1655] 1664]
1656 1665
1657[[package]] 1666[[package]]
1658name = "thin-dst"
1659version = "1.1.0"
1660source = "registry+https://github.com/rust-lang/crates.io-index"
1661checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e"
1662
1663[[package]]
1664name = "thread_local" 1667name = "thread_local"
1665version = "1.1.0" 1668version = "1.0.1"
1666source = "registry+https://github.com/rust-lang/crates.io-index" 1669source = "registry+https://github.com/rust-lang/crates.io-index"
1667checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" 1670checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
1668dependencies = [ 1671dependencies = [
1669 "lazy_static", 1672 "lazy_static",
1670] 1673]
@@ -1802,6 +1805,17 @@ dependencies = [
1802] 1805]
1803 1806
1804[[package]] 1807[[package]]
1808name = "triomphe"
1809version = "0.1.2"
1810source = "registry+https://github.com/rust-lang/crates.io-index"
1811checksum = "6e9d872053cf9e5a833d8c1dd772cdc38ab66a908129d6f73c049c986161d07c"
1812dependencies = [
1813 "memoffset 0.5.6",
1814 "serde",
1815 "stable_deref_trait",
1816]
1817
1818[[package]]
1805name = "tt" 1819name = "tt"
1806version = "0.0.0" 1820version = "0.0.0"
1807dependencies = [ 1821dependencies = [
@@ -1817,9 +1831,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1817 1831
1818[[package]] 1832[[package]]
1819name = "ungrammar" 1833name = "ungrammar"
1820version = "1.6.0" 1834version = "1.8.0"
1821source = "registry+https://github.com/rust-lang/crates.io-index" 1835source = "registry+https://github.com/rust-lang/crates.io-index"
1822checksum = "f96cc1b6938f7c548fbcc630bac5c896ae77a130909829ab18b8eab78c51b7ee" 1836checksum = "e33a2183403af89252547c4219a06a6cc8aef6302fee67e10e8431866af3ee72"
1823 1837
1824[[package]] 1838[[package]]
1825name = "unicase" 1839name = "unicase"
diff --git a/Cargo.toml b/Cargo.toml
index ff0d9e1ce..46c64d35c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
1[workspace] 1[workspace]
2members = [ "xtask/", "lib/*", "crates/*" ] 2members = ["xtask/", "lib/*", "crates/*"]
3 3
4[profile.dev] 4[profile.dev]
5# Disabling debug info speeds up builds a bunch, 5# Disabling debug info speeds up builds a bunch,
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index 4fe8ea761..9cabf037c 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -4,7 +4,7 @@
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//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; 7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8 8
9use crate::AssistKind; 9use crate::AssistKind;
10 10
@@ -14,9 +14,3 @@ pub struct AssistConfig {
14 pub allowed: Option<Vec<AssistKind>>, 14 pub allowed: Option<Vec<AssistKind>>,
15 pub insert_use: InsertUseConfig, 15 pub insert_use: InsertUseConfig,
16} 16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind,
22}
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 91cc63427..8d93edba2 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -2,7 +2,6 @@
2 2
3use std::mem; 3use std::mem;
4 4
5use algo::find_covering_element;
6use hir::Semantics; 5use hir::Semantics;
7use ide_db::{ 6use ide_db::{
8 base_db::{AnchoredPathBuf, FileId, FileRange}, 7 base_db::{AnchoredPathBuf, FileId, FileRange},
@@ -10,7 +9,7 @@ use ide_db::{
10}; 9};
11use ide_db::{ 10use ide_db::{
12 label::Label, 11 label::Label,
13 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 12 source_change::{FileSystemEdit, SourceChange},
14 RootDatabase, 13 RootDatabase,
15}; 14};
16use syntax::{ 15use syntax::{
@@ -94,11 +93,11 @@ impl<'a> AssistContext<'a> {
94 self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) 93 self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
95 } 94 }
96 pub(crate) fn covering_element(&self) -> SyntaxElement { 95 pub(crate) fn covering_element(&self) -> SyntaxElement {
97 find_covering_element(self.source_file.syntax(), self.frange.range) 96 self.source_file.syntax().covering_element(self.frange.range)
98 } 97 }
99 // FIXME: remove 98 // FIXME: remove
100 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { 99 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
101 find_covering_element(self.source_file.syntax(), range) 100 self.source_file.syntax().covering_element(range)
102 } 101 }
103} 102}
104 103
@@ -180,20 +179,12 @@ impl Assists {
180pub(crate) struct AssistBuilder { 179pub(crate) struct AssistBuilder {
181 edit: TextEditBuilder, 180 edit: TextEditBuilder,
182 file_id: FileId, 181 file_id: FileId,
183 is_snippet: bool, 182 source_change: SourceChange,
184 source_file_edits: Vec<SourceFileEdit>,
185 file_system_edits: Vec<FileSystemEdit>,
186} 183}
187 184
188impl AssistBuilder { 185impl AssistBuilder {
189 pub(crate) fn new(file_id: FileId) -> AssistBuilder { 186 pub(crate) fn new(file_id: FileId) -> AssistBuilder {
190 AssistBuilder { 187 AssistBuilder { edit: TextEdit::builder(), file_id, source_change: SourceChange::default() }
191 edit: TextEdit::builder(),
192 file_id,
193 is_snippet: false,
194 source_file_edits: Vec::default(),
195 file_system_edits: Vec::default(),
196 }
197 } 188 }
198 189
199 pub(crate) fn edit_file(&mut self, file_id: FileId) { 190 pub(crate) fn edit_file(&mut self, file_id: FileId) {
@@ -204,15 +195,7 @@ impl AssistBuilder {
204 fn commit(&mut self) { 195 fn commit(&mut self) {
205 let edit = mem::take(&mut self.edit).finish(); 196 let edit = mem::take(&mut self.edit).finish();
206 if !edit.is_empty() { 197 if !edit.is_empty() {
207 match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) { 198 self.source_change.insert_source_edit(self.file_id, edit);
208 Ok(idx) => self.source_file_edits[idx]
209 .edit
210 .union(edit)
211 .expect("overlapping edits for same file"),
212 Err(idx) => self
213 .source_file_edits
214 .insert(idx, SourceFileEdit { file_id: self.file_id, edit }),
215 }
216 } 199 }
217 } 200 }
218 201
@@ -231,7 +214,7 @@ impl AssistBuilder {
231 offset: TextSize, 214 offset: TextSize,
232 snippet: impl Into<String>, 215 snippet: impl Into<String>,
233 ) { 216 ) {
234 self.is_snippet = true; 217 self.source_change.is_snippet = true;
235 self.insert(offset, snippet); 218 self.insert(offset, snippet);
236 } 219 }
237 /// Replaces specified `range` of text with a given string. 220 /// Replaces specified `range` of text with a given string.
@@ -245,7 +228,7 @@ impl AssistBuilder {
245 range: TextRange, 228 range: TextRange,
246 snippet: impl Into<String>, 229 snippet: impl Into<String>,
247 ) { 230 ) {
248 self.is_snippet = true; 231 self.source_change.is_snippet = true;
249 self.replace(range, snippet); 232 self.replace(range, snippet);
250 } 233 }
251 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 234 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
@@ -260,15 +243,11 @@ impl AssistBuilder {
260 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { 243 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
261 let file_system_edit = 244 let file_system_edit =
262 FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() }; 245 FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() };
263 self.file_system_edits.push(file_system_edit); 246 self.source_change.push_file_system_edit(file_system_edit);
264 } 247 }
265 248
266 fn finish(mut self) -> SourceChange { 249 fn finish(mut self) -> SourceChange {
267 self.commit(); 250 self.commit();
268 SourceChange { 251 mem::take(&mut self.source_change)
269 source_file_edits: mem::take(&mut self.source_file_edits),
270 file_system_edits: mem::take(&mut self.file_system_edits),
271 is_snippet: self.is_snippet,
272 }
273 } 252 }
274} 253}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 55620f0f3..4e2a4fcd9 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,13 +1,11 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate},
2 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
3 mod_path_to_ast, 4 mod_path_to_ast,
4}; 5};
5use syntax::ast; 6use syntax::ast;
6 7
7use crate::{ 8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
8 utils::import_assets::{ImportAssets, ImportCandidate},
9 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
10};
11 9
12// Feature: Auto Import 10// Feature: Auto Import
13// 11//
@@ -121,8 +119,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
121 119
122fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
123 let name = match import_candidate { 121 let name = match import_candidate {
124 ImportCandidate::UnqualifiedName(candidate) 122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name),
125 | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name),
126 ImportCandidate::TraitAssocItem(candidate) => { 123 ImportCandidate::TraitAssocItem(candidate) => {
127 format!("Import a trait for item {}", &candidate.name) 124 format!("Import a trait for item {}", &candidate.name)
128 } 125 }
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index dc798daaa..0e63a60e8 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -79,29 +79,30 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
79 None => return Ok(false), 79 None => return Ok(false),
80 }; 80 };
81 81
82 Ok(!matches!((&initializer_expr, usage_parent), 82 Ok(!matches!(
83 (&initializer_expr, usage_parent),
83 (ast::Expr::CallExpr(_), _) 84 (ast::Expr::CallExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 85 | (ast::Expr::IndexExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 86 | (ast::Expr::MethodCallExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 87 | (ast::Expr::FieldExpr(_), _)
87 | (ast::Expr::TryExpr(_), _) 88 | (ast::Expr::TryExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 89 | (ast::Expr::RefExpr(_), _)
89 | (ast::Expr::Literal(_), _) 90 | (ast::Expr::Literal(_), _)
90 | (ast::Expr::TupleExpr(_), _) 91 | (ast::Expr::TupleExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 92 | (ast::Expr::ArrayExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 93 | (ast::Expr::ParenExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 94 | (ast::Expr::PathExpr(_), _)
94 | (ast::Expr::BlockExpr(_), _) 95 | (ast::Expr::BlockExpr(_), _)
95 | (ast::Expr::EffectExpr(_), _) 96 | (ast::Expr::EffectExpr(_), _)
96 | (_, ast::Expr::CallExpr(_)) 97 | (_, ast::Expr::CallExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 98 | (_, ast::Expr::TupleExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 99 | (_, ast::Expr::ArrayExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 100 | (_, ast::Expr::ParenExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 101 | (_, ast::Expr::ForExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 102 | (_, ast::Expr::WhileExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 103 | (_, ast::Expr::BreakExpr(_))
103 | (_, ast::Expr::ReturnExpr(_)) 104 | (_, ast::Expr::ReturnExpr(_))
104 | (_, ast::Expr::MatchExpr(_)) 105 | (_, ast::Expr::MatchExpr(_))
105 )) 106 ))
106 }) 107 })
107 .collect::<Result<Vec<_>, _>>()?; 108 .collect::<Result<Vec<_>, _>>()?;
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f7fbf37f4..a7d9fd4dc 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,7 +1,10 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::mod_path_to_ast; 4use ide_db::helpers::{
5 import_assets::{ImportAssets, ImportCandidate},
6 mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
@@ -12,7 +15,6 @@ use test_utils::mark;
12 15
13use crate::{ 16use crate::{
14 assist_context::{AssistContext, Assists}, 17 assist_context::{AssistContext, Assists},
15 utils::import_assets::{ImportAssets, ImportCandidate},
16 AssistId, AssistKind, GroupLabel, 18 AssistId, AssistKind, GroupLabel,
17}; 19};
18 20
@@ -53,17 +55,18 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
53 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 55 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
54 56
55 let qualify_candidate = match candidate { 57 let qualify_candidate = match candidate {
56 ImportCandidate::QualifierStart(_) => { 58 ImportCandidate::Path(candidate) => {
57 mark::hit!(qualify_path_qualifier_start); 59 if candidate.qualifier.is_some() {
58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 60 mark::hit!(qualify_path_qualifier_start);
59 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 61 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
60 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 62 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
61 } 63 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
62 ImportCandidate::UnqualifiedName(_) => { 64 } else {
63 mark::hit!(qualify_path_unqualified_name); 65 mark::hit!(qualify_path_unqualified_name);
64 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 66 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65 let generics = path.segment()?.generic_arg_list(); 67 let generics = path.segment()?.generic_arg_list();
66 QualifyCandidate::UnqualifiedName(generics) 68 QualifyCandidate::UnqualifiedName(generics)
69 }
67 } 70 }
68 ImportCandidate::TraitAssocItem(_) => { 71 ImportCandidate::TraitAssocItem(_) => {
69 mark::hit!(qualify_path_trait_assoc_item); 72 mark::hit!(qualify_path_trait_assoc_item);
@@ -186,7 +189,7 @@ fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> {
186 189
187fn group_label(candidate: &ImportCandidate) -> GroupLabel { 190fn group_label(candidate: &ImportCandidate) -> GroupLabel {
188 let name = match candidate { 191 let name = match candidate {
189 ImportCandidate::UnqualifiedName(it) | ImportCandidate::QualifierStart(it) => &it.name, 192 ImportCandidate::Path(it) => &it.name,
190 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 193 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
191 }; 194 };
192 GroupLabel(format!("Qualify {}", name)) 195 GroupLabel(format!("Qualify {}", name))
@@ -194,8 +197,13 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
194 197
195fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 198fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
196 match candidate { 199 match candidate {
197 ImportCandidate::UnqualifiedName(_) => format!("Qualify as `{}`", &import), 200 ImportCandidate::Path(candidate) => {
198 ImportCandidate::QualifierStart(_) => format!("Qualify with `{}`", &import), 201 if candidate.qualifier.is_some() {
202 format!("Qualify with `{}`", &import)
203 } else {
204 format!("Qualify as `{}`", &import)
205 }
206 }
199 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 207 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
200 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 208 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
201 } 209 }
diff --git a/crates/assists/src/handlers/unmerge_use.rs b/crates/assists/src/handlers/unmerge_use.rs
new file mode 100644
index 000000000..3dbef8e51
--- /dev/null
+++ b/crates/assists/src/handlers/unmerge_use.rs
@@ -0,0 +1,231 @@
1use syntax::{
2 algo::SyntaxRewriter,
3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind,
5};
6use test_utils::mark;
7
8use crate::{
9 assist_context::{AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13// Assist: unmerge_use
14//
15// Extracts single use item from use list.
16//
17// ```
18// use std::fmt::{Debug, Display$0};
19// ```
20// ->
21// ```
22// use std::fmt::{Debug};
23// use std::fmt::Display;
24// ```
25pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let tree: ast::UseTree = ctx.find_node_at_offset()?;
27
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item);
31 return None;
32 }
33
34 let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
35 let path = resolve_full_path(&tree)?;
36
37 let target = tree.syntax().text_range();
38 acc.add(
39 AssistId("unmerge_use", AssistKind::RefactorRewrite),
40 "Unmerge use",
41 target,
42 |builder| {
43 let new_use = ast::make::use_(
44 use_.visibility(),
45 ast::make::use_tree(
46 path,
47 tree.use_tree_list(),
48 tree.rename(),
49 tree.star_token().is_some(),
50 ),
51 );
52
53 let mut rewriter = SyntaxRewriter::default();
54 rewriter += tree.remove();
55 rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline());
56 if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize {
57 rewriter.insert_after(
58 use_.syntax(),
59 &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)),
60 );
61 }
62 rewriter.insert_after(use_.syntax(), new_use.syntax());
63
64 builder.rewrite(rewriter);
65 },
66 )
67}
68
69fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
70 let mut paths = tree
71 .syntax()
72 .ancestors()
73 .take_while(|n| n.kind() != SyntaxKind::USE_KW)
74 .filter_map(ast::UseTree::cast)
75 .filter_map(|t| t.path());
76
77 let mut final_path = paths.next()?;
78 for path in paths {
79 final_path = ast::make::path_concat(path, final_path)
80 }
81 Some(final_path)
82}
83
84#[cfg(test)]
85mod tests {
86 use crate::tests::{check_assist, check_assist_not_applicable};
87
88 use super::*;
89
90 #[test]
91 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item);
93 check_assist_not_applicable(
94 unmerge_use,
95 r"
96use std::fmt::Debug$0;
97",
98 );
99 check_assist_not_applicable(
100 unmerge_use,
101 r"
102use std::fmt::{Debug$0};
103",
104 );
105 check_assist_not_applicable(
106 unmerge_use,
107 r"
108use std::fmt::Debug as Dbg$0;
109",
110 );
111 }
112
113 #[test]
114 fn skip_single_glob_import() {
115 check_assist_not_applicable(
116 unmerge_use,
117 r"
118use std::fmt::*$0;
119",
120 );
121 }
122
123 #[test]
124 fn unmerge_use_item() {
125 check_assist(
126 unmerge_use,
127 r"
128use std::fmt::{Debug, Display$0};
129",
130 r"
131use std::fmt::{Debug};
132use std::fmt::Display;
133",
134 );
135
136 check_assist(
137 unmerge_use,
138 r"
139use std::fmt::{Debug, format$0, Display};
140",
141 r"
142use std::fmt::{Debug, Display};
143use std::fmt::format;
144",
145 );
146 }
147
148 #[test]
149 fn unmerge_glob_import() {
150 check_assist(
151 unmerge_use,
152 r"
153use std::fmt::{*$0, Display};
154",
155 r"
156use std::fmt::{Display};
157use std::fmt::*;
158",
159 );
160 }
161
162 #[test]
163 fn unmerge_renamed_use_item() {
164 check_assist(
165 unmerge_use,
166 r"
167use std::fmt::{Debug, Display as Disp$0};
168",
169 r"
170use std::fmt::{Debug};
171use std::fmt::Display as Disp;
172",
173 );
174 }
175
176 #[test]
177 fn unmerge_indented_use_item() {
178 check_assist(
179 unmerge_use,
180 r"
181mod format {
182 use std::fmt::{Debug, Display$0 as Disp, format};
183}
184",
185 r"
186mod format {
187 use std::fmt::{Debug, format};
188 use std::fmt::Display as Disp;
189}
190",
191 );
192 }
193
194 #[test]
195 fn unmerge_nested_use_item() {
196 check_assist(
197 unmerge_use,
198 r"
199use foo::bar::{baz::{qux$0, foobar}, barbaz};
200",
201 r"
202use foo::bar::{baz::{foobar}, barbaz};
203use foo::bar::baz::qux;
204",
205 );
206 check_assist(
207 unmerge_use,
208 r"
209use foo::bar::{baz$0::{qux, foobar}, barbaz};
210",
211 r"
212use foo::bar::{barbaz};
213use foo::bar::baz::{qux, foobar};
214",
215 );
216 }
217
218 #[test]
219 fn unmerge_use_item_with_visibility() {
220 check_assist(
221 unmerge_use,
222 r"
223pub use std::fmt::{Debug, Display$0};
224",
225 r"
226pub use std::fmt::{Debug};
227pub use std::fmt::Display;
228",
229 );
230 }
231}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 1080294ab..14178a651 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -24,7 +24,7 @@ use syntax::TextRange;
24 24
25pub(crate) use crate::assist_context::{AssistContext, Assists}; 25pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::{AssistConfig, InsertUseConfig}; 27pub use assist_config::AssistConfig;
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind { 30pub enum AssistKind {
@@ -156,6 +156,7 @@ mod handlers {
156 mod replace_unwrap_with_match; 156 mod replace_unwrap_with_match;
157 mod split_import; 157 mod split_import;
158 mod toggle_ignore; 158 mod toggle_ignore;
159 mod unmerge_use;
159 mod unwrap_block; 160 mod unwrap_block;
160 mod wrap_return_type_in_result; 161 mod wrap_return_type_in_result;
161 162
@@ -213,6 +214,7 @@ mod handlers {
213 replace_unwrap_with_match::replace_unwrap_with_match, 214 replace_unwrap_with_match::replace_unwrap_with_match,
214 split_import::split_import, 215 split_import::split_import,
215 toggle_ignore::toggle_ignore, 216 toggle_ignore::toggle_ignore,
217 unmerge_use::unmerge_use,
216 unwrap_block::unwrap_block, 218 unwrap_block::unwrap_block,
217 wrap_return_type_in_result::wrap_return_type_in_result, 219 wrap_return_type_in_result::wrap_return_type_in_result,
218 // These are manually sorted for better priorities 220 // These are manually sorted for better priorities
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index fef29a0b8..32bd8698b 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -3,16 +3,17 @@ mod generated;
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{ 4use ide_db::{
5 base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, 5 base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
6 helpers::{insert_use::MergeBehavior, SnippetCap}, 6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
7 source_change::FileSystemEdit, 10 source_change::FileSystemEdit,
8 RootDatabase, 11 RootDatabase,
9}; 12};
10use syntax::TextRange; 13use syntax::TextRange;
11use test_utils::{assert_eq_text, extract_offset, extract_range}; 14use test_utils::{assert_eq_text, extract_offset, extract_range};
12 15
13use crate::{ 16use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
14 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists, InsertUseConfig,
15};
16use stdx::{format_to, trim_indent}; 17use stdx::{format_to, trim_indent};
17 18
18pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { 19pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
@@ -80,10 +81,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
80 let actual = { 81 let actual = {
81 let source_change = assist.source_change.unwrap(); 82 let source_change = assist.source_change.unwrap();
82 let mut actual = before; 83 let mut actual = before;
83 for source_file_edit in source_change.source_file_edits { 84 if let Some(source_file_edit) = source_change.get_source_edit(file_id) {
84 if source_file_edit.file_id == file_id { 85 source_file_edit.apply(&mut actual);
85 source_file_edit.edit.apply(&mut actual)
86 }
87 } 86 }
88 actual 87 actual
89 }; 88 };
@@ -116,37 +115,33 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
116 115
117 match (assist, expected) { 116 match (assist, expected) {
118 (Some(assist), ExpectedResult::After(after)) => { 117 (Some(assist), ExpectedResult::After(after)) => {
119 let mut source_change = assist.source_change.unwrap(); 118 let source_change = assist.source_change.unwrap();
120 assert!(!source_change.source_file_edits.is_empty()); 119 assert!(!source_change.source_file_edits.is_empty());
121 let skip_header = source_change.source_file_edits.len() == 1 120 let skip_header = source_change.source_file_edits.len() == 1
122 && source_change.file_system_edits.len() == 0; 121 && source_change.file_system_edits.len() == 0;
123 source_change.source_file_edits.sort_by_key(|it| it.file_id);
124 122
125 let mut buf = String::new(); 123 let mut buf = String::new();
126 for source_file_edit in source_change.source_file_edits { 124 for (file_id, edit) in source_change.source_file_edits {
127 let mut text = db.file_text(source_file_edit.file_id).as_ref().to_owned(); 125 let mut text = db.file_text(file_id).as_ref().to_owned();
128 source_file_edit.edit.apply(&mut text); 126 edit.apply(&mut text);
129 if !skip_header { 127 if !skip_header {
130 let sr = db.file_source_root(source_file_edit.file_id); 128 let sr = db.file_source_root(file_id);
131 let sr = db.source_root(sr); 129 let sr = db.source_root(sr);
132 let path = sr.path_for_file(&source_file_edit.file_id).unwrap(); 130 let path = sr.path_for_file(&file_id).unwrap();
133 format_to!(buf, "//- {}\n", path) 131 format_to!(buf, "//- {}\n", path)
134 } 132 }
135 buf.push_str(&text); 133 buf.push_str(&text);
136 } 134 }
137 135
138 for file_system_edit in source_change.file_system_edits.clone() { 136 for file_system_edit in source_change.file_system_edits {
139 match file_system_edit { 137 if let FileSystemEdit::CreateFile { dst, initial_contents } = file_system_edit {
140 FileSystemEdit::CreateFile { dst, initial_contents } => { 138 let sr = db.file_source_root(dst.anchor);
141 let sr = db.file_source_root(dst.anchor); 139 let sr = db.source_root(sr);
142 let sr = db.source_root(sr); 140 let mut base = sr.path_for_file(&dst.anchor).unwrap().clone();
143 let mut base = sr.path_for_file(&dst.anchor).unwrap().clone(); 141 base.pop();
144 base.pop(); 142 let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]);
145 let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]); 143 format_to!(buf, "//- {}\n", created_file_path);
146 format_to!(buf, "//- {}\n", created_file_path); 144 buf.push_str(&initial_contents);
147 buf.push_str(&initial_contents);
148 }
149 _ => (),
150 } 145 }
151 } 146 }
152 147
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 217f577eb..d48d063b4 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -1138,6 +1138,20 @@ fn arithmetics {
1138} 1138}
1139 1139
1140#[test] 1140#[test]
1141fn doctest_unmerge_use() {
1142 check_doc_test(
1143 "unmerge_use",
1144 r#####"
1145use std::fmt::{Debug, Display$0};
1146"#####,
1147 r#####"
1148use std::fmt::{Debug};
1149use std::fmt::Display;
1150"#####,
1151 )
1152}
1153
1154#[test]
1141fn doctest_unwrap_block() { 1155fn doctest_unwrap_block() {
1142 check_doc_test( 1156 check_doc_test(
1143 "unwrap_block", 1157 "unwrap_block",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 9ea96eb73..fc9f83bab 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -1,5 +1,4 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod import_assets;
3 2
4use std::ops; 3use std::ops;
5 4
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 00c9e76f0..c3ce6e51d 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -13,6 +13,7 @@ pub(crate) mod postfix;
13pub(crate) mod macro_in_item_position; 13pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl; 14pub(crate) mod trait_impl;
15pub(crate) mod mod_; 15pub(crate) mod mod_;
16pub(crate) mod flyimport;
16 17
17use hir::{ModPath, ScopeDef, Type}; 18use hir::{ModPath, ScopeDef, Type};
18 19
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
new file mode 100644
index 000000000..222809638
--- /dev/null
+++ b/crates/completion/src/completions/flyimport.rs
@@ -0,0 +1,291 @@
1//! Feature: completion with imports-on-the-fly
2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input
5//! (case-insensitive, in any order or places).
6//!
7//! ```
8//! fn main() {
9//! pda$0
10//! }
11//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
12//! ```
13//! ->
14//! ```
15//! use std::marker::PhantomData;
16//!
17//! fn main() {
18//! PhantomData
19//! }
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ```
22//!
23//! .Fuzzy search details
24//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
26//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
27//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
28//!
29//! .Import configuration
30//!
31//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
32//! Mimics the corresponding behavior of the `Auto Import` feature.
33//!
34//! .LSP and performance implications
35//!
36//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
37//! (case sensitive) resolve client capability in its client capabilities.
38//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
39//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
40//! which might be slow ergo the feature is automatically disabled.
41//!
42//! .Feature toggle
43//!
44//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
46//! capability enabled.
47
48use either::Either;
49use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator};
51use syntax::AstNode;
52use test_utils::mark;
53
54use crate::{
55 context::CompletionContext,
56 render::{render_resolution_with_import, RenderContext},
57 ImportEdit,
58};
59
60use super::Completions;
61
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions {
64 return None;
65 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None;
68 }
69 let potential_import_name = ctx.token.to_string();
70 if potential_import_name.len() < 2 {
71 return None;
72 }
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
74
75 let current_module = ctx.scope.module()?;
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78
79 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports(
81 &ctx.sema,
82 ctx.krate?,
83 Some(40),
84 potential_import_name,
85 true,
86 true,
87 )
88 .filter_map(|import_candidate| {
89 Some(match import_candidate {
90 Either::Left(module_def) => {
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
92 }
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 })
97 })
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 });
104
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import(
107 RenderContext::new(ctx),
108 ImportEdit { import_path, import_scope: import_scope.clone() },
109 &definition,
110 )
111 }));
112 Some(())
113}
114
115fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str,
118) -> usize {
119 mark::hit!(certain_fuzzy_order_test);
120 let proposed_import_name = match proposed_mod_path.segments.last() {
121 Some(name) => name.to_string().to_lowercase(),
122 None => return usize::MAX,
123 };
124 match proposed_import_name.match_indices(user_input_lowercased).next() {
125 Some((first_matching_index, _)) => first_matching_index,
126 None => usize::MAX,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use expect_test::{expect, Expect};
133 use test_utils::mark;
134
135 use crate::{
136 item::CompletionKind,
137 test_utils::{check_edit, completion_list},
138 };
139
140 fn check(ra_fixture: &str, expect: Expect) {
141 let actual = completion_list(ra_fixture, CompletionKind::Magic);
142 expect.assert_eq(&actual);
143 }
144
145 #[test]
146 fn function_fuzzy_completion() {
147 check_edit(
148 "stdin",
149 r#"
150//- /lib.rs crate:dep
151pub mod io {
152 pub fn stdin() {}
153};
154
155//- /main.rs crate:main deps:dep
156fn main() {
157 stdi$0
158}
159"#,
160 r#"
161use dep::io::stdin;
162
163fn main() {
164 stdin()$0
165}
166"#,
167 );
168 }
169
170 #[test]
171 fn macro_fuzzy_completion() {
172 check_edit(
173 "macro_with_curlies!",
174 r#"
175//- /lib.rs crate:dep
176/// Please call me as macro_with_curlies! {}
177#[macro_export]
178macro_rules! macro_with_curlies {
179 () => {}
180}
181
182//- /main.rs crate:main deps:dep
183fn main() {
184 curli$0
185}
186"#,
187 r#"
188use dep::macro_with_curlies;
189
190fn main() {
191 macro_with_curlies! {$0}
192}
193"#,
194 );
195 }
196
197 #[test]
198 fn struct_fuzzy_completion() {
199 check_edit(
200 "ThirdStruct",
201 r#"
202//- /lib.rs crate:dep
203pub struct FirstStruct;
204pub mod some_module {
205 pub struct SecondStruct;
206 pub struct ThirdStruct;
207}
208
209//- /main.rs crate:main deps:dep
210use dep::{FirstStruct, some_module::SecondStruct};
211
212fn main() {
213 this$0
214}
215"#,
216 r#"
217use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
218
219fn main() {
220 ThirdStruct
221}
222"#,
223 );
224 }
225
226 #[test]
227 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test);
229 check(
230 r#"
231//- /lib.rs crate:dep
232pub struct FirstStruct;
233pub mod some_module {
234 // already imported, omitted
235 pub struct SecondStruct;
236 // does not contain all letters from the query, omitted
237 pub struct UnrelatedOne;
238 // contains all letters from the query, but not in sequence, displayed last
239 pub struct ThiiiiiirdStruct;
240 // contains all letters from the query, but not in the beginning, displayed second
241 pub struct AfterThirdStruct;
242 // contains all letters from the query in the begginning, displayed first
243 pub struct ThirdStruct;
244}
245
246//- /main.rs crate:main deps:dep
247use dep::{FirstStruct, some_module::SecondStruct};
248
249fn main() {
250 hir$0
251}
252"#,
253 expect![[r#"
254 st dep::some_module::ThirdStruct
255 st dep::some_module::AfterThirdStruct
256 st dep::some_module::ThiiiiiirdStruct
257 "#]],
258 );
259 }
260
261 #[test]
262 fn does_not_propose_names_in_scope() {
263 check(
264 r#"
265//- /lib.rs crate:dep
266pub mod test_mod {
267 pub trait TestTrait {
268 const SPECIAL_CONST: u8;
269 type HumbleType;
270 fn weird_function();
271 fn random_method(&self);
272 }
273 pub struct TestStruct {}
274 impl TestTrait for TestStruct {
275 const SPECIAL_CONST: u8 = 42;
276 type HumbleType = ();
277 fn weird_function() {}
278 fn random_method(&self) {}
279 }
280}
281
282//- /main.rs crate:main deps:dep
283use dep::test_mod::TestStruct;
284fn main() {
285 TestSt$0
286}
287"#,
288 expect![[r#""#]],
289 );
290 }
291}
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
index 425a688ff..c1af348dc 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -1,6 +1,6 @@
1//! Completes keywords. 1//! Completes keywords.
2 2
3use syntax::{ast, SyntaxKind}; 3use syntax::SyntaxKind;
4use test_utils::mark; 4use test_utils::mark;
5 5
6use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 6use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
@@ -86,8 +86,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
86 add_keyword(ctx, acc, "match", "match $0 {}"); 86 add_keyword(ctx, acc, "match", "match $0 {}");
87 add_keyword(ctx, acc, "while", "while $0 {}"); 87 add_keyword(ctx, acc, "while", "while $0 {}");
88 add_keyword(ctx, acc, "loop", "loop {$0}"); 88 add_keyword(ctx, acc, "loop", "loop {$0}");
89 add_keyword(ctx, acc, "if", "if "); 89 add_keyword(ctx, acc, "if", "if $0 {}");
90 add_keyword(ctx, acc, "if let", "if let "); 90 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
91 } 91 }
92 92
93 if ctx.if_is_prev || ctx.block_expr_parent { 93 if ctx.if_is_prev || ctx.block_expr_parent {
@@ -143,47 +143,49 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
143 Some(it) => it, 143 Some(it) => it,
144 None => return, 144 None => return,
145 }; 145 };
146 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
147}
148
149fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
150 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
151 .kind(CompletionItemKind::Keyword);
152 146
153 match ctx.config.snippet_cap { 147 add_keyword(
154 Some(cap) => res.insert_snippet(cap, snippet), 148 ctx,
155 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), 149 acc,
156 } 150 "return",
157 .build() 151 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
152 (true, true) => "return $0;",
153 (true, false) => "return;",
154 (false, true) => "return $0",
155 (false, false) => "return",
156 },
157 )
158} 158}
159 159
160fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 160fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
161 acc.add(keyword(ctx, kw, snippet)); 161 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
162} 162 .kind(CompletionItemKind::Keyword);
163 163 let builder = match ctx.config.snippet_cap {
164fn complete_return( 164 Some(cap) => {
165 ctx: &CompletionContext, 165 let tmp;
166 fn_def: &ast::Fn, 166 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
167 can_be_stmt: bool, 167 mark::hit!(let_semi);
168) -> Option<CompletionItem> { 168 tmp = format!("{};", snippet);
169 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { 169 &tmp
170 (true, true) => "return $0;", 170 } else {
171 (true, false) => "return;", 171 snippet
172 (false, true) => "return $0", 172 };
173 (false, false) => "return", 173 builder.insert_snippet(cap, snippet)
174 }
175 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
174 }; 176 };
175 Some(keyword(ctx, "return", snip)) 177 acc.add(builder.build());
176} 178}
177 179
178#[cfg(test)] 180#[cfg(test)]
179mod tests { 181mod tests {
180 use expect_test::{expect, Expect}; 182 use expect_test::{expect, Expect};
183 use test_utils::mark;
181 184
182 use crate::{ 185 use crate::{
183 test_utils::{check_edit, completion_list}, 186 test_utils::{check_edit, completion_list},
184 CompletionKind, 187 CompletionKind,
185 }; 188 };
186 use test_utils::mark;
187 189
188 fn check(ra_fixture: &str, expect: Expect) { 190 fn check(ra_fixture: &str, expect: Expect) {
189 let actual = completion_list(ra_fixture, CompletionKind::Keyword); 191 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
@@ -609,4 +611,50 @@ fn foo() {
609 "#]], 611 "#]],
610 ); 612 );
611 } 613 }
614
615 #[test]
616 fn let_semi() {
617 mark::check!(let_semi);
618 check_edit(
619 "match",
620 r#"
621fn main() { let x = $0 }
622"#,
623 r#"
624fn main() { let x = match $0 {}; }
625"#,
626 );
627
628 check_edit(
629 "if",
630 r#"
631fn main() {
632 let x = $0
633 let y = 92;
634}
635"#,
636 r#"
637fn main() {
638 let x = if $0 {};
639 let y = 92;
640}
641"#,
642 );
643
644 check_edit(
645 "loop",
646 r#"
647fn main() {
648 let x = $0
649 bar();
650}
651"#,
652 r#"
653fn main() {
654 let x = loop {$0};
655 bar();
656}
657"#,
658 );
659 }
612} 660}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 53e1391f3..ac5596ca4 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -2,17 +2,11 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use either::Either; 5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type};
7use ide_db::helpers::insert_use::ImportScope;
8use ide_db::imports_locator;
9use syntax::AstNode; 6use syntax::AstNode;
10use test_utils::mark; 7use test_utils::mark;
11 8
12use crate::{ 9use crate::{CompletionContext, Completions};
13 render::{render_resolution_with_import, RenderContext},
14 CompletionContext, Completions, ImportEdit,
15};
16 10
17pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 11pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
18 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 12 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -45,10 +39,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
45 } 39 }
46 acc.add_resolution(ctx, name.to_string(), &res) 40 acc.add_resolution(ctx, name.to_string(), &res)
47 }); 41 });
48
49 if ctx.config.enable_autoimport_completions {
50 fuzzy_completion(acc, ctx);
51 }
52} 42}
53 43
54fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 44fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
@@ -77,124 +67,13 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
77 } 67 }
78} 68}
79 69
80// Feature: Fuzzy Completion and Autoimports
81//
82// When completing names in the current scope, proposes additional imports from other modules or crates,
83// if they can be qualified in the scope and their name contains all symbols from the completion input
84// (case-insensitive, in any order or places).
85//
86// ```
87// fn main() {
88// pda$0
89// }
90// # pub mod std { pub mod marker { pub struct PhantomData { } } }
91// ```
92// ->
93// ```
94// use std::marker::PhantomData;
95//
96// fn main() {
97// PhantomData
98// }
99// # pub mod std { pub mod marker { pub struct PhantomData { } } }
100// ```
101//
102// .Fuzzy search details
103//
104// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
105// (i.e. in `HashMap` in the `std::collections::HashMap` path).
106// For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
107//
108// .Merge Behavior
109//
110// It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
111// Mimics the corresponding behavior of the `Auto Import` feature.
112//
113// .LSP and performance implications
114//
115// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
116// (case sensitive) resolve client capability in its client capabilities.
117// This way the server is able to defer the costly computations, doing them for a selected completion item only.
118// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
119// which might be slow ergo the feature is automatically disabled.
120//
121// .Feature toggle
122//
123// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
125// capability enabled.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
127 let potential_import_name = ctx.token.to_string();
128 let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone());
129
130 if potential_import_name.len() < 2 {
131 return None;
132 }
133
134 let current_module = ctx.scope.module()?;
135 let anchor = ctx.name_ref_syntax.as_ref()?;
136 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
137
138 let user_input_lowercased = potential_import_name.to_lowercase();
139 let mut all_mod_paths = imports_locator::find_similar_imports(
140 &ctx.sema,
141 ctx.krate?,
142 Some(40),
143 potential_import_name,
144 true,
145 true,
146 )
147 .filter_map(|import_candidate| {
148 Some(match import_candidate {
149 Either::Left(module_def) => {
150 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
151 }
152 Either::Right(macro_def) => {
153 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
154 }
155 })
156 })
157 .filter(|(mod_path, _)| mod_path.len() > 1)
158 .collect::<Vec<_>>();
159
160 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
161 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
162 });
163
164 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
165 render_resolution_with_import(
166 RenderContext::new(ctx),
167 ImportEdit { import_path, import_scope: import_scope.clone() },
168 &definition,
169 )
170 }));
171 Some(())
172}
173
174fn compute_fuzzy_completion_order_key(
175 proposed_mod_path: &ModPath,
176 user_input_lowercased: &str,
177) -> usize {
178 mark::hit!(certain_fuzzy_order_test);
179 let proposed_import_name = match proposed_mod_path.segments.last() {
180 Some(name) => name.to_string().to_lowercase(),
181 None => return usize::MAX,
182 };
183 match proposed_import_name.match_indices(user_input_lowercased).next() {
184 Some((first_matching_index, _)) => first_matching_index,
185 None => usize::MAX,
186 }
187}
188
189#[cfg(test)] 70#[cfg(test)]
190mod tests { 71mod tests {
191 use expect_test::{expect, Expect}; 72 use expect_test::{expect, Expect};
192 use test_utils::mark; 73 use test_utils::mark;
193 74
194 use crate::{ 75 use crate::{
195 test_utils::{ 76 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
196 check_edit, check_edit_with_config, completion_list_with_config, TEST_CONFIG,
197 },
198 CompletionConfig, CompletionKind, 77 CompletionConfig, CompletionKind,
199 }; 78 };
200 79
@@ -855,128 +734,4 @@ impl My$0
855 "#]], 734 "#]],
856 ) 735 )
857 } 736 }
858
859 #[test]
860 fn function_fuzzy_completion() {
861 check_edit_with_config(
862 TEST_CONFIG,
863 "stdin",
864 r#"
865//- /lib.rs crate:dep
866pub mod io {
867 pub fn stdin() {}
868};
869
870//- /main.rs crate:main deps:dep
871fn main() {
872 stdi$0
873}
874"#,
875 r#"
876use dep::io::stdin;
877
878fn main() {
879 stdin()$0
880}
881"#,
882 );
883 }
884
885 #[test]
886 fn macro_fuzzy_completion() {
887 check_edit_with_config(
888 TEST_CONFIG,
889 "macro_with_curlies!",
890 r#"
891//- /lib.rs crate:dep
892/// Please call me as macro_with_curlies! {}
893#[macro_export]
894macro_rules! macro_with_curlies {
895 () => {}
896}
897
898//- /main.rs crate:main deps:dep
899fn main() {
900 curli$0
901}
902"#,
903 r#"
904use dep::macro_with_curlies;
905
906fn main() {
907 macro_with_curlies! {$0}
908}
909"#,
910 );
911 }
912
913 #[test]
914 fn struct_fuzzy_completion() {
915 check_edit_with_config(
916 TEST_CONFIG,
917 "ThirdStruct",
918 r#"
919//- /lib.rs crate:dep
920pub struct FirstStruct;
921pub mod some_module {
922 pub struct SecondStruct;
923 pub struct ThirdStruct;
924}
925
926//- /main.rs crate:main deps:dep
927use dep::{FirstStruct, some_module::SecondStruct};
928
929fn main() {
930 this$0
931}
932"#,
933 r#"
934use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
935
936fn main() {
937 ThirdStruct
938}
939"#,
940 );
941 }
942
943 #[test]
944 fn fuzzy_completions_come_in_specific_order() {
945 mark::check!(certain_fuzzy_order_test);
946 check_with_config(
947 TEST_CONFIG,
948 r#"
949//- /lib.rs crate:dep
950pub struct FirstStruct;
951pub mod some_module {
952 // already imported, omitted
953 pub struct SecondStruct;
954 // does not contain all letters from the query, omitted
955 pub struct UnrelatedOne;
956 // contains all letters from the query, but not in sequence, displayed last
957 pub struct ThiiiiiirdStruct;
958 // contains all letters from the query, but not in the beginning, displayed second
959 pub struct AfterThirdStruct;
960 // contains all letters from the query in the begginning, displayed first
961 pub struct ThirdStruct;
962}
963
964//- /main.rs crate:main deps:dep
965use dep::{FirstStruct, some_module::SecondStruct};
966
967fn main() {
968 hir$0
969}
970"#,
971 expect![[r#"
972 fn main() fn main()
973 st SecondStruct
974 st FirstStruct
975 md dep
976 st dep::some_module::ThirdStruct
977 st dep::some_module::AfterThirdStruct
978 st dep::some_module::ThiiiiiirdStruct
979 "#]],
980 );
981 }
982} 737}
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index b4439b7d1..58fc700f3 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,7 +4,7 @@
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 ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; 7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
@@ -13,5 +13,5 @@ pub struct CompletionConfig {
13 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
16 pub merge: Option<MergeBehavior>, 16 pub insert_use: InsertUseConfig,
17} 17}
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index ebf28e887..b1e8eba85 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -4,10 +4,8 @@ use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::base_db::{FilePosition, SourceDatabase};
5use ide_db::{call_info::ActiveParameter, RootDatabase}; 5use ide_db::{call_info::ActiveParameter, RootDatabase};
6use syntax::{ 6use syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode,
8 ast, match_ast, AstNode, NodeOrToken, 8 SyntaxToken, TextRange, TextSize,
9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 9};
12use test_utils::mark; 10use test_utils::mark;
13use text_edit::Indel; 11use text_edit::Indel;
@@ -92,6 +90,7 @@ pub(crate) struct CompletionContext<'a> {
92 pub(super) has_item_list_or_source_file_parent: bool, 90 pub(super) has_item_list_or_source_file_parent: bool,
93 pub(super) for_is_prev2: bool, 91 pub(super) for_is_prev2: bool,
94 pub(super) fn_is_prev: bool, 92 pub(super) fn_is_prev: bool,
93 pub(super) incomplete_let: bool,
95 pub(super) locals: Vec<(String, Local)>, 94 pub(super) locals: Vec<(String, Local)>,
96} 95}
97 96
@@ -132,9 +131,9 @@ impl<'a> CompletionContext<'a> {
132 scope, 131 scope,
133 db, 132 db,
134 config, 133 config,
134 position,
135 original_token, 135 original_token,
136 token, 136 token,
137 position,
138 krate, 137 krate,
139 expected_type: None, 138 expected_type: None,
140 name_ref_syntax: None, 139 name_ref_syntax: None,
@@ -155,30 +154,31 @@ impl<'a> CompletionContext<'a> {
155 is_expr: false, 154 is_expr: false,
156 is_new_item: false, 155 is_new_item: false,
157 dot_receiver: None, 156 dot_receiver: None,
157 dot_receiver_is_ambiguous_float_literal: false,
158 is_call: false, 158 is_call: false,
159 is_pattern_call: false, 159 is_pattern_call: false,
160 is_macro_call: false, 160 is_macro_call: false,
161 is_path_type: false, 161 is_path_type: false,
162 has_type_args: false, 162 has_type_args: false,
163 dot_receiver_is_ambiguous_float_literal: false,
164 attribute_under_caret: None, 163 attribute_under_caret: None,
165 mod_declaration_under_caret: None, 164 mod_declaration_under_caret: None,
166 unsafe_is_prev: false, 165 unsafe_is_prev: false,
167 in_loop_body: false, 166 if_is_prev: false,
168 ref_pat_parent: false,
169 bind_pat_parent: false,
170 block_expr_parent: false, 167 block_expr_parent: false,
168 bind_pat_parent: false,
169 ref_pat_parent: false,
170 in_loop_body: false,
171 has_trait_parent: false, 171 has_trait_parent: false,
172 has_impl_parent: false, 172 has_impl_parent: false,
173 inside_impl_trait_block: false, 173 inside_impl_trait_block: false,
174 has_field_list_parent: false, 174 has_field_list_parent: false,
175 trait_as_prev_sibling: false, 175 trait_as_prev_sibling: false,
176 impl_as_prev_sibling: false, 176 impl_as_prev_sibling: false,
177 if_is_prev: false,
178 is_match_arm: false, 177 is_match_arm: false,
179 has_item_list_or_source_file_parent: false, 178 has_item_list_or_source_file_parent: false,
180 for_is_prev2: false, 179 for_is_prev2: false,
181 fn_is_prev: false, 180 fn_is_prev: false,
181 incomplete_let: false,
182 locals, 182 locals,
183 }; 183 };
184 184
@@ -270,6 +270,10 @@ impl<'a> CompletionContext<'a> {
270 .filter(|module| module.item_list().is_none()); 270 .filter(|module| module.item_list().is_none());
271 self.for_is_prev2 = for_is_prev2(syntax_element.clone()); 271 self.for_is_prev2 = for_is_prev2(syntax_element.clone());
272 self.fn_is_prev = fn_is_prev(syntax_element.clone()); 272 self.fn_is_prev = fn_is_prev(syntax_element.clone());
273 self.incomplete_let =
274 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
275 it.syntax().text_range().end() == syntax_element.text_range().end()
276 });
273 } 277 }
274 278
275 fn fill( 279 fn fill(
@@ -507,7 +511,7 @@ impl<'a> CompletionContext<'a> {
507} 511}
508 512
509fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { 513fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
510 find_covering_element(syntax, range).ancestors().find_map(N::cast) 514 syntax.covering_element(range).ancestors().find_map(N::cast)
511} 515}
512 516
513fn is_node<N: AstNode>(node: &SyntaxNode) -> bool { 517fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 6cba88a6b..ee1b822e7 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -127,6 +127,7 @@ pub fn completions(
127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
129 completions::mod_::complete_mod(&mut acc, &ctx); 129 completions::mod_::complete_mod(&mut acc, &ctx);
130 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
130 131
131 Some(acc) 132 Some(acc)
132} 133}
@@ -153,7 +154,9 @@ pub fn resolve_completion_edits(
153 }) 154 })
154 .find(|mod_path| mod_path.to_string() == full_import_path)?; 155 .find(|mod_path| mod_path.to_string() == full_import_path)?;
155 156
156 ImportEdit { import_path, import_scope }.to_text_edit(config.merge).map(|edit| vec![edit]) 157 ImportEdit { import_path, import_scope }
158 .to_text_edit(config.insert_use.merge)
159 .map(|edit| vec![edit])
157} 160}
158 161
159#[cfg(test)] 162#[cfg(test)]
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index e93c59f71..820dd01d1 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -51,11 +51,16 @@ pub(crate) fn render_resolution_with_import<'a>(
51 import_edit: ImportEdit, 51 import_edit: ImportEdit,
52 resolution: &ScopeDef, 52 resolution: &ScopeDef,
53) -> Option<CompletionItem> { 53) -> Option<CompletionItem> {
54 Render::new(ctx).render_resolution( 54 Render::new(ctx)
55 import_edit.import_path.segments.last()?.to_string(), 55 .render_resolution(
56 Some(import_edit), 56 import_edit.import_path.segments.last()?.to_string(),
57 resolution, 57 Some(import_edit),
58 ) 58 resolution,
59 )
60 .map(|mut item| {
61 item.completion_kind = CompletionKind::Magic;
62 item
63 })
59} 64}
60 65
61/// Interface for data and methods required for items rendering. 66/// Interface for data and methods required for items rendering.
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 3f4b9d4ac..6ea6da989 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -1,9 +1,12 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics; 3use hir::{PrefixKind, Semantics};
4use ide_db::{ 4use ide_db::{
5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, 5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
6 helpers::{insert_use::MergeBehavior, SnippetCap}, 6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
7 RootDatabase, 10 RootDatabase,
8}; 11};
9use itertools::Itertools; 12use itertools::Itertools;
@@ -19,7 +22,10 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
19 add_call_parenthesis: true, 22 add_call_parenthesis: true,
20 add_call_argument_snippets: true, 23 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true), 24 snippet_cap: SnippetCap::new(true),
22 merge: Some(MergeBehavior::Full), 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain,
28 },
23}; 29};
24 30
25/// Creates analysis from a multi-file fixture, returns positions marked with $0. 31/// Creates analysis from a multi-file fixture, returns positions marked with $0.
@@ -110,7 +116,7 @@ pub(crate) fn check_edit_with_config(
110 116
111 let mut combined_edit = completion.text_edit().to_owned(); 117 let mut combined_edit = completion.text_edit().to_owned();
112 if let Some(import_text_edit) = 118 if let Some(import_text_edit) =
113 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.merge)) 119 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge))
114 { 120 {
115 combined_edit.union(import_text_edit).expect( 121 combined_edit.union(import_text_edit).expect(
116 "Failed to apply completion resolve changes: change ranges overlap, but should not", 122 "Failed to apply completion resolve changes: change ranges overlap, but should not",
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index cd689c869..ab213e04c 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -750,6 +750,7 @@ to_def_impls![
750 (crate::ConstParam, ast::ConstParam, const_param_to_def), 750 (crate::ConstParam, ast::ConstParam, const_param_to_def),
751 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 751 (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
752 (crate::Local, ast::IdentPat, bind_pat_to_def), 752 (crate::Local, ast::IdentPat, bind_pat_to_def),
753 (crate::Local, ast::SelfParam, self_param_to_def),
753 (crate::Label, ast::Label, label_to_def), 754 (crate::Label, ast::Label, label_to_def),
754]; 755];
755 756
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 4b9ebff72..9bf60c72a 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -114,6 +114,15 @@ impl SourceToDefCtx<'_, '_> {
114 let pat_id = source_map.node_pat(src.as_ref())?; 114 let pat_id = source_map.node_pat(src.as_ref())?;
115 Some((container, pat_id)) 115 Some((container, pat_id))
116 } 116 }
117 pub(super) fn self_param_to_def(
118 &mut self,
119 src: InFile<ast::SelfParam>,
120 ) -> Option<(DefWithBodyId, PatId)> {
121 let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?;
122 let (_body, source_map) = self.db.body_with_source_map(container);
123 let pat_id = source_map.node_self_param(src.as_ref())?;
124 Some((container, pat_id))
125 }
117 pub(super) fn label_to_def( 126 pub(super) fn label_to_def(
118 &mut self, 127 &mut self,
119 src: InFile<ast::Label>, 128 src: InFile<ast::Label>,
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 5d21283f7..023d288d1 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -20,7 +20,7 @@ fst = { version = "0.4", default-features = false }
20itertools = "0.10.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "1.4.0"
23la-arena = "0.1.0" 23la-arena = { version = "0.1.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
26base_db = { path = "../base_db", version = "0.0.0" } 26base_db = { path = "../base_db", version = "0.0.0" }
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 27575c537..4ce5e5b72 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -386,6 +386,10 @@ impl ExprCollector<'_> {
386 let expr = e.expr().map(|e| self.collect_expr(e)); 386 let expr = e.expr().map(|e| self.collect_expr(e));
387 self.alloc_expr(Expr::Return { expr }, syntax_ptr) 387 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
388 } 388 }
389 ast::Expr::YieldExpr(e) => {
390 let expr = e.expr().map(|e| self.collect_expr(e));
391 self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
392 }
389 ast::Expr::RecordExpr(e) => { 393 ast::Expr::RecordExpr(e) => {
390 let path = e.path().and_then(|path| self.expander.parse_path(path)); 394 let path = e.path().and_then(|path| self.expander.parse_path(path));
391 let mut field_ptrs = Vec::new(); 395 let mut field_ptrs = Vec::new();
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index af01d32dc..a293df9f1 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -99,6 +99,9 @@ pub enum Expr {
99 Return { 99 Return {
100 expr: Option<ExprId>, 100 expr: Option<ExprId>,
101 }, 101 },
102 Yield {
103 expr: Option<ExprId>,
104 },
102 RecordLit { 105 RecordLit {
103 path: Option<Path>, 106 path: Option<Path>,
104 fields: Vec<RecordLitField>, 107 fields: Vec<RecordLitField>,
@@ -294,7 +297,7 @@ impl Expr {
294 } 297 }
295 } 298 }
296 Expr::Continue { .. } => {} 299 Expr::Continue { .. } => {}
297 Expr::Break { expr, .. } | Expr::Return { expr } => { 300 Expr::Break { expr, .. } | Expr::Return { expr } | Expr::Yield { expr } => {
298 if let Some(expr) = expr { 301 if let Some(expr) = expr {
299 f(*expr); 302 f(*expr);
300 } 303 }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 61059c349..85ddc2c47 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -258,7 +258,7 @@ impl Resolver {
258 ) -> Option<ResolveValueResult> { 258 ) -> Option<ResolveValueResult> {
259 let n_segments = path.segments.len(); 259 let n_segments = path.segments.len();
260 let tmp = name![self]; 260 let tmp = name![self];
261 let first_name = if path.is_self() { &tmp } else { &path.segments.first()? }; 261 let first_name = if path.is_self() { &tmp } else { path.segments.first()? };
262 let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); 262 let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
263 for scope in self.scopes.iter().rev() { 263 for scope in self.scopes.iter().rev() {
264 match scope { 264 match scope {
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index b535a3d4f..1b1c7da65 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13log = "0.4.8" 13log = "0.4.8"
14either = "1.5.3" 14either = "1.5.3"
15rustc-hash = "1.0.0" 15rustc-hash = "1.0.0"
16la-arena = "0.1.0" 16la-arena = { version = "0.1.0", path = "../../lib/arena" }
17 17
18base_db = { path = "../base_db", version = "0.0.0" } 18base_db = { path = "../base_db", version = "0.0.0" }
19syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs
index f4f6e11fd..2401b0cc5 100644
--- a/crates/hir_expand/src/ast_id_map.rs
+++ b/crates/hir_expand/src/ast_id_map.rs
@@ -72,10 +72,12 @@ impl AstIdMap {
72 // get lower ids then children. That is, adding a new child does not 72 // get lower ids then children. That is, adding a new child does not
73 // change parent's id. This means that, say, adding a new function to a 73 // change parent's id. This means that, say, adding a new function to a
74 // trait does not change ids of top-level items, which helps caching. 74 // trait does not change ids of top-level items, which helps caching.
75 bfs(node, |it| { 75 bdfs(node, |it| match ast::Item::cast(it) {
76 if let Some(module_item) = ast::Item::cast(it) { 76 Some(module_item) => {
77 res.alloc(module_item.syntax()); 77 res.alloc(module_item.syntax());
78 true
78 } 79 }
80 None => false,
79 }); 81 });
80 res 82 res
81 } 83 }
@@ -105,14 +107,30 @@ impl AstIdMap {
105 } 107 }
106} 108}
107 109
108/// Walks the subtree in bfs order, calling `f` for each node. 110/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
109fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) { 111/// order? It is a mix of breadth-first and depth first orders. Nodes for which
112/// `f` returns true are visited breadth-first, all the other nodes are explored
113/// depth-first.
114///
115/// In other words, the size of the bfs queue is bound by the number of "true"
116/// nodes.
117fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> bool) {
110 let mut curr_layer = vec![node.clone()]; 118 let mut curr_layer = vec![node.clone()];
111 let mut next_layer = vec![]; 119 let mut next_layer = vec![];
112 while !curr_layer.is_empty() { 120 while !curr_layer.is_empty() {
113 curr_layer.drain(..).for_each(|node| { 121 curr_layer.drain(..).for_each(|node| {
114 next_layer.extend(node.children()); 122 let mut preorder = node.preorder();
115 f(node); 123 while let Some(event) = preorder.next() {
124 match event {
125 syntax::WalkEvent::Enter(node) => {
126 if f(node.clone()) {
127 next_layer.extend(node.children());
128 preorder.skip_subtree();
129 }
130 }
131 syntax::WalkEvent::Leave(_) => {}
132 }
133 }
116 }); 134 });
117 std::mem::swap(&mut curr_layer, &mut next_layer); 135 std::mem::swap(&mut curr_layer, &mut next_layer);
118 } 136 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index c62086390..467516eb7 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -118,7 +118,7 @@ pub fn expand_hypothetical(
118 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; 118 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?;
119 let token_id = macro_def.0.map_id_down(token_id); 119 let token_id = macro_def.0.map_id_down(token_id);
120 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; 120 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
121 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; 121 let token = node.syntax_node().covering_element(range).into_token()?;
122 Some((node.syntax_node(), token)) 122 Some((node.syntax_node(), token))
123} 123}
124 124
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 3fa1b1d77..e388ddacc 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -22,7 +22,7 @@ use std::sync::Arc;
22 22
23use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange}; 23use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
24use syntax::{ 24use syntax::{
25 algo::{self, skip_trivia_token}, 25 algo::skip_trivia_token,
26 ast::{self, AstNode}, 26 ast::{self, AstNode},
27 Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, 27 Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
28}; 28};
@@ -335,7 +335,7 @@ impl ExpansionInfo {
335 335
336 let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 336 let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
337 337
338 let token = algo::find_covering_element(&self.expanded.value, range).into_token()?; 338 let token = self.expanded.value.covering_element(range).into_token()?;
339 339
340 Some(self.expanded.with_value(token)) 340 Some(self.expanded.with_value(token))
341 } 341 }
@@ -360,8 +360,8 @@ impl ExpansionInfo {
360 }; 360 };
361 361
362 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 362 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
363 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) 363 let token =
364 .into_token()?; 364 tt.value.covering_element(range + tt.value.text_range().start()).into_token()?;
365 Some((tt.with_value(token), origin)) 365 Some((tt.with_value(token), origin))
366 } 366 }
367} 367}
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 436c1405b..a6916af90 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -20,7 +20,7 @@ scoped-tls = "1"
20chalk-solve = { version = "0.47", default-features = false } 20chalk-solve = { version = "0.47", default-features = false }
21chalk-ir = "0.47" 21chalk-ir = "0.47"
22chalk-recursive = "0.47" 22chalk-recursive = "0.47"
23la-arena = "0.1.0" 23la-arena = { version = "0.1.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
26hir_def = { path = "../hir_def", version = "0.0.0" } 26hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index f2fc69b2f..9bf3b51b0 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -367,6 +367,13 @@ impl<'a> InferenceContext<'a> {
367 } 367 }
368 Ty::simple(TypeCtor::Never) 368 Ty::simple(TypeCtor::Never)
369 } 369 }
370 Expr::Yield { expr } => {
371 // FIXME: track yield type for coercion
372 if let Some(expr) = expr {
373 self.infer_expr(*expr, &Expectation::none());
374 }
375 Ty::simple(TypeCtor::Never)
376 }
370 Expr::RecordLit { path, fields, spread } => { 377 Expr::RecordLit { path, fields, spread } => {
371 let (ty, def_id) = self.resolve_variant(path.as_ref()); 378 let (ty, def_id) = self.resolve_variant(path.as_ref());
372 if let Some(variant) = def_id { 379 if let Some(variant) = def_id {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 055c0a79c..b35bc2bae 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -13,17 +13,16 @@ use hir::{
13 diagnostics::{Diagnostic as _, DiagnosticCode, 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, RootDatabase};
17use ide_db::RootDatabase;
18use itertools::Itertools; 17use itertools::Itertools;
19use rustc_hash::FxHashSet; 18use rustc_hash::FxHashSet;
20use syntax::{ 19use syntax::{
21 ast::{self, AstNode}, 20 ast::{self, AstNode},
22 SyntaxNode, TextRange, T, 21 SyntaxNode, TextRange,
23}; 22};
24use text_edit::TextEdit; 23use text_edit::TextEdit;
25 24
26use crate::{FileId, Label, SourceChange, SourceFileEdit}; 25use crate::{FileId, Label, SourceChange};
27 26
28use self::fixes::DiagnosticWithFix; 27use self::fixes::DiagnosticWithFix;
29 28
@@ -220,7 +219,7 @@ fn check_unnecessary_braces_in_use_statement(
220 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) 219 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
221 .with_fix(Some(Fix::new( 220 .with_fix(Some(Fix::new(
222 "Remove unnecessary braces", 221 "Remove unnecessary braces",
223 SourceFileEdit { file_id, edit }.into(), 222 SourceChange::from_text_edit(file_id, edit),
224 use_range, 223 use_range,
225 ))), 224 ))),
226 ); 225 );
@@ -233,7 +232,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
233 single_use_tree: &ast::UseTree, 232 single_use_tree: &ast::UseTree,
234) -> Option<TextEdit> { 233) -> Option<TextEdit> {
235 let use_tree_list_node = single_use_tree.syntax().parent()?; 234 let use_tree_list_node = single_use_tree.syntax().parent()?;
236 if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] { 235 if single_use_tree.path()?.segment()?.self_token().is_some() {
237 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); 236 let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
238 let end = use_tree_list_node.text_range().end(); 237 let end = use_tree_list_node.text_range().end();
239 return Some(TextEdit::delete(TextRange::new(start, end))); 238 return Some(TextEdit::delete(TextRange::new(start, end)));
@@ -265,13 +264,11 @@ mod tests {
265 .unwrap(); 264 .unwrap();
266 let fix = diagnostic.fix.unwrap(); 265 let fix = diagnostic.fix.unwrap();
267 let actual = { 266 let actual = {
268 let file_id = fix.source_change.source_file_edits.first().unwrap().file_id; 267 let file_id = *fix.source_change.source_file_edits.keys().next().unwrap();
269 let mut actual = analysis.file_text(file_id).unwrap().to_string(); 268 let mut actual = analysis.file_text(file_id).unwrap().to_string();
270 269
271 // Go from the last one to the first one, so that ranges won't be affected by previous edits. 270 for edit in fix.source_change.source_file_edits.values() {
272 // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309 271 edit.apply(&mut actual);
273 for edit in fix.source_change.source_file_edits.iter().rev() {
274 edit.edit.apply(&mut actual);
275 } 272 }
276 actual 273 actual
277 }; 274 };
@@ -616,7 +613,7 @@ fn test_fn() {
616 Fix { 613 Fix {
617 label: "Create module", 614 label: "Create module",
618 source_change: SourceChange { 615 source_change: SourceChange {
619 source_file_edits: [], 616 source_file_edits: {},
620 file_system_edits: [ 617 file_system_edits: [
621 CreateFile { 618 CreateFile {
622 dst: AnchoredPathBuf { 619 dst: AnchoredPathBuf {
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 16c6ea827..5c89e2170 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -1,8 +1,7 @@
1//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both 1//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
2//! expressions and patterns. 2//! expressions and patterns.
3 3
4use ide_db::base_db::FileId; 4use ide_db::{base_db::FileId, source_change::SourceChange};
5use ide_db::source_change::SourceFileEdit;
6use syntax::{ast, match_ast, AstNode, SyntaxNode}; 5use syntax::{ast, match_ast, AstNode, SyntaxNode};
7use text_edit::TextEdit; 6use text_edit::TextEdit;
8 7
@@ -50,7 +49,7 @@ fn check_expr_field_shorthand(
50 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( 49 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
51 Some(Fix::new( 50 Some(Fix::new(
52 "Use struct shorthand initialization", 51 "Use struct shorthand initialization",
53 SourceFileEdit { file_id, edit }.into(), 52 SourceChange::from_text_edit(file_id, edit),
54 field_range, 53 field_range,
55 )), 54 )),
56 ), 55 ),
@@ -89,7 +88,7 @@ fn check_pat_field_shorthand(
89 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( 88 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
90 Some(Fix::new( 89 Some(Fix::new(
91 "Use struct field shorthand", 90 "Use struct field shorthand",
92 SourceFileEdit { file_id, edit }.into(), 91 SourceChange::from_text_edit(file_id, edit),
93 field_range, 92 field_range,
94 )), 93 )),
95 )); 94 ));
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d7ad88ed5..e4335119b 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -8,9 +8,9 @@ use hir::{
8 }, 8 },
9 HasSource, HirDisplay, InFile, Semantics, VariantDef, 9 HasSource, HirDisplay, InFile, Semantics, VariantDef,
10}; 10};
11use ide_db::base_db::{AnchoredPathBuf, FileId};
12use ide_db::{ 11use ide_db::{
13 source_change::{FileSystemEdit, SourceFileEdit}, 12 base_db::{AnchoredPathBuf, FileId},
13 source_change::{FileSystemEdit, SourceChange},
14 RootDatabase, 14 RootDatabase,
15}; 15};
16use syntax::{ 16use syntax::{
@@ -88,7 +88,7 @@ impl DiagnosticWithFix for MissingFields {
88 }; 88 };
89 Some(Fix::new( 89 Some(Fix::new(
90 "Fill struct fields", 90 "Fill struct fields",
91 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(), 91 SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
92 sema.original_range(&field_list_parent.syntax()).range, 92 sema.original_range(&field_list_parent.syntax()).range,
93 )) 93 ))
94 } 94 }
@@ -101,8 +101,7 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
101 let tail_expr_range = tail_expr.syntax().text_range(); 101 let tail_expr_range = tail_expr.syntax().text_range();
102 let replacement = format!("{}({})", self.required, tail_expr.syntax()); 102 let replacement = format!("{}({})", self.required, tail_expr.syntax());
103 let edit = TextEdit::replace(tail_expr_range, replacement); 103 let edit = TextEdit::replace(tail_expr_range, replacement);
104 let source_change = 104 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
105 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
106 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; 105 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
107 Some(Fix::new(name, source_change, tail_expr_range)) 106 Some(Fix::new(name, source_change, tail_expr_range))
108 } 107 }
@@ -122,8 +121,7 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
122 .text_range(); 121 .text_range();
123 122
124 let edit = TextEdit::delete(semicolon); 123 let edit = TextEdit::delete(semicolon);
125 let source_change = 124 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
126 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
127 125
128 Some(Fix::new("Remove this semicolon", source_change, semicolon)) 126 Some(Fix::new("Remove this semicolon", source_change, semicolon))
129 } 127 }
@@ -204,15 +202,11 @@ fn missing_record_expr_field_fix(
204 new_field = format!(",{}", new_field); 202 new_field = format!(",{}", new_field);
205 } 203 }
206 204
207 let source_change = SourceFileEdit { 205 let source_change = SourceChange::from_text_edit(
208 file_id: def_file_id, 206 def_file_id,
209 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), 207 TextEdit::insert(last_field_syntax.text_range().end(), new_field),
210 }; 208 );
211 return Some(Fix::new( 209 return Some(Fix::new("Create field", source_change, record_expr_field.syntax().text_range()));
212 "Create field",
213 source_change.into(),
214 record_expr_field.syntax().text_range(),
215 ));
216 210
217 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { 211 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
218 match field_def_list { 212 match field_def_list {
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 4eecae697..685052e7f 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -400,24 +400,33 @@ impl TryToNav for hir::GenericParam {
400impl ToNav for hir::Local { 400impl ToNav for hir::Local {
401 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 401 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
402 let src = self.source(db); 402 let src = self.source(db);
403 let node = match &src.value { 403 let (node, focus_range) = match &src.value {
404 Either::Left(bind_pat) => { 404 Either::Left(bind_pat) => (
405 bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone()) 405 bind_pat.syntax().clone(),
406 } 406 bind_pat
407 Either::Right(it) => it.syntax().clone(), 407 .name()
408 .map(|it| src.with_value(&it.syntax().clone()).original_file_range(db).range),
409 ),
410 Either::Right(it) => (it.syntax().clone(), it.self_token().map(|it| it.text_range())),
408 }; 411 };
409 let full_range = src.with_value(&node).original_file_range(db); 412 let full_range = src.with_value(&node).original_file_range(db);
410 let name = match self.name(db) { 413 let name = match self.name(db) {
411 Some(it) => it.to_string().into(), 414 Some(it) => it.to_string().into(),
412 None => "".into(), 415 None => "".into(),
413 }; 416 };
414 let kind = if self.is_param(db) { SymbolKind::ValueParam } else { SymbolKind::Local }; 417 let kind = if self.is_self(db) {
418 SymbolKind::SelfParam
419 } else if self.is_param(db) {
420 SymbolKind::ValueParam
421 } else {
422 SymbolKind::Local
423 };
415 NavigationTarget { 424 NavigationTarget {
416 file_id: full_range.file_id, 425 file_id: full_range.file_id,
417 name, 426 name,
418 kind: Some(kind), 427 kind: Some(kind),
419 full_range: full_range.range, 428 full_range: full_range.range,
420 focus_range: None, 429 focus_range,
421 container_name: None, 430 container_name: None,
422 description: None, 431 description: None,
423 docs: None, 432 docs: None,
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 56418c960..17a540972 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -3,7 +3,7 @@ use std::iter::successors;
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::{self, find_covering_element, skip_trivia_token}, 6 algo::{self, skip_trivia_token},
7 ast::{self, AstNode, AstToken}, 7 ast::{self, AstNode, AstToken},
8 Direction, NodeOrToken, 8 Direction, NodeOrToken,
9 SyntaxKind::{self, *}, 9 SyntaxKind::{self, *},
@@ -76,7 +76,7 @@ fn try_extend_selection(
76 }; 76 };
77 return Some(leaf_range); 77 return Some(leaf_range);
78 }; 78 };
79 let node = match find_covering_element(root, range) { 79 let node = match root.covering_element(range) {
80 NodeOrToken::Token(token) => { 80 NodeOrToken::Token(token) => {
81 if token.text_range() != range { 81 if token.text_range() != range {
82 return Some(token.text_range()); 82 return Some(token.text_range());
@@ -120,7 +120,7 @@ fn extend_tokens_from_range(
120 macro_call: ast::MacroCall, 120 macro_call: ast::MacroCall,
121 original_range: TextRange, 121 original_range: TextRange,
122) -> Option<TextRange> { 122) -> Option<TextRange> {
123 let src = find_covering_element(&macro_call.syntax(), original_range); 123 let src = macro_call.syntax().covering_element(original_range);
124 let (first_token, last_token) = match src { 124 let (first_token, last_token) = match src {
125 NodeOrToken::Node(it) => (it.first_token()?, it.last_token()?), 125 NodeOrToken::Node(it) => (it.first_token()?, it.last_token()?),
126 NodeOrToken::Token(it) => (it.clone(), it), 126 NodeOrToken::Token(it) => (it.clone(), it),
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index cd4afc804..988a5668f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,7 +1,6 @@
1use either::Either; 1use either::Either;
2use hir::{HasAttrs, ModuleDef, Semantics}; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 base_db::FileId,
5 defs::{Definition, NameClass, NameRefClass}, 4 defs::{Definition, NameClass, NameRefClass},
6 symbol_index, RootDatabase, 5 symbol_index, RootDatabase,
7}; 6};
@@ -13,7 +12,7 @@ use crate::{
13 display::{ToNav, TryToNav}, 12 display::{ToNav, TryToNav},
14 doc_links::extract_definitions_from_markdown, 13 doc_links::extract_definitions_from_markdown,
15 runnables::doc_owner_to_def, 14 runnables::doc_owner_to_def,
16 FilePosition, NavigationTarget, RangeInfo, SymbolKind, 15 FilePosition, NavigationTarget, RangeInfo,
17}; 16};
18 17
19// Feature: Go to Definition 18// Feature: Go to Definition
@@ -49,19 +48,6 @@ pub(crate) fn goto_definition(
49 let nav = def.try_to_nav(sema.db)?; 48 let nav = def.try_to_nav(sema.db)?;
50 vec![nav] 49 vec![nav]
51 }, 50 },
52 ast::SelfParam(self_param) => {
53 vec![self_to_nav_target(self_param, position.file_id)?]
54 },
55 ast::PathSegment(segment) => {
56 segment.self_token()?;
57 let path = segment.parent_path();
58 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
59 return None;
60 }
61 let func = segment.syntax().ancestors().find_map(ast::Fn::cast)?;
62 let self_param = func.param_list()?.self_param()?;
63 vec![self_to_nav_target(self_param, position.file_id)?]
64 },
65 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 51 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
66 let def = name_class.referenced_or_defined(sema.db); 52 let def = name_class.referenced_or_defined(sema.db);
67 let nav = def.try_to_nav(sema.db)?; 53 let nav = def.try_to_nav(sema.db)?;
@@ -69,6 +55,11 @@ pub(crate) fn goto_definition(
69 } else { 55 } else {
70 reference_definition(&sema, Either::Left(&lt)).to_vec() 56 reference_definition(&sema, Either::Left(&lt)).to_vec()
71 }, 57 },
58 ast::SelfParam(self_param) => {
59 let def = NameClass::classify_self_param(&sema, &self_param)?.referenced_or_defined(sema.db);
60 let nav = def.try_to_nav(sema.db)?;
61 vec![nav]
62 },
72 _ => return None, 63 _ => return None,
73 } 64 }
74 }; 65 };
@@ -134,20 +125,6 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
134 } 125 }
135} 126}
136 127
137fn self_to_nav_target(self_param: ast::SelfParam, file_id: FileId) -> Option<NavigationTarget> {
138 let self_token = self_param.self_token()?;
139 Some(NavigationTarget {
140 file_id,
141 full_range: self_param.syntax().text_range(),
142 focus_range: Some(self_token.text_range()),
143 name: self_token.text().clone(),
144 kind: Some(SymbolKind::SelfParam),
145 container_name: None,
146 description: None,
147 docs: None,
148 })
149}
150
151#[derive(Debug)] 128#[derive(Debug)]
152pub(crate) enum ReferenceResult { 129pub(crate) enum ReferenceResult {
153 Exact(NavigationTarget), 130 Exact(NavigationTarget),
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 317b6f011..6022bd275 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -98,6 +98,7 @@ pub(crate) fn hover(
98 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 98 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
99 ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime) 99 ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
100 .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)), 100 .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
101 ast::SelfParam(self_param) => NameClass::classify_self_param(&sema, &self_param).and_then(|d| d.defined(sema.db)),
101 _ => None, 102 _ => None,
102 } 103 }
103 }; 104 };
@@ -134,17 +135,14 @@ pub(crate) fn hover(
134 return None; 135 return None;
135 } 136 }
136 137
137 let node = token.ancestors().find(|n| { 138 let node = token
138 ast::Expr::can_cast(n.kind()) 139 .ancestors()
139 || ast::Pat::can_cast(n.kind()) 140 .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
140 || ast::SelfParam::can_cast(n.kind())
141 })?;
142 141
143 let ty = match_ast! { 142 let ty = match_ast! {
144 match node { 143 match node {
145 ast::Expr(it) => sema.type_of_expr(&it)?, 144 ast::Expr(it) => sema.type_of_expr(&it)?,
146 ast::Pat(it) => sema.type_of_pat(&it)?, 145 ast::Pat(it) => sema.type_of_pat(&it)?,
147 ast::SelfParam(self_param) => sema.type_of_self(&self_param)?,
148 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 146 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
149 // (e.g expanding a builtin macro). So we give up here. 147 // (e.g expanding a builtin macro). So we give up here.
150 ast::MacroCall(_it) => return None, 148 ast::MacroCall(_it) => return None,
@@ -386,7 +384,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
386 return tokens.max_by_key(priority); 384 return tokens.max_by_key(priority);
387 fn priority(n: &SyntaxToken) -> usize { 385 fn priority(n: &SyntaxToken) -> usize {
388 match n.kind() { 386 match n.kind() {
389 IDENT | INT_NUMBER | LIFETIME_IDENT => 3, 387 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3,
390 T!['('] | T![')'] => 2, 388 T!['('] | T![')'] => 2,
391 kind if kind.is_trivia() => 0, 389 kind if kind.is_trivia() => 0,
392 _ => 1, 390 _ => 1,
@@ -3130,6 +3128,39 @@ fn foo<T: Foo>(t: T$0){}
3130 } 3128 }
3131 3129
3132 #[test] 3130 #[test]
3131 fn test_hover_self_has_go_to_type() {
3132 check_actions(
3133 r#"
3134struct Foo;
3135impl Foo {
3136 fn foo(&self$0) {}
3137}
3138"#,
3139 expect![[r#"
3140 [
3141 GoToType(
3142 [
3143 HoverGotoTypeData {
3144 mod_path: "test::Foo",
3145 nav: NavigationTarget {
3146 file_id: FileId(
3147 0,
3148 ),
3149 full_range: 0..11,
3150 focus_range: 7..10,
3151 name: "Foo",
3152 kind: Struct,
3153 description: "struct Foo",
3154 },
3155 },
3156 ],
3157 ),
3158 ]
3159 "#]],
3160 );
3161 }
3162
3163 #[test]
3133 fn hover_displays_normalized_crate_names() { 3164 fn hover_displays_normalized_crate_names() {
3134 check( 3165 check(
3135 r#" 3166 r#"
@@ -3193,6 +3224,7 @@ impl Foo {
3193"#, 3224"#,
3194 expect![[r#" 3225 expect![[r#"
3195 *&self* 3226 *&self*
3227
3196 ```rust 3228 ```rust
3197 &Foo 3229 &Foo
3198 ``` 3230 ```
@@ -3212,6 +3244,7 @@ impl Foo {
3212"#, 3244"#,
3213 expect![[r#" 3245 expect![[r#"
3214 *self: Arc<Foo>* 3246 *self: Arc<Foo>*
3247
3215 ```rust 3248 ```rust
3216 Arc<Foo> 3249 Arc<Foo>
3217 ``` 3250 ```
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 05380f2a1..981467c8d 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -1,7 +1,7 @@
1use assists::utils::extract_trivial_expression; 1use assists::utils::extract_trivial_expression;
2use itertools::Itertools; 2use itertools::Itertools;
3use syntax::{ 3use syntax::{
4 algo::{find_covering_element, non_trivia_sibling}, 4 algo::non_trivia_sibling,
5 ast::{self, AstNode, AstToken}, 5 ast::{self, AstNode, AstToken},
6 Direction, NodeOrToken, SourceFile, 6 Direction, NodeOrToken, SourceFile,
7 SyntaxKind::{self, USE_TREE, WHITESPACE}, 7 SyntaxKind::{self, USE_TREE, WHITESPACE},
@@ -31,7 +31,7 @@ pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
31 range 31 range
32 }; 32 };
33 33
34 let node = match find_covering_element(file.syntax(), range) { 34 let node = match file.syntax().covering_element(range) {
35 NodeOrToken::Node(node) => node, 35 NodeOrToken::Node(node) => node,
36 NodeOrToken::Token(token) => token.parent(), 36 NodeOrToken::Token(token) => token.parent(),
37 }; 37 };
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 1e03832ec..f8d69382e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -80,7 +80,7 @@ pub use crate::{
80 HlRange, 80 HlRange,
81 }, 81 },
82}; 82};
83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; 83pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
84pub use completion::{ 84pub use completion::{
85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, 85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit,
86 InsertTextFormat, 86 InsertTextFormat,
@@ -98,7 +98,7 @@ pub use ide_db::{
98 label::Label, 98 label::Label,
99 line_index::{LineCol, LineIndex}, 99 line_index::{LineCol, LineIndex},
100 search::SearchScope, 100 search::SearchScope,
101 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 101 source_change::{FileSystemEdit, SourceChange},
102 symbol_index::Query, 102 symbol_index::Query,
103 RootDatabase, 103 RootDatabase,
104}; 104};
@@ -553,7 +553,7 @@ impl Analysis {
553 let rule: ssr::SsrRule = query.parse()?; 553 let rule: ssr::SsrRule = query.parse()?;
554 let mut match_finder = ssr::MatchFinder::in_context(db, resolve_context, selections); 554 let mut match_finder = ssr::MatchFinder::in_context(db, resolve_context, selections);
555 match_finder.add_rule(rule)?; 555 match_finder.add_rule(rule)?;
556 let edits = if parse_only { Vec::new() } else { match_finder.edits() }; 556 let edits = if parse_only { Default::default() } else { match_finder.edits() };
557 Ok(SourceChange::from(edits)) 557 Ok(SourceChange::from(edits))
558 }) 558 })
559 } 559 }
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 7d4757e02..51a2f4327 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -11,6 +11,7 @@
11 11
12pub(crate) mod rename; 12pub(crate) mod rename;
13 13
14use either::Either;
14use hir::Semantics; 15use hir::Semantics;
15use ide_db::{ 16use ide_db::{
16 base_db::FileId, 17 base_db::FileId,
@@ -21,10 +22,10 @@ use ide_db::{
21use syntax::{ 22use syntax::{
22 algo::find_node_at_offset, 23 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 24 ast::{self, NameOwner},
24 match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T, 25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
25}; 26};
26 27
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; 28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
28 29
29#[derive(Debug, Clone)] 30#[derive(Debug, Clone)]
30pub struct ReferenceSearchResult { 31pub struct ReferenceSearchResult {
@@ -90,10 +91,6 @@ pub(crate) fn find_all_refs(
90 let _p = profile::span("find_all_refs"); 91 let _p = profile::span("find_all_refs");
91 let syntax = sema.parse(position.file_id).syntax().clone(); 92 let syntax = sema.parse(position.file_id).syntax().clone();
92 93
93 if let Some(res) = try_find_self_references(&syntax, position) {
94 return Some(res);
95 }
96
97 let (opt_name, search_kind) = if let Some(name) = 94 let (opt_name, search_kind) = if let Some(name) =
98 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) 95 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
99 { 96 {
@@ -122,13 +119,16 @@ pub(crate) fn find_all_refs(
122 119
123 let mut kind = ReferenceKind::Other; 120 let mut kind = ReferenceKind::Other;
124 if let Definition::Local(local) = def { 121 if let Definition::Local(local) = def {
125 if let either::Either::Left(pat) = local.source(sema.db).value { 122 match local.source(sema.db).value {
126 if matches!( 123 Either::Left(pat) => {
127 pat.syntax().parent().and_then(ast::RecordPatField::cast), 124 if matches!(
128 Some(pat_field) if pat_field.name_ref().is_none() 125 pat.syntax().parent().and_then(ast::RecordPatField::cast),
129 ) { 126 Some(pat_field) if pat_field.name_ref().is_none()
130 kind = ReferenceKind::FieldShorthandForLocal; 127 ) {
128 kind = ReferenceKind::FieldShorthandForLocal;
129 }
131 } 130 }
131 Either::Right(_) => kind = ReferenceKind::SelfParam,
132 } 132 }
133 } else if matches!( 133 } else if matches!(
134 def, 134 def,
@@ -251,79 +251,6 @@ fn get_enum_def_name_for_struct_literal_search(
251 None 251 None
252} 252}
253 253
254fn try_find_self_references(
255 syntax: &SyntaxNode,
256 position: FilePosition,
257) -> Option<RangeInfo<ReferenceSearchResult>> {
258 let FilePosition { file_id, offset } = position;
259 let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
260 let parent = self_token.parent();
261 match_ast! {
262 match parent {
263 ast::SelfParam(it) => (),
264 ast::PathSegment(segment) => {
265 segment.self_token()?;
266 let path = segment.parent_path();
267 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
268 return None;
269 }
270 },
271 _ => return None,
272 }
273 };
274 let function = parent.ancestors().find_map(ast::Fn::cast)?;
275 let self_param = function.param_list()?.self_param()?;
276 let param_self_token = self_param.self_token()?;
277
278 let declaration = Declaration {
279 nav: NavigationTarget {
280 file_id,
281 full_range: self_param.syntax().text_range(),
282 focus_range: Some(param_self_token.text_range()),
283 name: param_self_token.text().clone(),
284 kind: Some(SymbolKind::SelfParam),
285 container_name: None,
286 description: None,
287 docs: None,
288 },
289 kind: ReferenceKind::SelfKw,
290 access: Some(if self_param.mut_token().is_some() {
291 ReferenceAccess::Write
292 } else {
293 ReferenceAccess::Read
294 }),
295 };
296 let refs = function
297 .body()
298 .map(|body| {
299 body.syntax()
300 .descendants()
301 .filter_map(ast::PathExpr::cast)
302 .filter_map(|expr| {
303 let path = expr.path()?;
304 if path.qualifier().is_none() {
305 path.segment()?.self_token()
306 } else {
307 None
308 }
309 })
310 .map(|token| FileReference {
311 range: token.text_range(),
312 kind: ReferenceKind::SelfKw,
313 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
314 })
315 .collect()
316 })
317 .unwrap_or_default();
318 let mut references = UsageSearchResult::default();
319 references.references.insert(file_id, refs);
320
321 Some(RangeInfo::new(
322 param_self_token.text_range(),
323 ReferenceSearchResult { declaration, references },
324 ))
325}
326
327#[cfg(test)] 254#[cfg(test)]
328mod tests { 255mod tests {
329 use expect_test::{expect, Expect}; 256 use expect_test::{expect, Expect};
@@ -512,7 +439,7 @@ fn main() {
512 i = 5; 439 i = 5;
513}"#, 440}"#,
514 expect![[r#" 441 expect![[r#"
515 i Local FileId(0) 24..25 Other Write 442 i Local FileId(0) 20..25 24..25 Other Write
516 443
517 FileId(0) 50..51 Other Write 444 FileId(0) 50..51 Other Write
518 FileId(0) 54..55 Other Read 445 FileId(0) 54..55 Other Read
@@ -536,7 +463,7 @@ fn bar() {
536} 463}
537"#, 464"#,
538 expect![[r#" 465 expect![[r#"
539 spam Local FileId(0) 19..23 Other 466 spam Local FileId(0) 19..23 19..23 Other
540 467
541 FileId(0) 34..38 Other Read 468 FileId(0) 34..38 Other Read
542 FileId(0) 41..45 Other Read 469 FileId(0) 41..45 Other Read
@@ -551,7 +478,7 @@ fn bar() {
551fn foo(i : u32) -> u32 { i$0 } 478fn foo(i : u32) -> u32 { i$0 }
552"#, 479"#,
553 expect![[r#" 480 expect![[r#"
554 i ValueParam FileId(0) 7..8 Other 481 i ValueParam FileId(0) 7..8 7..8 Other
555 482
556 FileId(0) 25..26 Other Read 483 FileId(0) 25..26 Other Read
557 "#]], 484 "#]],
@@ -565,7 +492,7 @@ fn foo(i : u32) -> u32 { i$0 }
565fn foo(i$0 : u32) -> u32 { i } 492fn foo(i$0 : u32) -> u32 { i }
566"#, 493"#,
567 expect![[r#" 494 expect![[r#"
568 i ValueParam FileId(0) 7..8 Other 495 i ValueParam FileId(0) 7..8 7..8 Other
569 496
570 FileId(0) 25..26 Other Read 497 FileId(0) 25..26 Other Read
571 "#]], 498 "#]],
@@ -813,7 +740,7 @@ fn foo() {
813} 740}
814"#, 741"#,
815 expect![[r#" 742 expect![[r#"
816 i Local FileId(0) 23..24 Other Write 743 i Local FileId(0) 19..24 23..24 Other Write
817 744
818 FileId(0) 34..35 Other Write 745 FileId(0) 34..35 Other Write
819 FileId(0) 38..39 Other Read 746 FileId(0) 38..39 Other Read
@@ -853,7 +780,7 @@ fn foo() {
853} 780}
854"#, 781"#,
855 expect![[r#" 782 expect![[r#"
856 i Local FileId(0) 19..20 Other 783 i Local FileId(0) 19..20 19..20 Other
857 784
858 FileId(0) 26..27 Other Write 785 FileId(0) 26..27 Other Write
859 "#]], 786 "#]],
@@ -995,10 +922,10 @@ impl Foo {
995} 922}
996"#, 923"#,
997 expect![[r#" 924 expect![[r#"
998 self SelfParam FileId(0) 47..51 47..51 SelfKw Read 925 self SelfParam FileId(0) 47..51 47..51 SelfParam
999 926
1000 FileId(0) 71..75 SelfKw Read 927 FileId(0) 71..75 Other Read
1001 FileId(0) 152..156 SelfKw Read 928 FileId(0) 152..156 Other Read
1002 "#]], 929 "#]],
1003 ); 930 );
1004 } 931 }
@@ -1105,7 +1032,7 @@ fn main() {
1105} 1032}
1106"#, 1033"#,
1107 expect![[r#" 1034 expect![[r#"
1108 a Local FileId(0) 59..60 Other 1035 a Local FileId(0) 59..60 59..60 Other
1109 1036
1110 FileId(0) 80..81 Other Read 1037 FileId(0) 80..81 Other Read
1111 "#]], 1038 "#]],
@@ -1123,7 +1050,7 @@ fn main() {
1123} 1050}
1124"#, 1051"#,
1125 expect![[r#" 1052 expect![[r#"
1126 a Local FileId(0) 59..60 Other 1053 a Local FileId(0) 59..60 59..60 Other
1127 1054
1128 FileId(0) 80..81 Other Read 1055 FileId(0) 80..81 Other Read
1129 "#]], 1056 "#]],
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index c3ae568c2..9ac4af026 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -21,7 +21,7 @@ use text_edit::TextEdit;
21 21
22use crate::{ 22use crate::{
23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange, 23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
24 SourceFileEdit, TextRange, TextSize, 24 TextRange, TextSize,
25}; 25};
26 26
27type RenameResult<T> = Result<T, RenameError>; 27type RenameResult<T> = Result<T, RenameError>;
@@ -58,7 +58,7 @@ pub(crate) fn prepare_rename(
58 rename_self_to_param(&sema, position, self_token, "dummy") 58 rename_self_to_param(&sema, position, self_token, "dummy")
59 } else { 59 } else {
60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?; 60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?;
61 Ok(RangeInfo::new(range, SourceChange::from(vec![]))) 61 Ok(RangeInfo::new(range, SourceChange::default()))
62 } 62 }
63 .map(|info| RangeInfo::new(info.range, ())) 63 .map(|info| RangeInfo::new(info.range, ()))
64} 64}
@@ -176,7 +176,7 @@ fn source_edit_from_references(
176 file_id: FileId, 176 file_id: FileId,
177 references: &[FileReference], 177 references: &[FileReference],
178 new_name: &str, 178 new_name: &str,
179) -> SourceFileEdit { 179) -> (FileId, TextEdit) {
180 let mut edit = TextEdit::builder(); 180 let mut edit = TextEdit::builder();
181 for reference in references { 181 for reference in references {
182 let mut replacement_text = String::new(); 182 let mut replacement_text = String::new();
@@ -209,8 +209,7 @@ fn source_edit_from_references(
209 }; 209 };
210 edit.replace(range, replacement_text); 210 edit.replace(range, replacement_text);
211 } 211 }
212 212 (file_id, edit.finish())
213 SourceFileEdit { file_id, edit: edit.finish() }
214} 213}
215 214
216fn edit_text_range_for_record_field_expr_or_pat( 215fn edit_text_range_for_record_field_expr_or_pat(
@@ -250,8 +249,8 @@ fn rename_mod(
250 if IdentifierKind::Ident != check_identifier(new_name)? { 249 if IdentifierKind::Ident != check_identifier(new_name)? {
251 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); 250 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
252 } 251 }
253 let mut source_file_edits = Vec::new(); 252
254 let mut file_system_edits = Vec::new(); 253 let mut source_change = SourceChange::default();
255 254
256 let src = module.definition_source(sema.db); 255 let src = module.definition_source(sema.db);
257 let file_id = src.file_id.original_file(sema.db); 256 let file_id = src.file_id.original_file(sema.db);
@@ -265,7 +264,7 @@ fn rename_mod(
265 }; 264 };
266 let dst = AnchoredPathBuf { anchor: file_id, path }; 265 let dst = AnchoredPathBuf { anchor: file_id, path };
267 let move_file = FileSystemEdit::MoveFile { src: file_id, dst }; 266 let move_file = FileSystemEdit::MoveFile { src: file_id, dst };
268 file_system_edits.push(move_file); 267 source_change.push_file_system_edit(move_file);
269 } 268 }
270 ModuleSource::Module(..) => {} 269 ModuleSource::Module(..) => {}
271 } 270 }
@@ -273,20 +272,19 @@ fn rename_mod(
273 if let Some(src) = module.declaration_source(sema.db) { 272 if let Some(src) = module.declaration_source(sema.db) {
274 let file_id = src.file_id.original_file(sema.db); 273 let file_id = src.file_id.original_file(sema.db);
275 let name = src.value.name().unwrap(); 274 let name = src.value.name().unwrap();
276 let edit = SourceFileEdit { 275 source_change.insert_source_edit(
277 file_id, 276 file_id,
278 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), 277 TextEdit::replace(name.syntax().text_range(), new_name.into()),
279 }; 278 );
280 source_file_edits.push(edit);
281 } 279 }
282 280
283 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 281 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
284 let ref_edits = refs.references().iter().map(|(&file_id, references)| { 282 let ref_edits = refs.references().iter().map(|(&file_id, references)| {
285 source_edit_from_references(sema, file_id, references, new_name) 283 source_edit_from_references(sema, file_id, references, new_name)
286 }); 284 });
287 source_file_edits.extend(ref_edits); 285 source_change.extend(ref_edits);
288 286
289 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 287 Ok(RangeInfo::new(range, source_change))
290} 288}
291 289
292fn rename_to_self( 290fn rename_to_self(
@@ -335,20 +333,16 @@ fn rename_to_self(
335 333
336 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 334 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
337 335
338 let mut edits = refs 336 let mut source_change = SourceChange::default();
339 .references() 337 source_change.extend(refs.references().iter().map(|(&file_id, references)| {
340 .iter() 338 source_edit_from_references(sema, file_id, references, "self")
341 .map(|(&file_id, references)| { 339 }));
342 source_edit_from_references(sema, file_id, references, "self") 340 source_change.insert_source_edit(
343 }) 341 position.file_id,
344 .collect::<Vec<_>>(); 342 TextEdit::replace(param_range, String::from(self_param)),
345 343 );
346 edits.push(SourceFileEdit {
347 file_id: position.file_id,
348 edit: TextEdit::replace(param_range, String::from(self_param)),
349 });
350 344
351 Ok(RangeInfo::new(range, SourceChange::from(edits))) 345 Ok(RangeInfo::new(range, source_change))
352} 346}
353 347
354fn text_edit_from_self_param( 348fn text_edit_from_self_param(
@@ -402,7 +396,7 @@ fn rename_self_to_param(
402 .ok_or_else(|| format_err!("No surrounding method declaration found"))?; 396 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
403 let search_range = fn_def.syntax().text_range(); 397 let search_range = fn_def.syntax().text_range();
404 398
405 let mut edits: Vec<SourceFileEdit> = vec![]; 399 let mut source_change = SourceChange::default();
406 400
407 for (idx, _) in text.match_indices("self") { 401 for (idx, _) in text.match_indices("self") {
408 let offset: TextSize = idx.try_into().unwrap(); 402 let offset: TextSize = idx.try_into().unwrap();
@@ -416,18 +410,18 @@ fn rename_self_to_param(
416 } else { 410 } else {
417 TextEdit::replace(usage.text_range(), String::from(new_name)) 411 TextEdit::replace(usage.text_range(), String::from(new_name))
418 }; 412 };
419 edits.push(SourceFileEdit { file_id: position.file_id, edit }); 413 source_change.insert_source_edit(position.file_id, edit);
420 } 414 }
421 } 415 }
422 416
423 if edits.len() > 1 && ident_kind == IdentifierKind::Underscore { 417 if source_change.source_file_edits.len() > 1 && ident_kind == IdentifierKind::Underscore {
424 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 418 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
425 } 419 }
426 420
427 let range = ast::SelfParam::cast(self_token.parent()) 421 let range = ast::SelfParam::cast(self_token.parent())
428 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 422 .map_or(self_token.text_range(), |p| p.syntax().text_range());
429 423
430 Ok(RangeInfo::new(range, SourceChange::from(edits))) 424 Ok(RangeInfo::new(range, source_change))
431} 425}
432 426
433fn rename_reference( 427fn rename_reference(
@@ -450,7 +444,7 @@ fn rename_reference(
450 mark::hit!(rename_not_an_ident_ref); 444 mark::hit!(rename_not_an_ident_ref);
451 bail!("Invalid name `{}`: not an identifier", new_name) 445 bail!("Invalid name `{}`: not an identifier", new_name)
452 } 446 }
453 (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => { 447 (IdentifierKind::ToSelf, ReferenceKind::SelfParam) => {
454 unreachable!("rename_self_to_param should've been called instead") 448 unreachable!("rename_self_to_param should've been called instead")
455 } 449 }
456 (IdentifierKind::ToSelf, _) => { 450 (IdentifierKind::ToSelf, _) => {
@@ -464,14 +458,12 @@ fn rename_reference(
464 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 458 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident),
465 } 459 }
466 460
467 let edit = refs 461 let mut source_change = SourceChange::default();
468 .into_iter() 462 source_change.extend(refs.into_iter().map(|(file_id, references)| {
469 .map(|(file_id, references)| { 463 source_edit_from_references(sema, file_id, &references, new_name)
470 source_edit_from_references(sema, file_id, &references, new_name) 464 }));
471 })
472 .collect::<Vec<_>>();
473 465
474 Ok(RangeInfo::new(range, SourceChange::from(edit))) 466 Ok(RangeInfo::new(range, source_change))
475} 467}
476 468
477#[cfg(test)] 469#[cfg(test)]
@@ -494,8 +486,8 @@ mod tests {
494 let mut text_edit_builder = TextEdit::builder(); 486 let mut text_edit_builder = TextEdit::builder();
495 let mut file_id: Option<FileId> = None; 487 let mut file_id: Option<FileId> = None;
496 for edit in source_change.info.source_file_edits { 488 for edit in source_change.info.source_file_edits {
497 file_id = Some(edit.file_id); 489 file_id = Some(edit.0);
498 for indel in edit.edit.into_iter() { 490 for indel in edit.1.into_iter() {
499 text_edit_builder.replace(indel.delete, indel.insert); 491 text_edit_builder.replace(indel.delete, indel.insert);
500 } 492 }
501 } 493 }
@@ -895,21 +887,18 @@ mod foo$0;
895 RangeInfo { 887 RangeInfo {
896 range: 4..7, 888 range: 4..7,
897 info: SourceChange { 889 info: SourceChange {
898 source_file_edits: [ 890 source_file_edits: {
899 SourceFileEdit { 891 FileId(
900 file_id: FileId( 892 1,
901 1, 893 ): TextEdit {
902 ), 894 indels: [
903 edit: TextEdit { 895 Indel {
904 indels: [ 896 insert: "foo2",
905 Indel { 897 delete: 4..7,
906 insert: "foo2", 898 },
907 delete: 4..7, 899 ],
908 },
909 ],
910 },
911 }, 900 },
912 ], 901 },
913 file_system_edits: [ 902 file_system_edits: [
914 MoveFile { 903 MoveFile {
915 src: FileId( 904 src: FileId(
@@ -950,34 +939,28 @@ use crate::foo$0::FooContent;
950 RangeInfo { 939 RangeInfo {
951 range: 11..14, 940 range: 11..14,
952 info: SourceChange { 941 info: SourceChange {
953 source_file_edits: [ 942 source_file_edits: {
954 SourceFileEdit { 943 FileId(
955 file_id: FileId( 944 0,
956 0, 945 ): TextEdit {
957 ), 946 indels: [
958 edit: TextEdit { 947 Indel {
959 indels: [ 948 insert: "quux",
960 Indel { 949 delete: 8..11,
961 insert: "quux", 950 },
962 delete: 8..11, 951 ],
963 },
964 ],
965 },
966 }, 952 },
967 SourceFileEdit { 953 FileId(
968 file_id: FileId( 954 2,
969 2, 955 ): TextEdit {
970 ), 956 indels: [
971 edit: TextEdit { 957 Indel {
972 indels: [ 958 insert: "quux",
973 Indel { 959 delete: 11..14,
974 insert: "quux", 960 },
975 delete: 11..14, 961 ],
976 },
977 ],
978 },
979 }, 962 },
980 ], 963 },
981 file_system_edits: [ 964 file_system_edits: [
982 MoveFile { 965 MoveFile {
983 src: FileId( 966 src: FileId(
@@ -1012,21 +995,18 @@ mod fo$0o;
1012 RangeInfo { 995 RangeInfo {
1013 range: 4..7, 996 range: 4..7,
1014 info: SourceChange { 997 info: SourceChange {
1015 source_file_edits: [ 998 source_file_edits: {
1016 SourceFileEdit { 999 FileId(
1017 file_id: FileId( 1000 0,
1018 0, 1001 ): TextEdit {
1019 ), 1002 indels: [
1020 edit: TextEdit { 1003 Indel {
1021 indels: [ 1004 insert: "foo2",
1022 Indel { 1005 delete: 4..7,
1023 insert: "foo2", 1006 },
1024 delete: 4..7, 1007 ],
1025 },
1026 ],
1027 },
1028 }, 1008 },
1029 ], 1009 },
1030 file_system_edits: [ 1010 file_system_edits: [
1031 MoveFile { 1011 MoveFile {
1032 src: FileId( 1012 src: FileId(
@@ -1062,21 +1042,18 @@ mod outer { mod fo$0o; }
1062 RangeInfo { 1042 RangeInfo {
1063 range: 16..19, 1043 range: 16..19,
1064 info: SourceChange { 1044 info: SourceChange {
1065 source_file_edits: [ 1045 source_file_edits: {
1066 SourceFileEdit { 1046 FileId(
1067 file_id: FileId( 1047 0,
1068 0, 1048 ): TextEdit {
1069 ), 1049 indels: [
1070 edit: TextEdit { 1050 Indel {
1071 indels: [ 1051 insert: "bar",
1072 Indel { 1052 delete: 16..19,
1073 insert: "bar", 1053 },
1074 delete: 16..19, 1054 ],
1075 },
1076 ],
1077 },
1078 }, 1055 },
1079 ], 1056 },
1080 file_system_edits: [ 1057 file_system_edits: [
1081 MoveFile { 1058 MoveFile {
1082 src: FileId( 1059 src: FileId(
@@ -1135,34 +1112,28 @@ pub mod foo$0;
1135 RangeInfo { 1112 RangeInfo {
1136 range: 8..11, 1113 range: 8..11,
1137 info: SourceChange { 1114 info: SourceChange {
1138 source_file_edits: [ 1115 source_file_edits: {
1139 SourceFileEdit { 1116 FileId(
1140 file_id: FileId( 1117 0,
1141 1, 1118 ): TextEdit {
1142 ), 1119 indels: [
1143 edit: TextEdit { 1120 Indel {
1144 indels: [ 1121 insert: "foo2",
1145 Indel { 1122 delete: 27..30,
1146 insert: "foo2", 1123 },
1147 delete: 8..11, 1124 ],
1148 },
1149 ],
1150 },
1151 }, 1125 },
1152 SourceFileEdit { 1126 FileId(
1153 file_id: FileId( 1127 1,
1154 0, 1128 ): TextEdit {
1155 ), 1129 indels: [
1156 edit: TextEdit { 1130 Indel {
1157 indels: [ 1131 insert: "foo2",
1158 Indel { 1132 delete: 8..11,
1159 insert: "foo2", 1133 },
1160 delete: 27..30, 1134 ],
1161 },
1162 ],
1163 },
1164 }, 1135 },
1165 ], 1136 },
1166 file_system_edits: [ 1137 file_system_edits: [
1167 MoveFile { 1138 MoveFile {
1168 src: FileId( 1139 src: FileId(
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 34bae49a8..87578e70a 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -226,35 +226,16 @@ pub(super) fn element(
226 T![unsafe] => h | HlMod::Unsafe, 226 T![unsafe] => h | HlMod::Unsafe,
227 T![true] | T![false] => HlTag::BoolLiteral.into(), 227 T![true] | T![false] => HlTag::BoolLiteral.into(),
228 T![self] => { 228 T![self] => {
229 let self_param_is_mut = element 229 let self_param = element.parent().and_then(ast::SelfParam::cast);
230 .parent() 230 if let Some(NameClass::Definition(def)) = self_param
231 .and_then(ast::SelfParam::cast) 231 .and_then(|self_param| NameClass::classify_self_param(sema, &self_param))
232 .and_then(|p| p.mut_token())
233 .is_some();
234 let self_path = &element
235 .parent()
236 .as_ref()
237 .and_then(SyntaxNode::parent)
238 .and_then(ast::Path::cast)
239 .and_then(|p| sema.resolve_path(&p));
240 let mut h = HlTag::Symbol(SymbolKind::SelfParam).into();
241 if self_param_is_mut
242 || matches!(self_path,
243 Some(hir::PathResolution::Local(local))
244 if local.is_self(db)
245 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
246 )
247 { 232 {
248 h |= HlMod::Mutable 233 highlight_def(db, def) | HlMod::Definition
249 } 234 } else if element.ancestors().any(|it| it.kind() == USE_TREE) {
250 235 HlTag::Symbol(SymbolKind::SelfParam).into()
251 if let Some(hir::PathResolution::Local(local)) = self_path { 236 } else {
252 if is_consumed_lvalue(element, &local, db) { 237 return None;
253 h |= HlMod::Consuming;
254 }
255 } 238 }
256
257 h
258 } 239 }
259 T![ref] => element 240 T![ref] => element
260 .parent() 241 .parent()
@@ -345,7 +326,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
345 hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), 326 hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam),
346 }, 327 },
347 Definition::Local(local) => { 328 Definition::Local(local) => {
348 let tag = if local.is_param(db) { 329 let tag = if local.is_self(db) {
330 HlTag::Symbol(SymbolKind::SelfParam)
331 } else if local.is_param(db) {
349 HlTag::Symbol(SymbolKind::ValueParam) 332 HlTag::Symbol(SymbolKind::ValueParam)
350 } else { 333 } else {
351 HlTag::Symbol(SymbolKind::Local) 334 HlTag::Symbol(SymbolKind::Local)
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index e36e6fc3f..d421a7803 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -42,16 +42,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42 42
43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span> 43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
46<span class="brace">}</span> 46<span class="brace">}</span>
47 47
48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span> 48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51<span class="brace">}</span> 51<span class="brace">}</span>
52 52
53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span> 53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56<span class="brace">}</span> 56<span class="brace">}</span>
57 </code></pre> \ No newline at end of file 57 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 6dadda1c1..5e877df88 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -93,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
93 <span class="comment documentation">/// ```sh</span> 93 <span class="comment documentation">/// ```sh</span>
94 <span class="comment documentation">/// echo 1</span> 94 <span class="comment documentation">/// echo 1</span>
95 <span class="comment documentation">/// ```</span> 95 <span class="comment documentation">/// ```</span>
96 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span> 96 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
97 <span class="bool_literal">true</span> 97 <span class="bool_literal">true</span>
98 <span class="brace">}</span> 98 <span class="brace">}</span>
99<span class="brace">}</span> 99<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 9d4d6d4a0..036cb6c11 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -46,7 +46,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span> 46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
47 47
48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span> 48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50<span class="brace">}</span> 50<span class="brace">}</span>
51 51
52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span> 52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
@@ -61,11 +61,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
61<span class="brace">}</span> 61<span class="brace">}</span>
62 62
63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> 63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> 64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
65<span class="brace">}</span> 65<span class="brace">}</span>
66 66
67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> 67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
69<span class="brace">}</span> 69<span class="brace">}</span>
70 70
71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 6b7447c46..237149566 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -66,25 +66,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
66<span class="brace">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> 68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span> 69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
70<span class="brace">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> 72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
75 <span class="brace">}</span> 75 <span class="brace">}</span>
76<span class="brace">}</span> 76<span class="brace">}</span>
77 77
78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> 78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span> 80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span>
81 <span class="brace">}</span> 81 <span class="brace">}</span>
82 82
83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
85 <span class="brace">}</span> 85 <span class="brace">}</span>
86 86
87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
89 <span class="brace">}</span> 89 <span class="brace">}</span>
90<span class="brace">}</span> 90<span class="brace">}</span>
@@ -95,15 +95,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
95<span class="brace">}</span> 95<span class="brace">}</span>
96 96
97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span> 97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span>
98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span> 99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
100 <span class="brace">}</span> 100 <span class="brace">}</span>
101 101
102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
104 <span class="brace">}</span> 104 <span class="brace">}</span>
105 105
106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
108 <span class="brace">}</span> 108 <span class="brace">}</span>
109<span class="brace">}</span> 109<span class="brace">}</span>
@@ -213,7 +213,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
213<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> 213<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
214 214
215<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span> 215<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
216 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span> 216 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
217 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span> 217 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
218 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> 218 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
219 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span> 219 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 1f26f8043..1d4bac7ad 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -1,7 +1,7 @@
1use ide_db::base_db::{FileId, SourceDatabase}; 1use ide_db::base_db::{FileId, SourceDatabase};
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::{ 3use syntax::{
4 algo, AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize, 4 AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
5}; 5};
6 6
7// Feature: Show Syntax Tree 7// Feature: Show Syntax Tree
@@ -21,7 +21,7 @@ pub(crate) fn syntax_tree(
21) -> String { 21) -> String {
22 let parse = db.parse(file_id); 22 let parse = db.parse(file_id);
23 if let Some(text_range) = text_range { 23 if let Some(text_range) = text_range {
24 let node = match algo::find_covering_element(parse.tree().syntax(), text_range) { 24 let node = match parse.tree().syntax().covering_element(text_range) {
25 NodeOrToken::Node(node) => node, 25 NodeOrToken::Node(node) => node,
26 NodeOrToken::Token(token) => { 26 NodeOrToken::Token(token) => {
27 if let Some(tree) = syntax_tree_for_string(&token, text_range) { 27 if let Some(tree) = syntax_tree_for_string(&token, text_range) {
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 88c905003..e3c3aebac 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -15,8 +15,10 @@
15 15
16mod on_enter; 16mod on_enter;
17 17
18use ide_db::base_db::{FilePosition, SourceDatabase}; 18use ide_db::{
19use ide_db::{source_change::SourceFileEdit, RootDatabase}; 19 base_db::{FilePosition, SourceDatabase},
20 RootDatabase,
21};
20use syntax::{ 22use syntax::{
21 algo::find_node_at_offset, 23 algo::find_node_at_offset,
22 ast::{self, edit::IndentLevel, AstToken}, 24 ast::{self, edit::IndentLevel, AstToken},
@@ -56,7 +58,7 @@ pub(crate) fn on_char_typed(
56 let file = &db.parse(position.file_id).tree(); 58 let file = &db.parse(position.file_id).tree();
57 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 59 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
58 let edit = on_char_typed_inner(file, position.offset, char_typed)?; 60 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
59 Some(SourceFileEdit { file_id: position.file_id, edit }.into()) 61 Some(SourceChange::from_text_edit(position.file_id, edit))
60} 62}
61 63
62fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 64fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d68fe42b0..231e886a9 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -10,7 +10,7 @@ use hir::{
10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility, 10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode, PathSegmentKind},
14 match_ast, SyntaxKind, SyntaxNode, 14 match_ast, SyntaxKind, SyntaxNode,
15}; 15};
16 16
@@ -117,6 +117,13 @@ impl NameClass {
117 } 117 }
118 } 118 }
119 119
120 pub fn classify_self_param(
121 sema: &Semantics<RootDatabase>,
122 self_param: &ast::SelfParam,
123 ) -> Option<NameClass> {
124 sema.to_def(self_param).map(Definition::Local).map(NameClass::Definition)
125 }
126
120 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 127 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
121 let _p = profile::span("classify_name"); 128 let _p = profile::span("classify_name");
122 129
@@ -135,24 +142,26 @@ impl NameClass {
135 let path = use_tree.path()?; 142 let path = use_tree.path()?;
136 let path_segment = path.segment()?; 143 let path_segment = path.segment()?;
137 let name_ref_class = path_segment 144 let name_ref_class = path_segment
138 .name_ref() 145 .kind()
139 // The rename might be from a `self` token, so fallback to the name higher 146 .and_then(|kind| {
140 // in the use tree. 147 match kind {
141 .or_else(||{ 148 // The rename might be from a `self` token, so fallback to the name higher
142 if path_segment.self_token().is_none() { 149 // in the use tree.
143 return None; 150 PathSegmentKind::SelfKw => {
151 let use_tree = use_tree
152 .syntax()
153 .parent()
154 .as_ref()
155 // Skip over UseTreeList
156 .and_then(SyntaxNode::parent)
157 .and_then(ast::UseTree::cast)?;
158 let path = use_tree.path()?;
159 let path_segment = path.segment()?;
160 path_segment.name_ref()
161 },
162 PathSegmentKind::Name(name_ref) => Some(name_ref),
163 _ => return None,
144 } 164 }
145
146 let use_tree = use_tree
147 .syntax()
148 .parent()
149 .as_ref()
150 // Skip over UseTreeList
151 .and_then(SyntaxNode::parent)
152 .and_then(ast::UseTree::cast)?;
153 let path = use_tree.path()?;
154 let path_segment = path.segment()?;
155 path_segment.name_ref()
156 }) 165 })
157 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?; 166 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
158 167
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index c6763ae36..0dcc4dd29 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,5 +1,6 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets;
3 4
4use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
5use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 4ce82c1ba..edc3da318 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,17 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
4use ide_db::{imports_locator, RootDatabase};
5use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
6use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode, SyntaxNode};
7 6
8use crate::assist_config::InsertUseConfig; 7use crate::{imports_locator, RootDatabase};
8
9use super::insert_use::InsertUseConfig;
9 10
10#[derive(Debug)] 11#[derive(Debug)]
11pub(crate) enum ImportCandidate { 12pub enum ImportCandidate {
12 /// Simple name like 'HashMap' 13 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
13 UnqualifiedName(PathImportCandidate), 14 Path(PathImportCandidate),
14 /// First part of the qualified name.
15 /// For 'std::collections::HashMap', that will be 'std'.
16 QualifierStart(PathImportCandidate),
17 /// A trait associated function (with no self parameter) or associated constant. 15 /// A trait associated function (with no self parameter) or associated constant.
18 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 16 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
19 /// and `name` is the `test_function` 17 /// and `name` is the `test_function`
@@ -25,25 +23,26 @@ pub(crate) enum ImportCandidate {
25} 23}
26 24
27#[derive(Debug)] 25#[derive(Debug)]
28pub(crate) struct TraitImportCandidate { 26pub struct TraitImportCandidate {
29 pub(crate) ty: hir::Type, 27 pub ty: hir::Type,
30 pub(crate) name: ast::NameRef, 28 pub name: ast::NameRef,
31} 29}
32 30
33#[derive(Debug)] 31#[derive(Debug)]
34pub(crate) struct PathImportCandidate { 32pub struct PathImportCandidate {
35 pub(crate) name: ast::NameRef, 33 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef,
36} 35}
37 36
38#[derive(Debug)] 37#[derive(Debug)]
39pub(crate) struct ImportAssets { 38pub struct ImportAssets {
40 import_candidate: ImportCandidate, 39 import_candidate: ImportCandidate,
41 module_with_name_to_import: hir::Module, 40 module_with_name_to_import: hir::Module,
42 syntax_under_caret: SyntaxNode, 41 syntax_under_caret: SyntaxNode,
43} 42}
44 43
45impl ImportAssets { 44impl ImportAssets {
46 pub(crate) fn for_method_call( 45 pub fn for_method_call(
47 method_call: ast::MethodCallExpr, 46 method_call: ast::MethodCallExpr,
48 sema: &Semantics<RootDatabase>, 47 sema: &Semantics<RootDatabase>,
49 ) -> Option<Self> { 48 ) -> Option<Self> {
@@ -56,7 +55,7 @@ impl ImportAssets {
56 }) 55 })
57 } 56 }
58 57
59 pub(crate) fn for_regular_path( 58 pub fn for_regular_path(
60 path_under_caret: ast::Path, 59 path_under_caret: ast::Path,
61 sema: &Semantics<RootDatabase>, 60 sema: &Semantics<RootDatabase>,
62 ) -> Option<Self> { 61 ) -> Option<Self> {
@@ -73,24 +72,23 @@ impl ImportAssets {
73 }) 72 })
74 } 73 }
75 74
76 pub(crate) fn syntax_under_caret(&self) -> &SyntaxNode { 75 pub fn syntax_under_caret(&self) -> &SyntaxNode {
77 &self.syntax_under_caret 76 &self.syntax_under_caret
78 } 77 }
79 78
80 pub(crate) fn import_candidate(&self) -> &ImportCandidate { 79 pub fn import_candidate(&self) -> &ImportCandidate {
81 &self.import_candidate 80 &self.import_candidate
82 } 81 }
83 82
84 fn get_search_query(&self) -> &str { 83 fn get_search_query(&self) -> &str {
85 match &self.import_candidate { 84 match &self.import_candidate {
86 ImportCandidate::UnqualifiedName(candidate) 85 ImportCandidate::Path(candidate) => candidate.name.text(),
87 | ImportCandidate::QualifierStart(candidate) => candidate.name.text(),
88 ImportCandidate::TraitAssocItem(candidate) 86 ImportCandidate::TraitAssocItem(candidate)
89 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(),
90 } 88 }
91 } 89 }
92 90
93 pub(crate) fn search_for_imports( 91 pub fn search_for_imports(
94 &self, 92 &self,
95 sema: &Semantics<RootDatabase>, 93 sema: &Semantics<RootDatabase>,
96 config: &InsertUseConfig, 94 config: &InsertUseConfig,
@@ -101,7 +99,7 @@ impl ImportAssets {
101 99
102 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 100 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
103 #[allow(dead_code)] 101 #[allow(dead_code)]
104 pub(crate) fn search_for_relative_paths( 102 pub fn search_for_relative_paths(
105 &self, 103 &self,
106 sema: &Semantics<RootDatabase>, 104 sema: &Semantics<RootDatabase>,
107 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
@@ -253,10 +251,14 @@ impl ImportCandidate {
253 _ => return None, 251 _ => return None,
254 } 252 }
255 } else { 253 } else {
256 ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start }) 254 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier),
256 name: qualifier_start,
257 })
257 } 258 }
258 } else { 259 } else {
259 ImportCandidate::UnqualifiedName(PathImportCandidate { 260 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None,
260 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?,
261 }) 263 })
262 }; 264 };
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index d6b498be3..877d4f1c7 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -15,6 +15,12 @@ use syntax::{
15}; 15};
16use test_utils::mark; 16use test_utils::mark;
17 17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind,
22}
23
18#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
19pub enum ImportScope { 25pub enum ImportScope {
20 File(ast::SourceFile), 26 File(ast::SourceFile),
@@ -97,7 +103,7 @@ pub fn insert_use<'a>(
97) -> SyntaxRewriter<'a> { 103) -> SyntaxRewriter<'a> {
98 let _p = profile::span("insert_use"); 104 let _p = profile::span("insert_use");
99 let mut rewriter = SyntaxRewriter::default(); 105 let mut rewriter = SyntaxRewriter::default();
100 let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); 106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
101 // merge into existing imports if possible 107 // merge into existing imports if possible
102 if let Some(mb) = merge { 108 if let Some(mb) = merge {
103 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { 109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
@@ -444,8 +450,14 @@ fn use_tree_path_cmp(a: &ast::Path, a_has_tl: bool, b: &ast::Path, b_has_tl: boo
444} 450}
445 451
446fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { 452fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
447 let a = a.name_ref(); 453 let a = a.kind().and_then(|kind| match kind {
448 let b = b.name_ref(); 454 PathSegmentKind::Name(name_ref) => Some(name_ref),
455 _ => None,
456 });
457 let b = b.kind().and_then(|kind| match kind {
458 PathSegmentKind::Name(name_ref) => Some(name_ref),
459 _ => None,
460 });
449 a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) 461 a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text))
450} 462}
451 463
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index e9f23adf8..d111fba92 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -12,6 +12,8 @@ use crate::{
12use either::Either; 12use either::Either;
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14 14
15const QUERY_SEARCH_LIMIT: usize = 40;
16
15pub fn find_exact_imports<'a>( 17pub fn find_exact_imports<'a>(
16 sema: &Semantics<'a, RootDatabase>, 18 sema: &Semantics<'a, RootDatabase>,
17 krate: Crate, 19 krate: Crate,
@@ -24,11 +26,11 @@ pub fn find_exact_imports<'a>(
24 { 26 {
25 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 27 let mut local_query = symbol_index::Query::new(name_to_import.clone());
26 local_query.exact(); 28 local_query.exact();
27 local_query.limit(40); 29 local_query.limit(QUERY_SEARCH_LIMIT);
28 local_query 30 local_query
29 }, 31 },
30 import_map::Query::new(name_to_import) 32 import_map::Query::new(name_to_import)
31 .limit(40) 33 .limit(QUERY_SEARCH_LIMIT)
32 .name_only() 34 .name_only()
33 .search_mode(import_map::SearchMode::Equals) 35 .search_mode(import_map::SearchMode::Equals)
34 .case_sensitive(), 36 .case_sensitive(),
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index b5fa46642..0ecb13a64 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -65,7 +65,7 @@ pub enum ReferenceKind {
65 FieldShorthandForLocal, 65 FieldShorthandForLocal,
66 StructLiteral, 66 StructLiteral,
67 RecordFieldExprOrPat, 67 RecordFieldExprOrPat,
68 SelfKw, 68 SelfParam,
69 EnumLiteral, 69 EnumLiteral,
70 Lifetime, 70 Lifetime,
71 Other, 71 Other,
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index 10c0abdac..b1f87731b 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -3,12 +3,19 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use std::{
7 collections::hash_map::Entry,
8 iter::{self, FromIterator},
9};
10
6use base_db::{AnchoredPathBuf, FileId}; 11use base_db::{AnchoredPathBuf, FileId};
12use rustc_hash::FxHashMap;
13use stdx::assert_never;
7use text_edit::TextEdit; 14use text_edit::TextEdit;
8 15
9#[derive(Default, Debug, Clone)] 16#[derive(Default, Debug, Clone)]
10pub struct SourceChange { 17pub struct SourceChange {
11 pub source_file_edits: Vec<SourceFileEdit>, 18 pub source_file_edits: FxHashMap<FileId, TextEdit>,
12 pub file_system_edits: Vec<FileSystemEdit>, 19 pub file_system_edits: Vec<FileSystemEdit>,
13 pub is_snippet: bool, 20 pub is_snippet: bool,
14} 21}
@@ -17,27 +24,50 @@ impl SourceChange {
17 /// Creates a new SourceChange with the given label 24 /// Creates a new SourceChange with the given label
18 /// from the edits. 25 /// from the edits.
19 pub fn from_edits( 26 pub fn from_edits(
20 source_file_edits: Vec<SourceFileEdit>, 27 source_file_edits: FxHashMap<FileId, TextEdit>,
21 file_system_edits: Vec<FileSystemEdit>, 28 file_system_edits: Vec<FileSystemEdit>,
22 ) -> Self { 29 ) -> Self {
23 SourceChange { source_file_edits, file_system_edits, is_snippet: false } 30 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
24 } 31 }
25}
26 32
27#[derive(Debug, Clone)] 33 pub fn from_text_edit(file_id: FileId, edit: TextEdit) -> Self {
28pub struct SourceFileEdit { 34 SourceChange {
29 pub file_id: FileId, 35 source_file_edits: FxHashMap::from_iter(iter::once((file_id, edit))),
30 pub edit: TextEdit, 36 ..Default::default()
37 }
38 }
39
40 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
41 match self.source_file_edits.entry(file_id) {
42 Entry::Occupied(mut entry) => {
43 assert_never!(
44 entry.get_mut().union(edit).is_err(),
45 "overlapping edits for same file"
46 );
47 }
48 Entry::Vacant(entry) => {
49 entry.insert(edit);
50 }
51 }
52 }
53
54 pub fn push_file_system_edit(&mut self, edit: FileSystemEdit) {
55 self.file_system_edits.push(edit);
56 }
57
58 pub fn get_source_edit(&self, file_id: FileId) -> Option<&TextEdit> {
59 self.source_file_edits.get(&file_id)
60 }
31} 61}
32 62
33impl From<SourceFileEdit> for SourceChange { 63impl Extend<(FileId, TextEdit)> for SourceChange {
34 fn from(edit: SourceFileEdit) -> SourceChange { 64 fn extend<T: IntoIterator<Item = (FileId, TextEdit)>>(&mut self, iter: T) {
35 vec![edit].into() 65 iter.into_iter().for_each(|(file_id, edit)| self.insert_source_edit(file_id, edit));
36 } 66 }
37} 67}
38 68
39impl From<Vec<SourceFileEdit>> for SourceChange { 69impl From<FxHashMap<FileId, TextEdit>> for SourceChange {
40 fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange { 70 fn from(source_file_edits: FxHashMap<FileId, TextEdit>) -> SourceChange {
41 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } 71 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
42 } 72 }
43} 73}
@@ -51,7 +81,7 @@ pub enum FileSystemEdit {
51impl From<FileSystemEdit> for SourceChange { 81impl From<FileSystemEdit> for SourceChange {
52 fn from(edit: FileSystemEdit) -> SourceChange { 82 fn from(edit: FileSystemEdit) -> SourceChange {
53 SourceChange { 83 SourceChange {
54 source_file_edits: Vec::new(), 84 source_file_edits: Default::default(),
55 file_system_edits: vec![edit], 85 file_system_edits: vec![edit],
56 is_snippet: false, 86 is_snippet: false,
57 } 87 }
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index d61950b96..093a9890d 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -50,6 +50,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
50 T![match], 50 T![match],
51 T![unsafe], 51 T![unsafe],
52 T![return], 52 T![return],
53 T![yield],
53 T![break], 54 T![break],
54 T![continue], 55 T![continue],
55 T![async], 56 T![async],
@@ -142,6 +143,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
142 block_expr_unchecked(p) 143 block_expr_unchecked(p)
143 } 144 }
144 T![return] => return_expr(p), 145 T![return] => return_expr(p),
146 T![yield] => yield_expr(p),
145 T![continue] => continue_expr(p), 147 T![continue] => continue_expr(p),
146 T![break] => break_expr(p, r), 148 T![break] => break_expr(p, r),
147 _ => { 149 _ => {
@@ -508,6 +510,20 @@ fn return_expr(p: &mut Parser) -> CompletedMarker {
508 } 510 }
509 m.complete(p, RETURN_EXPR) 511 m.complete(p, RETURN_EXPR)
510} 512}
513// test yield_expr
514// fn foo() {
515// yield;
516// yield 1;
517// }
518fn yield_expr(p: &mut Parser) -> CompletedMarker {
519 assert!(p.at(T![yield]));
520 let m = p.start();
521 p.bump(T![yield]);
522 if p.at_ts(EXPR_FIRST) {
523 expr(p);
524 }
525 m.complete(p, YIELD_EXPR)
526}
511 527
512// test continue_expr 528// test continue_expr
513// fn foo() { 529// fn foo() {
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 5d297e2d6..b10f48fe1 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -82,7 +82,11 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
82 } 82 }
83 // test crate_path 83 // test crate_path
84 // use crate::foo; 84 // use crate::foo;
85 T![self] | T![super] | T![crate] => p.bump_any(), 85 T![self] | T![super] | T![crate] => {
86 let m = p.start();
87 p.bump_any();
88 m.complete(p, NAME_REF);
89 }
86 _ => { 90 _ => {
87 p.err_recover("expected identifier", items::ITEM_RECOVERY_SET); 91 p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
88 if empty { 92 if empty {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index f69e71bdb..7d53cc4cd 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -101,6 +101,7 @@ pub enum SyntaxKind {
101 USE_KW, 101 USE_KW,
102 WHERE_KW, 102 WHERE_KW,
103 WHILE_KW, 103 WHILE_KW,
104 YIELD_KW,
104 AUTO_KW, 105 AUTO_KW,
105 DEFAULT_KW, 106 DEFAULT_KW,
106 EXISTENTIAL_KW, 107 EXISTENTIAL_KW,
@@ -186,6 +187,7 @@ pub enum SyntaxKind {
186 LABEL, 187 LABEL,
187 BLOCK_EXPR, 188 BLOCK_EXPR,
188 RETURN_EXPR, 189 RETURN_EXPR,
190 YIELD_EXPR,
189 MATCH_EXPR, 191 MATCH_EXPR,
190 MATCH_ARM_LIST, 192 MATCH_ARM_LIST,
191 MATCH_ARM, 193 MATCH_ARM,
@@ -263,7 +265,8 @@ impl SyntaxKind {
263 | IMPL_KW | IN_KW | LET_KW | LOOP_KW | MACRO_KW | MATCH_KW | MOD_KW | MOVE_KW 265 | IMPL_KW | IN_KW | LET_KW | LOOP_KW | MACRO_KW | MATCH_KW | MOD_KW | MOVE_KW
264 | MUT_KW | PUB_KW | REF_KW | RETURN_KW | SELF_KW | STATIC_KW | STRUCT_KW | SUPER_KW 266 | MUT_KW | PUB_KW | REF_KW | RETURN_KW | SELF_KW | STATIC_KW | STRUCT_KW | SUPER_KW
265 | TRAIT_KW | TRUE_KW | TRY_KW | TYPE_KW | UNSAFE_KW | USE_KW | WHERE_KW | WHILE_KW 267 | TRAIT_KW | TRUE_KW | TRY_KW | TYPE_KW | UNSAFE_KW | USE_KW | WHERE_KW | WHILE_KW
266 | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW | MACRO_RULES_KW => true, 268 | YIELD_KW | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW
269 | MACRO_RULES_KW => true,
267 _ => false, 270 _ => false,
268 } 271 }
269 } 272 }
@@ -326,6 +329,7 @@ impl SyntaxKind {
326 "use" => USE_KW, 329 "use" => USE_KW,
327 "where" => WHERE_KW, 330 "where" => WHERE_KW,
328 "while" => WHILE_KW, 331 "while" => WHILE_KW,
332 "yield" => YIELD_KW,
329 _ => return None, 333 _ => return None,
330 }; 334 };
331 Some(kw) 335 Some(kw)
@@ -366,4 +370,4 @@ impl SyntaxKind {
366 } 370 }
367} 371}
368#[macro_export] 372#[macro_export]
369macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } 373macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 096233a09..1b8f610bb 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13once_cell = "1.3.1" 13once_cell = "1.3.1"
14cfg-if = "1" 14cfg-if = "1"
15libc = "0.2.73" 15libc = "0.2.73"
16la-arena = "0.1.0" 16la-arena = { version = "0.1.0", path = "../../lib/arena" }
17 17
18[target.'cfg(target_os = "linux")'.dependencies] 18[target.'cfg(target_os = "linux")'.dependencies]
19perf-event = "0.4" 19perf-event = "0.4"
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index 51e7a7070..0747fee96 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -17,7 +17,7 @@ serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19itertools = "0.10.0" 19itertools = "0.10.0"
20la-arena = "0.1.0" 20la-arena = { version = "0.1.0", path = "../../lib/arena" }
21 21
22cfg = { path = "../cfg", version = "0.0.0" } 22cfg = { path = "../cfg", version = "0.0.0" }
23base_db = { path = "../base_db", version = "0.0.0" } 23base_db = { path = "../base_db", version = "0.0.0" }
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7d3fda7a8..a02c8327f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -3,6 +3,7 @@
3use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant}; 3use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
4 4
5use anyhow::{bail, format_err, Result}; 5use anyhow::{bail, format_err, Result};
6use hir::PrefixKind;
6use ide::{ 7use ide::{
7 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol, 8 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
8}; 9};
@@ -11,7 +12,7 @@ use ide_db::{
11 salsa::{Database, Durability}, 12 salsa::{Database, Durability},
12 FileId, 13 FileId,
13 }, 14 },
14 helpers::SnippetCap, 15 helpers::{insert_use::InsertUseConfig, SnippetCap},
15}; 16};
16use vfs::AbsPathBuf; 17use vfs::AbsPathBuf;
17 18
@@ -96,7 +97,7 @@ impl BenchCmd {
96 add_call_parenthesis: true, 97 add_call_parenthesis: true,
97 add_call_argument_snippets: true, 98 add_call_argument_snippets: true,
98 snippet_cap: SnippetCap::new(true), 99 snippet_cap: SnippetCap::new(true),
99 merge: None, 100 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
100 }; 101 };
101 let res = do_work(&mut host, file_id, |analysis| { 102 let res = do_work(&mut host, file_id, |analysis| {
102 analysis.completions(&options, file_position) 103 analysis.completions(&options, file_position)
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index a06631dac..bbb550ec9 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -12,10 +12,10 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
12 match_finder.add_rule(rule)?; 12 match_finder.add_rule(rule)?;
13 } 13 }
14 let edits = match_finder.edits(); 14 let edits = match_finder.edits();
15 for edit in edits { 15 for (file_id, edit) in edits {
16 if let Some(path) = vfs.file_path(edit.file_id).as_path() { 16 if let Some(path) = vfs.file_path(file_id).as_path() {
17 let mut contents = db.file_text(edit.file_id).to_string(); 17 let mut contents = db.file_text(file_id).to_string();
18 edit.edit.apply(&mut contents); 18 edit.apply(&mut contents);
19 std::fs::write(path, contents)?; 19 std::fs::write(path, contents)?;
20 } 20 }
21 } 21 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 27b92a5a9..ce9655818 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,11 +11,11 @@ use std::{convert::TryFrom, ffi::OsString, iter, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
14use ide::{ 14use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, 15use ide_db::helpers::{
16 InsertUseConfig, 16 insert_use::{InsertUseConfig, MergeBehavior},
17 SnippetCap,
17}; 18};
18use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap};
19use itertools::Itertools; 19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 20use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
@@ -542,11 +542,18 @@ impl Config {
542 max_length: self.data.inlayHints_maxLength, 542 max_length: self.data.inlayHints_maxLength,
543 } 543 }
544 } 544 }
545 fn merge_behavior(&self) -> Option<MergeBehavior> { 545 fn insert_use_config(&self) -> InsertUseConfig {
546 match self.data.assist_importMergeBehavior { 546 InsertUseConfig {
547 MergeBehaviorDef::None => None, 547 merge: match self.data.assist_importMergeBehavior {
548 MergeBehaviorDef::Full => Some(MergeBehavior::Full), 548 MergeBehaviorDef::None => None,
549 MergeBehaviorDef::Last => Some(MergeBehavior::Last), 549 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
550 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
551 },
552 prefix_kind: match self.data.assist_importPrefix {
553 ImportPrefixDef::Plain => PrefixKind::Plain,
554 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
555 ImportPrefixDef::BySelf => PrefixKind::BySelf,
556 },
550 } 557 }
551 } 558 }
552 pub fn completion(&self) -> CompletionConfig { 559 pub fn completion(&self) -> CompletionConfig {
@@ -556,7 +563,7 @@ impl Config {
556 && completion_item_edit_resolve(&self.caps), 563 && completion_item_edit_resolve(&self.caps),
557 add_call_parenthesis: self.data.completion_addCallParenthesis, 564 add_call_parenthesis: self.data.completion_addCallParenthesis,
558 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, 565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
559 merge: self.merge_behavior(), 566 insert_use: self.insert_use_config(),
560 snippet_cap: SnippetCap::new(try_or!( 567 snippet_cap: SnippetCap::new(try_or!(
561 self.caps 568 self.caps
562 .text_document 569 .text_document
@@ -575,7 +582,11 @@ impl Config {
575 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), 582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
576 allowed: None, 583 allowed: None,
577 insert_use: InsertUseConfig { 584 insert_use: InsertUseConfig {
578 merge: self.merge_behavior(), 585 merge: match self.data.assist_importMergeBehavior {
586 MergeBehaviorDef::None => None,
587 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
588 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
589 },
579 prefix_kind: match self.data.assist_importPrefix { 590 prefix_kind: match self.data.assist_importPrefix {
580 ImportPrefixDef::Plain => PrefixKind::Plain, 591 ImportPrefixDef::Plain => PrefixKind::Plain,
581 ImportPrefixDef::ByCrate => PrefixKind::ByCrate, 592 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index 23d42b4d0..5c282fe67 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -66,52 +66,7 @@
66 ), 66 ),
67 data: None, 67 data: None,
68 }, 68 },
69 fixes: [ 69 fixes: [],
70 CodeAction {
71 title: "consider prefixing with an underscore",
72 group: None,
73 kind: Some(
74 CodeActionKind(
75 "quickfix",
76 ),
77 ),
78 edit: Some(
79 SnippetWorkspaceEdit {
80 changes: Some(
81 {
82 Url {
83 scheme: "file",
84 host: None,
85 port: None,
86 path: "/test/driver/subcommand/repl.rs",
87 query: None,
88 fragment: None,
89 }: [
90 TextEdit {
91 range: Range {
92 start: Position {
93 line: 290,
94 character: 8,
95 },
96 end: Position {
97 line: 290,
98 character: 11,
99 },
100 },
101 new_text: "_foo",
102 },
103 ],
104 },
105 ),
106 document_changes: None,
107 },
108 ),
109 is_preferred: Some(
110 true,
111 ),
112 data: None,
113 },
114 ],
115 }, 70 },
116 MappedRustDiagnostic { 71 MappedRustDiagnostic {
117 url: Url { 72 url: Url {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index 4e428bedc..d36d7693d 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -66,52 +66,7 @@
66 ), 66 ),
67 data: None, 67 data: None,
68 }, 68 },
69 fixes: [ 69 fixes: [],
70 CodeAction {
71 title: "consider prefixing with an underscore",
72 group: None,
73 kind: Some(
74 CodeActionKind(
75 "quickfix",
76 ),
77 ),
78 edit: Some(
79 SnippetWorkspaceEdit {
80 changes: Some(
81 {
82 Url {
83 scheme: "file",
84 host: None,
85 port: None,
86 path: "/test/driver/subcommand/repl.rs",
87 query: None,
88 fragment: None,
89 }: [
90 TextEdit {
91 range: Range {
92 start: Position {
93 line: 290,
94 character: 8,
95 },
96 end: Position {
97 line: 290,
98 character: 11,
99 },
100 },
101 new_text: "_foo",
102 },
103 ],
104 },
105 ),
106 document_changes: None,
107 },
108 ),
109 is_preferred: Some(
110 true,
111 ),
112 data: None,
113 },
114 ],
115 }, 70 },
116 MappedRustDiagnostic { 71 MappedRustDiagnostic {
117 url: Url { 72 url: Url {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 4ddd7efae..17845b711 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -66,52 +66,7 @@
66 ), 66 ),
67 data: None, 67 data: None,
68 }, 68 },
69 fixes: [ 69 fixes: [],
70 CodeAction {
71 title: "consider prefixing with an underscore",
72 group: None,
73 kind: Some(
74 CodeActionKind(
75 "quickfix",
76 ),
77 ),
78 edit: Some(
79 SnippetWorkspaceEdit {
80 changes: Some(
81 {
82 Url {
83 scheme: "file",
84 host: None,
85 port: None,
86 path: "/test/driver/subcommand/repl.rs",
87 query: None,
88 fragment: None,
89 }: [
90 TextEdit {
91 range: Range {
92 start: Position {
93 line: 290,
94 character: 8,
95 },
96 end: Position {
97 line: 290,
98 character: 11,
99 },
100 },
101 new_text: "_foo",
102 },
103 ],
104 },
105 ),
106 document_changes: None,
107 },
108 ),
109 is_preferred: Some(
110 true,
111 ),
112 data: None,
113 },
114 ],
115 }, 70 },
116 MappedRustDiagnostic { 71 MappedRustDiagnostic {
117 url: Url { 72 url: Url {
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 4cbdb3b92..a19962167 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
@@ -102,65 +102,7 @@
102 tags: None, 102 tags: None,
103 data: None, 103 data: None,
104 }, 104 },
105 fixes: [ 105 fixes: [],
106 CodeAction {
107 title: "return the expression directly",
108 group: None,
109 kind: Some(
110 CodeActionKind(
111 "quickfix",
112 ),
113 ),
114 edit: Some(
115 SnippetWorkspaceEdit {
116 changes: Some(
117 {
118 Url {
119 scheme: "file",
120 host: None,
121 port: None,
122 path: "/test/src/main.rs",
123 query: None,
124 fragment: None,
125 }: [
126 TextEdit {
127 range: Range {
128 start: Position {
129 line: 2,
130 character: 4,
131 },
132 end: Position {
133 line: 2,
134 character: 30,
135 },
136 },
137 new_text: "",
138 },
139 TextEdit {
140 range: Range {
141 start: Position {
142 line: 3,
143 character: 4,
144 },
145 end: Position {
146 line: 3,
147 character: 5,
148 },
149 },
150 new_text: "(0..10).collect()",
151 },
152 ],
153 },
154 ),
155 document_changes: None,
156 },
157 ),
158 is_preferred: Some(
159 true,
160 ),
161 data: None,
162 },
163 ],
164 }, 106 },
165 MappedRustDiagnostic { 107 MappedRustDiagnostic {
166 url: Url { 108 url: Url {
@@ -242,65 +184,7 @@
242 tags: None, 184 tags: None,
243 data: None, 185 data: None,
244 }, 186 },
245 fixes: [ 187 fixes: [],
246 CodeAction {
247 title: "return the expression directly",
248 group: None,
249 kind: Some(
250 CodeActionKind(
251 "quickfix",
252 ),
253 ),
254 edit: Some(
255 SnippetWorkspaceEdit {
256 changes: Some(
257 {
258 Url {
259 scheme: "file",
260 host: None,
261 port: None,
262 path: "/test/src/main.rs",
263 query: None,
264 fragment: None,
265 }: [
266 TextEdit {
267 range: Range {
268 start: Position {
269 line: 2,
270 character: 4,
271 },
272 end: Position {
273 line: 2,
274 character: 30,
275 },
276 },
277 new_text: "",
278 },
279 TextEdit {
280 range: Range {
281 start: Position {
282 line: 3,
283 character: 4,
284 },
285 end: Position {
286 line: 3,
287 character: 5,
288 },
289 },
290 new_text: "(0..10).collect()",
291 },
292 ],
293 },
294 ),
295 document_changes: None,
296 },
297 ),
298 is_preferred: Some(
299 true,
300 ),
301 data: None,
302 },
303 ],
304 }, 188 },
305 MappedRustDiagnostic { 189 MappedRustDiagnostic {
306 url: Url { 190 url: Url {
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 540759198..757899484 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -74,11 +74,13 @@ fn diagnostic_related_information(
74 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 74 Some(lsp_types::DiagnosticRelatedInformation { location, message })
75} 75}
76 76
77struct SubDiagnostic {
78 related: lsp_types::DiagnosticRelatedInformation,
79 suggested_fix: Option<lsp_ext::CodeAction>,
80}
81
77enum MappedRustChildDiagnostic { 82enum MappedRustChildDiagnostic {
78 Related { 83 SubDiagnostic(SubDiagnostic),
79 related: lsp_types::DiagnosticRelatedInformation,
80 suggested_fix: Option<lsp_ext::CodeAction>,
81 },
82 MessageLine(String), 84 MessageLine(String),
83} 85}
84 86
@@ -105,15 +107,15 @@ fn map_rust_child_diagnostic(
105 } 107 }
106 108
107 if edit_map.is_empty() { 109 if edit_map.is_empty() {
108 MappedRustChildDiagnostic::Related { 110 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
109 related: lsp_types::DiagnosticRelatedInformation { 111 related: lsp_types::DiagnosticRelatedInformation {
110 location: location(workspace_root, spans[0]), 112 location: location(workspace_root, spans[0]),
111 message: rd.message.clone(), 113 message: rd.message.clone(),
112 }, 114 },
113 suggested_fix: None, 115 suggested_fix: None,
114 } 116 })
115 } else { 117 } else {
116 MappedRustChildDiagnostic::Related { 118 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
117 related: lsp_types::DiagnosticRelatedInformation { 119 related: lsp_types::DiagnosticRelatedInformation {
118 location: location(workspace_root, spans[0]), 120 location: location(workspace_root, spans[0]),
119 message: rd.message.clone(), 121 message: rd.message.clone(),
@@ -130,7 +132,7 @@ fn map_rust_child_diagnostic(
130 is_preferred: Some(true), 132 is_preferred: Some(true),
131 data: None, 133 data: None,
132 }), 134 }),
133 } 135 })
134 } 136 }
135} 137}
136 138
@@ -175,26 +177,22 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
175 } 177 }
176 178
177 let mut needs_primary_span_label = true; 179 let mut needs_primary_span_label = true;
178 let mut related_information = Vec::new(); 180 let mut subdiagnostics = Vec::new();
179 let mut tags = Vec::new(); 181 let mut tags = Vec::new();
180 182
181 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 183 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
182 let related = diagnostic_related_information(workspace_root, secondary_span); 184 let related = diagnostic_related_information(workspace_root, secondary_span);
183 if let Some(related) = related { 185 if let Some(related) = related {
184 related_information.push(related); 186 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
185 } 187 }
186 } 188 }
187 189
188 let mut fixes = Vec::new();
189 let mut message = rd.message.clone(); 190 let mut message = rd.message.clone();
190 for child in &rd.children { 191 for child in &rd.children {
191 let child = map_rust_child_diagnostic(workspace_root, &child); 192 let child = map_rust_child_diagnostic(workspace_root, &child);
192 match child { 193 match child {
193 MappedRustChildDiagnostic::Related { related, suggested_fix } => { 194 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
194 related_information.push(related); 195 subdiagnostics.push(sub);
195 if let Some(code_action) = suggested_fix {
196 fixes.push(code_action);
197 }
198 } 196 }
199 MappedRustChildDiagnostic::MessageLine(message_line) => { 197 MappedRustChildDiagnostic::MessageLine(message_line) => {
200 format_to!(message, "\n{}", message_line); 198 format_to!(message, "\n{}", message_line);
@@ -284,7 +282,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
284 diagnostics.push(MappedRustDiagnostic { 282 diagnostics.push(MappedRustDiagnostic {
285 url: in_macro_location.uri, 283 url: in_macro_location.uri,
286 diagnostic, 284 diagnostic,
287 fixes: fixes.clone(), 285 fixes: Vec::new(),
288 }); 286 });
289 } 287 }
290 288
@@ -298,17 +296,20 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
298 code_description: code_description.clone(), 296 code_description: code_description.clone(),
299 source: Some(source.clone()), 297 source: Some(source.clone()),
300 message, 298 message,
301 related_information: if related_information.is_empty() { 299 related_information: if subdiagnostics.is_empty() {
302 None 300 None
303 } else { 301 } else {
304 let mut related = related_information.clone(); 302 let mut related = subdiagnostics
303 .iter()
304 .map(|sub| sub.related.clone())
305 .collect::<Vec<_>>();
305 related.extend(related_macro_info); 306 related.extend(related_macro_info);
306 Some(related) 307 Some(related)
307 }, 308 },
308 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 309 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
309 data: None, 310 data: None,
310 }, 311 },
311 fixes: fixes.clone(), 312 fixes: Vec::new(),
312 }); 313 });
313 314
314 // Emit hint-level diagnostics for all `related_information` entries such as "help"s. 315 // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
@@ -318,21 +319,21 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
318 location, 319 location,
319 message: "original diagnostic".to_string(), 320 message: "original diagnostic".to_string(),
320 }; 321 };
321 for info in &related_information { 322 for sub in &subdiagnostics {
322 // Filter out empty/non-existent messages, as they greatly confuse VS Code. 323 // Filter out empty/non-existent messages, as they greatly confuse VS Code.
323 if info.message.is_empty() { 324 if sub.related.message.is_empty() {
324 continue; 325 continue;
325 } 326 }
326 diagnostics.push(MappedRustDiagnostic { 327 diagnostics.push(MappedRustDiagnostic {
327 url: info.location.uri.clone(), 328 url: sub.related.location.uri.clone(),
328 fixes: fixes.clone(), // share fixes to make them easier to apply 329 fixes: sub.suggested_fix.iter().cloned().collect(),
329 diagnostic: lsp_types::Diagnostic { 330 diagnostic: lsp_types::Diagnostic {
330 range: info.location.range, 331 range: sub.related.location.range,
331 severity: Some(lsp_types::DiagnosticSeverity::Hint), 332 severity: Some(lsp_types::DiagnosticSeverity::Hint),
332 code: code.clone().map(lsp_types::NumberOrString::String), 333 code: code.clone().map(lsp_types::NumberOrString::String),
333 code_description: code_description.clone(), 334 code_description: code_description.clone(),
334 source: Some(source.clone()), 335 source: Some(source.clone()),
335 message: info.message.clone(), 336 message: sub.related.message.clone(),
336 related_information: Some(vec![back_ref.clone()]), 337 related_information: Some(vec![back_ref.clone()]),
337 tags: None, // don't apply modifiers again 338 tags: None, // don't apply modifiers again
338 data: None, 339 data: None,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index dc81f55d6..1a4e0dd32 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -260,15 +260,15 @@ pub(crate) fn handle_on_type_formatting(
260 } 260 }
261 261
262 let edit = snap.analysis.on_char_typed(position, char_typed)?; 262 let edit = snap.analysis.on_char_typed(position, char_typed)?;
263 let mut edit = match edit { 263 let edit = match edit {
264 Some(it) => it, 264 Some(it) => it,
265 None => return Ok(None), 265 None => return Ok(None),
266 }; 266 };
267 267
268 // This should be a single-file edit 268 // This should be a single-file edit
269 let edit = edit.source_file_edits.pop().unwrap(); 269 let (_, edit) = edit.source_file_edits.into_iter().next().unwrap();
270 270
271 let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); 271 let change = to_proto::text_edit_vec(&line_index, line_endings, edit);
272 Ok(Some(change)) 272 Ok(Some(change))
273} 273}
274 274
@@ -463,9 +463,11 @@ pub(crate) fn handle_will_rename_files(
463 .collect(); 463 .collect();
464 464
465 // Drop file system edits since we're just renaming things on the same level 465 // Drop file system edits since we're just renaming things on the same level
466 let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect(); 466 let mut source_changes = source_changes.into_iter();
467 let source_change = SourceChange::from_edits(edits, Vec::new()); 467 let mut source_change = source_changes.next().unwrap_or_default();
468 468 source_change.file_system_edits.clear();
469 // no collect here because we want to merge text edits on same file ids
470 source_change.extend(source_changes.map(|it| it.source_file_edits).flatten());
469 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; 471 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
470 Ok(Some(workspace_edit)) 472 Ok(Some(workspace_edit))
471} 473}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a7ff8975a..1ff2d3fea 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -8,8 +8,7 @@ use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, 10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
11 RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange, 11 RenameError, Runnable, Severity, SourceChange, SymbolKind, TextEdit, TextRange, TextSize,
12 TextSize,
13}; 12};
14use itertools::Itertools; 13use itertools::Itertools;
15 14
@@ -634,13 +633,13 @@ pub(crate) fn goto_definition_response(
634pub(crate) fn snippet_text_document_edit( 633pub(crate) fn snippet_text_document_edit(
635 snap: &GlobalStateSnapshot, 634 snap: &GlobalStateSnapshot,
636 is_snippet: bool, 635 is_snippet: bool,
637 source_file_edit: SourceFileEdit, 636 file_id: FileId,
637 edit: TextEdit,
638) -> Result<lsp_ext::SnippetTextDocumentEdit> { 638) -> Result<lsp_ext::SnippetTextDocumentEdit> {
639 let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id); 639 let text_document = optional_versioned_text_document_identifier(snap, file_id);
640 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; 640 let line_index = snap.analysis.file_line_index(file_id)?;
641 let line_endings = snap.file_line_endings(source_file_edit.file_id); 641 let line_endings = snap.file_line_endings(file_id);
642 let edits = source_file_edit 642 let edits = edit
643 .edit
644 .into_iter() 643 .into_iter()
645 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it)) 644 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it))
646 .collect(); 645 .collect();
@@ -699,8 +698,8 @@ pub(crate) fn snippet_workspace_edit(
699 let ops = snippet_text_document_ops(snap, op); 698 let ops = snippet_text_document_ops(snap, op);
700 document_changes.extend_from_slice(&ops); 699 document_changes.extend_from_slice(&ops);
701 } 700 }
702 for edit in source_change.source_file_edits { 701 for (file_id, edit) in source_change.source_file_edits {
703 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; 702 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?;
704 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 703 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
705 } 704 }
706 let workspace_edit = 705 let workspace_edit =
@@ -862,8 +861,9 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
862 861
863#[cfg(test)] 862#[cfg(test)]
864mod tests { 863mod tests {
864 use hir::PrefixKind;
865 use ide::Analysis; 865 use ide::Analysis;
866 use ide_db::helpers::SnippetCap; 866 use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
867 867
868 use super::*; 868 use super::*;
869 869
@@ -888,7 +888,7 @@ mod tests {
888 add_call_parenthesis: true, 888 add_call_parenthesis: true,
889 add_call_argument_snippets: true, 889 add_call_argument_snippets: true,
890 snippet_cap: SnippetCap::new(true), 890 snippet_cap: SnippetCap::new(true),
891 merge: None, 891 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
892 }, 892 },
893 ide_db::base_db::FilePosition { file_id, offset }, 893 ide_db::base_db::FilePosition { file_id, offset },
894 ) 894 )
diff --git a/crates/ssr/src/lib.rs b/crates/ssr/src/lib.rs
index 747ce495d..a97fc8bca 100644
--- a/crates/ssr/src/lib.rs
+++ b/crates/ssr/src/lib.rs
@@ -75,10 +75,10 @@ pub use crate::matching::Match;
75use crate::matching::MatchFailureReason; 75use crate::matching::MatchFailureReason;
76use hir::Semantics; 76use hir::Semantics;
77use ide_db::base_db::{FileId, FilePosition, FileRange}; 77use ide_db::base_db::{FileId, FilePosition, FileRange};
78use ide_db::source_change::SourceFileEdit;
79use resolving::ResolvedRule; 78use resolving::ResolvedRule;
80use rustc_hash::FxHashMap; 79use rustc_hash::FxHashMap;
81use syntax::{ast, AstNode, SyntaxNode, TextRange}; 80use syntax::{ast, AstNode, SyntaxNode, TextRange};
81use text_edit::TextEdit;
82 82
83// A structured search replace rule. Create by calling `parse` on a str. 83// A structured search replace rule. Create by calling `parse` on a str.
84#[derive(Debug)] 84#[derive(Debug)]
@@ -159,7 +159,7 @@ impl<'db> MatchFinder<'db> {
159 } 159 }
160 160
161 /// Finds matches for all added rules and returns edits for all found matches. 161 /// Finds matches for all added rules and returns edits for all found matches.
162 pub fn edits(&self) -> Vec<SourceFileEdit> { 162 pub fn edits(&self) -> FxHashMap<FileId, TextEdit> {
163 use ide_db::base_db::SourceDatabaseExt; 163 use ide_db::base_db::SourceDatabaseExt;
164 let mut matches_by_file = FxHashMap::default(); 164 let mut matches_by_file = FxHashMap::default();
165 for m in self.matches().matches { 165 for m in self.matches().matches {
@@ -169,13 +169,19 @@ impl<'db> MatchFinder<'db> {
169 .matches 169 .matches
170 .push(m); 170 .push(m);
171 } 171 }
172 let mut edits = vec![]; 172 matches_by_file
173 for (file_id, matches) in matches_by_file { 173 .into_iter()
174 let edit = 174 .map(|(file_id, matches)| {
175 replacing::matches_to_edit(&matches, &self.sema.db.file_text(file_id), &self.rules); 175 (
176 edits.push(SourceFileEdit { file_id, edit }); 176 file_id,
177 } 177 replacing::matches_to_edit(
178 edits 178 &matches,
179 &self.sema.db.file_text(file_id),
180 &self.rules,
181 ),
182 )
183 })
184 .collect()
179 } 185 }
180 186
181 /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you 187 /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs
index 6cf831431..42d313f91 100644
--- a/crates/ssr/src/matching.rs
+++ b/crates/ssr/src/matching.rs
@@ -810,9 +810,9 @@ mod tests {
810 810
811 let edits = match_finder.edits(); 811 let edits = match_finder.edits();
812 assert_eq!(edits.len(), 1); 812 assert_eq!(edits.len(), 1);
813 let edit = &edits[0]; 813 let edit = &edits[&position.file_id];
814 let mut after = input.to_string(); 814 let mut after = input.to_string();
815 edit.edit.apply(&mut after); 815 edit.apply(&mut after);
816 assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); 816 assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }");
817 } 817 }
818} 818}
diff --git a/crates/ssr/src/tests.rs b/crates/ssr/src/tests.rs
index d6918c22d..a3ea44f23 100644
--- a/crates/ssr/src/tests.rs
+++ b/crates/ssr/src/tests.rs
@@ -103,11 +103,10 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) {
103 if edits.is_empty() { 103 if edits.is_empty() {
104 panic!("No edits were made"); 104 panic!("No edits were made");
105 } 105 }
106 assert_eq!(edits[0].file_id, position.file_id);
107 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters 106 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters
108 // stuff. 107 // stuff.
109 let mut actual = db.file_text(position.file_id).to_string(); 108 let mut actual = db.file_text(position.file_id).to_string();
110 edits[0].edit.apply(&mut actual); 109 edits[&position.file_id].apply(&mut actual);
111 expected.assert_eq(&actual); 110 expected.assert_eq(&actual);
112} 111}
113 112
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs
index 263b938e3..4f5c6100d 100644
--- a/crates/stdx/src/macros.rs
+++ b/crates/stdx/src/macros.rs
@@ -66,7 +66,7 @@ macro_rules! impl_from {
66/// Shamelessly stolen from: https://www.sqlite.org/assert.html 66/// Shamelessly stolen from: https://www.sqlite.org/assert.html
67#[macro_export] 67#[macro_export]
68macro_rules! assert_never { 68macro_rules! assert_never {
69 ($cond:expr) => { $crate::assert_always!($cond, "") }; 69 ($cond:expr) => { $crate::assert_never!($cond, "") };
70 ($cond:expr, $($fmt:tt)*) => {{ 70 ($cond:expr, $($fmt:tt)*) => {{
71 let value = $cond; 71 let value = $cond;
72 if value { 72 if value {
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index cfeaed9e6..52394b337 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.10.0" 14itertools = "0.10.0"
15rowan = "0.10.0" 15rowan = "0.10.3"
16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 384d031e7..827ae78f9 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -45,7 +45,7 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextSize) ->
45} 45}
46 46
47pub fn find_node_at_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { 47pub fn find_node_at_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
48 find_covering_element(syntax, range).ancestors().find_map(N::cast) 48 syntax.covering_element(range).ancestors().find_map(N::cast)
49} 49}
50 50
51/// Skip to next non `trivia` token 51/// Skip to next non `trivia` token
@@ -74,10 +74,6 @@ pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Optio
74 } 74 }
75} 75}
76 76
77pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxElement {
78 root.covering_element(range)
79}
80
81pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> { 77pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> {
82 if u == v { 78 if u == v {
83 return Some(u.clone()); 79 return Some(u.clone());
@@ -883,7 +879,7 @@ use crate::AstNode;
883 879
884 replacements: 880 replacements:
885 881
886 Line 2: Node(NAME_REF@5..14) -> crate 882 Line 2: Token(IDENT@5..14 "text_edit") -> crate
887 Line 2: Token([email protected] "TextEdit") -> AstNode 883 Line 2: Token([email protected] "TextEdit") -> AstNode
888 Line 2: Token([email protected] "\n\n") -> "\n" 884 Line 2: Token([email protected] "\n\n") -> "\n"
889 885
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 92ed2ee9d..1d722db3c 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -18,6 +18,9 @@ pub struct NameRef {
18} 18}
19impl NameRef { 19impl NameRef {
20 pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) } 20 pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
21 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
22 pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
23 pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
21} 24}
22#[derive(Debug, Clone, PartialEq, Eq, Hash)] 25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub struct Lifetime { 26pub struct Lifetime {
@@ -42,9 +45,6 @@ pub struct PathSegment {
42 pub(crate) syntax: SyntaxNode, 45 pub(crate) syntax: SyntaxNode,
43} 46}
44impl PathSegment { 47impl PathSegment {
45 pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
46 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
47 pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
48 pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) } 48 pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
49 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } 49 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
50 pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) } 50 pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
@@ -931,6 +931,15 @@ impl WhileExpr {
931 pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) } 931 pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
932} 932}
933#[derive(Debug, Clone, PartialEq, Eq, Hash)] 933#[derive(Debug, Clone, PartialEq, Eq, Hash)]
934pub struct YieldExpr {
935 pub(crate) syntax: SyntaxNode,
936}
937impl ast::AttrsOwner for YieldExpr {}
938impl YieldExpr {
939 pub fn yield_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![yield]) }
940 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
941}
942#[derive(Debug, Clone, PartialEq, Eq, Hash)]
934pub struct Label { 943pub struct Label {
935 pub(crate) syntax: SyntaxNode, 944 pub(crate) syntax: SyntaxNode,
936} 945}
@@ -1334,6 +1343,7 @@ pub enum Expr {
1334 TryExpr(TryExpr), 1343 TryExpr(TryExpr),
1335 TupleExpr(TupleExpr), 1344 TupleExpr(TupleExpr),
1336 WhileExpr(WhileExpr), 1345 WhileExpr(WhileExpr),
1346 YieldExpr(YieldExpr),
1337} 1347}
1338#[derive(Debug, Clone, PartialEq, Eq, Hash)] 1348#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1339pub enum Item { 1349pub enum Item {
@@ -2386,6 +2396,17 @@ impl AstNode for WhileExpr {
2386 } 2396 }
2387 fn syntax(&self) -> &SyntaxNode { &self.syntax } 2397 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2388} 2398}
2399impl AstNode for YieldExpr {
2400 fn can_cast(kind: SyntaxKind) -> bool { kind == YIELD_EXPR }
2401 fn cast(syntax: SyntaxNode) -> Option<Self> {
2402 if Self::can_cast(syntax.kind()) {
2403 Some(Self { syntax })
2404 } else {
2405 None
2406 }
2407 }
2408 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2409}
2389impl AstNode for Label { 2410impl AstNode for Label {
2390 fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL } 2411 fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL }
2391 fn cast(syntax: SyntaxNode) -> Option<Self> { 2412 fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3028,6 +3049,9 @@ impl From<TupleExpr> for Expr {
3028impl From<WhileExpr> for Expr { 3049impl From<WhileExpr> for Expr {
3029 fn from(node: WhileExpr) -> Expr { Expr::WhileExpr(node) } 3050 fn from(node: WhileExpr) -> Expr { Expr::WhileExpr(node) }
3030} 3051}
3052impl From<YieldExpr> for Expr {
3053 fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
3054}
3031impl AstNode for Expr { 3055impl AstNode for Expr {
3032 fn can_cast(kind: SyntaxKind) -> bool { 3056 fn can_cast(kind: SyntaxKind) -> bool {
3033 match kind { 3057 match kind {
@@ -3035,7 +3059,8 @@ impl AstNode for Expr {
3035 | CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR 3059 | CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR
3036 | IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MATCH_EXPR 3060 | IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MATCH_EXPR
3037 | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR 3061 | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
3038 | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR => true, 3062 | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
3063 | YIELD_EXPR => true,
3039 _ => false, 3064 _ => false,
3040 } 3065 }
3041 } 3066 }
@@ -3071,6 +3096,7 @@ impl AstNode for Expr {
3071 TRY_EXPR => Expr::TryExpr(TryExpr { syntax }), 3096 TRY_EXPR => Expr::TryExpr(TryExpr { syntax }),
3072 TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }), 3097 TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }),
3073 WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }), 3098 WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }),
3099 YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }),
3074 _ => return None, 3100 _ => return None,
3075 }; 3101 };
3076 Some(res) 3102 Some(res)
@@ -3107,6 +3133,7 @@ impl AstNode for Expr {
3107 Expr::TryExpr(it) => &it.syntax, 3133 Expr::TryExpr(it) => &it.syntax,
3108 Expr::TupleExpr(it) => &it.syntax, 3134 Expr::TupleExpr(it) => &it.syntax,
3109 Expr::WhileExpr(it) => &it.syntax, 3135 Expr::WhileExpr(it) => &it.syntax,
3136 Expr::YieldExpr(it) => &it.syntax,
3110 } 3137 }
3111 } 3138 }
3112} 3139}
@@ -3983,6 +4010,11 @@ impl std::fmt::Display for WhileExpr {
3983 std::fmt::Display::fmt(self.syntax(), f) 4010 std::fmt::Display::fmt(self.syntax(), f)
3984 } 4011 }
3985} 4012}
4013impl std::fmt::Display for YieldExpr {
4014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4015 std::fmt::Display::fmt(self.syntax(), f)
4016 }
4017}
3986impl std::fmt::Display for Label { 4018impl std::fmt::Display for Label {
3987 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 4019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3988 std::fmt::Display::fmt(self.syntax(), f) 4020 std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 1ed8a96e5..9ffc3ae11 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -108,8 +108,12 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::
108 ast_from_text(&format!("use {{{}}};", use_trees)) 108 ast_from_text(&format!("use {{{}}};", use_trees))
109} 109}
110 110
111pub fn use_(use_tree: ast::UseTree) -> ast::Use { 111pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
112 ast_from_text(&format!("use {};", use_tree)) 112 let visibility = match visibility {
113 None => String::new(),
114 Some(it) => format!("{} ", it),
115 };
116 ast_from_text(&format!("{}use {};", visibility, use_tree))
113} 117}
114 118
115pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { 119pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 27381ba80..b8ce71d27 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -156,14 +156,28 @@ impl ast::PathSegment {
156 .expect("segments are always nested in paths") 156 .expect("segments are always nested in paths")
157 } 157 }
158 158
159 pub fn crate_token(&self) -> Option<SyntaxToken> {
160 self.name_ref().and_then(|it| it.crate_token())
161 }
162
163 pub fn self_token(&self) -> Option<SyntaxToken> {
164 self.name_ref().and_then(|it| it.self_token())
165 }
166
167 pub fn super_token(&self) -> Option<SyntaxToken> {
168 self.name_ref().and_then(|it| it.super_token())
169 }
170
159 pub fn kind(&self) -> Option<PathSegmentKind> { 171 pub fn kind(&self) -> Option<PathSegmentKind> {
160 let res = if let Some(name_ref) = self.name_ref() { 172 let res = if let Some(name_ref) = self.name_ref() {
161 PathSegmentKind::Name(name_ref) 173 match name_ref.syntax().first_token().map(|it| it.kind()) {
174 Some(T![self]) => PathSegmentKind::SelfKw,
175 Some(T![super]) => PathSegmentKind::SuperKw,
176 Some(T![crate]) => PathSegmentKind::CrateKw,
177 _ => PathSegmentKind::Name(name_ref),
178 }
162 } else { 179 } else {
163 match self.syntax().first_child_or_token()?.kind() { 180 match self.syntax().first_child_or_token()?.kind() {
164 T![self] => PathSegmentKind::SelfKw,
165 T![super] => PathSegmentKind::SuperKw,
166 T![crate] => PathSegmentKind::CrateKw,
167 T![<] => { 181 T![<] => {
168 // <T> or <T as Trait> 182 // <T> or <T as Trait>
169 // T is any TypeRef, Trait has to be a PathType 183 // T is any TypeRef, Trait has to be a PathType
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 78eaf3410..76f01084c 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -10,7 +10,6 @@ use parser::Reparser;
10use text_edit::Indel; 10use text_edit::Indel;
11 11
12use crate::{ 12use crate::{
13 algo,
14 parsing::{ 13 parsing::{
15 lexer::{lex_single_syntax_kind, tokenize, Token}, 14 lexer::{lex_single_syntax_kind, tokenize, Token},
16 text_token_source::TextTokenSource, 15 text_token_source::TextTokenSource,
@@ -41,7 +40,7 @@ fn reparse_token<'node>(
41 root: &'node SyntaxNode, 40 root: &'node SyntaxNode,
42 edit: &Indel, 41 edit: &Indel,
43) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 42) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
44 let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone(); 43 let prev_token = root.covering_element(edit.delete).as_token()?.clone();
45 let prev_token_kind = prev_token.kind(); 44 let prev_token_kind = prev_token.kind();
46 match prev_token_kind { 45 match prev_token_kind {
47 WHITESPACE | COMMENT | IDENT | STRING => { 46 WHITESPACE | COMMENT | IDENT | STRING => {
@@ -124,7 +123,7 @@ fn is_contextual_kw(text: &str) -> bool {
124} 123}
125 124
126fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> { 125fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
127 let node = algo::find_covering_element(node, range); 126 let node = node.covering_element(range);
128 127
129 let mut ancestors = match node { 128 let mut ancestors = match node {
130 NodeOrToken::Token(it) => it.parent().ancestors(), 129 NodeOrToken::Token(it) => it.parent().ancestors(),
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index bfa2dc4ba..7901580ee 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -256,7 +256,7 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
256 )); 256 ));
257 } 257 }
258 } else if let Some(token) = segment.super_token() { 258 } else if let Some(token) = segment.super_token() {
259 if !all_supers(&path) { 259 if segment.coloncolon_token().is_some() || !all_supers(&path) {
260 errors.push(SyntaxError::new( 260 errors.push(SyntaxError::new(
261 "The `super` keyword may only be preceded by other `super`s", 261 "The `super` keyword may only be preceded by other `super`s",
262 token.text_range(), 262 token.text_range(),
diff --git a/crates/syntax/test_data/parser/err/0018_incomplete_fn.rast b/crates/syntax/test_data/parser/err/0018_incomplete_fn.rast
index 72939fc98..060f47dc4 100644
--- a/crates/syntax/test_data/parser/err/0018_incomplete_fn.rast
+++ b/crates/syntax/test_data/parser/err/0018_incomplete_fn.rast
@@ -49,7 +49,8 @@ [email protected]
49 [email protected] 49 [email protected]
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] "self" 52 [email protected]
53 [email protected] "self"
53 [email protected] "." 54 [email protected] "."
54 [email protected] 55 [email protected]
55 [email protected] "scopes" 56 [email protected] "scopes"
@@ -66,7 +67,8 @@ [email protected]
66 [email protected] 67 [email protected]
67 [email protected] 68 [email protected]
68 [email protected] 69 [email protected]
69 [email protected] "self" 70 [email protected]
71 [email protected] "self"
70 [email protected] "." 72 [email protected] "."
71 [email protected] 73 [email protected]
72 [email protected] "scopes" 74 [email protected] "scopes"
diff --git a/crates/syntax/test_data/parser/err/0035_use_recover.rast b/crates/syntax/test_data/parser/err/0035_use_recover.rast
index 2f03709eb..a95151bc5 100644
--- a/crates/syntax/test_data/parser/err/0035_use_recover.rast
+++ b/crates/syntax/test_data/parser/err/0035_use_recover.rast
@@ -24,7 +24,8 @@ [email protected]
24 [email protected] 24 [email protected]
25 [email protected] 25 [email protected]
26 [email protected] 26 [email protected]
27 [email protected] "crate" 27 [email protected]
28 [email protected] "crate"
28 [email protected] "::" 29 [email protected] "::"
29 [email protected] 30 [email protected]
30 [email protected] 31 [email protected]
diff --git a/crates/syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
index 284c8715b..7449b5ddf 100644
--- a/crates/syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
+++ b/crates/syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] "::" 8 [email protected] "::"
9 [email protected] "crate" 9 [email protected]
10 [email protected] "crate"
10 [email protected] ";" 11 [email protected] ";"
11 [email protected] "\n" 12 [email protected] "\n"
12 [email protected] 13 [email protected]
@@ -18,7 +19,8 @@ [email protected]
18 [email protected] 19 [email protected]
19 [email protected] 20 [email protected]
20 [email protected] 21 [email protected]
21 [email protected] "crate" 22 [email protected]
23 [email protected] "crate"
22 [email protected] "," 24 [email protected] ","
23 [email protected] " " 25 [email protected] " "
24 [email protected] 26 [email protected]
@@ -35,7 +37,8 @@ [email protected]
35 [email protected] 37 [email protected]
36 [email protected] 38 [email protected]
37 [email protected] 39 [email protected]
38 [email protected] "crate" 40 [email protected]
41 [email protected] "crate"
39 [email protected] "::" 42 [email protected] "::"
40 [email protected] 43 [email protected]
41 [email protected] 44 [email protected]
@@ -63,7 +66,8 @@ [email protected]
63 [email protected] "hello" 66 [email protected] "hello"
64 [email protected] "::" 67 [email protected] "::"
65 [email protected] 68 [email protected]
66 [email protected] "crate" 69 [email protected]
70 [email protected] "crate"
67 [email protected] ";" 71 [email protected] ";"
68 [email protected] "\n" 72 [email protected] "\n"
69 [email protected] 73 [email protected]
@@ -78,7 +82,8 @@ [email protected]
78 [email protected] "hello" 82 [email protected] "hello"
79 [email protected] "::" 83 [email protected] "::"
80 [email protected] 84 [email protected]
81 [email protected] "crate" 85 [email protected]
86 [email protected] "crate"
82 [email protected] "::" 87 [email protected] "::"
83 [email protected] 88 [email protected]
84 [email protected] 89 [email protected]
diff --git a/crates/syntax/test_data/parser/err/0041_illegal_super_keyword_location.rast b/crates/syntax/test_data/parser/err/0041_illegal_super_keyword_location.rast
index 2049a9d72..271f8d780 100644
--- a/crates/syntax/test_data/parser/err/0041_illegal_super_keyword_location.rast
+++ b/crates/syntax/test_data/parser/err/0041_illegal_super_keyword_location.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] "::" 8 [email protected] "::"
9 [email protected] "super" 9 [email protected]
10 [email protected] "super"
10 [email protected] ";" 11 [email protected] ";"
11 [email protected] "\n" 12 [email protected] "\n"
12 [email protected] 13 [email protected]
@@ -20,7 +21,8 @@ [email protected]
20 [email protected] "a" 21 [email protected] "a"
21 [email protected] "::" 22 [email protected] "::"
22 [email protected] 23 [email protected]
23 [email protected] "super" 24 [email protected]
25 [email protected] "super"
24 [email protected] ";" 26 [email protected] ";"
25 [email protected] "\n" 27 [email protected] "\n"
26 [email protected] 28 [email protected]
@@ -31,14 +33,16 @@ [email protected]
31 [email protected] 33 [email protected]
32 [email protected] 34 [email protected]
33 [email protected] 35 [email protected]
34 [email protected] "super" 36 [email protected]
37 [email protected] "super"
35 [email protected] "::" 38 [email protected] "::"
36 [email protected] 39 [email protected]
37 [email protected] 40 [email protected]
38 [email protected] "a" 41 [email protected] "a"
39 [email protected] "::" 42 [email protected] "::"
40 [email protected] 43 [email protected]
41 [email protected] "super" 44 [email protected]
45 [email protected] "super"
42 [email protected] ";" 46 [email protected] ";"
43 [email protected] "\n" 47 [email protected] "\n"
44 [email protected] 48 [email protected]
@@ -56,7 +60,8 @@ [email protected]
56 [email protected] 60 [email protected]
57 [email protected] 61 [email protected]
58 [email protected] 62 [email protected]
59 [email protected] "super" 63 [email protected]
64 [email protected] "super"
60 [email protected] "::" 65 [email protected] "::"
61 [email protected] 66 [email protected]
62 [email protected] 67 [email protected]
diff --git a/crates/syntax/test_data/parser/err/0042_illegal_self_keyword_location.rast b/crates/syntax/test_data/parser/err/0042_illegal_self_keyword_location.rast
index deadf56b4..01f601091 100644
--- a/crates/syntax/test_data/parser/err/0042_illegal_self_keyword_location.rast
+++ b/crates/syntax/test_data/parser/err/0042_illegal_self_keyword_location.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] "::" 8 [email protected] "::"
9 [email protected] "self" 9 [email protected]
10 [email protected] "self"
10 [email protected] ";" 11 [email protected] ";"
11 [email protected] "\n" 12 [email protected] "\n"
12 [email protected] 13 [email protected]
@@ -20,7 +21,8 @@ [email protected]
20 [email protected] "a" 21 [email protected] "a"
21 [email protected] "::" 22 [email protected] "::"
22 [email protected] 23 [email protected]
23 [email protected] "self" 24 [email protected]
25 [email protected] "self"
24 [email protected] ";" 26 [email protected] ";"
25 [email protected] "\n" 27 [email protected] "\n"
26error 6..10: The `self` keyword is only allowed as the first segment of a path 28error 6..10: The `self` keyword is only allowed as the first segment of a path
diff --git a/crates/syntax/test_data/parser/inline/err/0015_empty_segment.rast b/crates/syntax/test_data/parser/inline/err/0015_empty_segment.rast
index e872526d9..d3c5dde58 100644
--- a/crates/syntax/test_data/parser/inline/err/0015_empty_segment.rast
+++ b/crates/syntax/test_data/parser/inline/err/0015_empty_segment.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "crate" 9 [email protected]
10 [email protected] "crate"
10 [email protected] "::" 11 [email protected] "::"
11 [email protected] ";" 12 [email protected] ";"
12 [email protected] "\n" 13 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
index f40500e38..970826739 100644
--- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
@@ -11,7 +11,8 @@ [email protected]
11 [email protected] 11 [email protected]
12 [email protected] 12 [email protected]
13 [email protected] 13 [email protected]
14 [email protected] "crate" 14 [email protected]
15 [email protected] "crate"
15 [email protected] "::" 16 [email protected] "::"
16 [email protected] 17 [email protected]
17 [email protected] 18 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0052_path_type.rast b/crates/syntax/test_data/parser/inline/ok/0052_path_type.rast
index 9bc36bea7..46a103d5b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0052_path_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0052_path_type.rast
@@ -42,7 +42,8 @@ [email protected]
42 [email protected] 42 [email protected]
43 [email protected] 43 [email protected]
44 [email protected] 44 [email protected]
45 [email protected] "self" 45 [email protected]
46 [email protected] "self"
46 [email protected] "::" 47 [email protected] "::"
47 [email protected] 48 [email protected]
48 [email protected] 49 [email protected]
@@ -61,7 +62,8 @@ [email protected]
61 [email protected] 62 [email protected]
62 [email protected] 63 [email protected]
63 [email protected] 64 [email protected]
64 [email protected] "super" 65 [email protected]
66 [email protected] "super"
65 [email protected] "::" 67 [email protected] "::"
66 [email protected] 68 [email protected]
67 [email protected] 69 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast b/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
index e4fb32de1..583dcac7e 100644
--- a/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
@@ -43,7 +43,8 @@ [email protected]
43 [email protected] 43 [email protected]
44 [email protected] 44 [email protected]
45 [email protected] 45 [email protected]
46 [email protected] "super" 46 [email protected]
47 [email protected] "super"
47 [email protected] "::" 48 [email protected] "::"
48 [email protected] 49 [email protected]
49 [email protected] 50 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0067_crate_path.rast b/crates/syntax/test_data/parser/inline/ok/0067_crate_path.rast
index 702f2e0b0..87c0c48dc 100644
--- a/crates/syntax/test_data/parser/inline/ok/0067_crate_path.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0067_crate_path.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "crate" 9 [email protected]
10 [email protected] "crate"
10 [email protected] "::" 11 [email protected] "::"
11 [email protected] 12 [email protected]
12 [email protected] 13 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast b/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast
index c4c5bc51e..192a9cca6 100644
--- a/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0069_use_tree_list_after_path.rast
@@ -5,7 +5,8 @@ [email protected]
5 [email protected] 5 [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] "crate" 8 [email protected]
9 [email protected] "crate"
9 [email protected] "::" 10 [email protected] "::"
10 [email protected] 11 [email protected]
11 [email protected] "{" 12 [email protected] "{"
@@ -23,7 +24,8 @@ [email protected]
23 [email protected] 24 [email protected]
24 [email protected] 25 [email protected]
25 [email protected] 26 [email protected]
26 [email protected] "self" 27 [email protected]
28 [email protected] "self"
27 [email protected] "::" 29 [email protected] "::"
28 [email protected] 30 [email protected]
29 [email protected] "{" 31 [email protected] "{"
diff --git a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
index f3d4ad72c..3016a6574 100644
--- a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
@@ -30,7 +30,8 @@ [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected]
33 [email protected] "crate" 33 [email protected]
34 [email protected] "crate"
34 [email protected] "::" 35 [email protected] "::"
35 [email protected] 36 [email protected]
36 [email protected] 37 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast b/crates/syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast
index aa4d7a784..0fed2d311 100644
--- a/crates/syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0125_crate_keyword_path.rast
@@ -17,7 +17,8 @@ [email protected]
17 [email protected] 17 [email protected]
18 [email protected] 18 [email protected]
19 [email protected] 19 [email protected]
20 [email protected] "crate" 20 [email protected]
21 [email protected] "crate"
21 [email protected] "::" 22 [email protected] "::"
22 [email protected] 23 [email protected]
23 [email protected] 24 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast
index c204f0e2d..a5ee07499 100644
--- a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast
@@ -16,7 +16,8 @@ [email protected]
16 [email protected] 16 [email protected]
17 [email protected] 17 [email protected]
18 [email protected] 18 [email protected]
19 [email protected] "super" 19 [email protected]
20 [email protected] "super"
20 [email protected] "::" 21 [email protected] "::"
21 [email protected] 22 [email protected]
22 [email protected] 23 [email protected]
@@ -42,7 +43,8 @@ [email protected]
42 [email protected] 43 [email protected]
43 [email protected] 44 [email protected]
44 [email protected] 45 [email protected]
45 [email protected] "crate" 46 [email protected]
47 [email protected] "crate"
46 [email protected] "::" 48 [email protected] "::"
47 [email protected] 49 [email protected]
48 [email protected] 50 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rast b/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rast
new file mode 100644
index 000000000..05fc90743
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rast
@@ -0,0 +1,28 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "foo"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] "\n "
14 [email protected]
15 [email protected]
16 [email protected] "yield"
17 [email protected] ";"
18 [email protected] "\n "
19 [email protected]
20 [email protected]
21 [email protected] "yield"
22 [email protected] " "
23 [email protected]
24 [email protected] "1"
25 [email protected] ";"
26 [email protected] "\n"
27 [email protected] "}"
28 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rs b/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rs
new file mode 100644
index 000000000..596e221f7
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0159_yield_expr.rs
@@ -0,0 +1,4 @@
1fn foo() {
2 yield;
3 yield 1;
4}
diff --git a/crates/syntax/test_data/parser/ok/0013_use_path_self_super.rast b/crates/syntax/test_data/parser/ok/0013_use_path_self_super.rast
index 66ab13660..dba74e222 100644
--- a/crates/syntax/test_data/parser/ok/0013_use_path_self_super.rast
+++ b/crates/syntax/test_data/parser/ok/0013_use_path_self_super.rast
@@ -6,7 +6,8 @@ [email protected]
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "self" 9 [email protected]
10 [email protected] "self"
10 [email protected] "::" 11 [email protected] "::"
11 [email protected] 12 [email protected]
12 [email protected] 13 [email protected]
@@ -21,10 +22,12 @@ [email protected]
21 [email protected] 22 [email protected]
22 [email protected] 23 [email protected]
23 [email protected] 24 [email protected]
24 [email protected] "super" 25 [email protected]
26 [email protected] "super"
25 [email protected] "::" 27 [email protected] "::"
26 [email protected] 28 [email protected]
27 [email protected] "super" 29 [email protected]
30 [email protected] "super"
28 [email protected] "::" 31 [email protected] "::"
29 [email protected] 32 [email protected]
30 [email protected] 33 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast b/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
index 0612a71de..21c564a20 100644
--- a/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
+++ b/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
@@ -187,7 +187,8 @@ [email protected]
187 [email protected] 187 [email protected]
188 [email protected] 188 [email protected]
189 [email protected] 189 [email protected]
190 [email protected] "self" 190 [email protected]
191 [email protected] "self"
191 [email protected] "::" 192 [email protected] "::"
192 [email protected] 193 [email protected]
193 [email protected] 194 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0034_crate_path_in_call.rast b/crates/syntax/test_data/parser/ok/0034_crate_path_in_call.rast
index 5ad8c570d..a0a5ca7f5 100644
--- a/crates/syntax/test_data/parser/ok/0034_crate_path_in_call.rast
+++ b/crates/syntax/test_data/parser/ok/0034_crate_path_in_call.rast
@@ -25,7 +25,8 @@ [email protected]
25 [email protected] 25 [email protected]
26 [email protected] 26 [email protected]
27 [email protected] 27 [email protected]
28 [email protected] "crate" 28 [email protected]
29 [email protected] "crate"
29 [email protected] "::" 30 [email protected] "::"
30 [email protected] 31 [email protected]
31 [email protected] 32 [email protected]
diff --git a/lib/arena/Cargo.toml b/lib/arena/Cargo.toml
index 183a5bb6a..b9dbb7ef3 100644
--- a/lib/arena/Cargo.toml
+++ b/lib/arena/Cargo.toml
@@ -1,10 +1,10 @@
1[package] 1[package]
2name = "la-arena" 2name = "la-arena"
3version = "0.1.0" 3version = "0.1.1"
4description = "Thy rope of sands..." 4description = "Simple index-based arena without deletion."
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6repository = "https://github.com/rust-analyzer/rust-analyzer"
7documentation = "https://docs.rs/la-arena"
8categories = ["data-structures", "memory-management", "rust-patterns"]
6authors = ["rust-analyzer developers"] 9authors = ["rust-analyzer developers"]
7edition = "2018" 10edition = "2018"
8
9[lib]
10doctest = false
diff --git a/lib/arena/src/lib.rs b/lib/arena/src/lib.rs
index b15fe941d..1de3a1d2f 100644
--- a/lib/arena/src/lib.rs
+++ b/lib/arena/src/lib.rs
@@ -1,4 +1,6 @@
1//! Yet another index-based arena. 1//! Yet another ID-based arena.
2
3#![warn(missing_docs)]
2 4
3use std::{ 5use std::{
4 fmt, 6 fmt,
@@ -11,6 +13,7 @@ use std::{
11mod map; 13mod map;
12pub use map::ArenaMap; 14pub use map::ArenaMap;
13 15
16/// The raw ID of a value in an arena.
14#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 17#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct RawId(u32); 18pub struct RawId(u32);
16 19
@@ -38,6 +41,7 @@ impl fmt::Display for RawId {
38 } 41 }
39} 42}
40 43
44/// The ID of a value allocated in an arena that holds `T`s.
41pub struct Idx<T> { 45pub struct Idx<T> {
42 raw: RawId, 46 raw: RawId,
43 _ty: PhantomData<fn() -> T>, 47 _ty: PhantomData<fn() -> T>,
@@ -74,14 +78,18 @@ impl<T> fmt::Debug for Idx<T> {
74} 78}
75 79
76impl<T> Idx<T> { 80impl<T> Idx<T> {
81 /// Creates a new ID from a [`RawId`].
77 pub fn from_raw(raw: RawId) -> Self { 82 pub fn from_raw(raw: RawId) -> Self {
78 Idx { raw, _ty: PhantomData } 83 Idx { raw, _ty: PhantomData }
79 } 84 }
85
86 /// Converts this ID into the underlying [`RawId`].
80 pub fn into_raw(self) -> RawId { 87 pub fn into_raw(self) -> RawId {
81 self.raw 88 self.raw
82 } 89 }
83} 90}
84 91
92/// Yet another ID-based arena.
85#[derive(Clone, PartialEq, Eq)] 93#[derive(Clone, PartialEq, Eq)]
86pub struct Arena<T> { 94pub struct Arena<T> {
87 data: Vec<T>, 95 data: Vec<T>,
@@ -94,29 +102,99 @@ impl<T: fmt::Debug> fmt::Debug for Arena<T> {
94} 102}
95 103
96impl<T> Arena<T> { 104impl<T> Arena<T> {
105 /// Creates a new empty arena.
106 ///
107 /// ```
108 /// let arena: la_arena::Arena<i32> = la_arena::Arena::new();
109 /// assert!(arena.is_empty());
110 /// ```
97 pub const fn new() -> Arena<T> { 111 pub const fn new() -> Arena<T> {
98 Arena { data: Vec::new() } 112 Arena { data: Vec::new() }
99 } 113 }
114
115 /// Empties the arena, removing all contained values.
116 ///
117 /// ```
118 /// let mut arena = la_arena::Arena::new();
119 ///
120 /// arena.alloc(1);
121 /// arena.alloc(2);
122 /// arena.alloc(3);
123 /// assert_eq!(arena.len(), 3);
124 ///
125 /// arena.clear();
126 /// assert!(arena.is_empty());
127 /// ```
100 pub fn clear(&mut self) { 128 pub fn clear(&mut self) {
101 self.data.clear(); 129 self.data.clear();
102 } 130 }
103 131
132 /// Returns the length of the arena.
133 ///
134 /// ```
135 /// let mut arena = la_arena::Arena::new();
136 /// assert_eq!(arena.len(), 0);
137 ///
138 /// arena.alloc("foo");
139 /// assert_eq!(arena.len(), 1);
140 ///
141 /// arena.alloc("bar");
142 /// assert_eq!(arena.len(), 2);
143 ///
144 /// arena.alloc("baz");
145 /// assert_eq!(arena.len(), 3);
146 /// ```
104 pub fn len(&self) -> usize { 147 pub fn len(&self) -> usize {
105 self.data.len() 148 self.data.len()
106 } 149 }
150
151 /// Returns whether the arena contains no elements.
152 ///
153 /// ```
154 /// let mut arena = la_arena::Arena::new();
155 /// assert!(arena.is_empty());
156 ///
157 /// arena.alloc(0.5);
158 /// assert!(!arena.is_empty());
159 /// ```
107 pub fn is_empty(&self) -> bool { 160 pub fn is_empty(&self) -> bool {
108 self.data.is_empty() 161 self.data.is_empty()
109 } 162 }
163
164 /// Allocates a new value on the arena, returning the value’s ID.
165 ///
166 /// ```
167 /// let mut arena = la_arena::Arena::new();
168 /// let id = arena.alloc(50);
169 ///
170 /// assert_eq!(arena[id], 50);
171 /// ```
110 pub fn alloc(&mut self, value: T) -> Idx<T> { 172 pub fn alloc(&mut self, value: T) -> Idx<T> {
111 let id = RawId(self.data.len() as u32); 173 let id = RawId(self.data.len() as u32);
112 self.data.push(value); 174 self.data.push(value);
113 Idx::from_raw(id) 175 Idx::from_raw(id)
114 } 176 }
177
178 /// Returns an iterator over the arena’s elements.
179 ///
180 /// ```
181 /// let mut arena = la_arena::Arena::new();
182 /// let id1 = arena.alloc(20);
183 /// let id2 = arena.alloc(40);
184 /// let id3 = arena.alloc(60);
185 ///
186 /// let mut iterator = arena.iter();
187 /// assert_eq!(iterator.next(), Some((id1, &20)));
188 /// assert_eq!(iterator.next(), Some((id2, &40)));
189 /// assert_eq!(iterator.next(), Some((id3, &60)));
190 /// ```
115 pub fn iter( 191 pub fn iter(
116 &self, 192 &self,
117 ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { 193 ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
118 self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value)) 194 self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
119 } 195 }
196
197 /// Reallocates the arena to make it take up as little space as possible.
120 pub fn shrink_to_fit(&mut self) { 198 pub fn shrink_to_fit(&mut self) {
121 self.data.shrink_to_fit(); 199 self.data.shrink_to_fit();
122 } 200 }
diff --git a/lib/arena/src/map.rs b/lib/arena/src/map.rs
index 8a8063b7e..5ebaa9b82 100644
--- a/lib/arena/src/map.rs
+++ b/lib/arena/src/map.rs
@@ -10,6 +10,7 @@ pub struct ArenaMap<ID, V> {
10} 10}
11 11
12impl<T, V> ArenaMap<Idx<T>, V> { 12impl<T, V> ArenaMap<Idx<T>, V> {
13 /// Inserts a value associated with a given arena ID into the map.
13 pub fn insert(&mut self, id: Idx<T>, t: V) { 14 pub fn insert(&mut self, id: Idx<T>, t: V) {
14 let idx = Self::to_idx(id); 15 let idx = Self::to_idx(id);
15 16
@@ -17,22 +18,27 @@ impl<T, V> ArenaMap<Idx<T>, V> {
17 self.v[idx] = Some(t); 18 self.v[idx] = Some(t);
18 } 19 }
19 20
21 /// Returns a reference to the value associated with the provided ID if it is present.
20 pub fn get(&self, id: Idx<T>) -> Option<&V> { 22 pub fn get(&self, id: Idx<T>) -> Option<&V> {
21 self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref()) 23 self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref())
22 } 24 }
23 25
26 /// Returns a mutable reference to the value associated with the provided ID if it is present.
24 pub fn get_mut(&mut self, id: Idx<T>) -> Option<&mut V> { 27 pub fn get_mut(&mut self, id: Idx<T>) -> Option<&mut V> {
25 self.v.get_mut(Self::to_idx(id)).and_then(|it| it.as_mut()) 28 self.v.get_mut(Self::to_idx(id)).and_then(|it| it.as_mut())
26 } 29 }
27 30
31 /// Returns an iterator over the values in the map.
28 pub fn values(&self) -> impl Iterator<Item = &V> { 32 pub fn values(&self) -> impl Iterator<Item = &V> {
29 self.v.iter().filter_map(|o| o.as_ref()) 33 self.v.iter().filter_map(|o| o.as_ref())
30 } 34 }
31 35
36 /// Returns an iterator over mutable references to the values in the map.
32 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> { 37 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
33 self.v.iter_mut().filter_map(|o| o.as_mut()) 38 self.v.iter_mut().filter_map(|o| o.as_mut())
34 } 39 }
35 40
41 /// Returns an iterator over the arena IDs and values in the map.
36 pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> { 42 pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> {
37 self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?))) 43 self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
38 } 44 }
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 7a2937f0e..1bb9222d7 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -15,7 +15,7 @@ flate2 = "1.0"
15pico-args = "0.3.1" 15pico-args = "0.3.1"
16proc-macro2 = "1.0.8" 16proc-macro2 = "1.0.8"
17quote = "1.0.2" 17quote = "1.0.2"
18ungrammar = "1.6" 18ungrammar = "1.8"
19walkdir = "2.3.1" 19walkdir = "2.3.1"
20write-json = "0.1.0" 20write-json = "0.1.0"
21xshell = "0.1" 21xshell = "0.1"
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 2b8012bdd..046d68f52 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -68,7 +68,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
68 "as", "async", "await", "box", "break", "const", "continue", "crate", "dyn", "else", 68 "as", "async", "await", "box", "break", "const", "continue", "crate", "dyn", "else",
69 "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "macro", 69 "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "macro",
70 "match", "mod", "move", "mut", "pub", "ref", "return", "self", "static", "struct", "super", 70 "match", "mod", "move", "mut", "pub", "ref", "return", "self", "static", "struct", "super",
71 "trait", "true", "try", "type", "unsafe", "use", "where", "while", 71 "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
72 ], 72 ],
73 contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules"], 73 contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules"],
74 literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"], 74 literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
@@ -149,6 +149,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
149 "LABEL", 149 "LABEL",
150 "BLOCK_EXPR", 150 "BLOCK_EXPR",
151 "RETURN_EXPR", 151 "RETURN_EXPR",
152 "YIELD_EXPR",
152 "MATCH_EXPR", 153 "MATCH_EXPR",
153 "MATCH_ARM_LIST", 154 "MATCH_ARM_LIST",
154 "MATCH_ARM", 155 "MATCH_ARM",