aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock261
-rw-r--r--crates/assists/src/handlers/auto_import.rs41
-rw-r--r--crates/assists/src/handlers/generate_function.rs19
-rw-r--r--crates/assists/src/handlers/generate_impl.rs11
-rw-r--r--crates/assists/src/handlers/generate_new.rs8
-rw-r--r--crates/assists/src/handlers/introduce_named_lifetime.rs2
-rw-r--r--crates/assists/src/handlers/qualify_path.rs43
-rw-r--r--crates/assists/src/handlers/raw_string.rs2
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs19
-rw-r--r--crates/assists/src/utils.rs2
-rw-r--r--crates/base_db/src/input.rs2
-rw-r--r--crates/completion/src/completions/attribute.rs9
-rw-r--r--crates/completion/src/completions/flyimport.rs452
-rw-r--r--crates/completion/src/completions/unqualified_path.rs24
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/item.rs27
-rw-r--r--crates/completion/src/lib.rs11
-rw-r--r--crates/completion/src/render.rs84
-rw-r--r--crates/completion/src/render/const_.rs5
-rw-r--r--crates/completion/src/render/function.rs4
-rw-r--r--crates/completion/src/render/type_alias.rs5
-rw-r--r--crates/completion/src/test_utils.rs5
-rw-r--r--crates/hir/src/attrs.rs6
-rw-r--r--crates/hir/src/code_model.rs25
-rw-r--r--crates/hir/src/db.rs14
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/hir_def/src/attr.rs1
-rw-r--r--crates/hir_def/src/body.rs6
-rw-r--r--crates/hir_def/src/body/tests.rs41
-rw-r--r--crates/hir_def/src/db.rs17
-rw-r--r--crates/hir_def/src/find_path.rs22
-rw-r--r--crates/hir_def/src/import_map.rs68
-rw-r--r--crates/hir_def/src/item_tree.rs41
-rw-r--r--crates/hir_def/src/item_tree/lower.rs45
-rw-r--r--crates/hir_def/src/lang_item.rs2
-rw-r--r--crates/hir_def/src/nameres.rs150
-rw-r--r--crates/hir_def/src/nameres/collector.rs68
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs41
-rw-r--r--crates/hir_def/src/nameres/tests.rs22
-rw-r--r--crates/hir_def/src/nameres/tests/block.rs97
-rw-r--r--crates/hir_def/src/path.rs2
-rw-r--r--crates/hir_def/src/resolver.rs60
-rw-r--r--crates/hir_def/src/test_db.rs6
-rw-r--r--crates/hir_def/src/type_ref.rs2
-rw-r--r--crates/hir_def/src/visibility.rs6
-rw-r--r--crates/hir_expand/src/ast_id_map.rs20
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs2
-rw-r--r--crates/hir_expand/src/name.rs8
-rw-r--r--crates/hir_expand/src/proc_macro.rs4
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/diagnostics.rs2
-rw-r--r--crates/hir_ty/src/method_resolution.rs4
-rw-r--r--crates/hir_ty/src/test_db.rs4
-rw-r--r--crates/hir_ty/src/tests.rs10
-rw-r--r--crates/ide/src/call_hierarchy.rs2
-rw-r--r--crates/ide/src/diagnostics/fixes.rs2
-rw-r--r--crates/ide/src/display/navigation_target.rs18
-rw-r--r--crates/ide/src/display/short_label.rs10
-rw-r--r--crates/ide/src/doc_links.rs31
-rw-r--r--crates/ide/src/extend_selection.rs4
-rw-r--r--crates/ide/src/goto_definition.rs95
-rw-r--r--crates/ide/src/hover.rs42
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/lib.rs21
-rw-r--r--crates/ide/src/references.rs72
-rw-r--r--crates/ide/src/references/rename.rs676
-rw-r--r--crates/ide/src/runnables.rs230
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs22
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_tree.rs28
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/defs.rs13
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs356
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs2
-rw-r--r--crates/ide_db/src/imports_locator.rs76
-rw-r--r--crates/ide_db/src/search.rs10
-rw-r--r--crates/ide_db/src/symbol_index.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs36
-rw-r--r--crates/mbe/src/tests.rs10
-rw-r--r--crates/parser/src/event.rs5
-rw-r--r--crates/parser/src/grammar.rs12
-rw-r--r--crates/parser/src/grammar/items.rs2
-rw-r--r--crates/parser/src/grammar/items/traits.rs4
-rw-r--r--crates/parser/src/grammar/params.rs31
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/proc_macro_api/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt8
-rw-r--r--crates/proc_macro_srv/src/tests/mod.rs8
-rw-r--r--crates/proc_macro_srv/src/tests/utils.rs2
-rw-r--r--crates/profile/Cargo.toml3
-rw-r--r--crates/profile/src/hprof.rs4
-rw-r--r--crates/profile/src/lib.rs7
-rw-r--r--crates/profile/src/memory_usage.rs7
-rw-r--r--crates/project_model/Cargo.toml1
-rw-r--r--crates/project_model/src/build_data.rs206
-rw-r--r--crates/project_model/src/cargo_workspace.rs208
-rw-r--r--crates/project_model/src/lib.rs2
-rw-r--r--crates/project_model/src/rustc_cfg.rs34
-rw-r--r--crates/project_model/src/workspace.rs143
-rw-r--r--crates/rust-analyzer/Cargo.toml8
-rw-r--r--crates/rust-analyzer/src/bin/args.rs23
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs5
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/handlers.rs24
-rw-r--r--crates/rust-analyzer/src/reload.rs16
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/ssr/src/matching.rs13
-rw-r--r--crates/ssr/src/replacing.rs2
-rw-r--r--crates/ssr/src/resolving.rs2
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--crates/syntax/Cargo.toml7
-rw-r--r--crates/syntax/src/algo.rs7
-rw-r--r--crates/syntax/src/ast.rs4
-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.rs43
-rw-r--r--crates/syntax/src/ast/token_ext.rs12
-rw-r--r--crates/syntax/src/lib.rs4
-rw-r--r--crates/syntax/src/parsing/reparsing.rs3
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs4
-rw-r--r--crates/syntax/src/syntax_node.rs4
-rw-r--r--crates/syntax/src/validation.rs2
-rw-r--r--crates/syntax/test_data/parser/err/0037_visibility_in_traits.rast10
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0006_self_param.rast15
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0018_arb_self_types.rast6
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rast50
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rs3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rast42
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast24
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs1
-rw-r--r--crates/syntax/test_data/parser/ok/0007_extern_crate.rast3
-rw-r--r--crates/syntax/test_data/parser/ok/0012_visibility.rast10
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast3
-rw-r--r--crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast24
-rw-r--r--docs/dev/README.md3
-rw-r--r--docs/dev/style.md49
-rw-r--r--xtask/Cargo.toml4
-rw-r--r--xtask/src/ast_src.rs1
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs4
-rw-r--r--xtask/src/codegen/gen_diagnostic_docs.rs2
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs2
-rw-r--r--xtask/src/install.rs2
-rw-r--r--xtask/src/lib.rs6
-rw-r--r--xtask/src/main.rs46
-rw-r--r--xtask/tests/tidy.rs2
157 files changed, 3217 insertions, 1672 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b52616974..bf1b9e417 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.37" 29version = "1.0.38"
30source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
31checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" 31checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
32 32
33[[package]] 33[[package]]
34name = "anymap" 34name = "anymap"
@@ -77,15 +77,15 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
77 77
78[[package]] 78[[package]]
79name = "backtrace" 79name = "backtrace"
80version = "0.3.55" 80version = "0.3.56"
81source = "registry+https://github.com/rust-lang/crates.io-index" 81source = "registry+https://github.com/rust-lang/crates.io-index"
82checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" 82checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc"
83dependencies = [ 83dependencies = [
84 "addr2line", 84 "addr2line",
85 "cfg-if 1.0.0", 85 "cfg-if 1.0.0",
86 "libc", 86 "libc",
87 "miniz_oxide", 87 "miniz_oxide",
88 "object 0.22.0", 88 "object",
89 "rustc-demangle", 89 "rustc-demangle",
90] 90]
91 91
@@ -118,9 +118,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
118 118
119[[package]] 119[[package]]
120name = "byteorder" 120name = "byteorder"
121version = "1.3.4" 121version = "1.4.2"
122source = "registry+https://github.com/rust-lang/crates.io-index" 122source = "registry+https://github.com/rust-lang/crates.io-index"
123checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 123checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
124 124
125[[package]] 125[[package]]
126name = "cargo-platform" 126name = "cargo-platform"
@@ -133,9 +133,9 @@ dependencies = [
133 133
134[[package]] 134[[package]]
135name = "cargo_metadata" 135name = "cargo_metadata"
136version = "0.12.2" 136version = "0.12.3"
137source = "registry+https://github.com/rust-lang/crates.io-index" 137source = "registry+https://github.com/rust-lang/crates.io-index"
138checksum = "11a47b6286279a9998588ef7050d1ebc2500c69892a557c90fe5d071c64415dc" 138checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f"
139dependencies = [ 139dependencies = [
140 "cargo-platform", 140 "cargo-platform",
141 "semver", 141 "semver",
@@ -175,9 +175,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
175 175
176[[package]] 176[[package]]
177name = "chalk-derive" 177name = "chalk-derive"
178version = "0.47.0" 178version = "0.50.0"
179source = "registry+https://github.com/rust-lang/crates.io-index" 179source = "registry+https://github.com/rust-lang/crates.io-index"
180checksum = "3f00f6342a387edc822002d36a381e117afcac9f744951ff75fbf4a218edea5c" 180checksum = "ac605cf409013573e971d7292d4bec6f5495b19d5f98fc9d8b1a12270c3888e0"
181dependencies = [ 181dependencies = [
182 "proc-macro2", 182 "proc-macro2",
183 "quote", 183 "quote",
@@ -187,9 +187,9 @@ dependencies = [
187 187
188[[package]] 188[[package]]
189name = "chalk-ir" 189name = "chalk-ir"
190version = "0.47.0" 190version = "0.50.0"
191source = "registry+https://github.com/rust-lang/crates.io-index" 191source = "registry+https://github.com/rust-lang/crates.io-index"
192checksum = "c686e69913591ae753e5526e73cbee39db3d9b0a92cc9078ab780cabf1c70aa9" 192checksum = "fa1dbfb3c2c8b67edb5cd981f720550e43579090574f786145731f90c5d401ff"
193dependencies = [ 193dependencies = [
194 "bitflags", 194 "bitflags",
195 "chalk-derive", 195 "chalk-derive",
@@ -198,9 +198,9 @@ dependencies = [
198 198
199[[package]] 199[[package]]
200name = "chalk-recursive" 200name = "chalk-recursive"
201version = "0.47.0" 201version = "0.50.0"
202source = "registry+https://github.com/rust-lang/crates.io-index" 202source = "registry+https://github.com/rust-lang/crates.io-index"
203checksum = "310fdcac0340dab4163b766baa8067266e3b909108d1ac1b5246c033bde63975" 203checksum = "0882e2a3ba66901717a64f8bb0655e809f800ac6abed05cb605e7a41d4bf8999"
204dependencies = [ 204dependencies = [
205 "chalk-derive", 205 "chalk-derive",
206 "chalk-ir", 206 "chalk-ir",
@@ -211,9 +211,9 @@ dependencies = [
211 211
212[[package]] 212[[package]]
213name = "chalk-solve" 213name = "chalk-solve"
214version = "0.47.0" 214version = "0.50.0"
215source = "registry+https://github.com/rust-lang/crates.io-index" 215source = "registry+https://github.com/rust-lang/crates.io-index"
216checksum = "c3c3252116111c3548f1164ab8d98c67c49848b3bde10dd11b650fd023e91c72" 216checksum = "0d43cce07150eac39771ff4b198537cefef744734b2218a89c682295b54cd8d0"
217dependencies = [ 217dependencies = [
218 "chalk-derive", 218 "chalk-derive",
219 "chalk-ir", 219 "chalk-ir",
@@ -269,9 +269,20 @@ dependencies = [
269 269
270[[package]] 270[[package]]
271name = "const_fn" 271name = "const_fn"
272version = "0.4.4" 272version = "0.4.5"
273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
275
276[[package]]
277name = "countme"
278version = "2.0.0-pre.2"
273source = "registry+https://github.com/rust-lang/crates.io-index" 279source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" 280checksum = "c5716604cba7c02a846ecad3f4a3fd2d2b641faccc2a24a51efb21aff0d01f35"
281dependencies = [
282 "dashmap",
283 "once_cell",
284 "rustc-hash",
285]
275 286
276[[package]] 287[[package]]
277name = "crc32fast" 288name = "crc32fast"
@@ -323,7 +334,7 @@ dependencies = [
323 "const_fn", 334 "const_fn",
324 "crossbeam-utils 0.8.1", 335 "crossbeam-utils 0.8.1",
325 "lazy_static", 336 "lazy_static",
326 "memoffset 0.6.1", 337 "memoffset",
327 "scopeguard", 338 "scopeguard",
328] 339]
329 340
@@ -350,6 +361,16 @@ dependencies = [
350] 361]
351 362
352[[package]] 363[[package]]
364name = "dashmap"
365version = "4.0.2"
366source = "registry+https://github.com/rust-lang/crates.io-index"
367checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
368dependencies = [
369 "cfg-if 1.0.0",
370 "num_cpus",
371]
372
373[[package]]
353name = "dissimilar" 374name = "dissimilar"
354version = "1.0.2" 375version = "1.0.2"
355source = "registry+https://github.com/rust-lang/crates.io-index" 376source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -397,13 +418,13 @@ dependencies = [
397 418
398[[package]] 419[[package]]
399name = "filetime" 420name = "filetime"
400version = "0.2.13" 421version = "0.2.14"
401source = "registry+https://github.com/rust-lang/crates.io-index" 422source = "registry+https://github.com/rust-lang/crates.io-index"
402checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" 423checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
403dependencies = [ 424dependencies = [
404 "cfg-if 1.0.0", 425 "cfg-if 1.0.0",
405 "libc", 426 "libc",
406 "redox_syscall", 427 "redox_syscall 0.2.4",
407 "winapi 0.3.9", 428 "winapi 0.3.9",
408] 429]
409 430
@@ -449,6 +470,12 @@ dependencies = [
449] 470]
450 471
451[[package]] 472[[package]]
473name = "fs_extra"
474version = "1.2.0"
475source = "registry+https://github.com/rust-lang/crates.io-index"
476checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
477
478[[package]]
452name = "fsevent" 479name = "fsevent"
453version = "2.0.2" 480version = "2.0.2"
454source = "registry+https://github.com/rust-lang/crates.io-index" 481source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -502,12 +529,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
502checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 529checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
503 530
504[[package]] 531[[package]]
505name = "hashbrown"
506version = "0.10.0"
507source = "registry+https://github.com/rust-lang/crates.io-index"
508checksum = "2140e9c963869f01789fa4fef4805211081ec794af5fc77c0d5b377906118853"
509
510[[package]]
511name = "heck" 532name = "heck"
512version = "0.3.2" 533version = "0.3.2"
513source = "registry+https://github.com/rust-lang/crates.io-index" 534source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -518,9 +539,9 @@ dependencies = [
518 539
519[[package]] 540[[package]]
520name = "hermit-abi" 541name = "hermit-abi"
521version = "0.1.17" 542version = "0.1.18"
522source = "registry+https://github.com/rust-lang/crates.io-index" 543source = "registry+https://github.com/rust-lang/crates.io-index"
523checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 544checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
524dependencies = [ 545dependencies = [
525 "libc", 546 "libc",
526] 547]
@@ -692,7 +713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
692checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 713checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
693dependencies = [ 714dependencies = [
694 "autocfg", 715 "autocfg",
695 "hashbrown 0.9.1", 716 "hashbrown",
696] 717]
697 718
698[[package]] 719[[package]]
@@ -708,9 +729,9 @@ dependencies = [
708 729
709[[package]] 730[[package]]
710name = "inotify-sys" 731name = "inotify-sys"
711version = "0.1.4" 732version = "0.1.5"
712source = "registry+https://github.com/rust-lang/crates.io-index" 733source = "registry+https://github.com/rust-lang/crates.io-index"
713checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" 734checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
714dependencies = [ 735dependencies = [
715 "libc", 736 "libc",
716] 737]
@@ -758,6 +779,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
758checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 779checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
759 780
760[[package]] 781[[package]]
782name = "jemalloc-ctl"
783version = "0.3.3"
784source = "registry+https://github.com/rust-lang/crates.io-index"
785checksum = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7"
786dependencies = [
787 "jemalloc-sys",
788 "libc",
789 "paste",
790]
791
792[[package]]
793name = "jemalloc-sys"
794version = "0.3.2"
795source = "registry+https://github.com/rust-lang/crates.io-index"
796checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
797dependencies = [
798 "cc",
799 "fs_extra",
800 "libc",
801]
802
803[[package]]
804name = "jemallocator"
805version = "0.3.2"
806source = "registry+https://github.com/rust-lang/crates.io-index"
807checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69"
808dependencies = [
809 "jemalloc-sys",
810 "libc",
811]
812
813[[package]]
761name = "jod-thread" 814name = "jod-thread"
762version = "0.1.2" 815version = "0.1.2"
763source = "registry+https://github.com/rust-lang/crates.io-index" 816source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -791,15 +844,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
791 844
792[[package]] 845[[package]]
793name = "libc" 846name = "libc"
794version = "0.2.81" 847version = "0.2.82"
795source = "registry+https://github.com/rust-lang/crates.io-index" 848source = "registry+https://github.com/rust-lang/crates.io-index"
796checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 849checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
797 850
798[[package]] 851[[package]]
799name = "libloading" 852name = "libloading"
800version = "0.6.6" 853version = "0.6.7"
801source = "registry+https://github.com/rust-lang/crates.io-index" 854source = "registry+https://github.com/rust-lang/crates.io-index"
802checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc" 855checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
803dependencies = [ 856dependencies = [
804 "cfg-if 1.0.0", 857 "cfg-if 1.0.0",
805 "winapi 0.3.9", 858 "winapi 0.3.9",
@@ -825,9 +878,9 @@ dependencies = [
825 878
826[[package]] 879[[package]]
827name = "log" 880name = "log"
828version = "0.4.11" 881version = "0.4.13"
829source = "registry+https://github.com/rust-lang/crates.io-index" 882source = "registry+https://github.com/rust-lang/crates.io-index"
830checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 883checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
831dependencies = [ 884dependencies = [
832 "cfg-if 0.1.10", 885 "cfg-if 0.1.10",
833] 886]
@@ -910,15 +963,6 @@ dependencies = [
910 963
911[[package]] 964[[package]]
912name = "memoffset" 965name = "memoffset"
913version = "0.5.6"
914source = "registry+https://github.com/rust-lang/crates.io-index"
915checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
916dependencies = [
917 "autocfg",
918]
919
920[[package]]
921name = "memoffset"
922version = "0.6.1" 966version = "0.6.1"
923source = "registry+https://github.com/rust-lang/crates.io-index" 967source = "registry+https://github.com/rust-lang/crates.io-index"
924checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 968checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
@@ -1050,12 +1094,6 @@ dependencies = [
1050 1094
1051[[package]] 1095[[package]]
1052name = "object" 1096name = "object"
1053version = "0.22.0"
1054source = "registry+https://github.com/rust-lang/crates.io-index"
1055checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
1056
1057[[package]]
1058name = "object"
1059version = "0.23.0" 1097version = "0.23.0"
1060source = "registry+https://github.com/rust-lang/crates.io-index" 1098source = "registry+https://github.com/rust-lang/crates.io-index"
1061checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" 1099checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
@@ -1092,7 +1130,7 @@ dependencies = [
1092 "cfg-if 1.0.0", 1130 "cfg-if 1.0.0",
1093 "instant", 1131 "instant",
1094 "libc", 1132 "libc",
1095 "redox_syscall", 1133 "redox_syscall 0.1.57",
1096 "smallvec", 1134 "smallvec",
1097 "winapi 0.3.9", 1135 "winapi 0.3.9",
1098] 1136]
@@ -1105,6 +1143,25 @@ dependencies = [
1105] 1143]
1106 1144
1107[[package]] 1145[[package]]
1146name = "paste"
1147version = "0.1.18"
1148source = "registry+https://github.com/rust-lang/crates.io-index"
1149checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
1150dependencies = [
1151 "paste-impl",
1152 "proc-macro-hack",
1153]
1154
1155[[package]]
1156name = "paste-impl"
1157version = "0.1.18"
1158source = "registry+https://github.com/rust-lang/crates.io-index"
1159checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
1160dependencies = [
1161 "proc-macro-hack",
1162]
1163
1164[[package]]
1108name = "paths" 1165name = "paths"
1109version = "0.0.0" 1166version = "0.0.0"
1110 1167
@@ -1154,15 +1211,21 @@ dependencies = [
1154 1211
1155[[package]] 1212[[package]]
1156name = "pico-args" 1213name = "pico-args"
1157version = "0.3.4" 1214version = "0.4.0"
1158source = "registry+https://github.com/rust-lang/crates.io-index" 1215source = "registry+https://github.com/rust-lang/crates.io-index"
1159checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" 1216checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6"
1160 1217
1161[[package]] 1218[[package]]
1162name = "pin-project-lite" 1219name = "pin-project-lite"
1163version = "0.2.0" 1220version = "0.2.4"
1221source = "registry+https://github.com/rust-lang/crates.io-index"
1222checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
1223
1224[[package]]
1225name = "proc-macro-hack"
1226version = "0.5.19"
1164source = "registry+https://github.com/rust-lang/crates.io-index" 1227source = "registry+https://github.com/rust-lang/crates.io-index"
1165checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 1228checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
1166 1229
1167[[package]] 1230[[package]]
1168name = "proc-macro2" 1231name = "proc-macro2"
@@ -1194,7 +1257,7 @@ dependencies = [
1194 "libloading", 1257 "libloading",
1195 "mbe", 1258 "mbe",
1196 "memmap", 1259 "memmap",
1197 "object 0.23.0", 1260 "object",
1198 "proc_macro_api", 1261 "proc_macro_api",
1199 "proc_macro_test", 1262 "proc_macro_test",
1200 "serde_derive", 1263 "serde_derive",
@@ -1212,6 +1275,8 @@ name = "profile"
1212version = "0.0.0" 1275version = "0.0.0"
1213dependencies = [ 1276dependencies = [
1214 "cfg-if 1.0.0", 1277 "cfg-if 1.0.0",
1278 "countme",
1279 "jemalloc-ctl",
1215 "la-arena", 1280 "la-arena",
1216 "libc", 1281 "libc",
1217 "once_cell", 1282 "once_cell",
@@ -1231,6 +1296,7 @@ dependencies = [
1231 "log", 1296 "log",
1232 "paths", 1297 "paths",
1233 "proc_macro_api", 1298 "proc_macro_api",
1299 "profile",
1234 "rustc-hash", 1300 "rustc-hash",
1235 "serde", 1301 "serde",
1236 "serde_json", 1302 "serde_json",
@@ -1299,10 +1365,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1299checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1365checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1300 1366
1301[[package]] 1367[[package]]
1368name = "redox_syscall"
1369version = "0.2.4"
1370source = "registry+https://github.com/rust-lang/crates.io-index"
1371checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
1372dependencies = [
1373 "bitflags",
1374]
1375
1376[[package]]
1302name = "regex" 1377name = "regex"
1303version = "1.4.2" 1378version = "1.4.3"
1304source = "registry+https://github.com/rust-lang/crates.io-index" 1379source = "registry+https://github.com/rust-lang/crates.io-index"
1305checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1380checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
1306dependencies = [ 1381dependencies = [
1307 "regex-syntax", 1382 "regex-syntax",
1308] 1383]
@@ -1319,21 +1394,21 @@ dependencies = [
1319 1394
1320[[package]] 1395[[package]]
1321name = "regex-syntax" 1396name = "regex-syntax"
1322version = "0.6.21" 1397version = "0.6.22"
1323source = "registry+https://github.com/rust-lang/crates.io-index" 1398source = "registry+https://github.com/rust-lang/crates.io-index"
1324checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1399checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
1325 1400
1326[[package]] 1401[[package]]
1327name = "rowan" 1402name = "rowan"
1328version = "0.10.5" 1403version = "0.12.1"
1329source = "registry+https://github.com/rust-lang/crates.io-index" 1404source = "registry+https://github.com/rust-lang/crates.io-index"
1330checksum = "e1898adeafc7d3c69913b33ee1acbbb39c726a9dbe05ff77c08b52957643e8db" 1405checksum = "24c2d78254049413f9d73495f883e7fa0b7a7d4b88468cd72a3bbbd0ad585cd1"
1331dependencies = [ 1406dependencies = [
1332 "hashbrown 0.10.0", 1407 "countme",
1408 "hashbrown",
1409 "memoffset",
1333 "rustc-hash", 1410 "rustc-hash",
1334 "smol_str",
1335 "text-size", 1411 "text-size",
1336 "triomphe",
1337] 1412]
1338 1413
1339[[package]] 1414[[package]]
@@ -1353,6 +1428,7 @@ dependencies = [
1353 "ide", 1428 "ide",
1354 "ide_db", 1429 "ide_db",
1355 "itertools 0.10.0", 1430 "itertools 0.10.0",
1431 "jemallocator",
1356 "jod-thread", 1432 "jod-thread",
1357 "log", 1433 "log",
1358 "lsp-server", 1434 "lsp-server",
@@ -1388,9 +1464,9 @@ dependencies = [
1388 1464
1389[[package]] 1465[[package]]
1390name = "rustc-ap-rustc_lexer" 1466name = "rustc-ap-rustc_lexer"
1391version = "697.0.0" 1467version = "700.0.0"
1392source = "registry+https://github.com/rust-lang/crates.io-index" 1468source = "registry+https://github.com/rust-lang/crates.io-index"
1393checksum = "67adbe260a0a11910624d6d28c0304fcf7b063e666682111005c83b09f73429d" 1469checksum = "5ed36784376b69c941d7aa36e960a52ac712e2663960357121a4d9f2cc58e225"
1394dependencies = [ 1470dependencies = [
1395 "unicode-xid", 1471 "unicode-xid",
1396] 1472]
@@ -1484,18 +1560,18 @@ dependencies = [
1484 1560
1485[[package]] 1561[[package]]
1486name = "serde" 1562name = "serde"
1487version = "1.0.118" 1563version = "1.0.120"
1488source = "registry+https://github.com/rust-lang/crates.io-index" 1564source = "registry+https://github.com/rust-lang/crates.io-index"
1489checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1565checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
1490dependencies = [ 1566dependencies = [
1491 "serde_derive", 1567 "serde_derive",
1492] 1568]
1493 1569
1494[[package]] 1570[[package]]
1495name = "serde_derive" 1571name = "serde_derive"
1496version = "1.0.118" 1572version = "1.0.120"
1497source = "registry+https://github.com/rust-lang/crates.io-index" 1573source = "registry+https://github.com/rust-lang/crates.io-index"
1498checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1574checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
1499dependencies = [ 1575dependencies = [
1500 "proc-macro2", 1576 "proc-macro2",
1501 "quote", 1577 "quote",
@@ -1551,9 +1627,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1551 1627
1552[[package]] 1628[[package]]
1553name = "smallvec" 1629name = "smallvec"
1554version = "1.6.0" 1630version = "1.6.1"
1555source = "registry+https://github.com/rust-lang/crates.io-index" 1631source = "registry+https://github.com/rust-lang/crates.io-index"
1556checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" 1632checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
1557 1633
1558[[package]] 1634[[package]]
1559name = "smol_str" 1635name = "smol_str"
@@ -1579,12 +1655,6 @@ dependencies = [
1579] 1655]
1580 1656
1581[[package]] 1657[[package]]
1582name = "stable_deref_trait"
1583version = "1.2.0"
1584source = "registry+https://github.com/rust-lang/crates.io-index"
1585checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1586
1587[[package]]
1588name = "stdx" 1658name = "stdx"
1589version = "0.0.0" 1659version = "0.0.0"
1590dependencies = [ 1660dependencies = [
@@ -1593,9 +1663,9 @@ dependencies = [
1593 1663
1594[[package]] 1664[[package]]
1595name = "syn" 1665name = "syn"
1596version = "1.0.57" 1666version = "1.0.58"
1597source = "registry+https://github.com/rust-lang/crates.io-index" 1667source = "registry+https://github.com/rust-lang/crates.io-index"
1598checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 1668checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
1599dependencies = [ 1669dependencies = [
1600 "proc-macro2", 1670 "proc-macro2",
1601 "quote", 1671 "quote",
@@ -1672,9 +1742,9 @@ dependencies = [
1672 1742
1673[[package]] 1743[[package]]
1674name = "thread_local" 1744name = "thread_local"
1675version = "1.0.1" 1745version = "1.1.0"
1676source = "registry+https://github.com/rust-lang/crates.io-index" 1746source = "registry+https://github.com/rust-lang/crates.io-index"
1677checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1747checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
1678dependencies = [ 1748dependencies = [
1679 "lazy_static", 1749 "lazy_static",
1680] 1750]
@@ -1812,17 +1882,6 @@ dependencies = [
1812] 1882]
1813 1883
1814[[package]] 1884[[package]]
1815name = "triomphe"
1816version = "0.1.2"
1817source = "registry+https://github.com/rust-lang/crates.io-index"
1818checksum = "6e9d872053cf9e5a833d8c1dd772cdc38ab66a908129d6f73c049c986161d07c"
1819dependencies = [
1820 "memoffset 0.5.6",
1821 "serde",
1822 "stable_deref_trait",
1823]
1824
1825[[package]]
1826name = "tt" 1885name = "tt"
1827version = "0.0.0" 1886version = "0.0.0"
1828dependencies = [ 1887dependencies = [
@@ -1838,9 +1897,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1838 1897
1839[[package]] 1898[[package]]
1840name = "ungrammar" 1899name = "ungrammar"
1841version = "1.8.0" 1900version = "1.9.3"
1842source = "registry+https://github.com/rust-lang/crates.io-index" 1901source = "registry+https://github.com/rust-lang/crates.io-index"
1843checksum = "e33a2183403af89252547c4219a06a6cc8aef6302fee67e10e8431866af3ee72" 1902checksum = "f5901372c0f3a6a1a9d880aef134c8eaf5e54409343637508c0a344270b42d7b"
1844 1903
1845[[package]] 1904[[package]]
1846name = "unicase" 1905name = "unicase"
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 4e2a4fcd9..e93901cb3 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -3,7 +3,7 @@ use ide_db::helpers::{
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast, 4 mod_path_to_ast,
5}; 5};
6use syntax::ast; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
9 9
@@ -82,25 +82,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
82// # pub mod std { pub mod collections { pub struct HashMap { } } } 82// # pub mod std { pub mod collections { pub struct HashMap { } } }
83// ``` 83// ```
84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85 let import_assets = 85 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
86 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { 86 let proposed_imports =
87 ImportAssets::for_regular_path(path_under_caret, &ctx.sema) 87 import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
88 } else if let Some(method_under_caret) =
89 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
90 {
91 ImportAssets::for_method_call(method_under_caret, &ctx.sema)
92 } else {
93 None
94 }?;
95 let proposed_imports = import_assets.search_for_imports(&ctx.sema, &ctx.config.insert_use);
96 if proposed_imports.is_empty() { 88 if proposed_imports.is_empty() {
97 return None; 89 return None;
98 } 90 }
99 91
100 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
101 let group = import_group_message(import_assets.import_candidate()); 93 let group = import_group_message(import_assets.import_candidate());
102 let scope = 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
103 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
104 for (import, _) in proposed_imports { 95 for (import, _) in proposed_imports {
105 acc.add_group( 96 acc.add_group(
106 &group, 97 &group,
@@ -117,14 +108,28 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
117 Some(()) 108 Some(())
118} 109}
119 110
111pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
112 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
113 ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
114 .zip(Some(path_under_caret.syntax().clone()))
115 } else if let Some(method_under_caret) =
116 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
117 {
118 ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
119 .zip(Some(method_under_caret.syntax().clone()))
120 } else {
121 None
122 }
123}
124
120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 125fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
121 let name = match import_candidate { 126 let name = match import_candidate {
122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name), 127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
123 ImportCandidate::TraitAssocItem(candidate) => { 128 ImportCandidate::TraitAssocItem(candidate) => {
124 format!("Import a trait for item {}", &candidate.name) 129 format!("Import a trait for item {}", candidate.name.text())
125 } 130 }
126 ImportCandidate::TraitMethod(candidate) => { 131 ImportCandidate::TraitMethod(candidate) => {
127 format!("Import a trait for method {}", &candidate.name) 132 format!("Import a trait for method {}", candidate.name.text())
128 } 133 }
129 }; 134 };
130 GroupLabel(name) 135 GroupLabel(name)
diff --git a/crates/assists/src/handlers/generate_function.rs b/crates/assists/src/handlers/generate_function.rs
index 06ac85f67..1805c1dfd 100644
--- a/crates/assists/src/handlers/generate_function.rs
+++ b/crates/assists/src/handlers/generate_function.rs
@@ -158,11 +158,11 @@ impl FunctionBuilder {
158 it.text_range().end() 158 it.text_range().end()
159 } 159 }
160 GeneratedFunctionTarget::InEmptyItemList(it) => { 160 GeneratedFunctionTarget::InEmptyItemList(it) => {
161 let indent = IndentLevel::from_node(it.syntax()); 161 let indent = IndentLevel::from_node(&it);
162 leading_ws = format!("\n{}", indent + 1); 162 leading_ws = format!("\n{}", indent + 1);
163 fn_def = fn_def.indent(indent + 1); 163 fn_def = fn_def.indent(indent + 1);
164 trailing_ws = format!("\n{}", indent); 164 trailing_ws = format!("\n{}", indent);
165 it.syntax().text_range().start() + TextSize::of('{') 165 it.text_range().start() + TextSize::of('{')
166 } 166 }
167 }; 167 };
168 168
@@ -179,14 +179,14 @@ impl FunctionBuilder {
179 179
180enum GeneratedFunctionTarget { 180enum GeneratedFunctionTarget {
181 BehindItem(SyntaxNode), 181 BehindItem(SyntaxNode),
182 InEmptyItemList(ast::ItemList), 182 InEmptyItemList(SyntaxNode),
183} 183}
184 184
185impl GeneratedFunctionTarget { 185impl GeneratedFunctionTarget {
186 fn syntax(&self) -> &SyntaxNode { 186 fn syntax(&self) -> &SyntaxNode {
187 match self { 187 match self {
188 GeneratedFunctionTarget::BehindItem(it) => it, 188 GeneratedFunctionTarget::BehindItem(it) => it,
189 GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), 189 GeneratedFunctionTarget::InEmptyItemList(it) => it,
190 } 190 }
191 } 191 }
192} 192}
@@ -323,7 +323,16 @@ fn next_space_for_fn_in_module(
323 if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) { 323 if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
324 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) 324 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
325 } else { 325 } else {
326 GeneratedFunctionTarget::InEmptyItemList(it.item_list()?) 326 GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone())
327 }
328 }
329 hir::ModuleSource::BlockExpr(it) => {
330 if let Some(last_item) =
331 it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last()
332 {
333 GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
334 } else {
335 GeneratedFunctionTarget::InEmptyItemList(it.syntax().clone())
327 } 336 }
328 } 337 }
329 }; 338 };
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs
index 9af45192b..827477272 100644
--- a/crates/assists/src/handlers/generate_impl.rs
+++ b/crates/assists/src/handlers/generate_impl.rs
@@ -1,6 +1,9 @@
1use itertools::Itertools; 1use itertools::Itertools;
2use stdx::format_to; 2use stdx::format_to;
3use syntax::ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner}; 3use syntax::{
4 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
5 SmolStr,
6};
4 7
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
6 9
@@ -49,16 +52,16 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
49 format_to!(buf, "{}", type_params.syntax()); 52 format_to!(buf, "{}", type_params.syntax());
50 } 53 }
51 buf.push_str(" "); 54 buf.push_str(" ");
52 buf.push_str(name.text().as_str()); 55 buf.push_str(name.text());
53 if let Some(type_params) = type_params { 56 if let Some(type_params) = type_params {
54 let lifetime_params = type_params 57 let lifetime_params = type_params
55 .lifetime_params() 58 .lifetime_params()
56 .filter_map(|it| it.lifetime()) 59 .filter_map(|it| it.lifetime())
57 .map(|it| it.text().clone()); 60 .map(|it| SmolStr::from(it.text()));
58 let type_params = type_params 61 let type_params = type_params
59 .type_params() 62 .type_params()
60 .filter_map(|it| it.name()) 63 .filter_map(|it| it.name())
61 .map(|it| it.text().clone()); 64 .map(|it| SmolStr::from(it.text()));
62 65
63 let generic_params = lifetime_params.chain(type_params).format(", "); 66 let generic_params = lifetime_params.chain(type_params).format(", ");
64 format_to!(buf, "<{}>", generic_params) 67 format_to!(buf, "<{}>", generic_params)
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs
index 5c52b2bc8..b7390855a 100644
--- a/crates/assists/src/handlers/generate_new.rs
+++ b/crates/assists/src/handlers/generate_new.rs
@@ -3,7 +3,7 @@ use itertools::Itertools;
3use stdx::format_to; 3use stdx::format_to;
4use syntax::{ 4use syntax::{
5 ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner}, 5 ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
6 T, 6 SmolStr, T,
7}; 7};
8 8
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 9use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -95,14 +95,14 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
95 format_to!(buf, "{}", type_params.syntax()); 95 format_to!(buf, "{}", type_params.syntax());
96 } 96 }
97 buf.push_str(" "); 97 buf.push_str(" ");
98 buf.push_str(strukt.name().unwrap().text().as_str()); 98 buf.push_str(strukt.name().unwrap().text());
99 if let Some(type_params) = type_params { 99 if let Some(type_params) = type_params {
100 let lifetime_params = type_params 100 let lifetime_params = type_params
101 .lifetime_params() 101 .lifetime_params()
102 .filter_map(|it| it.lifetime()) 102 .filter_map(|it| it.lifetime())
103 .map(|it| it.text().clone()); 103 .map(|it| SmolStr::from(it.text()));
104 let type_params = 104 let type_params =
105 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 105 type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text()));
106 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) 106 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
107 } 107 }
108 108
diff --git a/crates/assists/src/handlers/introduce_named_lifetime.rs b/crates/assists/src/handlers/introduce_named_lifetime.rs
index 3f5f44d69..02782eb6d 100644
--- a/crates/assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/assists/src/handlers/introduce_named_lifetime.rs
@@ -61,7 +61,7 @@ fn generate_fn_def_assist(
61 // compute the location which implicitly has the same lifetime as the anonymous lifetime 61 // compute the location which implicitly has the same lifetime as the anonymous lifetime
62 let loc_needing_lifetime = if let Some(self_param) = self_param { 62 let loc_needing_lifetime = if let Some(self_param) = self_param {
63 // if we have a self reference, use that 63 // if we have a self reference, use that
64 Some(self_param.self_token()?.text_range().start()) 64 Some(self_param.name()?.syntax().text_range().start())
65 } else { 65 } else {
66 // otherwise, if there's a single reference parameter without a named liftime, use that 66 // otherwise, if there's a single reference parameter without a named liftime, use that
67 let fn_params_without_lifetime: Vec<_> = param_list 67 let fn_params_without_lifetime: Vec<_> = param_list
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index a7d9fd4dc..b0b0d31b4 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,10 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::{AsAssocItem, AsName};
4use ide_db::helpers::{ 4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast};
5 import_assets::{ImportAssets, ImportCandidate},
6 mod_path_to_ast,
7};
8use ide_db::RootDatabase; 5use ide_db::RootDatabase;
9use syntax::{ 6use syntax::{
10 ast, 7 ast,
@@ -18,6 +15,8 @@ use crate::{
18 AssistId, AssistKind, GroupLabel, 15 AssistId, AssistKind, GroupLabel,
19}; 16};
20 17
18use super::auto_import::find_importable_node;
19
21// Assist: qualify_path 20// Assist: qualify_path
22// 21//
23// If the name is unresolved, provides all possible qualified paths for it. 22// If the name is unresolved, provides all possible qualified paths for it.
@@ -36,47 +35,38 @@ use crate::{
36// # pub mod std { pub mod collections { pub struct HashMap { } } } 35// # pub mod std { pub mod collections { pub struct HashMap { } } }
37// ``` 36// ```
38pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let import_assets = 38 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
40 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
41 ImportAssets::for_regular_path(path_under_caret, &ctx.sema)
42 } else if let Some(method_under_caret) =
43 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
44 {
45 ImportAssets::for_method_call(method_under_caret, &ctx.sema)
46 } else {
47 None
48 }?;
49 let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); 39 let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema);
50 if proposed_imports.is_empty() { 40 if proposed_imports.is_empty() {
51 return None; 41 return None;
52 } 42 }
53 43
54 let candidate = import_assets.import_candidate(); 44 let candidate = import_assets.import_candidate();
55 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 45 let range = ctx.sema.original_range(&syntax_under_caret).range;
56 46
57 let qualify_candidate = match candidate { 47 let qualify_candidate = match candidate {
58 ImportCandidate::Path(candidate) => { 48 ImportCandidate::Path(candidate) => {
59 if candidate.qualifier.is_some() { 49 if candidate.qualifier.is_some() {
60 mark::hit!(qualify_path_qualifier_start); 50 mark::hit!(qualify_path_qualifier_start);
61 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 51 let path = ast::Path::cast(syntax_under_caret)?;
62 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
63 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
64 } else { 54 } else {
65 mark::hit!(qualify_path_unqualified_name); 55 mark::hit!(qualify_path_unqualified_name);
66 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 56 let path = ast::Path::cast(syntax_under_caret)?;
67 let generics = path.segment()?.generic_arg_list(); 57 let generics = path.segment()?.generic_arg_list();
68 QualifyCandidate::UnqualifiedName(generics) 58 QualifyCandidate::UnqualifiedName(generics)
69 } 59 }
70 } 60 }
71 ImportCandidate::TraitAssocItem(_) => { 61 ImportCandidate::TraitAssocItem(_) => {
72 mark::hit!(qualify_path_trait_assoc_item); 62 mark::hit!(qualify_path_trait_assoc_item);
73 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 63 let path = ast::Path::cast(syntax_under_caret)?;
74 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 64 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
75 QualifyCandidate::TraitAssocItem(qualifier, segment) 65 QualifyCandidate::TraitAssocItem(qualifier, segment)
76 } 66 }
77 ImportCandidate::TraitMethod(_) => { 67 ImportCandidate::TraitMethod(_) => {
78 mark::hit!(qualify_path_trait_method); 68 mark::hit!(qualify_path_trait_method);
79 let mcall_expr = ast::MethodCallExpr::cast(import_assets.syntax_under_caret().clone())?; 69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
80 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
81 } 71 }
82 }; 72 };
@@ -140,7 +130,7 @@ impl QualifyCandidate<'_> {
140 let generics = 130 let generics =
141 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); 131 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
142 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); 132 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
143 let trait_ = item_as_trait(item)?; 133 let trait_ = item_as_trait(db, item)?;
144 let method = find_trait_method(db, trait_, &trait_method_name)?; 134 let method = find_trait_method(db, trait_, &trait_method_name)?;
145 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { 135 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
146 let receiver = match self_access { 136 let receiver = match self_access {
@@ -179,11 +169,13 @@ fn find_trait_method(
179 } 169 }
180} 170}
181 171
182fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> { 172fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
183 if let hir::ModuleDef::Trait(trait_) = hir::ModuleDef::from(item.as_module_def_id()?) { 173 let item_module_def = hir::ModuleDef::from(item.as_module_def_id()?);
174
175 if let hir::ModuleDef::Trait(trait_) = item_module_def {
184 Some(trait_) 176 Some(trait_)
185 } else { 177 } else {
186 None 178 item_module_def.as_assoc_item(db)?.containing_trait(db)
187 } 179 }
188} 180}
189 181
@@ -191,7 +183,8 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
191 let name = match candidate { 183 let name = match candidate {
192 ImportCandidate::Path(it) => &it.name, 184 ImportCandidate::Path(it) => &it.name,
193 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 185 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
194 }; 186 }
187 .text();
195 GroupLabel(format!("Qualify {}", name)) 188 GroupLabel(format!("Qualify {}", name))
196} 189}
197 190
diff --git a/crates/assists/src/handlers/raw_string.rs b/crates/assists/src/handlers/raw_string.rs
index be963f162..d95267607 100644
--- a/crates/assists/src/handlers/raw_string.rs
+++ b/crates/assists/src/handlers/raw_string.rs
@@ -138,7 +138,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
138 return None; 138 return None;
139 } 139 }
140 140
141 let text = token.text().as_str(); 141 let text = token.text();
142 if !text.starts_with("r#") && text.ends_with('#') { 142 if !text.starts_with("r#") && text.ends_with('#') {
143 return None; 143 return None;
144 } 144 }
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index bd4c1c806..6aa9d2f2c 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -3,7 +3,7 @@ use ide_db::imports_locator;
3use itertools::Itertools; 3use itertools::Itertools;
4use syntax::{ 4use syntax::{
5 ast::{self, make, AstNode}, 5 ast::{self, make, AstNode},
6 Direction, SmolStr, 6 Direction,
7 SyntaxKind::{IDENT, WHITESPACE}, 7 SyntaxKind::{IDENT, WHITESPACE},
8 TextSize, 8 TextSize,
9}; 9};
@@ -43,17 +43,18 @@ pub(crate) fn replace_derive_with_manual_impl(
43) -> Option<()> { 43) -> Option<()> {
44 let attr = ctx.find_node_at_offset::<ast::Attr>()?; 44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
45 45
46 let attr_name = attr 46 let has_derive = attr
47 .syntax() 47 .syntax()
48 .descendants_with_tokens() 48 .descendants_with_tokens()
49 .filter(|t| t.kind() == IDENT) 49 .filter(|t| t.kind() == IDENT)
50 .find_map(syntax::NodeOrToken::into_token) 50 .find_map(syntax::NodeOrToken::into_token)
51 .filter(|t| t.text() == "derive")? 51 .filter(|t| t.text() == "derive")
52 .text() 52 .is_some();
53 .clone(); 53 if !has_derive {
54 return None;
55 }
54 56
55 let trait_token = 57 let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?;
56 ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
57 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); 58 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
58 59
59 let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; 60 let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
@@ -176,9 +177,9 @@ fn update_attribute(
176 .syntax() 177 .syntax()
177 .descendants_with_tokens() 178 .descendants_with_tokens()
178 .filter(|t| t.kind() == IDENT) 179 .filter(|t| t.kind() == IDENT)
179 .filter_map(|t| t.into_token().map(|t| t.text().clone())) 180 .filter_map(|t| t.into_token().map(|t| t.text().to_string()))
180 .filter(|t| t != trait_name.text()) 181 .filter(|t| t != trait_name.text())
181 .collect::<Vec<SmolStr>>(); 182 .collect::<Vec<_>>();
182 let has_more_derives = !new_attr_input.is_empty(); 183 let has_more_derives = !new_attr_input.is_empty();
183 184
184 if has_more_derives { 185 if has_more_derives {
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index fc9f83bab..44c35bafa 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -223,7 +223,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
223 let method = mce.name_ref()?; 223 let method = mce.name_ref()?;
224 let arg_list = mce.arg_list()?; 224 let arg_list = mce.arg_list()?;
225 225
226 let method = match method.text().as_str() { 226 let method = match method.text() {
227 "is_some" => "is_none", 227 "is_some" => "is_none",
228 "is_none" => "is_some", 228 "is_none" => "is_some",
229 "is_ok" => "is_err", 229 "is_ok" => "is_err",
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 2dd8fbe67..b5f7e4200 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -178,7 +178,7 @@ pub struct CrateData {
178 pub root_file_id: FileId, 178 pub root_file_id: FileId,
179 pub edition: Edition, 179 pub edition: Edition,
180 /// A name used in the package's project declaration: for Cargo projects, 180 /// A name used in the package's project declaration: for Cargo projects,
181 /// it's [package].name, can be different for other project types or even 181 /// its `[package].name` can be different for other project types or even
182 /// absent (a dummy crate for the code snippet, for example). 182 /// absent (a dummy crate for the code snippet, for example).
183 /// 183 ///
184 /// For purposes of analysis, crates are anonymous (only names in 184 /// For purposes of analysis, crates are anonymous (only names in
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index e5522980d..ab25a8c58 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -99,13 +99,14 @@ const ATTRIBUTES: &[AttrCompletion] = &[
99 Some("export_name"), 99 Some("export_name"),
100 Some(r#"export_name = "${0:exported_symbol_name}""#), 100 Some(r#"export_name = "${0:exported_symbol_name}""#),
101 ), 101 ),
102 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
102 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), 103 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
103 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 104 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
104 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 105 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
105 // FIXME: resolve through macro resolution? 106 // FIXME: resolve through macro resolution?
106 attr("global_allocator", None, None).prefer_inner(), 107 attr("global_allocator", None, None).prefer_inner(),
107 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 108 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
108 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")), 109 attr("inline", Some("inline"), Some("inline")),
109 attr("link", None, None), 110 attr("link", None, None),
110 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)), 111 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
111 attr( 112 attr(
@@ -468,10 +469,11 @@ struct Test {}
468 at deprecated 469 at deprecated
469 at derive(…) 470 at derive(…)
470 at export_name = "…" 471 at export_name = "…"
472 at doc(alias = "…")
471 at doc = "…" 473 at doc = "…"
472 at forbid(…) 474 at forbid(…)
473 at ignore = "…" 475 at ignore = "…"
474 at inline(…) 476 at inline
475 at link 477 at link
476 at link_name = "…" 478 at link_name = "…"
477 at link_section = "…" 479 at link_section = "…"
@@ -515,12 +517,13 @@ struct Test {}
515 at deprecated 517 at deprecated
516 at derive(…) 518 at derive(…)
517 at export_name = "…" 519 at export_name = "…"
520 at doc(alias = "…")
518 at doc = "…" 521 at doc = "…"
519 at feature(…) 522 at feature(…)
520 at forbid(…) 523 at forbid(…)
521 at global_allocator 524 at global_allocator
522 at ignore = "…" 525 at ignore = "…"
523 at inline(…) 526 at inline
524 at link 527 at link
525 at link_name = "…" 528 at link_name = "…"
526 at link_section = "…" 529 at link_section = "…"
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
index 222809638..dc0b38a16 100644
--- a/crates/completion/src/completions/flyimport.rs
+++ b/crates/completion/src/completions/flyimport.rs
@@ -20,11 +20,14 @@
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } } 20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports.
24//!
23//! .Fuzzy search details 25//! .Fuzzy search details
24//! 26//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only 27//! 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). 28//! (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. 29//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols
30//! (but shows all associated items for any input length).
28//! 31//!
29//! .Import configuration 32//! .Import configuration
30//! 33//!
@@ -45,10 +48,12 @@
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 48//! 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. 49//! capability enabled.
47 50
48use either::Either; 51use hir::{AsAssocItem, ModPath, ScopeDef};
49use hir::{ModPath, ScopeDef}; 52use ide_db::helpers::{
50use ide_db::{helpers::insert_use::ImportScope, imports_locator}; 53 import_assets::{ImportAssets, ImportCandidate},
51use syntax::AstNode; 54 insert_use::ImportScope,
55};
56use syntax::{AstNode, SyntaxNode, T};
52use test_utils::mark; 57use test_utils::mark;
53 58
54use crate::{ 59use crate::{
@@ -60,58 +65,108 @@ use crate::{
60use super::Completions; 65use super::Completions;
61 66
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 67pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions { 68 if !ctx.config.enable_imports_on_the_fly {
64 return None; 69 return None;
65 } 70 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { 71 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None; 72 return None;
68 } 73 }
69 let potential_import_name = ctx.token.to_string(); 74 let potential_import_name = {
70 if potential_import_name.len() < 2 { 75 let token_kind = ctx.token.kind();
71 return None; 76 if matches!(token_kind, T![.] | T![::]) {
72 } 77 String::new()
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); 78 } else {
79 ctx.token.to_string()
80 }
81 };
74 82
75 let current_module = ctx.scope.module()?; 83 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78 84
79 let user_input_lowercased = potential_import_name.to_lowercase(); 85 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports( 86 let import_assets = import_assets(ctx, potential_import_name)?;
87 let import_scope = ImportScope::find_insert_use_container(
88 position_for_import(ctx, Some(import_assets.import_candidate()))?,
81 &ctx.sema, 89 &ctx.sema,
82 ctx.krate?, 90 )?;
83 Some(40), 91 let mut all_mod_paths = import_assets
84 potential_import_name, 92 .search_for_relative_paths(&ctx.sema)
85 true, 93 .into_iter()
86 true, 94 .map(|(mod_path, item_in_ns)| {
87 ) 95 let scope_item = match item_in_ns {
88 .filter_map(|import_candidate| { 96 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
89 Some(match import_candidate { 97 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
90 Either::Left(module_def) => { 98 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) 99 };
92 } 100 (mod_path, scope_item)
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 }) 101 })
97 }) 102 .collect::<Vec<_>>();
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 103 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 104 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 }); 105 });
104 106
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { 107 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import( 108 let import_for_trait_assoc_item = match definition {
107 RenderContext::new(ctx), 109 ScopeDef::ModuleDef(module_def) => module_def
108 ImportEdit { import_path, import_scope: import_scope.clone() }, 110 .as_assoc_item(ctx.db)
109 &definition, 111 .and_then(|assoc| assoc.containing_trait(ctx.db))
110 ) 112 .is_some(),
113 _ => false,
114 };
115 let import_edit = ImportEdit {
116 import_path,
117 import_scope: import_scope.clone(),
118 import_for_trait_assoc_item,
119 };
120 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
111 })); 121 }));
112 Some(()) 122 Some(())
113} 123}
114 124
125pub(crate) fn position_for_import<'a>(
126 ctx: &'a CompletionContext,
127 import_candidate: Option<&ImportCandidate>,
128) -> Option<&'a SyntaxNode> {
129 Some(match import_candidate {
130 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
131 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
132 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
133 None => ctx
134 .name_ref_syntax
135 .as_ref()
136 .map(|name_ref| name_ref.syntax())
137 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
138 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
139 })
140}
141
142fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
143 let current_module = ctx.scope.module()?;
144 if let Some(dot_receiver) = &ctx.dot_receiver {
145 ImportAssets::for_fuzzy_method_call(
146 current_module,
147 ctx.sema.type_of_expr(dot_receiver)?,
148 fuzzy_name,
149 )
150 } else {
151 let fuzzy_name_length = fuzzy_name.len();
152 let assets_for_path = ImportAssets::for_fuzzy_path(
153 current_module,
154 ctx.path_qual.clone(),
155 fuzzy_name,
156 &ctx.sema,
157 );
158
159 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
160 && fuzzy_name_length < 2
161 {
162 mark::hit!(ignore_short_input_for_path);
163 None
164 } else {
165 assets_for_path
166 }
167 }
168}
169
115fn compute_fuzzy_completion_order_key( 170fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath, 171 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str, 172 user_input_lowercased: &str,
@@ -224,6 +279,30 @@ fn main() {
224 } 279 }
225 280
226 #[test] 281 #[test]
282 fn short_paths_are_ignored() {
283 mark::check!(ignore_short_input_for_path);
284
285 check(
286 r#"
287//- /lib.rs crate:dep
288pub struct FirstStruct;
289pub mod some_module {
290 pub struct SecondStruct;
291 pub struct ThirdStruct;
292}
293
294//- /main.rs crate:main deps:dep
295use dep::{FirstStruct, some_module::SecondStruct};
296
297fn main() {
298 t$0
299}
300"#,
301 expect![[r#""#]],
302 );
303 }
304
305 #[test]
227 fn fuzzy_completions_come_in_specific_order() { 306 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test); 307 mark::check!(certain_fuzzy_order_test);
229 check( 308 check(
@@ -259,6 +338,176 @@ fn main() {
259 } 338 }
260 339
261 #[test] 340 #[test]
341 fn trait_function_fuzzy_completion() {
342 let fixture = r#"
343 //- /lib.rs crate:dep
344 pub mod test_mod {
345 pub trait TestTrait {
346 const SPECIAL_CONST: u8;
347 type HumbleType;
348 fn weird_function();
349 fn random_method(&self);
350 }
351 pub struct TestStruct {}
352 impl TestTrait for TestStruct {
353 const SPECIAL_CONST: u8 = 42;
354 type HumbleType = ();
355 fn weird_function() {}
356 fn random_method(&self) {}
357 }
358 }
359
360 //- /main.rs crate:main deps:dep
361 fn main() {
362 dep::test_mod::TestStruct::wei$0
363 }
364 "#;
365
366 check(
367 fixture,
368 expect![[r#"
369 fn weird_function() (dep::test_mod::TestTrait) fn weird_function()
370 "#]],
371 );
372
373 check_edit(
374 "weird_function",
375 fixture,
376 r#"
377use dep::test_mod::TestTrait;
378
379fn main() {
380 dep::test_mod::TestStruct::weird_function()$0
381}
382"#,
383 );
384 }
385
386 #[test]
387 fn trait_const_fuzzy_completion() {
388 let fixture = r#"
389 //- /lib.rs crate:dep
390 pub mod test_mod {
391 pub trait TestTrait {
392 const SPECIAL_CONST: u8;
393 type HumbleType;
394 fn weird_function();
395 fn random_method(&self);
396 }
397 pub struct TestStruct {}
398 impl TestTrait for TestStruct {
399 const SPECIAL_CONST: u8 = 42;
400 type HumbleType = ();
401 fn weird_function() {}
402 fn random_method(&self) {}
403 }
404 }
405
406 //- /main.rs crate:main deps:dep
407 fn main() {
408 dep::test_mod::TestStruct::spe$0
409 }
410 "#;
411
412 check(
413 fixture,
414 expect![[r#"
415 ct SPECIAL_CONST (dep::test_mod::TestTrait)
416 "#]],
417 );
418
419 check_edit(
420 "SPECIAL_CONST",
421 fixture,
422 r#"
423use dep::test_mod::TestTrait;
424
425fn main() {
426 dep::test_mod::TestStruct::SPECIAL_CONST
427}
428"#,
429 );
430 }
431
432 #[test]
433 fn trait_method_fuzzy_completion() {
434 let fixture = r#"
435 //- /lib.rs crate:dep
436 pub mod test_mod {
437 pub trait TestTrait {
438 const SPECIAL_CONST: u8;
439 type HumbleType;
440 fn weird_function();
441 fn random_method(&self);
442 }
443 pub struct TestStruct {}
444 impl TestTrait for TestStruct {
445 const SPECIAL_CONST: u8 = 42;
446 type HumbleType = ();
447 fn weird_function() {}
448 fn random_method(&self) {}
449 }
450 }
451
452 //- /main.rs crate:main deps:dep
453 fn main() {
454 let test_struct = dep::test_mod::TestStruct {};
455 test_struct.ran$0
456 }
457 "#;
458
459 check(
460 fixture,
461 expect![[r#"
462 me random_method() (dep::test_mod::TestTrait) fn random_method(&self)
463 "#]],
464 );
465
466 check_edit(
467 "random_method",
468 fixture,
469 r#"
470use dep::test_mod::TestTrait;
471
472fn main() {
473 let test_struct = dep::test_mod::TestStruct {};
474 test_struct.random_method()$0
475}
476"#,
477 );
478 }
479
480 #[test]
481 fn no_trait_type_fuzzy_completion() {
482 check(
483 r#"
484//- /lib.rs crate:dep
485pub mod test_mod {
486 pub trait TestTrait {
487 const SPECIAL_CONST: u8;
488 type HumbleType;
489 fn weird_function();
490 fn random_method(&self);
491 }
492 pub struct TestStruct {}
493 impl TestTrait for TestStruct {
494 const SPECIAL_CONST: u8 = 42;
495 type HumbleType = ();
496 fn weird_function() {}
497 fn random_method(&self) {}
498 }
499}
500
501//- /main.rs crate:main deps:dep
502fn main() {
503 dep::test_mod::TestStruct::hum$0
504}
505"#,
506 expect![[r#""#]],
507 );
508 }
509
510 #[test]
262 fn does_not_propose_names_in_scope() { 511 fn does_not_propose_names_in_scope() {
263 check( 512 check(
264 r#" 513 r#"
@@ -288,4 +537,131 @@ fn main() {
288 expect![[r#""#]], 537 expect![[r#""#]],
289 ); 538 );
290 } 539 }
540
541 #[test]
542 fn does_not_propose_traits_in_scope() {
543 check(
544 r#"
545//- /lib.rs crate:dep
546pub mod test_mod {
547 pub trait TestTrait {
548 const SPECIAL_CONST: u8;
549 type HumbleType;
550 fn weird_function();
551 fn random_method(&self);
552 }
553 pub struct TestStruct {}
554 impl TestTrait for TestStruct {
555 const SPECIAL_CONST: u8 = 42;
556 type HumbleType = ();
557 fn weird_function() {}
558 fn random_method(&self) {}
559 }
560}
561
562//- /main.rs crate:main deps:dep
563use dep::test_mod::{TestStruct, TestTrait};
564fn main() {
565 dep::test_mod::TestStruct::hum$0
566}
567"#,
568 expect![[r#""#]],
569 );
570 }
571
572 #[test]
573 fn blanket_trait_impl_import() {
574 check_edit(
575 "another_function",
576 r#"
577//- /lib.rs crate:dep
578pub mod test_mod {
579 pub struct TestStruct {}
580 pub trait TestTrait {
581 fn another_function();
582 }
583 impl<T> TestTrait for T {
584 fn another_function() {}
585 }
586}
587
588//- /main.rs crate:main deps:dep
589fn main() {
590 dep::test_mod::TestStruct::ano$0
591}
592"#,
593 r#"
594use dep::test_mod::TestTrait;
595
596fn main() {
597 dep::test_mod::TestStruct::another_function()$0
598}
599"#,
600 );
601 }
602
603 #[test]
604 fn zero_input_deprecated_assoc_item_completion() {
605 check(
606 r#"
607//- /lib.rs crate:dep
608pub mod test_mod {
609 #[deprecated]
610 pub trait TestTrait {
611 const SPECIAL_CONST: u8;
612 type HumbleType;
613 fn weird_function();
614 fn random_method(&self);
615 }
616 pub struct TestStruct {}
617 impl TestTrait for TestStruct {
618 const SPECIAL_CONST: u8 = 42;
619 type HumbleType = ();
620 fn weird_function() {}
621 fn random_method(&self) {}
622 }
623}
624
625//- /main.rs crate:main deps:dep
626fn main() {
627 let test_struct = dep::test_mod::TestStruct {};
628 test_struct.$0
629}
630 "#,
631 expect![[r#"
632 me random_method() (dep::test_mod::TestTrait) fn random_method(&self) DEPRECATED
633 "#]],
634 );
635
636 check(
637 r#"
638//- /lib.rs crate:dep
639pub mod test_mod {
640 #[deprecated]
641 pub trait TestTrait {
642 const SPECIAL_CONST: u8;
643 type HumbleType;
644 fn weird_function();
645 fn random_method(&self);
646 }
647 pub struct TestStruct {}
648 impl TestTrait for TestStruct {
649 const SPECIAL_CONST: u8 = 42;
650 type HumbleType = ();
651 fn weird_function() {}
652 fn random_method(&self) {}
653 }
654}
655
656//- /main.rs crate:main deps:dep
657fn main() {
658 dep::test_mod::TestStruct::$0
659}
660"#,
661 expect![[r#"
662 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
663 fn weird_function() (dep::test_mod::TestTrait) fn weird_function() DEPRECATED
664 "#]],
665 );
666 }
291} 667}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index ac5596ca4..809e1645a 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -29,6 +29,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
29 } 29 }
30 30
31 ctx.scope.process_all_names(&mut |name, res| { 31 ctx.scope.process_all_names(&mut |name, res| {
32 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
33 mark::hit!(skip_lifetime_completion);
34 return;
35 }
32 if ctx.use_item_syntax.is_some() { 36 if ctx.use_item_syntax.is_some() {
33 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 37 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
34 if name_ref.syntax().text() == name.to_string().as_str() { 38 if name_ref.syntax().text() == name.to_string().as_str() {
@@ -37,7 +41,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
37 } 41 }
38 } 42 }
39 } 43 }
40 acc.add_resolution(ctx, name.to_string(), &res) 44 acc.add_resolution(ctx, name.to_string(), &res);
41 }); 45 });
42} 46}
43 47
@@ -234,6 +238,24 @@ fn main() {
234 fn quux() fn quux<T>() 238 fn quux() fn quux<T>()
235 "#]], 239 "#]],
236 ); 240 );
241 check(
242 r#"fn quux<const C: usize>() { $0 }"#,
243 expect![[r#"
244 tp C
245 fn quux() fn quux<const C: usize>()
246 "#]],
247 );
248 }
249
250 #[test]
251 fn does_not_complete_lifetimes() {
252 mark::check!(skip_lifetime_completion);
253 check(
254 r#"fn quux<'a>() { $0 }"#,
255 expect![[r#"
256 fn quux() fn quux<'a>()
257 "#]],
258 );
237 } 259 }
238 260
239 #[test] 261 #[test]
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 58fc700f3..d70ed6c1c 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -9,7 +9,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_autoimport_completions: bool, 12 pub enable_imports_on_the_fly: bool,
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>,
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 0134ff219..4147853e7 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -270,6 +270,7 @@ impl CompletionItem {
270pub struct ImportEdit { 270pub struct ImportEdit {
271 pub import_path: ModPath, 271 pub import_path: ModPath,
272 pub import_scope: ImportScope, 272 pub import_scope: ImportScope,
273 pub import_for_trait_assoc_item: bool,
273} 274}
274 275
275impl ImportEdit { 276impl ImportEdit {
@@ -321,17 +322,19 @@ impl Builder {
321 let mut insert_text = self.insert_text; 322 let mut insert_text = self.insert_text;
322 323
323 if let Some(import_to_add) = self.import_to_add.as_ref() { 324 if let Some(import_to_add) = self.import_to_add.as_ref() {
324 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 325 if import_to_add.import_for_trait_assoc_item {
325 let _ = import_path_without_last_segment.segments.pop(); 326 lookup = lookup.or_else(|| Some(label.clone()));
326 327 insert_text = insert_text.or_else(|| Some(label.clone()));
327 if !import_path_without_last_segment.segments.is_empty() { 328 label = format!("{} ({})", label, import_to_add.import_path);
328 if lookup.is_none() { 329 } else {
329 lookup = Some(label.clone()); 330 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
330 } 331 let _ = import_path_without_last_segment.segments.pop();
331 if insert_text.is_none() { 332
332 insert_text = Some(label.clone()); 333 if !import_path_without_last_segment.segments.is_empty() {
334 lookup = lookup.or_else(|| Some(label.clone()));
335 insert_text = insert_text.or_else(|| Some(label.clone()));
336 label = format!("{}::{}", import_path_without_last_segment, label);
333 } 337 }
334 label = format!("{}::{}", import_path_without_last_segment, label);
335 } 338 }
336 } 339 }
337 340
@@ -398,7 +401,9 @@ impl Builder {
398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 401 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
399 self.detail = detail.map(Into::into); 402 self.detail = detail.map(Into::into);
400 if let Some(detail) = &self.detail { 403 if let Some(detail) = &self.detail {
401 assert_never!(detail.contains('\n'), "multiline detail: {}", detail); 404 if assert_never!(detail.contains('\n'), "multiline detail: {}", detail) {
405 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
406 }
402 } 407 }
403 self 408 self
404 } 409 }
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index ee1b822e7..2c4e54524 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -11,10 +11,10 @@ mod render;
11 11
12mod completions; 12mod completions;
13 13
14use completions::flyimport::position_for_import;
14use ide_db::{ 15use ide_db::{
15 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
16}; 17};
17use syntax::AstNode;
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; 20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
@@ -139,12 +139,13 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 139 position: FilePosition,
140 full_import_path: &str, 140 full_import_path: &str,
141 imported_name: String, 141 imported_name: String,
142 import_for_trait_assoc_item: bool,
142) -> Option<Vec<TextEdit>> { 143) -> Option<Vec<TextEdit>> {
143 let ctx = CompletionContext::new(db, position, config)?; 144 let ctx = CompletionContext::new(db, position, config)?;
144 let anchor = ctx.name_ref_syntax.as_ref()?; 145 let position_for_import = position_for_import(&ctx, None)?;
145 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; 146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
146 147
147 let current_module = ctx.sema.scope(anchor.syntax()).module()?; 148 let current_module = ctx.sema.scope(position_for_import).module()?;
148 let current_crate = current_module.krate(); 149 let current_crate = current_module.krate();
149 150
150 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
@@ -154,7 +155,7 @@ pub fn resolve_completion_edits(
154 }) 155 })
155 .find(|mod_path| mod_path.to_string() == full_import_path)?; 156 .find(|mod_path| mod_path.to_string() == full_import_path)?;
156 157
157 ImportEdit { import_path, import_scope } 158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
158 .to_text_edit(config.insert_use.merge) 159 .to_text_edit(config.insert_use.merge)
159 .map(|edit| vec![edit]) 160 .map(|edit| vec![edit])
160} 161}
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 820dd01d1..4f622d28a 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -10,7 +10,9 @@ pub(crate) mod type_alias;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15};
14use ide_db::{helpers::SnippetCap, RootDatabase}; 16use ide_db::{helpers::SnippetCap, RootDatabase};
15use syntax::TextRange; 17use syntax::TextRange;
16use test_utils::mark; 18use test_utils::mark;
@@ -51,16 +53,16 @@ pub(crate) fn render_resolution_with_import<'a>(
51 import_edit: ImportEdit, 53 import_edit: ImportEdit,
52 resolution: &ScopeDef, 54 resolution: &ScopeDef,
53) -> Option<CompletionItem> { 55) -> Option<CompletionItem> {
54 Render::new(ctx) 56 let local_name = match resolution {
55 .render_resolution( 57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
56 import_edit.import_path.segments.last()?.to_string(), 58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
57 Some(import_edit), 59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
58 resolution, 60 _ => import_edit.import_path.segments.last()?.to_string(),
59 ) 61 };
60 .map(|mut item| { 62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
61 item.completion_kind = CompletionKind::Magic; 63 item.completion_kind = CompletionKind::Magic;
62 item 64 item
63 }) 65 })
64} 66}
65 67
66/// Interface for data and methods required for items rendering. 68/// Interface for data and methods required for items rendering.
@@ -87,7 +89,24 @@ impl<'a> RenderContext<'a> {
87 } 89 }
88 90
89 fn is_deprecated(&self, node: impl HasAttrs) -> bool { 91 fn is_deprecated(&self, node: impl HasAttrs) -> bool {
90 node.attrs(self.db()).by_key("deprecated").exists() 92 let attrs = node.attrs(self.db());
93 attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists()
94 }
95
96 fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
97 let db = self.db();
98 let assoc = match as_assoc_item.as_assoc_item(db) {
99 Some(assoc) => assoc,
100 None => return false,
101 };
102
103 let is_assoc_deprecated = match assoc {
104 hir::AssocItem::Function(it) => self.is_deprecated(it),
105 hir::AssocItem::Const(it) => self.is_deprecated(it),
106 hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
107 };
108 is_assoc_deprecated
109 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false)
91 } 110 }
92 111
93 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 112 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
@@ -206,8 +225,6 @@ impl<'a> Render<'a> {
206 } 225 }
207 }; 226 };
208 227
209 let docs = self.docs(resolution);
210
211 let mut item = 228 let mut item =
212 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); 229 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
213 if let ScopeDef::Local(local) = resolution { 230 if let ScopeDef::Local(local) = resolution {
@@ -253,13 +270,14 @@ impl<'a> Render<'a> {
253 } 270 }
254 } 271 }
255 272
256 let item = item 273 Some(
257 .kind(kind) 274 item.kind(kind)
258 .add_import(import_to_add) 275 .add_import(import_to_add)
259 .set_documentation(docs) 276 .set_ref_match(ref_match)
260 .set_ref_match(ref_match) 277 .set_documentation(self.docs(resolution))
261 .build(); 278 .set_deprecated(self.is_deprecated(resolution))
262 Some(item) 279 .build(),
280 )
263 } 281 }
264 282
265 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 283 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -275,6 +293,16 @@ impl<'a> Render<'a> {
275 _ => None, 293 _ => None,
276 } 294 }
277 } 295 }
296
297 fn is_deprecated(&self, resolution: &ScopeDef) -> bool {
298 match resolution {
299 ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it),
300 ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it),
301 ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it),
302 ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it),
303 _ => false,
304 }
305 }
278} 306}
279 307
280fn compute_score_from_active( 308fn compute_score_from_active(
@@ -485,7 +513,7 @@ fn main() { let _: m::Spam = S$0 }
485 r#" 513 r#"
486#[deprecated] 514#[deprecated]
487fn something_deprecated() {} 515fn something_deprecated() {}
488#[deprecated(since = "1.0.0")] 516#[rustc_deprecated(since = "1.0.0")]
489fn something_else_deprecated() {} 517fn something_else_deprecated() {}
490 518
491fn main() { som$0 } 519fn main() { som$0 }
@@ -494,8 +522,8 @@ fn main() { som$0 }
494 [ 522 [
495 CompletionItem { 523 CompletionItem {
496 label: "main()", 524 label: "main()",
497 source_range: 121..124, 525 source_range: 127..130,
498 delete: 121..124, 526 delete: 127..130,
499 insert: "main()$0", 527 insert: "main()$0",
500 kind: Function, 528 kind: Function,
501 lookup: "main", 529 lookup: "main",
@@ -503,8 +531,8 @@ fn main() { som$0 }
503 }, 531 },
504 CompletionItem { 532 CompletionItem {
505 label: "something_deprecated()", 533 label: "something_deprecated()",
506 source_range: 121..124, 534 source_range: 127..130,
507 delete: 121..124, 535 delete: 127..130,
508 insert: "something_deprecated()$0", 536 insert: "something_deprecated()$0",
509 kind: Function, 537 kind: Function,
510 lookup: "something_deprecated", 538 lookup: "something_deprecated",
@@ -513,8 +541,8 @@ fn main() { som$0 }
513 }, 541 },
514 CompletionItem { 542 CompletionItem {
515 label: "something_else_deprecated()", 543 label: "something_else_deprecated()",
516 source_range: 121..124, 544 source_range: 127..130,
517 delete: 121..124, 545 delete: 127..130,
518 insert: "something_else_deprecated()$0", 546 insert: "something_else_deprecated()$0",
519 kind: Function, 547 kind: Function,
520 lookup: "something_else_deprecated", 548 lookup: "something_else_deprecated",
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index ce924f309..e46452d4e 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -38,7 +38,10 @@ impl<'a> ConstRender<'a> {
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::Const) 39 .kind(CompletionItemKind::Const)
40 .set_documentation(self.ctx.docs(self.const_)) 40 .set_documentation(self.ctx.docs(self.const_))
41 .set_deprecated(self.ctx.is_deprecated(self.const_)) 41 .set_deprecated(
42 self.ctx.is_deprecated(self.const_)
43 || self.ctx.is_deprecated_assoc_item(self.const_),
44 )
42 .detail(detail) 45 .detail(detail)
43 .build(); 46 .build();
44 47
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index f5b0ce3e3..8f4c66211 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -44,7 +44,9 @@ impl<'a> FunctionRender<'a> {
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind()) 45 .kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func)) 46 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated(self.ctx.is_deprecated(self.func)) 47 .set_deprecated(
48 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
49 )
48 .detail(self.detail()) 50 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params) 51 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add) 52 .add_import(import_to_add)
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
index 69b445b9c..29287143a 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -38,7 +38,10 @@ impl<'a> TypeAliasRender<'a> {
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::TypeAlias) 39 .kind(CompletionItemKind::TypeAlias)
40 .set_documentation(self.ctx.docs(self.type_alias)) 40 .set_documentation(self.ctx.docs(self.type_alias))
41 .set_deprecated(self.ctx.is_deprecated(self.type_alias)) 41 .set_deprecated(
42 self.ctx.is_deprecated(self.type_alias)
43 || self.ctx.is_deprecated_assoc_item(self.type_alias),
44 )
42 .detail(detail) 45 .detail(detail)
43 .build(); 46 .build();
44 47
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 6ea6da989..baff83305 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -18,7 +18,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 enable_postfix_completions: true, 20 enable_postfix_completions: true,
21 enable_autoimport_completions: true, 21 enable_imports_on_the_fly: true,
22 add_call_parenthesis: true, 22 add_call_parenthesis: true,
23 add_call_argument_snippets: true, 23 add_call_argument_snippets: true,
24 snippet_cap: SnippetCap::new(true), 24 snippet_cap: SnippetCap::new(true),
@@ -83,6 +83,9 @@ pub(crate) fn completion_list_with_config(
83 let width = label_width.saturating_sub(monospace_width(it.label())); 83 let width = label_width.saturating_sub(monospace_width(it.label()));
84 format_to!(buf, "{:width$} {}", "", detail, width = width); 84 format_to!(buf, "{:width$} {}", "", detail, width = width);
85 } 85 }
86 if it.deprecated() {
87 format_to!(buf, " DEPRECATED");
88 }
86 format_to!(buf, "\n"); 89 format_to!(buf, "\n");
87 buf 90 buf
88 }) 91 })
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 99fb65bac..9e6a3e155 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -2,6 +2,7 @@
2use hir_def::{ 2use hir_def::{
3 attr::{Attrs, Documentation}, 3 attr::{Attrs, Documentation},
4 path::ModPath, 4 path::ModPath,
5 per_ns::PerNs,
5 resolver::HasResolver, 6 resolver::HasResolver,
6 AttrDefId, GenericParamId, ModuleDefId, 7 AttrDefId, GenericParamId, ModuleDefId,
7}; 8};
@@ -112,6 +113,11 @@ fn resolve_doc_path(
112 let path = ast::Path::parse(link).ok()?; 113 let path = ast::Path::parse(link).ok()?;
113 let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap(); 114 let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap();
114 let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); 115 let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
116 if resolved == PerNs::none() {
117 if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) {
118 return Some(ModuleDefId::TraitId(trait_id));
119 };
120 }
115 let def = match ns { 121 let def = match ns {
116 Some(Namespace::Types) => resolved.take_types()?, 122 Some(Namespace::Types) => resolved.take_types()?,
117 Some(Namespace::Values) => resolved.take_values()?, 123 Some(Namespace::Values) => resolved.take_values()?,
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 6cbf5cecf..aaa7013b6 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -90,7 +90,7 @@ impl Crate {
90 } 90 }
91 91
92 pub fn root_module(self, db: &dyn HirDatabase) -> Module { 92 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
93 let module_id = db.crate_def_map(self.id).root; 93 let module_id = db.crate_def_map(self.id).root();
94 Module::new(self, module_id) 94 Module::new(self, module_id)
95 } 95 }
96 96
@@ -302,7 +302,7 @@ impl Module {
302 /// in the module tree of any target in `Cargo.toml`. 302 /// in the module tree of any target in `Cargo.toml`.
303 pub fn crate_root(self, db: &dyn HirDatabase) -> Module { 303 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
304 let def_map = db.crate_def_map(self.id.krate); 304 let def_map = db.crate_def_map(self.id.krate);
305 self.with_module_id(def_map.root) 305 self.with_module_id(def_map.root())
306 } 306 }
307 307
308 /// Iterates over all child modules. 308 /// Iterates over all child modules.
@@ -1000,7 +1000,7 @@ impl MacroDef {
1000 /// early, in `hir_expand`, where modules simply do not exist yet. 1000 /// early, in `hir_expand`, where modules simply do not exist yet.
1001 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { 1001 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1002 let krate = self.id.krate; 1002 let krate = self.id.krate;
1003 let module_id = db.crate_def_map(krate).root; 1003 let module_id = db.crate_def_map(krate).root();
1004 Some(Module::new(Crate { id: krate }, module_id)) 1004 Some(Module::new(Crate { id: krate }, module_id))
1005 } 1005 }
1006 1006
@@ -1051,6 +1051,16 @@ impl AsAssocItem for TypeAlias {
1051 as_assoc_item(db, AssocItem::TypeAlias, self.id) 1051 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1052 } 1052 }
1053} 1053}
1054impl AsAssocItem for ModuleDef {
1055 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1056 match self {
1057 ModuleDef::Function(it) => it.as_assoc_item(db),
1058 ModuleDef::Const(it) => it.as_assoc_item(db),
1059 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1060 _ => None,
1061 }
1062 }
1063}
1054fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> 1064fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1055where 1065where
1056 ID: Lookup<Data = AssocItemLoc<AST>>, 1066 ID: Lookup<Data = AssocItemLoc<AST>>,
@@ -1091,6 +1101,13 @@ impl AssocItem {
1091 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), 1101 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"),
1092 } 1102 }
1093 } 1103 }
1104
1105 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1106 match self.container(db) {
1107 AssocItemContainer::Trait(t) => Some(t),
1108 _ => None,
1109 }
1110 }
1094} 1111}
1095 1112
1096impl HasVisibility for AssocItem { 1113impl HasVisibility for AssocItem {
@@ -2029,7 +2046,7 @@ impl Callable {
2029pub enum ScopeDef { 2046pub enum ScopeDef {
2030 ModuleDef(ModuleDef), 2047 ModuleDef(ModuleDef),
2031 MacroDef(MacroDef), 2048 MacroDef(MacroDef),
2032 GenericParam(TypeParam), 2049 GenericParam(GenericParam),
2033 ImplSelfType(Impl), 2050 ImplSelfType(Impl),
2034 AdtSelfType(Adt), 2051 AdtSelfType(Adt),
2035 Local(Local), 2052 Local(Local),
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index d5d4cf5b6..d444f4bbb 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, 4 AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, ExprScopesQuery, 5 CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery,
6 FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, InternConstQuery, 6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
7 InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, 7 InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
8 InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, 8 InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
9 ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, TraitDataQuery, 9 InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery,
10 TypeAliasDataQuery, UnionDataQuery, 10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index ab213e04c..0a30b4f5b 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -814,7 +814,7 @@ impl<'a> SemanticsScope<'a> {
814 } 814 }
815 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), 815 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
816 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), 816 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
817 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), 817 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
818 resolver::ScopeDef::Local(pat_id) => { 818 resolver::ScopeDef::Local(pat_id) => {
819 let parent = resolver.body_owner().unwrap().into(); 819 let parent = resolver.body_owner().unwrap().into();
820 ScopeDef::Local(Local { parent, pat_id }) 820 ScopeDef::Local(Local { parent, pat_id })
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 1b09ff816..c72649c41 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -207,6 +207,7 @@ impl Attrs {
207 mod_data.definition_source(db).as_ref().map(|src| match src { 207 mod_data.definition_source(db).as_ref().map(|src| match src {
208 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, 208 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
209 ModuleSource::Module(module) => module as &dyn AttrsOwner, 209 ModuleSource::Module(module) => module as &dyn AttrsOwner,
210 ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner,
210 }), 211 }),
211 ), 212 ),
212 } 213 }
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 344f0b6c0..2c2c999dd 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -29,7 +29,7 @@ use crate::{
29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
30 item_scope::BuiltinShadowMode, 30 item_scope::BuiltinShadowMode,
31 item_scope::ItemScope, 31 item_scope::ItemScope,
32 nameres::CrateDefMap, 32 nameres::DefMap,
33 path::{ModPath, Path}, 33 path::{ModPath, Path},
34 src::HasSource, 34 src::HasSource,
35 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 35 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
@@ -45,7 +45,7 @@ pub(crate) struct CfgExpander {
45 45
46pub(crate) struct Expander { 46pub(crate) struct Expander {
47 cfg_expander: CfgExpander, 47 cfg_expander: CfgExpander,
48 crate_def_map: Arc<CrateDefMap>, 48 crate_def_map: Arc<DefMap>,
49 current_file_id: HirFileId, 49 current_file_id: HirFileId,
50 ast_id_map: Arc<AstIdMap>, 50 ast_id_map: Arc<AstIdMap>,
51 module: ModuleId, 51 module: ModuleId,
@@ -122,7 +122,7 @@ impl Expander {
122 122
123 let mut err = None; 123 let mut err = None;
124 let call_id = 124 let call_id =
125 macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| { 125 macro_call.as_call_id_with_errors(db, self.crate_def_map.krate(), resolver, &mut |e| {
126 err.get_or_insert(e); 126 err.get_or_insert(e);
127 }); 127 });
128 let call_id = match call_id { 128 let call_id = match call_id {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index de77d5fc9..2e5d0a01e 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -6,18 +6,24 @@ use crate::{test_db::TestDB, ModuleDefId};
6use super::*; 6use super::*;
7 7
8fn lower(ra_fixture: &str) -> Arc<Body> { 8fn lower(ra_fixture: &str) -> Arc<Body> {
9 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); 9 let db = crate::test_db::TestDB::with_files(ra_fixture);
10 10
11 let krate = db.crate_graph().iter().next().unwrap(); 11 let krate = db.crate_graph().iter().next().unwrap();
12 let def_map = db.crate_def_map(krate); 12 let def_map = db.crate_def_map(krate);
13 let module = def_map.modules_for_file(file_id).next().unwrap(); 13 let mut fn_def = None;
14 let module = &def_map[module]; 14 'outer: for (_, module) in def_map.modules() {
15 let fn_def = match module.scope.declarations().next().unwrap() { 15 for decl in module.scope.declarations() {
16 ModuleDefId::FunctionId(it) => it, 16 match decl {
17 _ => panic!(), 17 ModuleDefId::FunctionId(it) => {
18 }; 18 fn_def = Some(it);
19 break 'outer;
20 }
21 _ => {}
22 }
23 }
24 }
19 25
20 db.body(fn_def.into()) 26 db.body(fn_def.unwrap().into())
21} 27}
22 28
23fn check_diagnostics(ra_fixture: &str) { 29fn check_diagnostics(ra_fixture: &str) {
@@ -42,6 +48,25 @@ fn main() { n_nuple!(1,2,3); }
42} 48}
43 49
44#[test] 50#[test]
51fn macro_resolve() {
52 // Regression test for a path resolution bug introduced with inner item handling.
53 lower(
54 r"
55macro_rules! vec {
56 () => { () };
57 ($elem:expr; $n:expr) => { () };
58 ($($x:expr),+ $(,)?) => { () };
59}
60mod m {
61 fn outer() {
62 let _ = vec![FileSet::default(); self.len()];
63 }
64}
65 ",
66 );
67}
68
69#[test]
45fn cfg_diagnostics() { 70fn cfg_diagnostics() {
46 check_diagnostics( 71 check_diagnostics(
47 r" 72 r"
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 6ef9fe790..a87c80b8a 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -2,9 +2,9 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 4use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 5use hir_expand::{db::AstDatabase, AstId, HirFileId};
6use la_arena::ArenaMap; 6use la_arena::ArenaMap;
7use syntax::SmolStr; 7use syntax::{ast, SmolStr};
8 8
9use crate::{ 9use crate::{
10 adt::{EnumData, StructData}, 10 adt::{EnumData, StructData},
@@ -15,7 +15,7 @@ use crate::{
15 import_map::ImportMap, 15 import_map::ImportMap,
16 item_tree::ItemTree, 16 item_tree::ItemTree,
17 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
18 nameres::CrateDefMap, 18 nameres::DefMap,
19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, 20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId,
21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, 21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
@@ -50,10 +50,13 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
50 50
51 #[salsa::invoke(crate_def_map_wait)] 51 #[salsa::invoke(crate_def_map_wait)]
52 #[salsa::transparent] 52 #[salsa::transparent]
53 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>; 53 fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
54 54
55 #[salsa::invoke(CrateDefMap::crate_def_map_query)] 55 #[salsa::invoke(DefMap::crate_def_map_query)]
56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<CrateDefMap>; 56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
57
58 #[salsa::invoke(DefMap::block_def_map_query)]
59 fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>;
57 60
58 #[salsa::invoke(StructData::struct_data_query)] 61 #[salsa::invoke(StructData::struct_data_query)]
59 fn struct_data(&self, id: StructId) -> Arc<StructData>; 62 fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -112,7 +115,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
112 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; 115 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
113} 116}
114 117
115fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 118fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<DefMap> {
116 let _p = profile::span("crate_def_map:wait"); 119 let _p = profile::span("crate_def_map:wait");
117 db.crate_def_map_query(krate) 120 db.crate_def_map_query(krate)
118} 121}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 4a212d291..db2d125ae 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -4,7 +4,7 @@ use hir_expand::name::{known, AsName, Name};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::nameres::CrateDefMap; 7use crate::nameres::DefMap;
8use crate::{ 8use crate::{
9 db::DefDatabase, 9 db::DefDatabase,
10 item_scope::ItemInNs, 10 item_scope::ItemInNs,
@@ -47,11 +47,11 @@ impl ModPath {
47 } 47 }
48} 48}
49 49
50fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 50fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
51 if item == ItemInNs::Types(from.into()) { 51 if item == ItemInNs::Types(from.into()) {
52 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
54 } else if let Some(parent_id) = def_map.modules[from.local_id].parent { 54 } else if let Some(parent_id) = def_map[from.local_id].parent {
55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
56 if item 56 if item
57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
@@ -111,7 +111,7 @@ fn find_path_inner(
111 111
112 // - if the item is already in scope, return the name under which it is 112 // - if the item is already in scope, return the name under which it is
113 let def_map = db.crate_def_map(from.krate); 113 let def_map = db.crate_def_map(from.krate);
114 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 114 let from_scope: &crate::item_scope::ItemScope = &def_map[from.local_id].scope;
115 let scope_name = 115 let scope_name =
116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
117 if prefixed.is_none() && scope_name.is_some() { 117 if prefixed.is_none() && scope_name.is_some() {
@@ -123,7 +123,7 @@ fn find_path_inner(
123 if item 123 if item
124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
125 krate: from.krate, 125 krate: from.krate,
126 local_id: def_map.root, 126 local_id: def_map.root(),
127 })) 127 }))
128 { 128 {
129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
@@ -136,7 +136,7 @@ fn find_path_inner(
136 } 136 }
137 137
138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
139 for (name, def_id) in &def_map.extern_prelude { 139 for (name, def_id) in def_map.extern_prelude() {
140 if item == ItemInNs::Types(*def_id) { 140 if item == ItemInNs::Types(*def_id) {
141 let name = scope_name.unwrap_or_else(|| name.clone()); 141 let name = scope_name.unwrap_or_else(|| name.clone());
142 return Some(ModPath::from_segments(PathKind::Plain, vec![name])); 142 return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -144,10 +144,10 @@ fn find_path_inner(
144 } 144 }
145 145
146 // - if the item is in the prelude, return the name from there 146 // - if the item is in the prelude, return the name from there
147 if let Some(prelude_module) = def_map.prelude { 147 if let Some(prelude_module) = def_map.prelude() {
148 let prelude_def_map = db.crate_def_map(prelude_module.krate); 148 let prelude_def_map = db.crate_def_map(prelude_module.krate);
149 let prelude_scope: &crate::item_scope::ItemScope = 149 let prelude_scope: &crate::item_scope::ItemScope =
150 &prelude_def_map.modules[prelude_module.local_id].scope; 150 &prelude_def_map[prelude_module.local_id].scope;
151 if let Some((name, vis)) = prelude_scope.name_of(item) { 151 if let Some((name, vis)) = prelude_scope.name_of(item) {
152 if vis.is_visible_from(db, from) { 152 if vis.is_visible_from(db, from) {
153 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); 153 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
@@ -175,7 +175,7 @@ fn find_path_inner(
175 175
176 // - otherwise, look for modules containing (reexporting) it and import it from one of those 176 // - otherwise, look for modules containing (reexporting) it and import it from one of those
177 177
178 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 178 let crate_root = ModuleId { local_id: def_map.root(), krate: from.krate };
179 let crate_attrs = db.attrs(crate_root.into()); 179 let crate_attrs = db.attrs(crate_root.into());
180 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 180 let prefer_no_std = crate_attrs.by_key("no_std").exists();
181 let mut best_path = None; 181 let mut best_path = None;
@@ -287,7 +287,7 @@ fn find_local_import_locations(
287 287
288 // Compute the initial worklist. We start with all direct child modules of `from` as well as all 288 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
289 // of its (recursive) parent modules. 289 // of its (recursive) parent modules.
290 let data = &def_map.modules[from.local_id]; 290 let data = &def_map[from.local_id];
291 let mut worklist = data 291 let mut worklist = data
292 .children 292 .children
293 .values() 293 .values()
@@ -296,7 +296,7 @@ fn find_local_import_locations(
296 let mut parent = data.parent; 296 let mut parent = data.parent;
297 while let Some(p) = parent { 297 while let Some(p) = parent {
298 worklist.push(ModuleId { krate: from.krate, local_id: p }); 298 worklist.push(ModuleId { krate: from.krate, local_id: p });
299 parent = def_map.modules[p].parent; 299 parent = def_map[p].parent;
300 } 300 }
301 301
302 let mut seen: FxHashSet<_> = FxHashSet::default(); 302 let mut seen: FxHashSet<_> = FxHashSet::default();
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index e5368b293..0251d016b 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -75,7 +75,7 @@ impl ImportMap {
75 75
76 // We look only into modules that are public(ly reexported), starting with the crate root. 76 // We look only into modules that are public(ly reexported), starting with the crate root.
77 let empty = ImportPath { segments: vec![] }; 77 let empty = ImportPath { segments: vec![] };
78 let root = ModuleId { krate, local_id: def_map.root }; 78 let root = ModuleId { krate, local_id: def_map.root() };
79 let mut worklist = vec![(root, empty)]; 79 let mut worklist = vec![(root, empty)];
80 while let Some((module, mod_path)) = worklist.pop() { 80 while let Some((module, mod_path)) = worklist.pop() {
81 let ext_def_map; 81 let ext_def_map;
@@ -263,6 +263,7 @@ pub enum ImportKind {
263 Trait, 263 Trait,
264 TypeAlias, 264 TypeAlias,
265 BuiltinType, 265 BuiltinType,
266 AssociatedItem,
266} 267}
267 268
268/// A way to match import map contents against the search query. 269/// A way to match import map contents against the search query.
@@ -282,6 +283,7 @@ pub struct Query {
282 query: String, 283 query: String,
283 lowercased: String, 284 lowercased: String,
284 name_only: bool, 285 name_only: bool,
286 assoc_items_only: bool,
285 search_mode: SearchMode, 287 search_mode: SearchMode,
286 case_sensitive: bool, 288 case_sensitive: bool,
287 limit: usize, 289 limit: usize,
@@ -295,6 +297,7 @@ impl Query {
295 query, 297 query,
296 lowercased, 298 lowercased,
297 name_only: false, 299 name_only: false,
300 assoc_items_only: false,
298 search_mode: SearchMode::Contains, 301 search_mode: SearchMode::Contains,
299 case_sensitive: false, 302 case_sensitive: false,
300 limit: usize::max_value(), 303 limit: usize::max_value(),
@@ -309,6 +312,11 @@ impl Query {
309 Self { name_only: true, ..self } 312 Self { name_only: true, ..self }
310 } 313 }
311 314
315 /// Matches only the entries that are associated items, ignoring the rest.
316 pub fn assoc_items_only(self) -> Self {
317 Self { assoc_items_only: true, ..self }
318 }
319
312 /// Specifies the way to search for the entries using the query. 320 /// Specifies the way to search for the entries using the query.
313 pub fn search_mode(self, search_mode: SearchMode) -> Self { 321 pub fn search_mode(self, search_mode: SearchMode) -> Self {
314 Self { search_mode, ..self } 322 Self { search_mode, ..self }
@@ -331,6 +339,14 @@ impl Query {
331 } 339 }
332 340
333 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { 341 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
342 if import.is_trait_assoc_item {
343 if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
344 return false;
345 }
346 } else if self.assoc_items_only {
347 return false;
348 }
349
334 let mut input = if import.is_trait_assoc_item || self.name_only { 350 let mut input = if import.is_trait_assoc_item || self.name_only {
335 import.path.segments.last().unwrap().to_string() 351 import.path.segments.last().unwrap().to_string()
336 } else { 352 } else {
@@ -814,6 +830,56 @@ mod tests {
814 } 830 }
815 831
816 #[test] 832 #[test]
833 fn assoc_items_filtering() {
834 let ra_fixture = r#"
835 //- /main.rs crate:main deps:dep
836 //- /dep.rs crate:dep
837 pub mod fmt {
838 pub trait Display {
839 type FmtTypeAlias;
840 const FMT_CONST: bool;
841
842 fn format_function();
843 fn format_method(&self);
844 }
845 }
846 "#;
847
848 check_search(
849 ra_fixture,
850 "main",
851 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
852 expect![[r#"
853 dep::fmt::Display::FMT_CONST (a)
854 dep::fmt::Display::format_function (a)
855 dep::fmt::Display::format_method (a)
856 "#]],
857 );
858
859 check_search(
860 ra_fixture,
861 "main",
862 Query::new("fmt".to_string())
863 .search_mode(SearchMode::Fuzzy)
864 .exclude_import_kind(ImportKind::AssociatedItem),
865 expect![[r#"
866 dep::fmt (t)
867 dep::fmt::Display (t)
868 "#]],
869 );
870
871 check_search(
872 ra_fixture,
873 "main",
874 Query::new("fmt".to_string())
875 .search_mode(SearchMode::Fuzzy)
876 .assoc_items_only()
877 .exclude_import_kind(ImportKind::AssociatedItem),
878 expect![[r#""#]],
879 );
880 }
881
882 #[test]
817 fn search_mode() { 883 fn search_mode() {
818 let ra_fixture = r#" 884 let ra_fixture = r#"
819 //- /main.rs crate:main deps:dep 885 //- /main.rs crate:main deps:dep
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 9a433b61c..b8d7608e7 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -21,6 +21,7 @@ use hir_expand::{
21 HirFileId, InFile, 21 HirFileId, InFile,
22}; 22};
23use la_arena::{Arena, Idx, RawIdx}; 23use la_arena::{Arena, Idx, RawIdx};
24use profile::Count;
24use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
25use smallvec::SmallVec; 26use smallvec::SmallVec;
26use syntax::{ast, match_ast}; 27use syntax::{ast, match_ast};
@@ -67,15 +68,16 @@ impl GenericParamsId {
67/// The item tree of a source file. 68/// The item tree of a source file.
68#[derive(Debug, Eq, PartialEq)] 69#[derive(Debug, Eq, PartialEq)]
69pub struct ItemTree { 70pub struct ItemTree {
71 _c: Count<Self>,
72
70 top_level: SmallVec<[ModItem; 1]>, 73 top_level: SmallVec<[ModItem; 1]>,
71 attrs: FxHashMap<AttrOwner, RawAttrs>, 74 attrs: FxHashMap<AttrOwner, RawAttrs>,
72 inner_items: FxHashMap<FileAstId<ast::Item>, SmallVec<[ModItem; 1]>>,
73 75
74 data: Option<Box<ItemTreeData>>, 76 data: Option<Box<ItemTreeData>>,
75} 77}
76 78
77impl ItemTree { 79impl ItemTree {
78 pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { 80 pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
79 let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); 81 let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
80 let syntax = if let Some(node) = db.parse_or_expand(file_id) { 82 let syntax = if let Some(node) = db.parse_or_expand(file_id) {
81 node 83 node
@@ -118,9 +120,9 @@ impl ItemTree {
118 120
119 fn empty() -> Self { 121 fn empty() -> Self {
120 Self { 122 Self {
123 _c: Count::new(),
121 top_level: Default::default(), 124 top_level: Default::default(),
122 attrs: Default::default(), 125 attrs: Default::default(),
123 inner_items: Default::default(),
124 data: Default::default(), 126 data: Default::default(),
125 } 127 }
126 } 128 }
@@ -145,9 +147,9 @@ impl ItemTree {
145 macro_calls, 147 macro_calls,
146 macro_rules, 148 macro_rules,
147 macro_defs, 149 macro_defs,
148 exprs,
149 vis, 150 vis,
150 generics, 151 generics,
152 inner_items,
151 } = &mut **data; 153 } = &mut **data;
152 154
153 imports.shrink_to_fit(); 155 imports.shrink_to_fit();
@@ -167,10 +169,11 @@ impl ItemTree {
167 macro_calls.shrink_to_fit(); 169 macro_calls.shrink_to_fit();
168 macro_rules.shrink_to_fit(); 170 macro_rules.shrink_to_fit();
169 macro_defs.shrink_to_fit(); 171 macro_defs.shrink_to_fit();
170 exprs.shrink_to_fit();
171 172
172 vis.arena.shrink_to_fit(); 173 vis.arena.shrink_to_fit();
173 generics.arena.shrink_to_fit(); 174 generics.arena.shrink_to_fit();
175
176 inner_items.shrink_to_fit();
174 } 177 }
175 } 178 }
176 179
@@ -193,16 +196,18 @@ impl ItemTree {
193 self.raw_attrs(of).clone().filter(db, krate) 196 self.raw_attrs(of).clone().filter(db, krate)
194 } 197 }
195 198
196 /// Returns the lowered inner items that `ast` corresponds to. 199 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
197 /// 200 match &self.data {
198 /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered 201 Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(),
199 /// to multiple items in the `ItemTree`. 202 None => None.into_iter().flatten(),
200 pub fn inner_items(&self, ast: FileAstId<ast::Item>) -> &[ModItem] { 203 }
201 &self.inner_items[&ast]
202 } 204 }
203 205
204 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { 206 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
205 self.inner_items.values().flatten().copied() 207 match &self.data {
208 Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
209 None => &[],
210 }
206 } 211 }
207 212
208 pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { 213 pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
@@ -296,10 +301,11 @@ struct ItemTreeData {
296 macro_calls: Arena<MacroCall>, 301 macro_calls: Arena<MacroCall>,
297 macro_rules: Arena<MacroRules>, 302 macro_rules: Arena<MacroRules>,
298 macro_defs: Arena<MacroDef>, 303 macro_defs: Arena<MacroDef>,
299 exprs: Arena<Expr>,
300 304
301 vis: ItemVisibilities, 305 vis: ItemVisibilities,
302 generics: GenericParamsStorage, 306 generics: GenericParamsStorage,
307
308 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
303} 309}
304 310
305#[derive(Debug, Eq, PartialEq, Hash)] 311#[derive(Debug, Eq, PartialEq, Hash)]
@@ -461,7 +467,7 @@ macro_rules! impl_index {
461 }; 467 };
462} 468}
463 469
464impl_index!(fields: Field, variants: Variant, exprs: Expr); 470impl_index!(fields: Field, variants: Variant);
465 471
466impl Index<RawVisibilityId> for ItemTree { 472impl Index<RawVisibilityId> for ItemTree {
467 type Output = RawVisibility; 473 type Output = RawVisibility;
@@ -664,11 +670,6 @@ pub struct MacroDef {
664 pub ast_id: FileAstId<ast::MacroDef>, 670 pub ast_id: FileAstId<ast::MacroDef>,
665} 671}
666 672
667// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
668// lengths, but we don't do much with them yet.
669#[derive(Debug, Clone, Eq, PartialEq)]
670pub struct Expr;
671
672macro_rules! impl_froms { 673macro_rules! impl_froms {
673 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { 674 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
674 $( 675 $(
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 5e71ca42c..ce470fc3b 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -6,7 +6,7 @@ use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}
6use smallvec::SmallVec; 6use smallvec::SmallVec;
7use syntax::{ 7use syntax::{
8 ast::{self, ModuleItemOwner}, 8 ast::{self, ModuleItemOwner},
9 SyntaxNode, 9 SyntaxNode, WalkEvent,
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -37,7 +37,6 @@ pub(super) struct Ctx {
37 file: HirFileId, 37 file: HirFileId,
38 source_ast_id_map: Arc<AstIdMap>, 38 source_ast_id_map: Arc<AstIdMap>,
39 body_ctx: crate::body::LowerCtx, 39 body_ctx: crate::body::LowerCtx,
40 inner_items: Vec<ModItem>,
41 forced_visibility: Option<RawVisibilityId>, 40 forced_visibility: Option<RawVisibilityId>,
42} 41}
43 42
@@ -49,7 +48,6 @@ impl Ctx {
49 file, 48 file,
50 source_ast_id_map: db.ast_id_map(file), 49 source_ast_id_map: db.ast_id_map(file),
51 body_ctx: crate::body::LowerCtx::new(db, file), 50 body_ctx: crate::body::LowerCtx::new(db, file),
52 inner_items: Vec::new(),
53 forced_visibility: None, 51 forced_visibility: None,
54 } 52 }
55 } 53 }
@@ -73,8 +71,6 @@ impl Ctx {
73 } 71 }
74 72
75 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> { 73 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> {
76 assert!(inner || self.inner_items.is_empty());
77
78 // Collect inner items for 1-to-1-lowered items. 74 // Collect inner items for 1-to-1-lowered items.
79 match item { 75 match item {
80 ast::Item::Struct(_) 76 ast::Item::Struct(_)
@@ -150,14 +146,37 @@ impl Ctx {
150 146
151 fn collect_inner_items(&mut self, container: &SyntaxNode) { 147 fn collect_inner_items(&mut self, container: &SyntaxNode) {
152 let forced_vis = self.forced_visibility.take(); 148 let forced_vis = self.forced_visibility.take();
153 let mut inner_items = mem::take(&mut self.tree.inner_items); 149
154 inner_items.extend(container.descendants().skip(1).filter_map(ast::Item::cast).filter_map( 150 let mut block_stack = Vec::new();
155 |item| { 151 for event in container.preorder().skip(1) {
156 let ast_id = self.source_ast_id_map.ast_id(&item); 152 match event {
157 Some((ast_id, self.lower_mod_item(&item, true)?.0)) 153 WalkEvent::Enter(node) => {
158 }, 154 match_ast! {
159 )); 155 match node {
160 self.tree.inner_items = inner_items; 156 ast::BlockExpr(block) => {
157 block_stack.push(self.source_ast_id_map.ast_id(&block));
158 },
159 ast::Item(item) => {
160 let mod_items = self.lower_mod_item(&item, true);
161 let current_block = block_stack.last();
162 if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
163 if !mod_items.0.is_empty() {
164 self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
165 }
166 }
167 },
168 _ => {}
169 }
170 }
171 }
172 WalkEvent::Leave(node) => {
173 if ast::BlockExpr::cast(node).is_some() {
174 block_stack.pop();
175 }
176 }
177 }
178 }
179
161 self.forced_visibility = forced_vis; 180 self.forced_visibility = forced_vis;
162 } 181 }
163 182
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs
index 30188b740..9e90f745c 100644
--- a/crates/hir_def/src/lang_item.rs
+++ b/crates/hir_def/src/lang_item.rs
@@ -84,7 +84,7 @@ impl LangItems {
84 84
85 let crate_def_map = db.crate_def_map(krate); 85 let crate_def_map = db.crate_def_map(krate);
86 86
87 for (_, module_data) in crate_def_map.modules.iter() { 87 for (_, module_data) in crate_def_map.modules() {
88 for impl_def in module_data.scope.impls() { 88 for impl_def in module_data.scope.impls() {
89 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId) 89 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId)
90 } 90 }
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 50acc3f54..bd3ea9b8b 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -59,9 +59,10 @@ use std::sync::Arc;
59use base_db::{CrateId, Edition, FileId}; 59use base_db::{CrateId, Edition, FileId};
60use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; 60use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
61use la_arena::Arena; 61use la_arena::Arena;
62use profile::Count;
62use rustc_hash::FxHashMap; 63use rustc_hash::FxHashMap;
63use stdx::format_to; 64use stdx::format_to;
64use syntax::ast; 65use syntax::{ast, AstNode};
65 66
66use crate::{ 67use crate::{
67 db::DefDatabase, 68 db::DefDatabase,
@@ -74,21 +75,23 @@ use crate::{
74 75
75/// Contains all top-level defs from a macro-expanded crate 76/// Contains all top-level defs from a macro-expanded crate
76#[derive(Debug, PartialEq, Eq)] 77#[derive(Debug, PartialEq, Eq)]
77pub struct CrateDefMap { 78pub struct DefMap {
78 pub root: LocalModuleId, 79 _c: Count<Self>,
79 pub modules: Arena<ModuleData>, 80 parent: Option<Arc<DefMap>>,
80 pub(crate) krate: CrateId, 81 root: LocalModuleId,
82 modules: Arena<ModuleData>,
83 krate: CrateId,
81 /// The prelude module for this crate. This either comes from an import 84 /// The prelude module for this crate. This either comes from an import
82 /// marked with the `prelude_import` attribute, or (in the normal case) from 85 /// marked with the `prelude_import` attribute, or (in the normal case) from
83 /// a dependency (`std` or `core`). 86 /// a dependency (`std` or `core`).
84 pub(crate) prelude: Option<ModuleId>, 87 prelude: Option<ModuleId>,
85 pub(crate) extern_prelude: FxHashMap<Name, ModuleDefId>, 88 extern_prelude: FxHashMap<Name, ModuleDefId>,
86 89
87 edition: Edition, 90 edition: Edition,
88 diagnostics: Vec<DefDiagnostic>, 91 diagnostics: Vec<DefDiagnostic>,
89} 92}
90 93
91impl std::ops::Index<LocalModuleId> for CrateDefMap { 94impl std::ops::Index<LocalModuleId> for DefMap {
92 type Output = ModuleData; 95 type Output = ModuleData;
93 fn index(&self, id: LocalModuleId) -> &ModuleData { 96 fn index(&self, id: LocalModuleId) -> &ModuleData {
94 &self.modules[id] 97 &self.modules[id]
@@ -109,6 +112,10 @@ pub enum ModuleOrigin {
109 Inline { 112 Inline {
110 definition: AstId<ast::Module>, 113 definition: AstId<ast::Module>,
111 }, 114 },
115 /// Pseudo-module introduced by a block scope (contains only inner items).
116 BlockExpr {
117 block: AstId<ast::BlockExpr>,
118 },
112} 119}
113 120
114impl Default for ModuleOrigin { 121impl Default for ModuleOrigin {
@@ -122,7 +129,7 @@ impl ModuleOrigin {
122 match self { 129 match self {
123 ModuleOrigin::File { declaration: module, .. } 130 ModuleOrigin::File { declaration: module, .. }
124 | ModuleOrigin::Inline { definition: module, .. } => Some(*module), 131 | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
125 ModuleOrigin::CrateRoot { .. } => None, 132 ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None,
126 } 133 }
127 } 134 }
128 135
@@ -137,7 +144,7 @@ impl ModuleOrigin {
137 144
138 pub fn is_inline(&self) -> bool { 145 pub fn is_inline(&self) -> bool {
139 match self { 146 match self {
140 ModuleOrigin::Inline { .. } => true, 147 ModuleOrigin::Inline { .. } | ModuleOrigin::BlockExpr { .. } => true,
141 ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false, 148 ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false,
142 } 149 }
143 } 150 }
@@ -155,6 +162,9 @@ impl ModuleOrigin {
155 definition.file_id, 162 definition.file_id,
156 ModuleSource::Module(definition.to_node(db.upcast())), 163 ModuleSource::Module(definition.to_node(db.upcast())),
157 ), 164 ),
165 ModuleOrigin::BlockExpr { block } => {
166 InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
167 }
158 } 168 }
159 } 169 }
160} 170}
@@ -169,29 +179,56 @@ pub struct ModuleData {
169 pub origin: ModuleOrigin, 179 pub origin: ModuleOrigin,
170} 180}
171 181
172impl CrateDefMap { 182impl DefMap {
173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 183 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
174 let _p = profile::span("crate_def_map_query").detail(|| { 184 let _p = profile::span("crate_def_map_query").detail(|| {
175 db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() 185 db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
176 }); 186 });
177 let def_map = { 187 let edition = db.crate_graph()[krate].edition;
178 let edition = db.crate_graph()[krate].edition; 188 let def_map = DefMap::empty(krate, edition);
179 let mut modules: Arena<ModuleData> = Arena::default(); 189 let def_map = collector::collect_defs(db, def_map, None);
180 let root = modules.alloc(ModuleData::default());
181 CrateDefMap {
182 krate,
183 edition,
184 extern_prelude: FxHashMap::default(),
185 prelude: None,
186 root,
187 modules,
188 diagnostics: Vec::new(),
189 }
190 };
191 let def_map = collector::collect_defs(db, def_map);
192 Arc::new(def_map) 190 Arc::new(def_map)
193 } 191 }
194 192
193 pub(crate) fn block_def_map_query(
194 db: &dyn DefDatabase,
195 krate: CrateId,
196 block: AstId<ast::BlockExpr>,
197 ) -> Arc<DefMap> {
198 let item_tree = db.item_tree(block.file_id);
199 let block_items = item_tree.inner_items_of_block(block.value);
200
201 let parent = parent_def_map(db, krate, block);
202
203 if block_items.is_empty() {
204 // If there are no inner items, nothing new is brought into scope, so we can just return
205 // the parent DefMap. This keeps DefMap parent chains short.
206 return parent;
207 }
208
209 let mut def_map = DefMap::empty(krate, parent.edition);
210 def_map.parent = Some(parent);
211
212 let def_map = collector::collect_defs(db, def_map, Some(block.value));
213 Arc::new(def_map)
214 }
215
216 fn empty(krate: CrateId, edition: Edition) -> DefMap {
217 let mut modules: Arena<ModuleData> = Arena::default();
218 let root = modules.alloc(ModuleData::default());
219 DefMap {
220 _c: Count::new(),
221 parent: None,
222 krate,
223 edition,
224 extern_prelude: FxHashMap::default(),
225 prelude: None,
226 root,
227 modules,
228 diagnostics: Vec::new(),
229 }
230 }
231
195 pub fn add_diagnostics( 232 pub fn add_diagnostics(
196 &self, 233 &self,
197 db: &dyn DefDatabase, 234 db: &dyn DefDatabase,
@@ -208,6 +245,26 @@ impl CrateDefMap {
208 .map(|(id, _data)| id) 245 .map(|(id, _data)| id)
209 } 246 }
210 247
248 pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
249 self.modules.iter()
250 }
251
252 pub fn root(&self) -> LocalModuleId {
253 self.root
254 }
255
256 pub(crate) fn krate(&self) -> CrateId {
257 self.krate
258 }
259
260 pub(crate) fn prelude(&self) -> Option<ModuleId> {
261 self.prelude
262 }
263
264 pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleDefId)> + '_ {
265 self.extern_prelude.iter()
266 }
267
211 pub(crate) fn resolve_path( 268 pub(crate) fn resolve_path(
212 &self, 269 &self,
213 db: &dyn DefDatabase, 270 db: &dyn DefDatabase,
@@ -224,10 +281,15 @@ impl CrateDefMap {
224 // even), as this should be a great debugging aid. 281 // even), as this should be a great debugging aid.
225 pub fn dump(&self) -> String { 282 pub fn dump(&self) -> String {
226 let mut buf = String::new(); 283 let mut buf = String::new();
227 go(&mut buf, self, "crate", self.root); 284 let mut current_map = self;
285 while let Some(parent) = &current_map.parent {
286 go(&mut buf, current_map, "block scope", current_map.root);
287 current_map = &**parent;
288 }
289 go(&mut buf, current_map, "crate", current_map.root);
228 return buf; 290 return buf;
229 291
230 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) { 292 fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
231 format_to!(buf, "{}\n", path); 293 format_to!(buf, "{}\n", path);
232 294
233 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); 295 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
@@ -276,10 +338,40 @@ impl ModuleData {
276 } 338 }
277} 339}
278 340
341fn parent_def_map(
342 db: &dyn DefDatabase,
343 krate: CrateId,
344 block: AstId<ast::BlockExpr>,
345) -> Arc<DefMap> {
346 // FIXME: store this info in the item tree instead of reparsing here
347 let ast_id_map = db.ast_id_map(block.file_id);
348 let block_ptr = ast_id_map.get(block.value);
349 let root = match db.parse_or_expand(block.file_id) {
350 Some(it) => it,
351 None => {
352 return Arc::new(DefMap::empty(krate, Edition::Edition2018));
353 }
354 };
355 let ast = block_ptr.to_node(&root);
356
357 for ancestor in ast.syntax().ancestors().skip(1) {
358 if let Some(block_expr) = ast::BlockExpr::cast(ancestor) {
359 let ancestor_id = ast_id_map.ast_id(&block_expr);
360 let ast_id = InFile::new(block.file_id, ancestor_id);
361 let parent_map = db.block_def_map(krate, ast_id);
362 return parent_map;
363 }
364 }
365
366 // No enclosing block scope, so the parent is the crate-level DefMap.
367 db.crate_def_map(krate)
368}
369
279#[derive(Debug, Clone, PartialEq, Eq)] 370#[derive(Debug, Clone, PartialEq, Eq)]
280pub enum ModuleSource { 371pub enum ModuleSource {
281 SourceFile(ast::SourceFile), 372 SourceFile(ast::SourceFile),
282 Module(ast::Module), 373 Module(ast::Module),
374 BlockExpr(ast::BlockExpr),
283} 375}
284 376
285mod diagnostics { 377mod diagnostics {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 0cd61698c..cd68efbe6 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -31,7 +31,7 @@ use crate::{
31 }, 31 },
32 nameres::{ 32 nameres::{
33 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 33 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
34 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 34 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
35 }, 35 },
36 path::{ImportAlias, ModPath, PathKind}, 36 path::{ImportAlias, ModPath, PathKind},
37 per_ns::PerNs, 37 per_ns::PerNs,
@@ -45,7 +45,11 @@ const GLOB_RECURSION_LIMIT: usize = 100;
45const EXPANSION_DEPTH_LIMIT: usize = 128; 45const EXPANSION_DEPTH_LIMIT: usize = 128;
46const FIXED_POINT_LIMIT: usize = 8192; 46const FIXED_POINT_LIMIT: usize = 8192;
47 47
48pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { 48pub(super) fn collect_defs(
49 db: &dyn DefDatabase,
50 mut def_map: DefMap,
51 block: Option<FileAstId<ast::BlockExpr>>,
52) -> DefMap {
49 let crate_graph = db.crate_graph(); 53 let crate_graph = db.crate_graph();
50 54
51 // populate external prelude 55 // populate external prelude
@@ -93,6 +97,14 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
93 exports_proc_macros: false, 97 exports_proc_macros: false,
94 from_glob_import: Default::default(), 98 from_glob_import: Default::default(),
95 }; 99 };
100 match block {
101 Some(block) => {
102 collector.seed_with_inner(block);
103 }
104 None => {
105 collector.seed_with_top_level();
106 }
107 }
96 collector.collect(); 108 collector.collect();
97 collector.finish() 109 collector.finish()
98} 110}
@@ -210,7 +222,7 @@ struct DefData<'a> {
210/// Walks the tree of module recursively 222/// Walks the tree of module recursively
211struct DefCollector<'a> { 223struct DefCollector<'a> {
212 db: &'a dyn DefDatabase, 224 db: &'a dyn DefDatabase,
213 def_map: CrateDefMap, 225 def_map: DefMap,
214 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, 226 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
215 unresolved_imports: Vec<ImportDirective>, 227 unresolved_imports: Vec<ImportDirective>,
216 resolved_imports: Vec<ImportDirective>, 228 resolved_imports: Vec<ImportDirective>,
@@ -228,7 +240,7 @@ struct DefCollector<'a> {
228} 240}
229 241
230impl DefCollector<'_> { 242impl DefCollector<'_> {
231 fn collect(&mut self) { 243 fn seed_with_top_level(&mut self) {
232 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; 244 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
233 let item_tree = self.db.item_tree(file_id.into()); 245 let item_tree = self.db.item_tree(file_id.into());
234 let module_id = self.def_map.root; 246 let module_id = self.def_map.root;
@@ -248,7 +260,31 @@ impl DefCollector<'_> {
248 } 260 }
249 .collect(item_tree.top_level_items()); 261 .collect(item_tree.top_level_items());
250 } 262 }
263 }
264
265 fn seed_with_inner(&mut self, block: FileAstId<ast::BlockExpr>) {
266 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
267 let item_tree = self.db.item_tree(file_id.into());
268 let module_id = self.def_map.root;
269 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
270 if item_tree
271 .top_level_attrs(self.db, self.def_map.krate)
272 .cfg()
273 .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false))
274 {
275 ModCollector {
276 def_collector: &mut *self,
277 macro_depth: 0,
278 module_id,
279 file_id: file_id.into(),
280 item_tree: &item_tree,
281 mod_dir: ModDir::root(),
282 }
283 .collect(item_tree.inner_items_of_block(block));
284 }
285 }
251 286
287 fn collect(&mut self) {
252 // main name resolution fixed-point loop. 288 // main name resolution fixed-point loop.
253 let mut i = 0; 289 let mut i = 0;
254 loop { 290 loop {
@@ -859,7 +895,7 @@ impl DefCollector<'_> {
859 .collect(item_tree.top_level_items()); 895 .collect(item_tree.top_level_items());
860 } 896 }
861 897
862 fn finish(mut self) -> CrateDefMap { 898 fn finish(mut self) -> DefMap {
863 // Emit diagnostics for all remaining unexpanded macros. 899 // Emit diagnostics for all remaining unexpanded macros.
864 900
865 for directive in &self.unexpanded_macros { 901 for directive in &self.unexpanded_macros {
@@ -1470,11 +1506,10 @@ impl ModCollector<'_, '_> {
1470mod tests { 1506mod tests {
1471 use crate::{db::DefDatabase, test_db::TestDB}; 1507 use crate::{db::DefDatabase, test_db::TestDB};
1472 use base_db::{fixture::WithFixture, SourceDatabase}; 1508 use base_db::{fixture::WithFixture, SourceDatabase};
1473 use la_arena::Arena;
1474 1509
1475 use super::*; 1510 use super::*;
1476 1511
1477 fn do_collect_defs(db: &dyn DefDatabase, def_map: CrateDefMap) -> CrateDefMap { 1512 fn do_collect_defs(db: &dyn DefDatabase, def_map: DefMap) -> DefMap {
1478 let mut collector = DefCollector { 1513 let mut collector = DefCollector {
1479 db, 1514 db,
1480 def_map, 1515 def_map,
@@ -1489,28 +1524,17 @@ mod tests {
1489 exports_proc_macros: false, 1524 exports_proc_macros: false,
1490 from_glob_import: Default::default(), 1525 from_glob_import: Default::default(),
1491 }; 1526 };
1527 collector.seed_with_top_level();
1492 collector.collect(); 1528 collector.collect();
1493 collector.def_map 1529 collector.def_map
1494 } 1530 }
1495 1531
1496 fn do_resolve(code: &str) -> CrateDefMap { 1532 fn do_resolve(code: &str) -> DefMap {
1497 let (db, _file_id) = TestDB::with_single_file(&code); 1533 let (db, _file_id) = TestDB::with_single_file(&code);
1498 let krate = db.test_crate(); 1534 let krate = db.test_crate();
1499 1535
1500 let def_map = { 1536 let edition = db.crate_graph()[krate].edition;
1501 let edition = db.crate_graph()[krate].edition; 1537 let def_map = DefMap::empty(krate, edition);
1502 let mut modules: Arena<ModuleData> = Arena::default();
1503 let root = modules.alloc(ModuleData::default());
1504 CrateDefMap {
1505 krate,
1506 edition,
1507 extern_prelude: FxHashMap::default(),
1508 prelude: None,
1509 root,
1510 modules,
1511 diagnostics: Vec::new(),
1512 }
1513 };
1514 do_collect_defs(&db, def_map) 1538 do_collect_defs(&db, def_map)
1515 } 1539 }
1516 1540
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index 88e10574e..ec90f4e65 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -19,7 +19,7 @@ use test_utils::mark;
19use crate::{ 19use crate::{
20 db::DefDatabase, 20 db::DefDatabase,
21 item_scope::BUILTIN_SCOPE, 21 item_scope::BUILTIN_SCOPE,
22 nameres::{BuiltinShadowMode, CrateDefMap}, 22 nameres::{BuiltinShadowMode, DefMap},
23 path::{ModPath, PathKind}, 23 path::{ModPath, PathKind},
24 per_ns::PerNs, 24 per_ns::PerNs,
25 visibility::{RawVisibility, Visibility}, 25 visibility::{RawVisibility, Visibility},
@@ -61,7 +61,7 @@ impl ResolvePathResult {
61 } 61 }
62} 62}
63 63
64impl CrateDefMap { 64impl DefMap {
65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
66 self.extern_prelude 66 self.extern_prelude
67 .get(name) 67 .get(name)
@@ -104,6 +104,43 @@ impl CrateDefMap {
104 path: &ModPath, 104 path: &ModPath,
105 shadow: BuiltinShadowMode, 105 shadow: BuiltinShadowMode,
106 ) -> ResolvePathResult { 106 ) -> ResolvePathResult {
107 let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
108 result.segment_index = Some(usize::max_value());
109
110 let mut current_map = self;
111 loop {
112 let new = current_map.resolve_path_fp_with_macro_single(
113 db,
114 mode,
115 original_module,
116 path,
117 shadow,
118 );
119
120 // Merge `new` into `result`.
121 result.resolved_def = result.resolved_def.or(new.resolved_def);
122 if result.reached_fixedpoint == ReachedFixedPoint::No {
123 result.reached_fixedpoint = new.reached_fixedpoint;
124 }
125 // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
126 result.krate = result.krate.or(new.krate);
127 result.segment_index = result.segment_index.min(new.segment_index);
128
129 match &current_map.parent {
130 Some(map) => current_map = map,
131 None => return result,
132 }
133 }
134 }
135
136 pub(super) fn resolve_path_fp_with_macro_single(
137 &self,
138 db: &dyn DefDatabase,
139 mode: ResolveMode,
140 original_module: LocalModuleId,
141 path: &ModPath,
142 shadow: BuiltinShadowMode,
143 ) -> ResolvePathResult {
107 let mut segments = path.segments.iter().enumerate(); 144 let mut segments = path.segments.iter().enumerate();
108 let mut curr_per_ns: PerNs = match path.kind { 145 let mut curr_per_ns: PerNs = match path.kind {
109 PathKind::DollarCrate(krate) => { 146 PathKind::DollarCrate(krate) => {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index c459fa66d..73e3a4702 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -4,27 +4,47 @@ mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics; 5mod diagnostics;
6mod primitives; 6mod primitives;
7mod block;
7 8
8use std::sync::Arc; 9use std::sync::Arc;
9 10
10use base_db::{fixture::WithFixture, SourceDatabase}; 11use base_db::{fixture::WithFixture, SourceDatabase};
11use expect_test::{expect, Expect}; 12use expect_test::{expect, Expect};
13use hir_expand::db::AstDatabase;
12use test_utils::mark; 14use test_utils::mark;
13 15
14use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; 16use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
15 17
16fn compute_crate_def_map(ra_fixture: &str) -> Arc<CrateDefMap> { 18fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
17 let db = TestDB::with_files(ra_fixture); 19 let db = TestDB::with_files(ra_fixture);
18 let krate = db.crate_graph().iter().next().unwrap(); 20 let krate = db.crate_graph().iter().next().unwrap();
19 db.crate_def_map(krate) 21 db.crate_def_map(krate)
20} 22}
21 23
24fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> {
25 let (db, position) = TestDB::with_position(ra_fixture);
26 let module = db.module_for_file(position.file_id);
27 let ast_map = db.ast_id_map(position.file_id.into());
28 let ast = db.parse(position.file_id);
29 let block: ast::BlockExpr =
30 syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap();
31 let block_id = ast_map.ast_id(&block);
32
33 db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id))
34}
35
22fn check(ra_fixture: &str, expect: Expect) { 36fn check(ra_fixture: &str, expect: Expect) {
23 let def_map = compute_crate_def_map(ra_fixture); 37 let def_map = compute_crate_def_map(ra_fixture);
24 let actual = def_map.dump(); 38 let actual = def_map.dump();
25 expect.assert_eq(&actual); 39 expect.assert_eq(&actual);
26} 40}
27 41
42fn check_at(ra_fixture: &str, expect: Expect) {
43 let def_map = compute_block_def_map(ra_fixture);
44 let actual = def_map.dump();
45 expect.assert_eq(&actual);
46}
47
28#[test] 48#[test]
29fn crate_def_map_smoke_test() { 49fn crate_def_map_smoke_test() {
30 check( 50 check(
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs
new file mode 100644
index 000000000..01d6326a7
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/block.rs
@@ -0,0 +1,97 @@
1use super::*;
2
3#[test]
4fn inner_item_smoke() {
5 check_at(
6 r#"
7struct inner {}
8fn outer() {
9 $0
10 fn inner() {}
11}
12"#,
13 expect![[r#"
14 block scope
15 inner: v
16 crate
17 inner: t
18 outer: v
19 "#]],
20 );
21}
22
23#[test]
24fn use_from_crate() {
25 check_at(
26 r#"
27struct Struct;
28fn outer() {
29 use Struct;
30 use crate::Struct as CrateStruct;
31 use self::Struct as SelfStruct;
32 $0
33}
34"#,
35 expect![[r#"
36 block scope
37 CrateStruct: t v
38 SelfStruct: t v
39 Struct: t v
40 crate
41 Struct: t v
42 outer: v
43 "#]],
44 );
45}
46
47#[test]
48fn merge_namespaces() {
49 check_at(
50 r#"
51struct name {}
52fn outer() {
53 fn name() {}
54
55 use name as imported; // should import both `name`s
56
57 $0
58}
59"#,
60 expect![[r#"
61 block scope
62 imported: t v
63 name: v
64 crate
65 name: t
66 outer: v
67 "#]],
68 );
69}
70
71#[test]
72fn nested_blocks() {
73 check_at(
74 r#"
75fn outer() {
76 struct inner1 {}
77 fn inner() {
78 use inner1;
79 use outer;
80 fn inner2() {}
81 $0
82 }
83}
84"#,
85 expect![[r#"
86 block scope
87 inner1: t
88 inner2: v
89 outer: v
90 block scope
91 inner: v
92 inner1: t
93 crate
94 outer: v
95 "#]],
96 );
97}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 3dd7c3cbb..e34cd7f2f 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -87,7 +87,7 @@ impl ModPath {
87 87
88 /// If this path is a single identifier, like `foo`, return its name. 88 /// If this path is a single identifier, like `foo`, return its name.
89 pub fn as_ident(&self) -> Option<&Name> { 89 pub fn as_ident(&self) -> Option<&Name> {
90 if self.kind != PathKind::Plain || self.segments.len() > 1 { 90 if !self.is_ident() {
91 return None; 91 return None;
92 } 92 }
93 self.segments.first() 93 self.segments.first()
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 85ddc2c47..b2f577649 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -16,13 +16,14 @@ use crate::{
16 expr::{ExprId, PatId}, 16 expr::{ExprId, PatId},
17 generics::GenericParams, 17 generics::GenericParams,
18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, 18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
19 nameres::CrateDefMap, 19 nameres::DefMap,
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility}, 22 visibility::{RawVisibility, Visibility},
23 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, 23 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId,
24 EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, 24 EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId,
25 ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
26 TypeParamId, VariantId,
26}; 27};
27 28
28#[derive(Debug, Clone, Default)] 29#[derive(Debug, Clone, Default)]
@@ -34,7 +35,7 @@ pub struct Resolver {
34// FIXME how to store these best 35// FIXME how to store these best
35#[derive(Debug, Clone)] 36#[derive(Debug, Clone)]
36struct ModuleItemMap { 37struct ModuleItemMap {
37 crate_def_map: Arc<CrateDefMap>, 38 crate_def_map: Arc<DefMap>,
38 module_id: LocalModuleId, 39 module_id: LocalModuleId,
39} 40}
40 41
@@ -145,6 +146,19 @@ impl Resolver {
145 self.resolve_module_path(db, path, BuiltinShadowMode::Module) 146 self.resolve_module_path(db, path, BuiltinShadowMode::Module)
146 } 147 }
147 148
149 pub fn resolve_module_path_in_trait_items(
150 &self,
151 db: &dyn DefDatabase,
152 path: &ModPath,
153 ) -> Option<TraitId> {
154 let (item_map, module) = self.module_scope()?;
155 let (module_res, ..) = item_map.resolve_path(db, module, &path, BuiltinShadowMode::Module);
156 match module_res.take_types()? {
157 ModuleDefId::TraitId(it) => Some(it),
158 _ => None,
159 }
160 }
161
148 pub fn resolve_path_in_type_ns( 162 pub fn resolve_path_in_type_ns(
149 &self, 163 &self,
150 db: &dyn DefDatabase, 164 db: &dyn DefDatabase,
@@ -415,7 +429,7 @@ impl Resolver {
415 let mut traits = FxHashSet::default(); 429 let mut traits = FxHashSet::default();
416 for scope in &self.scopes { 430 for scope in &self.scopes {
417 if let Scope::ModuleScope(m) = scope { 431 if let Scope::ModuleScope(m) = scope {
418 if let Some(prelude) = m.crate_def_map.prelude { 432 if let Some(prelude) = m.crate_def_map.prelude() {
419 let prelude_def_map = db.crate_def_map(prelude.krate); 433 let prelude_def_map = db.crate_def_map(prelude.krate);
420 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 434 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
421 } 435 }
@@ -425,7 +439,7 @@ impl Resolver {
425 traits 439 traits
426 } 440 }
427 441
428 fn module_scope(&self) -> Option<(&CrateDefMap, LocalModuleId)> { 442 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> {
429 self.scopes.iter().rev().find_map(|scope| match scope { 443 self.scopes.iter().rev().find_map(|scope| match scope {
430 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), 444 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
431 445
@@ -445,11 +459,11 @@ impl Resolver {
445 459
446 pub fn module(&self) -> Option<ModuleId> { 460 pub fn module(&self) -> Option<ModuleId> {
447 let (def_map, local_id) = self.module_scope()?; 461 let (def_map, local_id) = self.module_scope()?;
448 Some(ModuleId { krate: def_map.krate, local_id }) 462 Some(ModuleId { krate: def_map.krate(), local_id })
449 } 463 }
450 464
451 pub fn krate(&self) -> Option<CrateId> { 465 pub fn krate(&self) -> Option<CrateId> {
452 self.module_scope().map(|t| t.0.krate) 466 self.module_scope().map(|t| t.0.krate())
453 } 467 }
454 468
455 pub fn where_predicates_in_scope<'a>( 469 pub fn where_predicates_in_scope<'a>(
@@ -484,7 +498,7 @@ pub enum ScopeDef {
484 PerNs(PerNs), 498 PerNs(PerNs),
485 ImplSelfType(ImplId), 499 ImplSelfType(ImplId),
486 AdtSelfType(AdtId), 500 AdtSelfType(AdtId),
487 GenericParam(TypeParamId), 501 GenericParam(GenericParamId),
488 Local(PatId), 502 Local(PatId),
489} 503}
490 504
@@ -508,13 +522,13 @@ impl Scope {
508 seen.insert((name.clone(), scope)); 522 seen.insert((name.clone(), scope));
509 f(name.clone(), ScopeDef::PerNs(scope)); 523 f(name.clone(), ScopeDef::PerNs(scope));
510 }); 524 });
511 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 525 m.crate_def_map.extern_prelude().for_each(|(name, &def)| {
512 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 526 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
513 }); 527 });
514 BUILTIN_SCOPE.iter().for_each(|(name, &def)| { 528 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
515 f(name.clone(), ScopeDef::PerNs(def)); 529 f(name.clone(), ScopeDef::PerNs(def));
516 }); 530 });
517 if let Some(prelude) = m.crate_def_map.prelude { 531 if let Some(prelude) = m.crate_def_map.prelude() {
518 let prelude_def_map = db.crate_def_map(prelude.krate); 532 let prelude_def_map = db.crate_def_map(prelude.krate);
519 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { 533 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
520 let seen_tuple = (name.clone(), def); 534 let seen_tuple = (name.clone(), def);
@@ -527,15 +541,21 @@ impl Scope {
527 Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| { 541 Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| {
528 f(name.clone(), ScopeDef::PerNs(def)); 542 f(name.clone(), ScopeDef::PerNs(def));
529 }), 543 }),
530 Scope::GenericParams { params, def } => { 544 &Scope::GenericParams { ref params, def: parent } => {
531 for (local_id, param) in params.types.iter() { 545 for (local_id, param) in params.types.iter() {
532 if let Some(name) = &param.name { 546 if let Some(ref name) = param.name {
533 f( 547 let id = TypeParamId { local_id, parent };
534 name.clone(), 548 f(name.clone(), ScopeDef::GenericParam(id.into()))
535 ScopeDef::GenericParam(TypeParamId { local_id, parent: *def }),
536 )
537 } 549 }
538 } 550 }
551 for (local_id, param) in params.consts.iter() {
552 let id = ConstParamId { local_id, parent };
553 f(param.name.clone(), ScopeDef::GenericParam(id.into()))
554 }
555 for (local_id, param) in params.lifetimes.iter() {
556 let id = LifetimeParamId { local_id, parent };
557 f(param.name.clone(), ScopeDef::GenericParam(id.into()))
558 }
539 } 559 }
540 Scope::ImplDefScope(i) => { 560 Scope::ImplDefScope(i) => {
541 f(name![Self], ScopeDef::ImplSelfType(*i)); 561 f(name![Self], ScopeDef::ImplSelfType(*i));
@@ -588,11 +608,7 @@ impl Resolver {
588 self.push_scope(Scope::ImplDefScope(impl_def)) 608 self.push_scope(Scope::ImplDefScope(impl_def))
589 } 609 }
590 610
591 fn push_module_scope( 611 fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver {
592 self,
593 crate_def_map: Arc<CrateDefMap>,
594 module_id: LocalModuleId,
595 ) -> Resolver {
596 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) 612 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
597 } 613 }
598 614
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index 574c0201a..4ff219fb7 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -75,7 +75,7 @@ impl TestDB {
75 pub(crate) fn module_for_file(&self, file_id: FileId) -> crate::ModuleId { 75 pub(crate) fn module_for_file(&self, file_id: FileId) -> crate::ModuleId {
76 for &krate in self.relevant_crates(file_id).iter() { 76 for &krate in self.relevant_crates(file_id).iter() {
77 let crate_def_map = self.crate_def_map(krate); 77 let crate_def_map = self.crate_def_map(krate);
78 for (local_id, data) in crate_def_map.modules.iter() { 78 for (local_id, data) in crate_def_map.modules() {
79 if data.origin.file_id() == Some(file_id) { 79 if data.origin.file_id() == Some(file_id) {
80 return crate::ModuleId { krate, local_id }; 80 return crate::ModuleId { krate, local_id };
81 } 81 }
@@ -110,7 +110,7 @@ impl TestDB {
110 let crate_graph = self.crate_graph(); 110 let crate_graph = self.crate_graph();
111 for krate in crate_graph.iter() { 111 for krate in crate_graph.iter() {
112 let crate_def_map = self.crate_def_map(krate); 112 let crate_def_map = self.crate_def_map(krate);
113 for (module_id, _) in crate_def_map.modules.iter() { 113 for (module_id, _) in crate_def_map.modules() {
114 let file_id = crate_def_map[module_id].origin.file_id(); 114 let file_id = crate_def_map[module_id].origin.file_id();
115 files.extend(file_id) 115 files.extend(file_id)
116 } 116 }
@@ -135,7 +135,7 @@ impl TestDB {
135 let crate_def_map = self.crate_def_map(krate); 135 let crate_def_map = self.crate_def_map(krate);
136 136
137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); 137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
138 for (module_id, module) in crate_def_map.modules.iter() { 138 for (module_id, module) in crate_def_map.modules() {
139 crate_def_map.add_diagnostics(self, module_id, &mut sink); 139 crate_def_map.add_diagnostics(self, module_id, &mut sink);
140 140
141 for decl in module.scope.declarations() { 141 for decl in module.scope.declarations() {
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index ae93d0d10..049b2e462 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -159,6 +159,8 @@ impl TypeRef {
159 ast::Type::DynTraitType(inner) => { 159 ast::Type::DynTraitType(inner) => {
160 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) 160 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
161 } 161 }
162 // FIXME: Macros in type position are not yet supported.
163 ast::Type::MacroType(_) => TypeRef::Error,
162 } 164 }
163 } 165 }
164 166
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index f3bc9d680..3134fa43d 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -5,7 +5,7 @@ use syntax::ast;
5 5
6use crate::{ 6use crate::{
7 db::DefDatabase, 7 db::DefDatabase,
8 nameres::CrateDefMap, 8 nameres::DefMap,
9 path::{ModPath, PathKind}, 9 path::{ModPath, PathKind},
10 ModuleId, 10 ModuleId,
11}; 11};
@@ -116,7 +116,7 @@ impl Visibility {
116 116
117 pub(crate) fn is_visible_from_def_map( 117 pub(crate) fn is_visible_from_def_map(
118 self, 118 self,
119 def_map: &CrateDefMap, 119 def_map: &DefMap,
120 from_module: crate::LocalModuleId, 120 from_module: crate::LocalModuleId,
121 ) -> bool { 121 ) -> bool {
122 let to_module = match self { 122 let to_module = match self {
@@ -135,7 +135,7 @@ impl Visibility {
135 /// 135 ///
136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only 136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137 /// visible in unrelated modules). 137 /// visible in unrelated modules).
138 pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> { 138 pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
139 match (self, other) { 139 match (self, other) {
140 (Visibility::Module(_), Visibility::Public) 140 (Visibility::Module(_), Visibility::Public)
141 | (Visibility::Public, Visibility::Module(_)) 141 | (Visibility::Public, Visibility::Module(_))
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs
index 2401b0cc5..0991fffd8 100644
--- a/crates/hir_expand/src/ast_id_map.rs
+++ b/crates/hir_expand/src/ast_id_map.rs
@@ -13,7 +13,7 @@ use std::{
13}; 13};
14 14
15use la_arena::{Arena, Idx}; 15use la_arena::{Arena, Idx};
16use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; 16use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
17 17
18/// `AstId` points to an AST node in a specific file. 18/// `AstId` points to an AST node in a specific file.
19pub struct FileAstId<N: AstNode> { 19pub struct FileAstId<N: AstNode> {
@@ -72,12 +72,20 @@ 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 bdfs(node, |it| match ast::Item::cast(it) { 75 bdfs(node, |it| {
76 Some(module_item) => { 76 match_ast! {
77 res.alloc(module_item.syntax()); 77 match it {
78 true 78 ast::Item(module_item) => {
79 res.alloc(module_item.syntax());
80 true
81 },
82 ast::BlockExpr(block) => {
83 res.alloc(block.syntax());
84 true
85 },
86 _ => false,
87 }
79 } 88 }
80 None => false,
81 }); 89 });
82 res 90 res
83 } 91 }
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index eb257579f..b7f1aae8f 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -102,7 +102,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
102 debug!("name token not found"); 102 debug!("name token not found");
103 mbe::ExpandError::ConversionError 103 mbe::ExpandError::ConversionError
104 })?; 104 })?;
105 let name_token = tt::Ident { id: name_token_id, text: name.text().clone() }; 105 let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
106 let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count()); 106 let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
107 Ok(BasicAdtInfo { name: name_token, type_params }) 107 Ok(BasicAdtInfo { name: name_token, type_params })
108} 108}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 467516eb7..cb6e23320 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -173,7 +173,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
173 }; 173 };
174 let loc = db.lookup_intern_macro(id); 174 let loc = db.lookup_intern_macro(id);
175 let arg = loc.kind.arg(db)?; 175 let arg = loc.kind.arg(db)?;
176 Some(arg.green().clone()) 176 Some(arg.green().to_owned())
177} 177}
178 178
179fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 179fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 8db581b77..c8ea81210 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -76,6 +76,8 @@ pub struct HygieneFrame {
76 76
77impl HygieneFrames { 77impl HygieneFrames {
78 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { 78 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
79 // Note that this intentionally avoids the `hygiene_frame` query to avoid blowing up memory
80 // usage. The query is only helpful for nested `HygieneFrame`s as it avoids redundant work.
79 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) 81 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id)))
80 } 82 }
81 83
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 95d853b6d..d692cec14 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -38,7 +38,7 @@ impl Name {
38 } 38 }
39 39
40 pub fn new_lifetime(lt: &ast::Lifetime) -> Name { 40 pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
41 Self::new_text(lt.text().clone()) 41 Self::new_text(lt.text().into())
42 } 42 }
43 43
44 /// Shortcut to create inline plain text name 44 /// Shortcut to create inline plain text name
@@ -47,12 +47,12 @@ impl Name {
47 } 47 }
48 48
49 /// Resolve a name from the text of token. 49 /// Resolve a name from the text of token.
50 fn resolve(raw_text: &SmolStr) -> Name { 50 fn resolve(raw_text: &str) -> Name {
51 let raw_start = "r#"; 51 let raw_start = "r#";
52 if raw_text.as_str().starts_with(raw_start) { 52 if raw_text.starts_with(raw_start) {
53 Name::new_text(SmolStr::new(&raw_text[raw_start.len()..])) 53 Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
54 } else { 54 } else {
55 Name::new_text(raw_text.clone()) 55 Name::new_text(raw_text.into())
56 } 56 }
57 } 57 }
58 58
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 1923daca5..75e950816 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -135,7 +135,6 @@ mod tests {
135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap()); 135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
136 136
137 assert_eq_text!( 137 assert_eq_text!(
138 &result,
139 r#" 138 r#"
140SUBTREE $ 139SUBTREE $
141 PUNCH # [alone] 0 140 PUNCH # [alone] 0
@@ -150,7 +149,8 @@ SUBTREE $
150 PUNCH : [alone] 19 149 PUNCH : [alone] 19
151 IDENT u32 20 150 IDENT u32 20
152"# 151"#
153 .trim() 152 .trim(),
153 &result
154 ); 154 );
155 } 155 }
156} 156}
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 98434b741..db42a00dc 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.47", default-features = false } 20chalk-solve = { version = "0.50", default-features = false }
21chalk-ir = "0.47" 21chalk-ir = "0.50"
22chalk-recursive = "0.47" 22chalk-recursive = "0.50"
23la-arena = { version = "0.2.0", path = "../../lib/arena" } 23la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index c67a289f2..247da43f2 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -409,7 +409,7 @@ mod tests {
409 let crate_def_map = self.crate_def_map(krate); 409 let crate_def_map = self.crate_def_map(krate);
410 410
411 let mut fns = Vec::new(); 411 let mut fns = Vec::new();
412 for (module_id, _) in crate_def_map.modules.iter() { 412 for (module_id, _) in crate_def_map.modules() {
413 for decl in crate_def_map[module_id].scope.declarations() { 413 for decl in crate_def_map[module_id].scope.declarations() {
414 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); 414 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
415 validate_module_item(self, krate, decl, &mut sink); 415 validate_module_item(self, krate, decl, &mut sink);
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 8a289f52a..f06aeeb42 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -112,7 +112,7 @@ impl TraitImpls {
112 let mut impls = Self { map: FxHashMap::default() }; 112 let mut impls = Self { map: FxHashMap::default() };
113 113
114 let crate_def_map = db.crate_def_map(krate); 114 let crate_def_map = db.crate_def_map(krate);
115 for (_module_id, module_data) in crate_def_map.modules.iter() { 115 for (_module_id, module_data) in crate_def_map.modules() {
116 for impl_id in module_data.scope.impls() { 116 for impl_id in module_data.scope.impls() {
117 let target_trait = match db.impl_trait(impl_id) { 117 let target_trait = match db.impl_trait(impl_id) {
118 Some(tr) => tr.value.trait_, 118 Some(tr) => tr.value.trait_,
@@ -198,7 +198,7 @@ impl InherentImpls {
198 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); 198 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default();
199 199
200 let crate_def_map = db.crate_def_map(krate); 200 let crate_def_map = db.crate_def_map(krate);
201 for (_module_id, module_data) in crate_def_map.modules.iter() { 201 for (_module_id, module_data) in crate_def_map.modules() {
202 for impl_id in module_data.scope.impls() { 202 for impl_id in module_data.scope.impls() {
203 let data = db.impl_data(impl_id); 203 let data = db.impl_data(impl_id);
204 if data.target_trait.is_some() { 204 if data.target_trait.is_some() {
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs
index 646e16bbe..3bbcbc242 100644
--- a/crates/hir_ty/src/test_db.rs
+++ b/crates/hir_ty/src/test_db.rs
@@ -81,7 +81,7 @@ impl TestDB {
81 pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { 81 pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
82 for &krate in self.relevant_crates(file_id).iter() { 82 for &krate in self.relevant_crates(file_id).iter() {
83 let crate_def_map = self.crate_def_map(krate); 83 let crate_def_map = self.crate_def_map(krate);
84 for (local_id, data) in crate_def_map.modules.iter() { 84 for (local_id, data) in crate_def_map.modules() {
85 if data.origin.file_id() == Some(file_id) { 85 if data.origin.file_id() == Some(file_id) {
86 return ModuleId { krate, local_id }; 86 return ModuleId { krate, local_id };
87 } 87 }
@@ -95,7 +95,7 @@ impl TestDB {
95 let crate_graph = self.crate_graph(); 95 let crate_graph = self.crate_graph();
96 for krate in crate_graph.iter() { 96 for krate in crate_graph.iter() {
97 let crate_def_map = self.crate_def_map(krate); 97 let crate_def_map = self.crate_def_map(krate);
98 for (module_id, _) in crate_def_map.modules.iter() { 98 for (module_id, _) in crate_def_map.modules() {
99 let file_id = crate_def_map[module_id].origin.file_id(); 99 let file_id = crate_def_map[module_id].origin.file_id();
100 files.extend(file_id) 100 files.extend(file_id)
101 } 101 }
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 3b1675f0b..4a3fcea8d 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -18,7 +18,7 @@ use hir_def::{
18 db::DefDatabase, 18 db::DefDatabase,
19 item_scope::ItemScope, 19 item_scope::ItemScope,
20 keys, 20 keys,
21 nameres::CrateDefMap, 21 nameres::DefMap,
22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
23}; 23};
24use hir_expand::{db::AstDatabase, InFile}; 24use hir_expand::{db::AstDatabase, InFile};
@@ -26,7 +26,7 @@ use once_cell::race::OnceBool;
26use stdx::format_to; 26use stdx::format_to;
27use syntax::{ 27use syntax::{
28 algo, 28 algo,
29 ast::{self, AstNode}, 29 ast::{self, AstNode, NameOwner},
30 SyntaxNode, 30 SyntaxNode,
31}; 31};
32use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; 32use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
@@ -153,7 +153,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
153 }); 153 });
154 for (node, ty) in &types { 154 for (node, ty) in &types {
155 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) { 155 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) {
156 (self_param.self_token().unwrap().text_range(), "self".to_string()) 156 (self_param.name().unwrap().syntax().text_range(), "self".to_string())
157 } else { 157 } else {
158 (node.value.text_range(), node.value.text().to_string().replace("\n", " ")) 158 (node.value.text_range(), node.value.text().to_string().replace("\n", " "))
159 }; 159 };
@@ -221,7 +221,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
221 221
222fn visit_module( 222fn visit_module(
223 db: &TestDB, 223 db: &TestDB,
224 crate_def_map: &CrateDefMap, 224 crate_def_map: &DefMap,
225 module_id: LocalModuleId, 225 module_id: LocalModuleId,
226 cb: &mut dyn FnMut(DefWithBodyId), 226 cb: &mut dyn FnMut(DefWithBodyId),
227) { 227) {
@@ -249,7 +249,7 @@ fn visit_module(
249 249
250 fn visit_scope( 250 fn visit_scope(
251 db: &TestDB, 251 db: &TestDB,
252 crate_def_map: &CrateDefMap, 252 crate_def_map: &DefMap,
253 scope: &ItemScope, 253 scope: &ItemScope,
254 cb: &mut dyn FnMut(DefWithBodyId), 254 cb: &mut dyn FnMut(DefWithBodyId),
255 ) { 255 ) {
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index e8999a7f3..b10a0a78b 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for (&file_id, references) in refs.info.references().iter() { 50 for (&file_id, references) in refs.references().iter() {
51 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
52 let file = file.syntax(); 52 let file = file.syntax();
53 for reference in references { 53 for reference in references {
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index e4335119b..579d5a308 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -140,7 +140,7 @@ impl DiagnosticWithFix for IncorrectCase {
140 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; 140 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?;
141 141
142 let label = format!("Rename to {}", self.suggested_text); 142 let label = format!("Rename to {}", self.suggested_text);
143 Some(Fix::new(&label, rename_changes.info, rename_changes.range)) 143 Some(Fix::new(&label, rename_changes, frange.range))
144 } 144 }
145} 145}
146 146
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 685052e7f..9c568c90c 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -153,8 +153,7 @@ impl NavigationTarget {
153 node: InFile<&dyn ast::NameOwner>, 153 node: InFile<&dyn ast::NameOwner>,
154 kind: SymbolKind, 154 kind: SymbolKind,
155 ) -> NavigationTarget { 155 ) -> NavigationTarget {
156 let name = 156 let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
157 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
158 let focus_range = 157 let focus_range =
159 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); 158 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range);
160 let frange = node.map(|it| it.syntax()).original_file_range(db); 159 let frange = node.map(|it| it.syntax()).original_file_range(db);
@@ -295,6 +294,7 @@ impl ToNav for hir::Module {
295 ModuleSource::Module(node) => { 294 ModuleSource::Module(node) => {
296 (node.syntax(), node.name().map(|it| it.syntax().text_range())) 295 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
297 } 296 }
297 ModuleSource::BlockExpr(node) => (node.syntax(), None),
298 }; 298 };
299 let frange = src.with_value(syntax).original_file_range(db); 299 let frange = src.with_value(syntax).original_file_range(db);
300 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) 300 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module)
@@ -400,15 +400,13 @@ 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, focus_range) = match &src.value { 403 let (node, name) = match &src.value {
404 Either::Left(bind_pat) => ( 404 Either::Left(bind_pat) => (bind_pat.syntax().clone(), bind_pat.name()),
405 bind_pat.syntax().clone(), 405 Either::Right(it) => (it.syntax().clone(), it.name()),
406 bind_pat
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())),
411 }; 406 };
407 let focus_range =
408 name.map(|it| src.with_value(&it.syntax().clone()).original_file_range(db).range);
409
412 let full_range = src.with_value(&node).original_file_range(db); 410 let full_range = src.with_value(&node).original_file_range(db);
413 let name = match self.name(db) { 411 let name = match self.name(db) {
414 Some(it) => it.to_string().into(), 412 Some(it) => it.to_string().into(),
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 990f740b8..7ac050473 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -53,6 +53,12 @@ impl ShortLabel for ast::SourceFile {
53 } 53 }
54} 54}
55 55
56impl ShortLabel for ast::BlockExpr {
57 fn short_label(&self) -> Option<String> {
58 None
59 }
60}
61
56impl ShortLabel for ast::TypeAlias { 62impl ShortLabel for ast::TypeAlias {
57 fn short_label(&self) -> Option<String> { 63 fn short_label(&self) -> Option<String> {
58 short_label_from_node(self, "type ") 64 short_label_from_node(self, "type ")
@@ -90,7 +96,7 @@ impl ShortLabel for ast::Variant {
90impl ShortLabel for ast::ConstParam { 96impl ShortLabel for ast::ConstParam {
91 fn short_label(&self) -> Option<String> { 97 fn short_label(&self) -> Option<String> {
92 let mut buf = "const ".to_owned(); 98 let mut buf = "const ".to_owned();
93 buf.push_str(self.name()?.text().as_str()); 99 buf.push_str(self.name()?.text());
94 if let Some(type_ref) = self.ty() { 100 if let Some(type_ref) = self.ty() {
95 format_to!(buf, ": {}", type_ref.syntax()); 101 format_to!(buf, ": {}", type_ref.syntax());
96 } 102 }
@@ -117,6 +123,6 @@ where
117{ 123{
118 let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default(); 124 let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
119 buf.push_str(label); 125 buf.push_str(label);
120 buf.push_str(node.name()?.text().as_str()); 126 buf.push_str(node.name()?.text());
121 Some(buf) 127 Some(buf)
122} 128}
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index de10406bc..730e0dd0a 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -221,14 +221,31 @@ fn rewrite_intra_doc_link(
221 }?; 221 }?;
222 let krate = resolved.module(db)?.krate(); 222 let krate = resolved.module(db)?.krate();
223 let canonical_path = resolved.canonical_path(db)?; 223 let canonical_path = resolved.canonical_path(db)?;
224 let new_target = get_doc_url(db, &krate)? 224 let mut new_url = get_doc_url(db, &krate)?
225 .join(&format!("{}/", krate.display_name(db)?)) 225 .join(&format!("{}/", krate.display_name(db)?))
226 .ok()? 226 .ok()?
227 .join(&canonical_path.replace("::", "/")) 227 .join(&canonical_path.replace("::", "/"))
228 .ok()? 228 .ok()?
229 .join(&get_symbol_filename(db, &resolved)?) 229 .join(&get_symbol_filename(db, &resolved)?)
230 .ok()? 230 .ok()?;
231 .into_string(); 231
232 if let ModuleDef::Trait(t) = resolved {
233 let items = t.items(db);
234 if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| {
235 if let Some(name) = assoc_item.name(db) {
236 if link.to_string() == format!("{}::{}", canonical_path, name) {
237 return Some(FieldOrAssocItem::AssocItem(*assoc_item));
238 }
239 }
240 None
241 }) {
242 if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) {
243 new_url = new_url.join(&fragment).ok()?;
244 }
245 };
246 }
247
248 let new_target = new_url.into_string();
232 let new_title = strip_prefixes_suffixes(title); 249 let new_title = strip_prefixes_suffixes(title);
233 Some((new_target, new_title.to_string())) 250 Some((new_target, new_title.to_string()))
234} 251}
@@ -438,10 +455,10 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
438 FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), 455 FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)),
439 FieldOrAssocItem::AssocItem(assoc) => match assoc { 456 FieldOrAssocItem::AssocItem(assoc) => match assoc {
440 AssocItem::Function(function) => { 457 AssocItem::Function(function) => {
441 let is_trait_method = matches!( 458 let is_trait_method = function
442 function.as_assoc_item(db).map(|assoc| assoc.container(db)), 459 .as_assoc_item(db)
443 Some(AssocItemContainer::Trait(..)) 460 .and_then(|assoc| assoc.containing_trait(db))
444 ); 461 .is_some();
445 // This distinction may get more complicated when specialization is available. 462 // This distinction may get more complicated when specialization is available.
446 // Rustdoc makes this decision based on whether a method 'has defaultness'. 463 // Rustdoc makes this decision based on whether a method 'has defaultness'.
447 // Currently this is only the case for provided trait methods. 464 // Currently this is only the case for provided trait methods.
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 17a540972..2d722dee0 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -213,8 +213,8 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange
213 let ws_text = ws.text(); 213 let ws_text = ws.text();
214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start(); 214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start();
215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); 215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start();
216 let ws_suffix = &ws_text.as_str()[suffix]; 216 let ws_suffix = &ws_text[suffix];
217 let ws_prefix = &ws_text.as_str()[prefix]; 217 let ws_prefix = &ws_text[prefix];
218 if ws_text.contains('\n') && !ws_suffix.contains('\n') { 218 if ws_text.contains('\n') && !ws_suffix.contains('\n') {
219 if let Some(node) = ws.next_sibling_or_token() { 219 if let Some(node) = ws.next_sibling_or_token() {
220 let start = match ws_prefix.rfind('\n') { 220 let start = match ws_prefix.rfind('\n') {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 988a5668f..1a997fa40 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -2,16 +2,14 @@ use either::Either;
2use hir::{HasAttrs, ModuleDef, Semantics}; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 defs::{Definition, NameClass, NameRefClass}, 4 defs::{Definition, NameClass, NameRefClass},
5 symbol_index, RootDatabase, 5 RootDatabase,
6}; 6};
7use syntax::{ 7use syntax::{
8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, 8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
9}; 9};
10 10
11use crate::{ 11use crate::{
12 display::{ToNav, TryToNav}, 12 display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def,
13 doc_links::extract_definitions_from_markdown,
14 runnables::doc_owner_to_def,
15 FilePosition, NavigationTarget, RangeInfo, 13 FilePosition, NavigationTarget, RangeInfo,
16}; 14};
17 15
@@ -38,33 +36,26 @@ pub(crate) fn goto_definition(
38 return Some(RangeInfo::new(original_token.text_range(), vec![nav])); 36 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
39 } 37 }
40 38
41 let nav_targets = match_ast! { 39 let nav = match_ast! {
42 match parent { 40 match parent {
43 ast::NameRef(name_ref) => { 41 ast::NameRef(name_ref) => {
44 reference_definition(&sema, Either::Right(&name_ref)).to_vec() 42 reference_definition(&sema, Either::Right(&name_ref))
45 }, 43 },
46 ast::Name(name) => { 44 ast::Name(name) => {
47 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 45 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
48 let nav = def.try_to_nav(sema.db)?; 46 def.try_to_nav(sema.db)
49 vec![nav]
50 }, 47 },
51 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 48 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
52 let def = name_class.referenced_or_defined(sema.db); 49 let def = name_class.referenced_or_defined(sema.db);
53 let nav = def.try_to_nav(sema.db)?; 50 def.try_to_nav(sema.db)
54 vec![nav]
55 } else { 51 } else {
56 reference_definition(&sema, Either::Left(&lt)).to_vec() 52 reference_definition(&sema, Either::Left(&lt))
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 }, 53 },
63 _ => return None, 54 _ => return None,
64 } 55 }
65 }; 56 };
66 57
67 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 58 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
68} 59}
69 60
70fn def_for_doc_comment( 61fn def_for_doc_comment(
@@ -125,42 +116,16 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
125 } 116 }
126} 117}
127 118
128#[derive(Debug)]
129pub(crate) enum ReferenceResult {
130 Exact(NavigationTarget),
131 Approximate(Vec<NavigationTarget>),
132}
133
134impl ReferenceResult {
135 fn to_vec(self) -> Vec<NavigationTarget> {
136 match self {
137 ReferenceResult::Exact(target) => vec![target],
138 ReferenceResult::Approximate(vec) => vec,
139 }
140 }
141}
142
143pub(crate) fn reference_definition( 119pub(crate) fn reference_definition(
144 sema: &Semantics<RootDatabase>, 120 sema: &Semantics<RootDatabase>,
145 name_ref: Either<&ast::Lifetime, &ast::NameRef>, 121 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
146) -> ReferenceResult { 122) -> Option<NavigationTarget> {
147 let name_kind = name_ref.either( 123 let name_kind = name_ref.either(
148 |lifetime| NameRefClass::classify_lifetime(sema, lifetime), 124 |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
149 |name_ref| NameRefClass::classify(sema, name_ref), 125 |name_ref| NameRefClass::classify(sema, name_ref),
150 ); 126 )?;
151 if let Some(def) = name_kind { 127 let def = name_kind.referenced(sema.db);
152 let def = def.referenced(sema.db); 128 def.try_to_nav(sema.db)
153 return match def.try_to_nav(sema.db) {
154 Some(nav) => ReferenceResult::Exact(nav),
155 None => ReferenceResult::Approximate(Vec::new()),
156 };
157 }
158
159 // Fallback index based approach:
160 let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
161 let navs =
162 symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
163 ReferenceResult::Approximate(navs)
164} 129}
165 130
166#[cfg(test)] 131#[cfg(test)]
@@ -197,12 +162,12 @@ mod tests {
197 fn goto_def_for_extern_crate() { 162 fn goto_def_for_extern_crate() {
198 check( 163 check(
199 r#" 164 r#"
200 //- /main.rs crate:main deps:std 165//- /main.rs crate:main deps:std
201 extern crate std$0; 166extern crate std$0;
202 //- /std/lib.rs crate:std 167//- /std/lib.rs crate:std
203 // empty 168// empty
204 //^ file 169//^ file
205 "#, 170"#,
206 ) 171 )
207 } 172 }
208 173
@@ -210,12 +175,12 @@ mod tests {
210 fn goto_def_for_renamed_extern_crate() { 175 fn goto_def_for_renamed_extern_crate() {
211 check( 176 check(
212 r#" 177 r#"
213 //- /main.rs crate:main deps:std 178//- /main.rs crate:main deps:std
214 extern crate std as abc$0; 179extern crate std as abc$0;
215 //- /std/lib.rs crate:std 180//- /std/lib.rs crate:std
216 // empty 181// empty
217 //^ file 182//^ file
218 "#, 183"#,
219 ) 184 )
220 } 185 }
221 186
@@ -302,13 +267,13 @@ fn bar() {
302 fn goto_def_for_macros_from_other_crates() { 267 fn goto_def_for_macros_from_other_crates() {
303 check( 268 check(
304 r#" 269 r#"
305//- /lib.rs 270//- /lib.rs crate:main deps:foo
306use foo::foo; 271use foo::foo;
307fn bar() { 272fn bar() {
308 $0foo!(); 273 $0foo!();
309} 274}
310 275
311//- /foo/lib.rs 276//- /foo/lib.rs crate:foo
312#[macro_export] 277#[macro_export]
313macro_rules! foo { () => { () } } 278macro_rules! foo { () => { () } }
314 //^^^ 279 //^^^
@@ -320,10 +285,10 @@ macro_rules! foo { () => { () } }
320 fn goto_def_for_macros_in_use_tree() { 285 fn goto_def_for_macros_in_use_tree() {
321 check( 286 check(
322 r#" 287 r#"
323//- /lib.rs 288//- /lib.rs crate:main deps:foo
324use foo::foo$0; 289use foo::foo$0;
325 290
326//- /foo/lib.rs 291//- /foo/lib.rs crate:foo
327#[macro_export] 292#[macro_export]
328macro_rules! foo { () => { () } } 293macro_rules! foo { () => { () } }
329 //^^^ 294 //^^^
@@ -981,10 +946,10 @@ type Alias<T> = T$0;
981 fn goto_def_for_macro_container() { 946 fn goto_def_for_macro_container() {
982 check( 947 check(
983 r#" 948 r#"
984//- /lib.rs 949//- /lib.rs crate:main deps:foo
985foo::module$0::mac!(); 950foo::module$0::mac!();
986 951
987//- /foo/lib.rs 952//- /foo/lib.rs crate:foo
988pub mod module { 953pub mod module {
989 //^^^^^^ 954 //^^^^^^
990 #[macro_export] 955 #[macro_export]
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 6022bd275..d47a4cb0f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -98,7 +98,6 @@ 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)),
102 _ => None, 101 _ => None,
103 } 102 }
104 }; 103 };
@@ -183,12 +182,7 @@ fn runnable_action(
183) -> Option<HoverAction> { 182) -> Option<HoverAction> {
184 match def { 183 match def {
185 Definition::ModuleDef(it) => match it { 184 Definition::ModuleDef(it) => match it {
186 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 185 ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)),
187 ModuleSource::Module(it) => {
188 runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it))
189 }
190 _ => None,
191 },
192 ModuleDef::Function(func) => { 186 ModuleDef::Function(func) => {
193 let src = func.source(sema.db)?; 187 let src = func.source(sema.db)?;
194 if src.file_id != file_id.into() { 188 if src.file_id != file_id.into() {
@@ -327,6 +321,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
327 match it.definition_source(db).value { 321 match it.definition_source(db).value {
328 ModuleSource::Module(it) => it.short_label(), 322 ModuleSource::Module(it) => it.short_label(),
329 ModuleSource::SourceFile(it) => it.short_label(), 323 ModuleSource::SourceFile(it) => it.short_label(),
324 ModuleSource::BlockExpr(it) => it.short_label(),
330 }, 325 },
331 mod_path, 326 mod_path,
332 ), 327 ),
@@ -1831,6 +1826,35 @@ pub struct B$0ar
1831 "#]], 1826 "#]],
1832 ); 1827 );
1833 } 1828 }
1829 #[test]
1830 fn test_hover_intra_link_reference_to_trait_method() {
1831 check(
1832 r#"
1833pub trait Foo {
1834 fn buzz() -> usize;
1835}
1836/// [Foo][buzz]
1837///
1838/// [buzz]: Foo::buzz
1839pub struct B$0ar
1840"#,
1841 expect![[r#"
1842 *Bar*
1843
1844 ```rust
1845 test
1846 ```
1847
1848 ```rust
1849 pub struct Bar
1850 ```
1851
1852 ---
1853
1854 [Foo](https://docs.rs/test/*/test/trait.Foo.html#tymethod.buzz)
1855 "#]],
1856 );
1857 }
1834 1858
1835 #[test] 1859 #[test]
1836 fn test_hover_external_url() { 1860 fn test_hover_external_url() {
@@ -3223,7 +3247,7 @@ impl Foo {
3223} 3247}
3224"#, 3248"#,
3225 expect![[r#" 3249 expect![[r#"
3226 *&self* 3250 *self*
3227 3251
3228 ```rust 3252 ```rust
3229 &Foo 3253 &Foo
@@ -3243,7 +3267,7 @@ impl Foo {
3243} 3267}
3244"#, 3268"#,
3245 expect![[r#" 3269 expect![[r#"
3246 *self: Arc<Foo>* 3270 *self*
3247 3271
3248 ```rust 3272 ```rust
3249 Arc<Foo> 3273 Arc<Foo>
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index a2039fcc7..54485fd30 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -411,7 +411,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
411 match expr { 411 match expr {
412 ast::Expr::MethodCallExpr(method_call_expr) => { 412 ast::Expr::MethodCallExpr(method_call_expr) => {
413 let name_ref = method_call_expr.name_ref()?; 413 let name_ref = method_call_expr.name_ref()?;
414 match name_ref.text().as_str() { 414 match name_ref.text() {
415 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), 415 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()),
416 name_ref => Some(name_ref.to_owned()), 416 name_ref => Some(name_ref.to_owned()),
417 } 417 }
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 981467c8d..631bde0f1 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -59,7 +59,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
59 // The node is either the first or the last in the file 59 // The node is either the first or the last in the file
60 let suff = &token.text()[TextRange::new( 60 let suff = &token.text()[TextRange::new(
61 offset - token.text_range().start() + TextSize::of('\n'), 61 offset - token.text_range().start() + TextSize::of('\n'),
62 TextSize::of(token.text().as_str()), 62 TextSize::of(token.text()),
63 )]; 63 )];
64 let spaces = suff.bytes().take_while(|&b| b == b' ').count(); 64 let spaces = suff.bytes().take_while(|&b| b == b' ').count();
65 65
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f8d69382e..567b8117e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -86,18 +86,15 @@ pub use completion::{
86 InsertTextFormat, 86 InsertTextFormat,
87}; 87};
88pub use hir::{Documentation, Semantics}; 88pub use hir::{Documentation, Semantics};
89pub use ide_db::base_db::{
90 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
91 SourceRootId,
92};
93pub use ide_db::{ 89pub use ide_db::{
90 base_db::{
91 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
92 SourceRoot, SourceRootId,
93 },
94 call_info::CallInfo, 94 call_info::CallInfo,
95 search::{FileReference, ReferenceAccess, ReferenceKind},
96};
97pub use ide_db::{
98 label::Label, 95 label::Label,
99 line_index::{LineCol, LineIndex}, 96 line_index::{LineCol, LineIndex},
100 search::SearchScope, 97 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope},
101 source_change::{FileSystemEdit, SourceChange}, 98 source_change::{FileSystemEdit, SourceChange},
102 symbol_index::Query, 99 symbol_index::Query,
103 RootDatabase, 100 RootDatabase,
@@ -372,9 +369,7 @@ impl Analysis {
372 position: FilePosition, 369 position: FilePosition,
373 search_scope: Option<SearchScope>, 370 search_scope: Option<SearchScope>,
374 ) -> Cancelable<Option<ReferenceSearchResult>> { 371 ) -> Cancelable<Option<ReferenceSearchResult>> {
375 self.with_db(|db| { 372 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
376 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
377 })
378 } 373 }
379 374
380 /// Finds all methods and free functions for the file. Does not return tests! 375 /// Finds all methods and free functions for the file. Does not return tests!
@@ -481,6 +476,7 @@ impl Analysis {
481 position: FilePosition, 476 position: FilePosition,
482 full_import_path: &str, 477 full_import_path: &str,
483 imported_name: String, 478 imported_name: String,
479 import_for_trait_assoc_item: bool,
484 ) -> Cancelable<Vec<TextEdit>> { 480 ) -> Cancelable<Vec<TextEdit>> {
485 Ok(self 481 Ok(self
486 .with_db(|db| { 482 .with_db(|db| {
@@ -490,6 +486,7 @@ impl Analysis {
490 position, 486 position,
491 full_import_path, 487 full_import_path,
492 imported_name, 488 imported_name,
489 import_for_trait_assoc_item,
493 ) 490 )
494 })? 491 })?
495 .unwrap_or_default()) 492 .unwrap_or_default())
@@ -523,7 +520,7 @@ impl Analysis {
523 &self, 520 &self,
524 position: FilePosition, 521 position: FilePosition,
525 new_name: &str, 522 new_name: &str,
526 ) -> Cancelable<Result<RangeInfo<SourceChange>, RenameError>> { 523 ) -> Cancelable<Result<SourceChange, RenameError>> {
527 self.with_db(|db| references::rename::rename(db, position, new_name)) 524 self.with_db(|db| references::rename::rename(db, position, new_name))
528 } 525 }
529 526
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 51a2f4327..3a4f4d80b 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -25,7 +25,7 @@ use syntax::{
25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T, 25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
26}; 26};
27 27
28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 28use crate::{display::TryToNav, FilePosition, NavigationTarget};
29 29
30#[derive(Debug, Clone)] 30#[derive(Debug, Clone)]
31pub struct ReferenceSearchResult { 31pub struct ReferenceSearchResult {
@@ -41,14 +41,6 @@ pub struct Declaration {
41} 41}
42 42
43impl ReferenceSearchResult { 43impl ReferenceSearchResult {
44 pub fn declaration(&self) -> &Declaration {
45 &self.declaration
46 }
47
48 pub fn decl_target(&self) -> &NavigationTarget {
49 &self.declaration.nav
50 }
51
52 pub fn references(&self) -> &UsageSearchResult { 44 pub fn references(&self) -> &UsageSearchResult {
53 &self.references 45 &self.references
54 } 46 }
@@ -87,7 +79,7 @@ pub(crate) fn find_all_refs(
87 sema: &Semantics<RootDatabase>, 79 sema: &Semantics<RootDatabase>,
88 position: FilePosition, 80 position: FilePosition,
89 search_scope: Option<SearchScope>, 81 search_scope: Option<SearchScope>,
90) -> Option<RangeInfo<ReferenceSearchResult>> { 82) -> Option<ReferenceSearchResult> {
91 let _p = profile::span("find_all_refs"); 83 let _p = profile::span("find_all_refs");
92 let syntax = sema.parse(position.file_id).syntax().clone(); 84 let syntax = sema.parse(position.file_id).syntax().clone();
93 85
@@ -105,7 +97,7 @@ pub(crate) fn find_all_refs(
105 ) 97 )
106 }; 98 };
107 99
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 100 let def = find_name(&sema, &syntax, position, opt_name)?;
109 101
110 let mut usages = def.usages(sema).set_scope(search_scope).all(); 102 let mut usages = def.usages(sema).set_scope(search_scope).all();
111 usages 103 usages
@@ -139,7 +131,7 @@ pub(crate) fn find_all_refs(
139 131
140 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 132 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
141 133
142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) 134 Some(ReferenceSearchResult { declaration, references: usages })
143} 135}
144 136
145fn find_name( 137fn find_name(
@@ -147,35 +139,27 @@ fn find_name(
147 syntax: &SyntaxNode, 139 syntax: &SyntaxNode,
148 position: FilePosition, 140 position: FilePosition,
149 opt_name: Option<ast::Name>, 141 opt_name: Option<ast::Name>,
150) -> Option<RangeInfo<Definition>> { 142) -> Option<Definition> {
151 if let Some(name) = opt_name { 143 let def = if let Some(name) = opt_name {
152 let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); 144 NameClass::classify(sema, &name)?.referenced_or_defined(sema.db)
153 let FileRange { range, .. } = sema.original_range(name.syntax()); 145 } else if let Some(lifetime) =
154 return Some(RangeInfo::new(range, def));
155 }
156
157 let (FileRange { range, .. }, def) = if let Some(lifetime) =
158 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) 146 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
159 { 147 {
160 if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) 148 if let Some(def) =
161 .map(|class| NameRefClass::referenced(class, sema.db)) 149 NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db))
162 { 150 {
163 (sema.original_range(lifetime.syntax()), def) 151 def
164 } else { 152 } else {
165 ( 153 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db)
166 sema.original_range(lifetime.syntax()),
167 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
168 )
169 } 154 }
155 } else if let Some(name_ref) =
156 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)
157 {
158 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db)
170 } else { 159 } else {
171 let name_ref = 160 return None;
172 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
173 (
174 sema.original_range(name_ref.syntax()),
175 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
176 )
177 }; 161 };
178 Some(RangeInfo::new(range, def)) 162 Some(def)
179} 163}
180 164
181fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 165fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -930,6 +914,26 @@ impl Foo {
930 ); 914 );
931 } 915 }
932 916
917 #[test]
918 fn test_find_self_refs_decl() {
919 check(
920 r#"
921struct Foo { bar: i32 }
922
923impl Foo {
924 fn foo(self$0) {
925 self;
926 }
927}
928"#,
929 expect![[r#"
930 self SelfParam FileId(0) 47..51 47..51 SelfParam
931
932 FileId(0) 63..67 Other Read
933 "#]],
934 );
935 }
936
933 fn check(ra_fixture: &str, expect: Expect) { 937 fn check(ra_fixture: &str, expect: Expect) {
934 check_with_scope(ra_fixture, None, expect) 938 check_with_scope(ra_fixture, None, expect)
935 } 939 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 9ac4af026..c25bcce50 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,27 +1,25 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{ 2use std::fmt::{self, Display};
3 convert::TryInto,
4 fmt::{self, Display},
5};
6 3
7use hir::{Module, ModuleDef, ModuleSource, Semantics}; 4use either::Either;
5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
8use ide_db::{ 6use ide_db::{
9 base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}, 7 base_db::{AnchoredPathBuf, FileId, FileRange},
10 defs::{Definition, NameClass, NameRefClass}, 8 defs::{Definition, NameClass, NameRefClass},
11 search::FileReference, 9 search::FileReference,
12 RootDatabase, 10 RootDatabase,
13}; 11};
12use stdx::assert_never;
14use syntax::{ 13use syntax::{
15 algo::find_node_at_offset,
16 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T, 15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
18}; 16};
19use test_utils::mark; 17use test_utils::mark;
20use text_edit::TextEdit; 18use text_edit::TextEdit;
21 19
22use crate::{ 20use crate::{
23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange, 21 display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange,
24 TextRange, TextSize, 22 TextRange,
25}; 23};
26 24
27type RenameResult<T> = Result<T, RenameError>; 25type RenameResult<T> = Result<T, RenameError>;
@@ -50,24 +48,22 @@ pub(crate) fn prepare_rename(
50 let sema = Semantics::new(db); 48 let sema = Semantics::new(db);
51 let source_file = sema.parse(position.file_id); 49 let source_file = sema.parse(position.file_id);
52 let syntax = source_file.syntax(); 50 let syntax = source_file.syntax();
53 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 51 let range = match &find_name_like(&sema, &syntax, position)
54 rename_mod(&sema, position, module, "dummy") 52 .ok_or_else(|| format_err!("No references found at position"))?
55 } else if let Some(self_token) =
56 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
57 { 53 {
58 rename_self_to_param(&sema, position, self_token, "dummy") 54 NameLike::Name(it) => it.syntax(),
59 } else { 55 NameLike::NameRef(it) => it.syntax(),
60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?; 56 NameLike::Lifetime(it) => it.syntax(),
61 Ok(RangeInfo::new(range, SourceChange::default()))
62 } 57 }
63 .map(|info| RangeInfo::new(info.range, ())) 58 .text_range();
59 Ok(RangeInfo::new(range, ()))
64} 60}
65 61
66pub(crate) fn rename( 62pub(crate) fn rename(
67 db: &RootDatabase, 63 db: &RootDatabase,
68 position: FilePosition, 64 position: FilePosition,
69 new_name: &str, 65 new_name: &str,
70) -> RenameResult<RangeInfo<SourceChange>> { 66) -> RenameResult<SourceChange> {
71 let sema = Semantics::new(db); 67 let sema = Semantics::new(db);
72 rename_with_semantics(&sema, position, new_name) 68 rename_with_semantics(&sema, position, new_name)
73} 69}
@@ -76,18 +72,15 @@ pub(crate) fn rename_with_semantics(
76 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
77 position: FilePosition, 73 position: FilePosition,
78 new_name: &str, 74 new_name: &str,
79) -> RenameResult<RangeInfo<SourceChange>> { 75) -> RenameResult<SourceChange> {
80 let source_file = sema.parse(position.file_id); 76 let source_file = sema.parse(position.file_id);
81 let syntax = source_file.syntax(); 77 let syntax = source_file.syntax();
82 78
83 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 79 let def = find_definition(sema, syntax, position)
84 rename_mod(&sema, position, module, new_name) 80 .ok_or_else(|| format_err!("No references found at position"))?;
85 } else if let Some(self_token) = 81 match def {
86 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self]) 82 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name),
87 { 83 def => rename_reference(sema, def, new_name),
88 rename_self_to_param(&sema, position, self_token, new_name)
89 } else {
90 rename_reference(&sema, position, new_name)
91 } 84 }
92} 85}
93 86
@@ -98,17 +91,12 @@ pub(crate) fn will_rename_file(
98) -> Option<SourceChange> { 91) -> Option<SourceChange> {
99 let sema = Semantics::new(db); 92 let sema = Semantics::new(db);
100 let module = sema.to_module_def(file_id)?; 93 let module = sema.to_module_def(file_id)?;
101 94 let mut change = rename_mod(&sema, module, new_name_stem).ok()?;
102 let decl = module.declaration_source(db)?;
103 let range = decl.value.name()?.syntax().text_range();
104
105 let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() };
106 let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info;
107 change.file_system_edits.clear(); 95 change.file_system_edits.clear();
108 Some(change) 96 Some(change)
109} 97}
110 98
111#[derive(Debug, PartialEq)] 99#[derive(Copy, Clone, Debug, PartialEq)]
112enum IdentifierKind { 100enum IdentifierKind {
113 Ident, 101 Ident,
114 Lifetime, 102 Lifetime,
@@ -135,40 +123,51 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
135 } 123 }
136} 124}
137 125
138fn find_module_at_offset( 126enum NameLike {
127 Name(ast::Name),
128 NameRef(ast::NameRef),
129 Lifetime(ast::Lifetime),
130}
131
132fn find_name_like(
139 sema: &Semantics<RootDatabase>, 133 sema: &Semantics<RootDatabase>,
140 position: FilePosition,
141 syntax: &SyntaxNode, 134 syntax: &SyntaxNode,
142) -> Option<Module> { 135 position: FilePosition,
143 let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?; 136) -> Option<NameLike> {
144 137 let namelike = if let Some(name_ref) =
145 let module = match_ast! { 138 sema.find_node_at_offset_with_descend::<ast::NameRef>(syntax, position.offset)
146 match (ident.parent()) { 139 {
147 ast::NameRef(name_ref) => { 140 NameLike::NameRef(name_ref)
148 match NameRefClass::classify(sema, &name_ref)? { 141 } else if let Some(name) =
149 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, 142 sema.find_node_at_offset_with_descend::<ast::Name>(syntax, position.offset)
150 _ => return None, 143 {
151 } 144 NameLike::Name(name)
152 }, 145 } else if let Some(lifetime) =
153 ast::Name(name) => { 146 sema.find_node_at_offset_with_descend::<ast::Lifetime>(syntax, position.offset)
154 match NameClass::classify(&sema, &name)? { 147 {
155 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, 148 NameLike::Lifetime(lifetime)
156 _ => return None, 149 } else {
157 } 150 return None;
158 },
159 _ => return None,
160 }
161 }; 151 };
162 152 Some(namelike)
163 Some(module)
164} 153}
165 154
166fn find_all_refs( 155fn find_definition(
167 sema: &Semantics<RootDatabase>, 156 sema: &Semantics<RootDatabase>,
157 syntax: &SyntaxNode,
168 position: FilePosition, 158 position: FilePosition,
169) -> RenameResult<RangeInfo<ReferenceSearchResult>> { 159) -> Option<Definition> {
170 crate::references::find_all_refs(sema, position, None) 160 let def = match find_name_like(sema, syntax, position)? {
171 .ok_or_else(|| format_err!("No references found at position")) 161 NameLike::Name(name) => NameClass::classify(sema, &name)?.referenced_or_defined(sema.db),
162 NameLike::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
163 NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
164 .map(|class| NameRefClass::referenced(class, sema.db))
165 .or_else(|| {
166 NameClass::classify_lifetime(sema, &lifetime)
167 .map(|it| it.referenced_or_defined(sema.db))
168 })?,
169 };
170 Some(def)
172} 171}
173 172
174fn source_edit_from_references( 173fn source_edit_from_references(
@@ -242,72 +241,84 @@ fn edit_text_range_for_record_field_expr_or_pat(
242 241
243fn rename_mod( 242fn rename_mod(
244 sema: &Semantics<RootDatabase>, 243 sema: &Semantics<RootDatabase>,
245 position: FilePosition,
246 module: Module, 244 module: Module,
247 new_name: &str, 245 new_name: &str,
248) -> RenameResult<RangeInfo<SourceChange>> { 246) -> RenameResult<SourceChange> {
249 if IdentifierKind::Ident != check_identifier(new_name)? { 247 if IdentifierKind::Ident != check_identifier(new_name)? {
250 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); 248 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
251 } 249 }
252 250
253 let mut source_change = SourceChange::default(); 251 let mut source_change = SourceChange::default();
254 252
255 let src = module.definition_source(sema.db); 253 let InFile { file_id, value: def_source } = module.definition_source(sema.db);
256 let file_id = src.file_id.original_file(sema.db); 254 let file_id = file_id.original_file(sema.db);
257 match src.value { 255 if let ModuleSource::SourceFile(..) = def_source {
258 ModuleSource::SourceFile(..) => { 256 // mod is defined in path/to/dir/mod.rs
259 // mod is defined in path/to/dir/mod.rs 257 let path = if module.is_mod_rs(sema.db) {
260 let path = if module.is_mod_rs(sema.db) { 258 format!("../{}/mod.rs", new_name)
261 format!("../{}/mod.rs", new_name) 259 } else {
262 } else { 260 format!("{}.rs", new_name)
263 format!("{}.rs", new_name) 261 };
264 }; 262 let dst = AnchoredPathBuf { anchor: file_id, path };
265 let dst = AnchoredPathBuf { anchor: file_id, path }; 263 let move_file = FileSystemEdit::MoveFile { src: file_id, dst };
266 let move_file = FileSystemEdit::MoveFile { src: file_id, dst }; 264 source_change.push_file_system_edit(move_file);
267 source_change.push_file_system_edit(move_file); 265 }
268 } 266
269 ModuleSource::Module(..) => {} 267 if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) {
270 } 268 let file_id = file_id.original_file(sema.db);
271 269 match decl_source.name() {
272 if let Some(src) = module.declaration_source(sema.db) { 270 Some(name) => source_change.insert_source_edit(
273 let file_id = src.file_id.original_file(sema.db); 271 file_id,
274 let name = src.value.name().unwrap(); 272 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
275 source_change.insert_source_edit( 273 ),
276 file_id, 274 _ => unreachable!(),
277 TextEdit::replace(name.syntax().text_range(), new_name.into()), 275 };
278 );
279 } 276 }
280 277 let def = Definition::ModuleDef(ModuleDef::Module(module));
281 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 278 let usages = def.usages(sema).all();
282 let ref_edits = refs.references().iter().map(|(&file_id, references)| { 279 let ref_edits = usages.iter().map(|(&file_id, references)| {
283 source_edit_from_references(sema, file_id, references, new_name) 280 source_edit_from_references(sema, file_id, references, new_name)
284 }); 281 });
285 source_change.extend(ref_edits); 282 source_change.extend(ref_edits);
286 283
287 Ok(RangeInfo::new(range, source_change)) 284 Ok(source_change)
288} 285}
289 286
290fn rename_to_self( 287fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
291 sema: &Semantics<RootDatabase>, 288 if assert_never!(local.is_self(sema.db)) {
292 position: FilePosition, 289 bail!("rename_to_self invoked on self");
293) -> Result<RangeInfo<SourceChange>, RenameError> { 290 }
294 let source_file = sema.parse(position.file_id); 291
295 let syn = source_file.syntax(); 292 let fn_def = match local.parent(sema.db) {
293 hir::DefWithBody::Function(func) => func,
294 _ => bail!("Cannot rename non-param local to self"),
295 };
296
297 // FIXME: reimplement this on the hir instead
298 // as of the time of this writing params in hir don't keep their names
299 let fn_ast =
300 fn_def.source(sema.db).ok_or(format_err!("Cannot rename non-param local to self"))?.value;
296 301
297 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) 302 let first_param_range = fn_ast
298 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
299 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
300 let param_range = fn_ast
301 .param_list() 303 .param_list()
302 .and_then(|p| p.params().next()) 304 .and_then(|p| p.params().next())
303 .ok_or_else(|| format_err!("Method has no parameters"))? 305 .ok_or_else(|| format_err!("Method has no parameters"))?
304 .syntax() 306 .syntax()
305 .text_range(); 307 .text_range();
306 if !param_range.contains(position.offset) { 308 let InFile { file_id, value: local_source } = local.source(sema.db);
307 bail!("Only the first parameter can be self"); 309 match local_source {
310 either::Either::Left(pat)
311 if !first_param_range.contains_range(pat.syntax().text_range()) =>
312 {
313 bail!("Only the first parameter can be self");
314 }
315 _ => (),
308 } 316 }
309 317
310 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) 318 let impl_block = fn_ast
319 .syntax()
320 .ancestors()
321 .find_map(|node| ast::Impl::cast(node))
311 .and_then(|def| sema.to_def(&def)) 322 .and_then(|def| sema.to_def(&def))
312 .ok_or_else(|| format_err!("No impl block found for function"))?; 323 .ok_or_else(|| format_err!("No impl block found for function"))?;
313 if fn_def.self_param(sema.db).is_some() { 324 if fn_def.self_param(sema.db).is_some() {
@@ -331,25 +342,21 @@ fn rename_to_self(
331 bail!("Parameter type differs from impl block type"); 342 bail!("Parameter type differs from impl block type");
332 } 343 }
333 344
334 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 345 let def = Definition::Local(local);
335 346 let usages = def.usages(sema).all();
336 let mut source_change = SourceChange::default(); 347 let mut source_change = SourceChange::default();
337 source_change.extend(refs.references().iter().map(|(&file_id, references)| { 348 source_change.extend(usages.iter().map(|(&file_id, references)| {
338 source_edit_from_references(sema, file_id, references, "self") 349 source_edit_from_references(sema, file_id, references, "self")
339 })); 350 }));
340 source_change.insert_source_edit( 351 source_change.insert_source_edit(
341 position.file_id, 352 file_id.original_file(sema.db),
342 TextEdit::replace(param_range, String::from(self_param)), 353 TextEdit::replace(first_param_range, String::from(self_param)),
343 ); 354 );
344 355
345 Ok(RangeInfo::new(range, source_change)) 356 Ok(source_change)
346} 357}
347 358
348fn text_edit_from_self_param( 359fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
349 syn: &SyntaxNode,
350 self_param: &ast::SelfParam,
351 new_name: &str,
352) -> Option<TextEdit> {
353 fn target_type_name(impl_def: &ast::Impl) -> Option<String> { 360 fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
354 if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { 361 if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
355 return Some(p.path()?.segment()?.name_ref()?.text().to_string()); 362 return Some(p.path()?.segment()?.name_ref()?.text().to_string());
@@ -357,7 +364,7 @@ fn text_edit_from_self_param(
357 None 364 None
358 } 365 }
359 366
360 let impl_def = find_node_at_offset::<ast::Impl>(syn, self_param.syntax().text_range().start())?; 367 let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?;
361 let type_name = target_type_name(&impl_def)?; 368 let type_name = target_type_name(&impl_def)?;
362 369
363 let mut replacement_text = String::from(new_name); 370 let mut replacement_text = String::from(new_name);
@@ -374,96 +381,119 @@ fn text_edit_from_self_param(
374 381
375fn rename_self_to_param( 382fn rename_self_to_param(
376 sema: &Semantics<RootDatabase>, 383 sema: &Semantics<RootDatabase>,
377 position: FilePosition, 384 local: hir::Local,
378 self_token: SyntaxToken,
379 new_name: &str, 385 new_name: &str,
380) -> Result<RangeInfo<SourceChange>, RenameError> { 386 identifier_kind: IdentifierKind,
381 let ident_kind = check_identifier(new_name)?; 387) -> RenameResult<SourceChange> {
382 match ident_kind { 388 let (file_id, self_param) = match local.source(sema.db) {
383 IdentifierKind::Lifetime => bail!("Invalid name `{}`: not an identifier", new_name), 389 InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param),
384 IdentifierKind::ToSelf => { 390 _ => {
385 // no-op 391 assert_never!(true, "rename_self_to_param invoked on a non-self local");
386 mark::hit!(rename_self_to_self); 392 bail!("rename_self_to_param invoked on a non-self local");
387 return Ok(RangeInfo::new(self_token.text_range(), SourceChange::default()));
388 }
389 _ => (),
390 }
391 let source_file = sema.parse(position.file_id);
392 let syn = source_file.syntax();
393
394 let text = sema.db.file_text(position.file_id);
395 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
396 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
397 let search_range = fn_def.syntax().text_range();
398
399 let mut source_change = SourceChange::default();
400
401 for (idx, _) in text.match_indices("self") {
402 let offset: TextSize = idx.try_into().unwrap();
403 if !search_range.contains_inclusive(offset) {
404 continue;
405 } 393 }
406 if let Some(ref usage) = syn.token_at_offset(offset).find(|t| t.kind() == T![self]) { 394 };
407 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
408 text_edit_from_self_param(syn, self_param, new_name)
409 .ok_or_else(|| format_err!("No target type found"))?
410 } else {
411 TextEdit::replace(usage.text_range(), String::from(new_name))
412 };
413 source_change.insert_source_edit(position.file_id, edit);
414 }
415 }
416 395
417 if source_change.source_file_edits.len() > 1 && ident_kind == IdentifierKind::Underscore { 396 let def = Definition::Local(local);
397 let usages = def.usages(sema).all();
398 let edit = text_edit_from_self_param(&self_param, new_name)
399 .ok_or_else(|| format_err!("No target type found"))?;
400 if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
418 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 401 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
419 } 402 }
420 403 let mut source_change = SourceChange::default();
421 let range = ast::SelfParam::cast(self_token.parent()) 404 source_change.insert_source_edit(file_id.original_file(sema.db), edit);
422 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 405 source_change.extend(usages.iter().map(|(&file_id, references)| {
423 406 source_edit_from_references(sema, file_id, &references, new_name)
424 Ok(RangeInfo::new(range, source_change)) 407 }));
408 Ok(source_change)
425} 409}
426 410
427fn rename_reference( 411fn rename_reference(
428 sema: &Semantics<RootDatabase>, 412 sema: &Semantics<RootDatabase>,
429 position: FilePosition, 413 def: Definition,
430 new_name: &str, 414 new_name: &str,
431) -> Result<RangeInfo<SourceChange>, RenameError> { 415) -> RenameResult<SourceChange> {
432 let ident_kind = check_identifier(new_name)?; 416 let ident_kind = check_identifier(new_name)?;
433 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
434 417
435 match (ident_kind, &refs.declaration.kind) { 418 let def_is_lbl_or_lt = matches!(def,
436 (IdentifierKind::ToSelf, ReferenceKind::Lifetime) 419 Definition::GenericParam(hir::GenericParam::LifetimeParam(_))
437 | (IdentifierKind::Underscore, ReferenceKind::Lifetime) 420 | Definition::Label(_)
438 | (IdentifierKind::Ident, ReferenceKind::Lifetime) => { 421 );
422 match (ident_kind, def) {
423 (IdentifierKind::ToSelf, _)
424 | (IdentifierKind::Underscore, _)
425 | (IdentifierKind::Ident, _)
426 if def_is_lbl_or_lt =>
427 {
439 mark::hit!(rename_not_a_lifetime_ident_ref); 428 mark::hit!(rename_not_a_lifetime_ident_ref);
440 bail!("Invalid name `{}`: not a lifetime identifier", new_name) 429 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
441 } 430 }
442 (IdentifierKind::Lifetime, ReferenceKind::Lifetime) => mark::hit!(rename_lifetime), 431 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime),
443 (IdentifierKind::Lifetime, _) => { 432 (IdentifierKind::Lifetime, _) => {
444 mark::hit!(rename_not_an_ident_ref); 433 mark::hit!(rename_not_an_ident_ref);
445 bail!("Invalid name `{}`: not an identifier", new_name) 434 bail!("Invalid name `{}`: not an identifier", new_name)
446 } 435 }
447 (IdentifierKind::ToSelf, ReferenceKind::SelfParam) => { 436 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
448 unreachable!("rename_self_to_param should've been called instead") 437 // no-op
438 mark::hit!(rename_self_to_self);
439 return Ok(SourceChange::default());
449 } 440 }
450 (IdentifierKind::ToSelf, _) => { 441 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
451 mark::hit!(rename_to_self); 442 mark::hit!(rename_self_to_param);
452 return rename_to_self(sema, position); 443 return rename_self_to_param(sema, local, new_name, ident_kind);
453 } 444 }
454 (IdentifierKind::Underscore, _) if !refs.references.is_empty() => { 445 (IdentifierKind::ToSelf, Definition::Local(local)) => {
455 mark::hit!(rename_underscore_multiple); 446 mark::hit!(rename_to_self);
456 bail!("Cannot rename reference to `_` as it is being referenced multiple times") 447 return rename_to_self(sema, local);
457 } 448 }
449 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
458 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 450 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident),
459 } 451 }
460 452
453 let usages = def.usages(sema).all();
454 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
455 mark::hit!(rename_underscore_multiple);
456 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
457 }
461 let mut source_change = SourceChange::default(); 458 let mut source_change = SourceChange::default();
462 source_change.extend(refs.into_iter().map(|(file_id, references)| { 459 source_change.extend(usages.iter().map(|(&file_id, references)| {
463 source_edit_from_references(sema, file_id, &references, new_name) 460 source_edit_from_references(sema, file_id, &references, new_name)
464 })); 461 }));
465 462
466 Ok(RangeInfo::new(range, source_change)) 463 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
464 source_change.insert_source_edit(file_id, edit);
465 Ok(source_change)
466}
467
468fn source_edit_from_def(
469 sema: &Semantics<RootDatabase>,
470 def: Definition,
471 new_name: &str,
472) -> RenameResult<(FileId, TextEdit)> {
473 let nav = def.try_to_nav(sema.db).unwrap();
474
475 let mut replacement_text = String::new();
476 let mut repl_range = nav.focus_or_full_range();
477 if let Definition::Local(local) = def {
478 if let Either::Left(pat) = local.source(sema.db).value {
479 if matches!(
480 pat.syntax().parent().and_then(ast::RecordPatField::cast),
481 Some(pat_field) if pat_field.name_ref().is_none()
482 ) {
483 replacement_text.push_str(": ");
484 replacement_text.push_str(new_name);
485 repl_range = TextRange::new(
486 pat.syntax().text_range().end(),
487 pat.syntax().text_range().end(),
488 );
489 }
490 }
491 }
492 if replacement_text.is_empty() {
493 replacement_text.push_str(new_name);
494 }
495 let edit = TextEdit::replace(repl_range, replacement_text);
496 Ok((nav.file_id, edit))
467} 497}
468 498
469#[cfg(test)] 499#[cfg(test)]
@@ -485,7 +515,7 @@ mod tests {
485 Ok(source_change) => { 515 Ok(source_change) => {
486 let mut text_edit_builder = TextEdit::builder(); 516 let mut text_edit_builder = TextEdit::builder();
487 let mut file_id: Option<FileId> = None; 517 let mut file_id: Option<FileId> = None;
488 for edit in source_change.info.source_file_edits { 518 for edit in source_change.source_file_edits {
489 file_id = Some(edit.0); 519 file_id = Some(edit.0);
490 for indel in edit.1.into_iter() { 520 for indel in edit.1.into_iter() {
491 text_edit_builder.replace(indel.delete, indel.insert); 521 text_edit_builder.replace(indel.delete, indel.insert);
@@ -884,36 +914,33 @@ mod foo$0;
884// empty 914// empty
885"#, 915"#,
886 expect![[r#" 916 expect![[r#"
887 RangeInfo { 917 SourceChange {
888 range: 4..7, 918 source_file_edits: {
889 info: SourceChange { 919 FileId(
890 source_file_edits: { 920 1,
891 FileId( 921 ): TextEdit {
892 1, 922 indels: [
893 ): TextEdit { 923 Indel {
894 indels: [ 924 insert: "foo2",
895 Indel { 925 delete: 4..7,
896 insert: "foo2", 926 },
897 delete: 4..7, 927 ],
898 },
899 ],
900 },
901 }, 928 },
902 file_system_edits: [ 929 },
903 MoveFile { 930 file_system_edits: [
904 src: FileId( 931 MoveFile {
932 src: FileId(
933 2,
934 ),
935 dst: AnchoredPathBuf {
936 anchor: FileId(
905 2, 937 2,
906 ), 938 ),
907 dst: AnchoredPathBuf { 939 path: "foo2.rs",
908 anchor: FileId(
909 2,
910 ),
911 path: "foo2.rs",
912 },
913 }, 940 },
914 ], 941 },
915 is_snippet: false, 942 ],
916 }, 943 is_snippet: false,
917 } 944 }
918 "#]], 945 "#]],
919 ); 946 );
@@ -936,46 +963,43 @@ pub struct FooContent;
936use crate::foo$0::FooContent; 963use crate::foo$0::FooContent;
937"#, 964"#,
938 expect![[r#" 965 expect![[r#"
939 RangeInfo { 966 SourceChange {
940 range: 11..14, 967 source_file_edits: {
941 info: SourceChange { 968 FileId(
942 source_file_edits: { 969 0,
943 FileId( 970 ): TextEdit {
944 0, 971 indels: [
945 ): TextEdit { 972 Indel {
946 indels: [ 973 insert: "quux",
947 Indel { 974 delete: 8..11,
948 insert: "quux", 975 },
949 delete: 8..11, 976 ],
950 }, 977 },
951 ], 978 FileId(
952 }, 979 2,
953 FileId( 980 ): TextEdit {
954 2, 981 indels: [
955 ): TextEdit { 982 Indel {
956 indels: [ 983 insert: "quux",
957 Indel { 984 delete: 11..14,
958 insert: "quux", 985 },
959 delete: 11..14, 986 ],
960 },
961 ],
962 },
963 }, 987 },
964 file_system_edits: [ 988 },
965 MoveFile { 989 file_system_edits: [
966 src: FileId( 990 MoveFile {
991 src: FileId(
992 1,
993 ),
994 dst: AnchoredPathBuf {
995 anchor: FileId(
967 1, 996 1,
968 ), 997 ),
969 dst: AnchoredPathBuf { 998 path: "quux.rs",
970 anchor: FileId(
971 1,
972 ),
973 path: "quux.rs",
974 },
975 }, 999 },
976 ], 1000 },
977 is_snippet: false, 1001 ],
978 }, 1002 is_snippet: false,
979 } 1003 }
980 "#]], 1004 "#]],
981 ); 1005 );
@@ -992,36 +1016,33 @@ mod fo$0o;
992// empty 1016// empty
993"#, 1017"#,
994 expect![[r#" 1018 expect![[r#"
995 RangeInfo { 1019 SourceChange {
996 range: 4..7, 1020 source_file_edits: {
997 info: SourceChange { 1021 FileId(
998 source_file_edits: { 1022 0,
999 FileId( 1023 ): TextEdit {
1000 0, 1024 indels: [
1001 ): TextEdit { 1025 Indel {
1002 indels: [ 1026 insert: "foo2",
1003 Indel { 1027 delete: 4..7,
1004 insert: "foo2", 1028 },
1005 delete: 4..7, 1029 ],
1006 },
1007 ],
1008 },
1009 }, 1030 },
1010 file_system_edits: [ 1031 },
1011 MoveFile { 1032 file_system_edits: [
1012 src: FileId( 1033 MoveFile {
1034 src: FileId(
1035 1,
1036 ),
1037 dst: AnchoredPathBuf {
1038 anchor: FileId(
1013 1, 1039 1,
1014 ), 1040 ),
1015 dst: AnchoredPathBuf { 1041 path: "../foo2/mod.rs",
1016 anchor: FileId(
1017 1,
1018 ),
1019 path: "../foo2/mod.rs",
1020 },
1021 }, 1042 },
1022 ], 1043 },
1023 is_snippet: false, 1044 ],
1024 }, 1045 is_snippet: false,
1025 } 1046 }
1026 "#]], 1047 "#]],
1027 ); 1048 );
@@ -1039,36 +1060,33 @@ mod outer { mod fo$0o; }
1039// empty 1060// empty
1040"#, 1061"#,
1041 expect![[r#" 1062 expect![[r#"
1042 RangeInfo { 1063 SourceChange {
1043 range: 16..19, 1064 source_file_edits: {
1044 info: SourceChange { 1065 FileId(
1045 source_file_edits: { 1066 0,
1046 FileId( 1067 ): TextEdit {
1047 0, 1068 indels: [
1048 ): TextEdit { 1069 Indel {
1049 indels: [ 1070 insert: "bar",
1050 Indel { 1071 delete: 16..19,
1051 insert: "bar", 1072 },
1052 delete: 16..19, 1073 ],
1053 },
1054 ],
1055 },
1056 }, 1074 },
1057 file_system_edits: [ 1075 },
1058 MoveFile { 1076 file_system_edits: [
1059 src: FileId( 1077 MoveFile {
1078 src: FileId(
1079 1,
1080 ),
1081 dst: AnchoredPathBuf {
1082 anchor: FileId(
1060 1, 1083 1,
1061 ), 1084 ),
1062 dst: AnchoredPathBuf { 1085 path: "bar.rs",
1063 anchor: FileId(
1064 1,
1065 ),
1066 path: "bar.rs",
1067 },
1068 }, 1086 },
1069 ], 1087 },
1070 is_snippet: false, 1088 ],
1071 }, 1089 is_snippet: false,
1072 } 1090 }
1073 "#]], 1091 "#]],
1074 ); 1092 );
@@ -1109,46 +1127,43 @@ pub mod foo$0;
1109// pub fn fun() {} 1127// pub fn fun() {}
1110"#, 1128"#,
1111 expect![[r#" 1129 expect![[r#"
1112 RangeInfo { 1130 SourceChange {
1113 range: 8..11, 1131 source_file_edits: {
1114 info: SourceChange { 1132 FileId(
1115 source_file_edits: { 1133 0,
1116 FileId( 1134 ): TextEdit {
1117 0, 1135 indels: [
1118 ): TextEdit { 1136 Indel {
1119 indels: [ 1137 insert: "foo2",
1120 Indel { 1138 delete: 27..30,
1121 insert: "foo2", 1139 },
1122 delete: 27..30, 1140 ],
1123 },
1124 ],
1125 },
1126 FileId(
1127 1,
1128 ): TextEdit {
1129 indels: [
1130 Indel {
1131 insert: "foo2",
1132 delete: 8..11,
1133 },
1134 ],
1135 },
1136 }, 1141 },
1137 file_system_edits: [ 1142 FileId(
1138 MoveFile { 1143 1,
1139 src: FileId( 1144 ): TextEdit {
1145 indels: [
1146 Indel {
1147 insert: "foo2",
1148 delete: 8..11,
1149 },
1150 ],
1151 },
1152 },
1153 file_system_edits: [
1154 MoveFile {
1155 src: FileId(
1156 2,
1157 ),
1158 dst: AnchoredPathBuf {
1159 anchor: FileId(
1140 2, 1160 2,
1141 ), 1161 ),
1142 dst: AnchoredPathBuf { 1162 path: "foo2.rs",
1143 anchor: FileId(
1144 2,
1145 ),
1146 path: "foo2.rs",
1147 },
1148 }, 1163 },
1149 ], 1164 },
1150 is_snippet: false, 1165 ],
1151 }, 1166 is_snippet: false,
1152 } 1167 }
1153 "#]], 1168 "#]],
1154 ); 1169 );
@@ -1350,6 +1365,7 @@ impl Foo {
1350 1365
1351 #[test] 1366 #[test]
1352 fn test_owned_self_to_parameter() { 1367 fn test_owned_self_to_parameter() {
1368 mark::check!(rename_self_to_param);
1353 check( 1369 check(
1354 "foo", 1370 "foo",
1355 r#" 1371 r#"
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index f5ee7de86..975abf47f 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -6,9 +6,10 @@ use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::{defs::Definition, RootDatabase}; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
12 13
13use crate::{ 14use crate::{
14 display::{ToNav, TryToNav}, 15 display::{ToNav, TryToNav},
@@ -95,27 +96,45 @@ impl Runnable {
95// |=== 96// |===
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 97pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 98 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 99 let module = match sema.to_module_def(file_id) {
99 source_file 100 None => return Vec::new(),
100 .syntax() 101 Some(it) => it,
101 .descendants() 102 };
102 .filter_map(|item| { 103
103 let runnable = match_ast! { 104 let mut res = Vec::new();
104 match item { 105 runnables_mod(&sema, &mut res, module);
105 ast::Fn(func) => { 106 res
106 let def = sema.to_def(&func)?; 107}
107 runnable_fn(&sema, def) 108
108 }, 109fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
109 ast::Module(it) => runnable_mod(&sema, it), 110 acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
110 _ => None, 111 let runnable = match def {
111 } 112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
112 }; 113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? { 114 _ => None,
114 Definition::ModuleDef(def) => module_def_doctest(&sema, def), 115 };
115 _ => None, 116 runnable.or_else(|| module_def_doctest(&sema, def))
116 }) 117 }));
117 }) 118
118 .collect() 119 acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
120 |def| match def {
121 hir::AssocItem::Function(it) => {
122 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
123 }
124 hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
125 hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
126 },
127 ));
128
129 for def in module.declarations(sema.db) {
130 if let hir::ModuleDef::Module(submodule) = def {
131 match submodule.definition_source(sema.db).value {
132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules),
134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 }
136 }
137 }
119} 138}
120 139
121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 140pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -150,26 +169,16 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
150 Some(Runnable { nav, kind, cfg }) 169 Some(Runnable { nav, kind, cfg })
151} 170}
152 171
153pub(crate) fn runnable_mod( 172pub(crate) fn runnable_mod(sema: &Semantics<RootDatabase>, def: hir::Module) -> Option<Runnable> {
154 sema: &Semantics<RootDatabase>, 173 if !has_test_function_or_multiple_test_submodules(sema, &def) {
155 module: ast::Module,
156) -> Option<Runnable> {
157 if !has_test_function_or_multiple_test_submodules(&module) {
158 return None; 174 return None;
159 } 175 }
160 let module_def = sema.to_def(&module)?; 176 let path =
177 def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
161 178
162 let path = module_def
163 .path_to_root(sema.db)
164 .into_iter()
165 .rev()
166 .filter_map(|it| it.name(sema.db))
167 .join("::");
168
169 let def = sema.to_def(&module)?;
170 let attrs = def.attrs(sema.db); 179 let attrs = def.attrs(sema.db);
171 let cfg = attrs.cfg(); 180 let cfg = attrs.cfg();
172 let nav = module_def.to_nav(sema.db); 181 let nav = def.to_nav(sema.db);
173 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) 182 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
174} 183}
175 184
@@ -289,35 +298,37 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
289 298
290// We could create runnables for modules with number_of_test_submodules > 0, 299// We could create runnables for modules with number_of_test_submodules > 0,
291// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already 300// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
292fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { 301fn has_test_function_or_multiple_test_submodules(
293 if let Some(item_list) = module.item_list() { 302 sema: &Semantics<RootDatabase>,
294 let mut number_of_test_submodules = 0; 303 module: &hir::Module,
295 304) -> bool {
296 for item in item_list.items() { 305 let mut number_of_test_submodules = 0;
297 match item { 306
298 ast::Item::Fn(f) => { 307 for item in module.declarations(sema.db) {
299 if test_related_attribute(&f).is_some() { 308 match item {
309 hir::ModuleDef::Function(f) => {
310 if let Some(it) = f.source(sema.db) {
311 if test_related_attribute(&it.value).is_some() {
300 return true; 312 return true;
301 } 313 }
302 } 314 }
303 ast::Item::Module(submodule) => { 315 }
304 if has_test_function_or_multiple_test_submodules(&submodule) { 316 hir::ModuleDef::Module(submodule) => {
305 number_of_test_submodules += 1; 317 if has_test_function_or_multiple_test_submodules(sema, &submodule) {
306 } 318 number_of_test_submodules += 1;
307 } 319 }
308 _ => (),
309 } 320 }
321 _ => (),
310 } 322 }
311
312 number_of_test_submodules > 1
313 } else {
314 false
315 } 323 }
324
325 number_of_test_submodules > 1
316} 326}
317 327
318#[cfg(test)] 328#[cfg(test)]
319mod tests { 329mod tests {
320 use expect_test::{expect, Expect}; 330 use expect_test::{expect, Expect};
331 use test_utils::mark;
321 332
322 use crate::fixture; 333 use crate::fixture;
323 334
@@ -753,6 +764,21 @@ mod root_tests {
753 file_id: FileId( 764 file_id: FileId(
754 0, 765 0,
755 ), 766 ),
767 full_range: 202..286,
768 focus_range: 206..220,
769 name: "nested_tests_2",
770 kind: Module,
771 },
772 kind: TestMod {
773 path: "root_tests::nested_tests_0::nested_tests_2",
774 },
775 cfg: None,
776 },
777 Runnable {
778 nav: NavigationTarget {
779 file_id: FileId(
780 0,
781 ),
756 full_range: 84..126, 782 full_range: 84..126,
757 focus_range: 107..121, 783 focus_range: 107..121,
758 name: "nested_test_11", 784 name: "nested_test_11",
@@ -793,21 +819,6 @@ mod root_tests {
793 file_id: FileId( 819 file_id: FileId(
794 0, 820 0,
795 ), 821 ),
796 full_range: 202..286,
797 focus_range: 206..220,
798 name: "nested_tests_2",
799 kind: Module,
800 },
801 kind: TestMod {
802 path: "root_tests::nested_tests_0::nested_tests_2",
803 },
804 cfg: None,
805 },
806 Runnable {
807 nav: NavigationTarget {
808 file_id: FileId(
809 0,
810 ),
811 full_range: 235..276, 822 full_range: 235..276,
812 focus_range: 258..271, 823 focus_range: 258..271,
813 name: "nested_test_2", 824 name: "nested_test_2",
@@ -982,4 +993,85 @@ impl Foo {
982 "#]], 993 "#]],
983 ); 994 );
984 } 995 }
996
997 #[test]
998 fn test_runnables_in_macro() {
999 check(
1000 r#"
1001//- /lib.rs
1002$0
1003macro_rules! gen {
1004 () => {
1005 #[test]
1006 fn foo_test() {
1007 }
1008 }
1009}
1010mod tests {
1011 gen!();
1012}
1013"#,
1014 &[&TEST, &TEST],
1015 expect![[r#"
1016 [
1017 Runnable {
1018 nav: NavigationTarget {
1019 file_id: FileId(
1020 0,
1021 ),
1022 full_range: 90..115,
1023 focus_range: 94..99,
1024 name: "tests",
1025 kind: Module,
1026 },
1027 kind: TestMod {
1028 path: "tests",
1029 },
1030 cfg: None,
1031 },
1032 Runnable {
1033 nav: NavigationTarget {
1034 file_id: FileId(
1035 0,
1036 ),
1037 full_range: 106..113,
1038 focus_range: 106..113,
1039 name: "foo_test",
1040 kind: Function,
1041 },
1042 kind: Test {
1043 test_id: Path(
1044 "tests::foo_test",
1045 ),
1046 attr: TestAttr {
1047 ignore: false,
1048 },
1049 },
1050 cfg: None,
1051 },
1052 ]
1053 "#]],
1054 );
1055 }
1056
1057 #[test]
1058 fn dont_recurse_in_outline_submodules() {
1059 mark::check!(dont_recurse_in_outline_submodules);
1060 check(
1061 r#"
1062//- /lib.rs
1063$0
1064mod m;
1065//- /m.rs
1066mod tests {
1067 #[test]
1068 fn t() {}
1069}
1070"#,
1071 &[],
1072 expect![[r#"
1073 []
1074 "#]],
1075 );
1076 }
985} 1077}
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index e10d7c3a4..137c38c0d 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -38,6 +38,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
38 format_to!(buf, "{}\n", syntax_tree_stats(db)); 38 format_to!(buf, "{}\n", syntax_tree_stats(db));
39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db)); 39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db));
40 format_to!(buf, "{} total\n", memory_usage()); 40 format_to!(buf, "{} total\n", memory_usage());
41 format_to!(buf, "\ncounts:\n{}", profile::countme::get_all());
41 42
42 if let Some(file_id) = file_id { 43 if let Some(file_id) = file_id {
43 format_to!(buf, "\nfile info:\n"); 44 format_to!(buf, "\nfile info:\n");
@@ -60,6 +61,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
60 None => format_to!(buf, "does not belong to any crate"), 61 None => format_to!(buf, "does not belong to any crate"),
61 } 62 }
62 } 63 }
64
63 buf 65 buf
64} 66}
65 67
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index a74ca844b..8a9b5ca8c 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -30,7 +30,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
30 let parent = string.syntax().parent(); 30 let parent = string.syntax().parent();
31 31
32 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; 32 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
33 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { 33 if !matches!(name.text(), "format_args" | "format_args_nl") {
34 return None; 34 return None;
35 } 35 }
36 36
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 87578e70a..8625ef5df 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -68,7 +68,8 @@ pub(super) fn element(
68 NAME_REF => { 68 NAME_REF => {
69 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 69 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
70 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { 70 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
71 match NameRefClass::classify(sema, &name_ref) { 71 let is_self = name_ref.self_token().is_some();
72 let h = match NameRefClass::classify(sema, &name_ref) {
72 Some(name_kind) => match name_kind { 73 Some(name_kind) => match name_kind {
73 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), 74 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(),
74 NameRefClass::Definition(def) => { 75 NameRefClass::Definition(def) => {
@@ -108,6 +109,11 @@ pub(super) fn element(
108 highlight_name_ref_by_syntax(name_ref, sema) 109 highlight_name_ref_by_syntax(name_ref, sema)
109 } 110 }
110 None => HlTag::UnresolvedReference.into(), 111 None => HlTag::UnresolvedReference.into(),
112 };
113 if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self {
114 HlTag::Symbol(SymbolKind::SelfParam).into()
115 } else {
116 h
111 } 117 }
112 }) 118 })
113 } 119 }
@@ -225,18 +231,8 @@ pub(super) fn element(
225 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, 231 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow,
226 T![unsafe] => h | HlMod::Unsafe, 232 T![unsafe] => h | HlMod::Unsafe,
227 T![true] | T![false] => HlTag::BoolLiteral.into(), 233 T![true] | T![false] => HlTag::BoolLiteral.into(),
228 T![self] => { 234 // self is handled as either a Name or NameRef already
229 let self_param = element.parent().and_then(ast::SelfParam::cast); 235 T![self] => return None,
230 if let Some(NameClass::Definition(def)) = self_param
231 .and_then(|self_param| NameClass::classify_self_param(sema, &self_param))
232 {
233 highlight_def(db, def) | HlMod::Definition
234 } else if element.ancestors().any(|it| it.kind() == USE_TREE) {
235 HlTag::Symbol(SymbolKind::SelfParam).into()
236 } else {
237 return None;
238 }
239 }
240 T![ref] => element 236 T![ref] => element
241 .parent() 237 .parent()
242 .and_then(ast::IdentPat::cast) 238 .and_then(ast::IdentPat::cast)
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 281461493..8cdc3688f 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -116,7 +116,7 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
116 None => (), 116 None => (),
117 } 117 }
118 118
119 let line: &str = comment.text().as_str(); 119 let line: &str = comment.text();
120 let range = comment.syntax().text_range(); 120 let range = comment.syntax().text_range();
121 121
122 let mut pos = TextSize::of(comment.prefix()); 122 let mut pos = TextSize::of(comment.prefix());
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 1d4bac7ad..4c63d3023 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -111,7 +111,6 @@ mod tests {
111 let syn = analysis.syntax_tree(file_id, None).unwrap(); 111 let syn = analysis.syntax_tree(file_id, None).unwrap();
112 112
113 assert_eq_text!( 113 assert_eq_text!(
114 syn.trim(),
115 r#" 114 r#"
116[email protected] 115[email protected]
117 [email protected] 116 [email protected]
@@ -127,7 +126,8 @@ [email protected]
127 [email protected] "{" 126 [email protected] "{"
128 [email protected] "}" 127 [email protected] "}"
129"# 128"#
130 .trim() 129 .trim(),
130 syn.trim()
131 ); 131 );
132 132
133 let (analysis, file_id) = fixture::file( 133 let (analysis, file_id) = fixture::file(
@@ -143,7 +143,6 @@ fn test() {
143 let syn = analysis.syntax_tree(file_id, None).unwrap(); 143 let syn = analysis.syntax_tree(file_id, None).unwrap();
144 144
145 assert_eq_text!( 145 assert_eq_text!(
146 syn.trim(),
147 r#" 146 r#"
148[email protected] 147[email protected]
149 [email protected] 148 [email protected]
@@ -176,7 +175,8 @@ [email protected]
176 [email protected] "\n" 175 [email protected] "\n"
177 [email protected] "}" 176 [email protected] "}"
178"# 177"#
179 .trim() 178 .trim(),
179 syn.trim()
180 ); 180 );
181 } 181 }
182 182
@@ -186,7 +186,6 @@ [email protected]
186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
187 187
188 assert_eq_text!( 188 assert_eq_text!(
189 syn.trim(),
190 r#" 189 r#"
191[email protected] 190[email protected]
192 [email protected] "fn" 191 [email protected] "fn"
@@ -201,7 +200,8 @@ [email protected]
201 [email protected] "{" 200 [email protected] "{"
202 [email protected] "}" 201 [email protected] "}"
203"# 202"#
204 .trim() 203 .trim(),
204 syn.trim()
205 ); 205 );
206 206
207 let (analysis, range) = fixture::range( 207 let (analysis, range) = fixture::range(
@@ -216,7 +216,6 @@ [email protected]
216 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 216 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
217 217
218 assert_eq_text!( 218 assert_eq_text!(
219 syn.trim(),
220 r#" 219 r#"
221[email protected] 220[email protected]
222 [email protected] 221 [email protected]
@@ -234,7 +233,8 @@ [email protected]
234 [email protected] ")" 233 [email protected] ")"
235 [email protected] ";" 234 [email protected] ";"
236"# 235"#
237 .trim() 236 .trim(),
237 syn.trim()
238 ); 238 );
239 } 239 }
240 240
@@ -253,7 +253,6 @@ fn bar() {
253 ); 253 );
254 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 254 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
255 assert_eq_text!( 255 assert_eq_text!(
256 syn.trim(),
257 r#" 256 r#"
258[email protected] 257[email protected]
259 [email protected] 258 [email protected]
@@ -270,7 +269,8 @@ [email protected]
270 [email protected] "\n" 269 [email protected] "\n"
271 [email protected] "}" 270 [email protected] "}"
272"# 271"#
273 .trim() 272 .trim(),
273 syn.trim()
274 ); 274 );
275 275
276 // With a raw string 276 // With a raw string
@@ -287,7 +287,6 @@ fn bar() {
287 ); 287 );
288 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 288 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
289 assert_eq_text!( 289 assert_eq_text!(
290 syn.trim(),
291 r#" 290 r#"
292[email protected] 291[email protected]
293 [email protected] 292 [email protected]
@@ -304,7 +303,8 @@ [email protected]
304 [email protected] "\n" 303 [email protected] "\n"
305 [email protected] "}" 304 [email protected] "}"
306"# 305"#
307 .trim() 306 .trim(),
307 syn.trim()
308 ); 308 );
309 309
310 // With a raw string 310 // With a raw string
@@ -320,7 +320,6 @@ fn bar() {
320 ); 320 );
321 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 321 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
322 assert_eq_text!( 322 assert_eq_text!(
323 syn.trim(),
324 r#" 323 r#"
325[email protected] 324[email protected]
326 [email protected] 325 [email protected]
@@ -351,7 +350,8 @@ [email protected]
351 [email protected] "\n" 350 [email protected] "\n"
352 [email protected] "}" 351 [email protected] "}"
353"# 352"#
354 .trim() 353 .trim(),
354 syn.trim()
355 ); 355 );
356 } 356 }
357} 357}
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index c770a236b..9d9b6de7a 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -149,6 +149,7 @@ impl RootDatabase {
149 149
150 // DefDatabase 150 // DefDatabase
151 hir::db::ItemTreeQuery 151 hir::db::ItemTreeQuery
152 hir::db::BlockDefMapQuery
152 hir::db::CrateDefMapQueryQuery 153 hir::db::CrateDefMapQueryQuery
153 hir::db::StructDataQuery 154 hir::db::StructDataQuery
154 hir::db::UnionDataQuery 155 hir::db::UnionDataQuery
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 231e886a9..a8091dbee 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -117,13 +117,6 @@ 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
127 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 120 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
128 let _p = profile::span("classify_name"); 121 let _p = profile::span("classify_name");
129 122
@@ -186,6 +179,10 @@ impl NameClass {
186 179
187 Some(NameClass::Definition(Definition::Local(local))) 180 Some(NameClass::Definition(Definition::Local(local)))
188 }, 181 },
182 ast::SelfParam(it) => {
183 let def = sema.to_def(&it)?;
184 Some(NameClass::Definition(Definition::Local(def.into())))
185 },
189 ast::RecordField(it) => { 186 ast::RecordField(it) => {
190 let field: hir::Field = sema.to_def(&it)?; 187 let field: hir::Field = sema.to_def(&it)?;
191 Some(NameClass::Definition(Definition::Field(field))) 188 Some(NameClass::Definition(Definition::Field(field)))
@@ -346,7 +343,7 @@ impl NameRefClass {
346 hir::AssocItem::TypeAlias(it) => Some(*it), 343 hir::AssocItem::TypeAlias(it) => Some(*it),
347 _ => None, 344 _ => None,
348 }) 345 })
349 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) 346 .find(|alias| &alias.name(sema.db).to_string() == name_ref.text())
350 { 347 {
351 return Some(NameRefClass::Definition(Definition::ModuleDef( 348 return Some(NameRefClass::Definition(Definition::ModuleDef(
352 ModuleDef::TypeAlias(ty), 349 ModuleDef::TypeAlias(ty),
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index edc3da318..517abbb4b 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,12 +1,13 @@
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, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode};
6 6
7use crate::{imports_locator, RootDatabase}; 7use crate::{
8 8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9use super::insert_use::InsertUseConfig; 9 RootDatabase,
10};
10 11
11#[derive(Debug)] 12#[derive(Debug)]
12pub enum ImportCandidate { 13pub enum ImportCandidate {
@@ -24,86 +25,141 @@ pub enum ImportCandidate {
24 25
25#[derive(Debug)] 26#[derive(Debug)]
26pub struct TraitImportCandidate { 27pub struct TraitImportCandidate {
27 pub ty: hir::Type, 28 pub receiver_ty: hir::Type,
28 pub name: ast::NameRef, 29 pub name: NameToImport,
29} 30}
30 31
31#[derive(Debug)] 32#[derive(Debug)]
32pub struct PathImportCandidate { 33pub struct PathImportCandidate {
33 pub qualifier: Option<ast::Path>, 34 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef, 35 pub name: NameToImport,
36}
37
38#[derive(Debug)]
39pub enum NameToImport {
40 Exact(String),
41 Fuzzy(String),
42}
43
44impl NameToImport {
45 pub fn text(&self) -> &str {
46 match self {
47 NameToImport::Exact(text) => text.as_str(),
48 NameToImport::Fuzzy(text) => text.as_str(),
49 }
50 }
35} 51}
36 52
37#[derive(Debug)] 53#[derive(Debug)]
38pub struct ImportAssets { 54pub struct ImportAssets {
39 import_candidate: ImportCandidate, 55 import_candidate: ImportCandidate,
40 module_with_name_to_import: hir::Module, 56 module_with_candidate: hir::Module,
41 syntax_under_caret: SyntaxNode,
42} 57}
43 58
44impl ImportAssets { 59impl ImportAssets {
45 pub fn for_method_call( 60 pub fn for_method_call(
46 method_call: ast::MethodCallExpr, 61 method_call: &ast::MethodCallExpr,
47 sema: &Semantics<RootDatabase>, 62 sema: &Semantics<RootDatabase>,
48 ) -> Option<Self> { 63 ) -> Option<Self> {
49 let syntax_under_caret = method_call.syntax().to_owned();
50 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
51 Some(Self { 64 Some(Self {
52 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, 65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
53 module_with_name_to_import, 66 module_with_candidate: sema.scope(method_call.syntax()).module()?,
54 syntax_under_caret,
55 }) 67 })
56 } 68 }
57 69
58 pub fn for_regular_path( 70 pub fn for_exact_path(
59 path_under_caret: ast::Path, 71 fully_qualified_path: &ast::Path,
60 sema: &Semantics<RootDatabase>, 72 sema: &Semantics<RootDatabase>,
61 ) -> Option<Self> { 73 ) -> Option<Self> {
62 let syntax_under_caret = path_under_caret.syntax().to_owned(); 74 let syntax_under_caret = fully_qualified_path.syntax();
63 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
64 return None; 76 return None;
65 } 77 }
66
67 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
68 Some(Self { 78 Some(Self {
69 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, 79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
70 module_with_name_to_import, 80 module_with_candidate: sema.scope(syntax_under_caret).module()?,
71 syntax_under_caret,
72 }) 81 })
73 } 82 }
74 83
75 pub fn syntax_under_caret(&self) -> &SyntaxNode { 84 pub fn for_fuzzy_path(
76 &self.syntax_under_caret 85 module_with_path: Module,
86 qualifier: Option<ast::Path>,
87 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>,
89 ) -> Option<Self> {
90 Some(match qualifier {
91 Some(qualifier) => {
92 let qualifier_resolution = sema.resolve_path(&qualifier)?;
93 match qualifier_resolution {
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 })
77 } 118 }
78 119
120 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module,
122 receiver_ty: hir::Type,
123 fuzzy_method_name: String,
124 ) -> Option<Self> {
125 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name),
129 }),
130 module_with_candidate: module_with_method_call,
131 })
132 }
133}
134
135impl ImportAssets {
79 pub fn import_candidate(&self) -> &ImportCandidate { 136 pub fn import_candidate(&self) -> &ImportCandidate {
80 &self.import_candidate 137 &self.import_candidate
81 } 138 }
82 139
83 fn get_search_query(&self) -> &str { 140 fn name_to_import(&self) -> &NameToImport {
84 match &self.import_candidate { 141 match &self.import_candidate {
85 ImportCandidate::Path(candidate) => candidate.name.text(), 142 ImportCandidate::Path(candidate) => &candidate.name,
86 ImportCandidate::TraitAssocItem(candidate) 143 ImportCandidate::TraitAssocItem(candidate)
87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
88 } 145 }
89 } 146 }
90 147
91 pub fn search_for_imports( 148 pub fn search_for_imports(
92 &self, 149 &self,
93 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
94 config: &InsertUseConfig, 151 prefix_kind: PrefixKind,
95 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
96 let _p = profile::span("import_assists::search_for_imports"); 153 let _p = profile::span("import_assets::search_for_imports");
97 self.search_for(sema, Some(config.prefix_kind)) 154 self.search_for(sema, Some(prefix_kind))
98 } 155 }
99 156
100 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 157 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
101 #[allow(dead_code)]
102 pub fn search_for_relative_paths( 158 pub fn search_for_relative_paths(
103 &self, 159 &self,
104 sema: &Semantics<RootDatabase>, 160 sema: &Semantics<RootDatabase>,
105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
106 let _p = profile::span("import_assists::search_for_relative_paths"); 162 let _p = profile::span("import_assets::search_for_relative_paths");
107 self.search_for(sema, None) 163 self.search_for(sema, None)
108 } 164 }
109 165
@@ -112,99 +168,142 @@ impl ImportAssets {
112 sema: &Semantics<RootDatabase>, 168 sema: &Semantics<RootDatabase>,
113 prefixed: Option<hir::PrefixKind>, 169 prefixed: Option<hir::PrefixKind>,
114 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
115 let db = sema.db; 171 let current_crate = self.module_with_candidate.krate();
116 let mut trait_candidates = FxHashSet::default();
117 let current_crate = self.module_with_name_to_import.krate();
118 172
119 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { 173 let unfiltered_imports = match self.name_to_import() {
120 trait_candidates.clear(); 174 NameToImport::Exact(exact_name) => {
121 match &self.import_candidate { 175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
122 ImportCandidate::TraitAssocItem(trait_candidate) => { 176 }
123 let located_assoc_item = match candidate { 177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
124 Either::Left(ModuleDef::Function(located_function)) => { 178 // instead, we need to look up all trait impls for a certain struct and search through them only
125 located_function.as_assoc_item(db) 179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
126 } 180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
127 Either::Left(ModuleDef::Const(located_const)) => { 181 // for the details
128 located_const.as_assoc_item(db) 182 NameToImport::Fuzzy(fuzzy_name) => {
129 } 183 let (assoc_item_search, limit) = match self.import_candidate {
130 _ => None, 184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
185 (AssocItemSearch::AssocItemsOnly, None)
131 } 186 }
132 .map(|assoc| assoc.container(db)) 187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
133 .and_then(Self::assoc_to_trait)?; 188 };
134 189 imports_locator::find_similar_imports(
135 trait_candidates.insert(located_assoc_item.into()); 190 sema,
191 current_crate,
192 fuzzy_name.clone(),
193 assoc_item_search,
194 limit,
195 )
196 }
197 };
136 198
137 trait_candidate 199 let db = sema.db;
138 .ty 200 let mut res =
139 .iterate_path_candidates( 201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports)
140 db, 202 .filter_map(|candidate| {
141 current_crate, 203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into);
142 &trait_candidates,
143 None,
144 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
145 )
146 .map(ModuleDef::from)
147 .map(Either::Left)
148 }
149 ImportCandidate::TraitMethod(trait_candidate) => {
150 let located_assoc_item =
151 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
152 located_function
153 .as_assoc_item(db)
154 .map(|assoc| assoc.container(db))
155 .and_then(Self::assoc_to_trait)
156 } else {
157 None
158 }?;
159 204
160 trait_candidates.insert(located_assoc_item.into()); 205 let item_to_search = match self.import_candidate {
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => {
207 let canidate_trait = match candidate {
208 Either::Left(module_def) => {
209 module_def.as_assoc_item(db)?.containing_trait(db)
210 }
211 _ => None,
212 }?;
213 ModuleDef::from(canidate_trait).into()
214 }
215 _ => item,
216 };
161 217
162 trait_candidate 218 if let Some(prefix_kind) = prefixed {
163 .ty 219 self.module_with_candidate.find_use_path_prefixed(
164 .iterate_method_candidates(
165 db, 220 db,
166 current_crate, 221 item_to_search,
167 &trait_candidates, 222 prefix_kind,
168 None,
169 |_, function| {
170 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
171 },
172 ) 223 )
173 .map(ModuleDef::from) 224 } else {
174 .map(Either::Left) 225 self.module_with_candidate.find_use_path(db, item_to_search)
175 } 226 }
176 _ => Some(candidate), 227 .map(|path| (path, item))
177 } 228 })
178 }; 229 .filter(|(use_path, _)| use_path.len() > 1)
179 230 .collect::<Vec<_>>();
180 let mut res = imports_locator::find_exact_imports( 231 res.sort_by_cached_key(|(path, _)| path.clone());
181 sema,
182 current_crate,
183 self.get_search_query().to_string(),
184 )
185 .filter_map(filter)
186 .filter_map(|candidate| {
187 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
188 if let Some(prefix_kind) = prefixed {
189 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
190 } else {
191 self.module_with_name_to_import.find_use_path(db, item)
192 }
193 .map(|path| (path, item))
194 })
195 .filter(|(use_path, _)| use_path.len() > 1)
196 .take(20)
197 .collect::<Vec<_>>();
198 res.sort_by_key(|(path, _)| path.clone());
199 res 232 res
200 } 233 }
234}
201 235
202 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { 236fn applicable_defs<'a>(
203 if let AssocItemContainer::Trait(extracted_trait) = assoc { 237 import_candidate: &ImportCandidate,
204 Some(extracted_trait) 238 current_crate: Crate,
205 } else { 239 db: &RootDatabase,
206 None 240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
242 let receiver_ty = match import_candidate {
243 ImportCandidate::Path(_) => return unfiltered_imports,
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => {
245 &candidate.receiver_ty
207 } 246 }
247 };
248
249 let mut required_assoc_items = FxHashSet::default();
250
251 let trait_candidates = unfiltered_imports
252 .filter_map(|input| match input {
253 Either::Left(module_def) => module_def.as_assoc_item(db),
254 _ => None,
255 })
256 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc);
259 Some(assoc_item_trait.into())
260 })
261 .collect();
262
263 let mut applicable_defs = FxHashSet::default();
264
265 match import_candidate {
266 ImportCandidate::Path(_) => unreachable!(),
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db,
269 current_crate,
270 &trait_candidates,
271 None,
272 |_, assoc| {
273 if required_assoc_items.contains(&assoc) {
274 if let AssocItem::Function(f) = assoc {
275 if f.self_param(db).is_some() {
276 return None;
277 }
278 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
280 }
281 None::<()>
282 },
283 ),
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates(
285 db,
286 current_crate,
287 &trait_candidates,
288 None,
289 |_, function| {
290 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc)));
293 }
294 None::<()>
295 },
296 ),
297 };
298
299 Box::new(applicable_defs.into_iter())
300}
301
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef {
303 match assoc {
304 AssocItem::Function(f) => f.into(),
305 AssocItem::Const(c) => c.into(),
306 AssocItem::TypeAlias(t) => t.into(),
208 } 307 }
209} 308}
210 309
@@ -216,22 +315,19 @@ impl ImportCandidate {
216 match sema.resolve_method_call(method_call) { 315 match sema.resolve_method_call(method_call) {
217 Some(_) => None, 316 Some(_) => None,
218 None => Some(Self::TraitMethod(TraitImportCandidate { 317 None => Some(Self::TraitMethod(TraitImportCandidate {
219 ty: sema.type_of_expr(&method_call.receiver()?)?, 318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
220 name: method_call.name_ref()?, 319 name: NameToImport::Exact(method_call.name_ref()?.to_string()),
221 })), 320 })),
222 } 321 }
223 } 322 }
224 323
225 fn for_regular_path( 324 fn for_regular_path(sema: &Semantics<RootDatabase>, path: &ast::Path) -> Option<Self> {
226 sema: &Semantics<RootDatabase>, 325 if sema.resolve_path(path).is_some() {
227 path_under_caret: &ast::Path,
228 ) -> Option<Self> {
229 if sema.resolve_path(path_under_caret).is_some() {
230 return None; 326 return None;
231 } 327 }
232 328
233 let segment = path_under_caret.segment()?; 329 let segment = path.segment()?;
234 let candidate = if let Some(qualifier) = path_under_caret.qualifier() { 330 let candidate = if let Some(qualifier) = path.qualifier() {
235 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
236 let qualifier_start_path = 332 let qualifier_start_path =
237 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -244,8 +340,8 @@ impl ImportCandidate {
244 match qualifier_resolution { 340 match qualifier_resolution {
245 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { 341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
246 ImportCandidate::TraitAssocItem(TraitImportCandidate { 342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
247 ty: assoc_item_path.ty(sema.db), 343 receiver_ty: assoc_item_path.ty(sema.db),
248 name: segment.name_ref()?, 344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
249 }) 345 })
250 } 346 }
251 _ => return None, 347 _ => return None,
@@ -253,13 +349,15 @@ impl ImportCandidate {
253 } else { 349 } else {
254 ImportCandidate::Path(PathImportCandidate { 350 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier), 351 qualifier: Some(qualifier),
256 name: qualifier_start, 352 name: NameToImport::Exact(qualifier_start.to_string()),
257 }) 353 })
258 } 354 }
259 } else { 355 } else {
260 ImportCandidate::Path(PathImportCandidate { 356 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None, 357 qualifier: None,
262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 358 name: NameToImport::Exact(
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(),
360 ),
263 }) 361 })
264 }; 362 };
265 Some(candidate) 363 Some(candidate)
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 877d4f1c7..fd4035198 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -507,7 +507,7 @@ impl ImportGroup {
507 PathSegmentKind::SelfKw => ImportGroup::ThisModule, 507 PathSegmentKind::SelfKw => ImportGroup::ThisModule,
508 PathSegmentKind::SuperKw => ImportGroup::SuperModule, 508 PathSegmentKind::SuperKw => ImportGroup::SuperModule,
509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate, 509 PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
510 PathSegmentKind::Name(name) => match name.text().as_str() { 510 PathSegmentKind::Name(name) => match name.text() {
511 "std" => ImportGroup::Std, 511 "std" => ImportGroup::Std,
512 "core" => ImportGroup::Std, 512 "core" => ImportGroup::Std,
513 _ => ImportGroup::ExternCrate, 513 _ => ImportGroup::ExternCrate,
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index a603fe87f..4bbe66f1f 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -599,7 +599,7 @@ fn check(
599 599
600 let rewriter = insert_use(&file, path, mb); 600 let rewriter = insert_use(&file, path, mb);
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 601 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(&result, ra_fixture_after); 602 assert_eq_text!(ra_fixture_after, &result);
603} 603}
604 604
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index d111fba92..502e8281a 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{
5 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
7};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 8use syntax::{ast, AstNode, SyntaxKind::NAME};
6 9
7use crate::{ 10use crate::{
@@ -12,69 +15,84 @@ use crate::{
12use either::Either; 15use either::Either;
13use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
14 17
15const QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
16 19
17pub fn find_exact_imports<'a>( 20pub fn find_exact_imports<'a>(
18 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'a, RootDatabase>,
19 krate: Crate, 22 krate: Crate,
20 name_to_import: String, 23 name_to_import: String,
21) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
22 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
23 find_imports( 26 Box::new(find_imports(
24 sema, 27 sema,
25 krate, 28 krate,
26 { 29 {
27 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(name_to_import.clone());
28 local_query.exact(); 31 local_query.exact();
29 local_query.limit(QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
30 local_query 33 local_query
31 }, 34 },
32 import_map::Query::new(name_to_import) 35 import_map::Query::new(name_to_import)
33 .limit(QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
34 .name_only() 37 .name_only()
35 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
36 .case_sensitive(), 39 .case_sensitive(),
37 ) 40 ))
41}
42
43pub enum AssocItemSearch {
44 Include,
45 Exclude,
46 AssocItemsOnly,
38} 47}
39 48
40pub fn find_similar_imports<'a>( 49pub fn find_similar_imports<'a>(
41 sema: &Semantics<'a, RootDatabase>, 50 sema: &Semantics<'a, RootDatabase>,
42 krate: Crate, 51 krate: Crate,
43 limit: Option<usize>,
44 fuzzy_search_string: String, 52 fuzzy_search_string: String,
45 ignore_assoc_items: bool, 53 assoc_item_search: AssocItemSearch,
46 name_only: bool, 54 limit: Option<usize>,
47) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { 55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
48 let _p = profile::span("find_similar_imports"); 56 let _p = profile::span("find_similar_imports");
49 57
50 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
51 .search_mode(import_map::SearchMode::Fuzzy); 59 .search_mode(import_map::SearchMode::Fuzzy)
52 if name_only { 60 .name_only();
53 external_query = external_query.name_only(); 61
62 match assoc_item_search {
63 AssocItemSearch::Include => {}
64 AssocItemSearch::Exclude => {
65 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
66 }
67 AssocItemSearch::AssocItemsOnly => {
68 external_query = external_query.assoc_items_only();
69 }
54 } 70 }
55 71
56 let mut local_query = symbol_index::Query::new(fuzzy_search_string); 72 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
57 73
58 if let Some(limit) = limit { 74 if let Some(limit) = limit {
59 local_query.limit(limit);
60 external_query = external_query.limit(limit); 75 external_query = external_query.limit(limit);
76 local_query.limit(limit);
61 } 77 }
62 78
63 let db = sema.db; 79 let db = sema.db;
64 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { 80 Box::new(find_imports(sema, krate, local_query, external_query).filter(
65 if ignore_assoc_items { 81 move |import_candidate| match assoc_item_search {
66 match import_candidate { 82 AssocItemSearch::Include => true,
67 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), 83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db),
68 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), 84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db),
69 Either::Left(ModuleDef::TypeAlias(type_alias)) => { 85 },
70 type_alias.as_assoc_item(db).is_none() 86 ))
71 } 87}
72 _ => true, 88
73 } 89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
74 } else { 90 match import_candidate {
75 true 91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
76 } 92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
77 }) 93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
78} 96}
79 97
80fn find_imports<'a>( 98fn find_imports<'a>(
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 0ecb13a64..b9ba0aed5 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -228,6 +228,15 @@ impl Definition {
228 // so do nothing. 228 // so do nothing.
229 } 229 }
230 } 230 }
231 ModuleSource::BlockExpr(b) => {
232 if is_first {
233 let range = Some(b.syntax().text_range());
234 res.insert(file_id, range);
235 } else {
236 // We have already added the enclosing file to the search scope,
237 // so do nothing.
238 }
239 }
231 ModuleSource::SourceFile(_) => { 240 ModuleSource::SourceFile(_) => {
232 res.insert(file_id, None); 241 res.insert(file_id, None);
233 } 242 }
@@ -257,6 +266,7 @@ impl Definition {
257 let mut res = FxHashMap::default(); 266 let mut res = FxHashMap::default();
258 let range = match module_src.value { 267 let range = match module_src.value {
259 ModuleSource::Module(m) => Some(m.syntax().text_range()), 268 ModuleSource::Module(m) => Some(m.syntax().text_range()),
269 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
260 ModuleSource::SourceFile(_) => None, 270 ModuleSource::SourceFile(_) => None,
261 }; 271 };
262 res.insert(file_id, range); 272 res.insert(file_id, range);
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 0aa6a0765..e954bd72e 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -191,7 +191,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
191 191
192 let def_map = db.crate_def_map(krate); 192 let def_map = db.crate_def_map(krate);
193 let mut files = Vec::new(); 193 let mut files = Vec::new();
194 let mut modules = vec![def_map.root]; 194 let mut modules = vec![def_map.root()];
195 while let Some(module) = modules.pop() { 195 while let Some(module) = modules.pop() {
196 let data = &def_map[module]; 196 let data = &def_map[module];
197 files.extend(data.origin.file_id()); 197 files.extend(data.origin.file_id());
@@ -209,7 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
209 query.search(&buf) 209 query.search(&buf)
210} 210}
211 211
212pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> { 212pub fn index_resolve(db: &RootDatabase, name: &str) -> Vec<FileSymbol> {
213 let mut query = Query::new(name.to_string()); 213 let mut query = Query::new(name.to_string());
214 query.exact(); 214 query.exact();
215 query.limit(4); 215 query.limit(4);
@@ -409,7 +409,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
409 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { 409 fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
410 let name = node.name()?; 410 let name = node.name()?;
411 let name_range = name.syntax().text_range(); 411 let name_range = name.syntax().text_range();
412 let name = name.text().clone(); 412 let name = name.text().into();
413 let ptr = SyntaxNodePtr::new(node.syntax()); 413 let ptr = SyntaxNodePtr::new(node.syntax());
414 414
415 Some((name, ptr, name_range)) 415 Some((name, ptr, name_range))
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index e648519f9..0cdc175be 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -16,16 +16,18 @@ use crate::ExpandError;
16#[derive(Debug, PartialEq, Eq, Clone, Copy)] 16#[derive(Debug, PartialEq, Eq, Clone, Copy)]
17pub enum TokenTextRange { 17pub enum TokenTextRange {
18 Token(TextRange), 18 Token(TextRange),
19 Delimiter(TextRange, TextRange), 19 Delimiter(TextRange),
20} 20}
21 21
22impl TokenTextRange { 22impl TokenTextRange {
23 pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> { 23 pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> {
24 match self { 24 match self {
25 TokenTextRange::Token(it) => Some(it), 25 TokenTextRange::Token(it) => Some(it),
26 TokenTextRange::Delimiter(open, close) => match kind { 26 TokenTextRange::Delimiter(it) => match kind {
27 T!['{'] | T!['('] | T!['['] => Some(open), 27 T!['{'] | T!['('] | T!['['] => Some(TextRange::at(it.start(), 1.into())),
28 T!['}'] | T![')'] | T![']'] => Some(close), 28 T!['}'] | T![')'] | T![']'] => {
29 Some(TextRange::at(it.end() - TextSize::of('}'), 1.into()))
30 }
29 _ => None, 31 _ => None,
30 }, 32 },
31 } 33 }
@@ -51,6 +53,7 @@ pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, Toke
51 let global_offset = node.text_range().start(); 53 let global_offset = node.text_range().start();
52 let mut c = Convertor::new(node, global_offset); 54 let mut c = Convertor::new(node, global_offset);
53 let subtree = c.go()?; 55 let subtree = c.go()?;
56 c.id_alloc.map.entries.shrink_to_fit();
54 Some((subtree, c.id_alloc.map)) 57 Some((subtree, c.id_alloc.map))
55} 58}
56 59
@@ -113,8 +116,10 @@ impl TokenMap {
113 pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { 116 pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> {
114 let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { 117 let &(token_id, _) = self.entries.iter().find(|(_, range)| match range {
115 TokenTextRange::Token(it) => *it == relative_range, 118 TokenTextRange::Token(it) => *it == relative_range,
116 TokenTextRange::Delimiter(open, close) => { 119 TokenTextRange::Delimiter(it) => {
117 *open == relative_range || *close == relative_range 120 let open = TextRange::at(it.start(), 1.into());
121 let close = TextRange::at(it.end() - TextSize::of('}'), 1.into());
122 open == relative_range || close == relative_range
118 } 123 }
119 })?; 124 })?;
120 Some(token_id) 125 Some(token_id)
@@ -136,15 +141,17 @@ impl TokenMap {
136 close_relative_range: TextRange, 141 close_relative_range: TextRange,
137 ) -> usize { 142 ) -> usize {
138 let res = self.entries.len(); 143 let res = self.entries.len();
139 self.entries 144 let cover = open_relative_range.cover(close_relative_range);
140 .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); 145
146 self.entries.push((token_id, TokenTextRange::Delimiter(cover)));
141 res 147 res
142 } 148 }
143 149
144 fn update_close_delim(&mut self, idx: usize, close_relative_range: TextRange) { 150 fn update_close_delim(&mut self, idx: usize, close_relative_range: TextRange) {
145 let (_, token_text_range) = &mut self.entries[idx]; 151 let (_, token_text_range) = &mut self.entries[idx];
146 if let TokenTextRange::Delimiter(dim, _) = token_text_range { 152 if let TokenTextRange::Delimiter(dim) = token_text_range {
147 *token_text_range = TokenTextRange::Delimiter(*dim, close_relative_range); 153 let cover = dim.cover(close_relative_range);
154 *token_text_range = TokenTextRange::Delimiter(cover);
148 } 155 }
149 } 156 }
150 157
@@ -500,7 +507,7 @@ impl SrcToken for SynToken {
500 } 507 }
501 } 508 }
502 fn to_text(&self) -> SmolStr { 509 fn to_text(&self) -> SmolStr {
503 self.token().text().clone() 510 self.token().text().into()
504 } 511 }
505} 512}
506 513
@@ -593,7 +600,8 @@ impl<'a> TtTreeSink<'a> {
593 } 600 }
594 } 601 }
595 602
596 fn finish(self) -> (Parse<SyntaxNode>, TokenMap) { 603 fn finish(mut self) -> (Parse<SyntaxNode>, TokenMap) {
604 self.token_map.entries.shrink_to_fit();
597 (self.inner.finish(), self.token_map) 605 (self.inner.finish(), self.token_map)
598 } 606 }
599} 607}
@@ -674,10 +682,8 @@ impl<'a> TreeSink for TtTreeSink<'a> {
674 self.text_pos += TextSize::of(text); 682 self.text_pos += TextSize::of(text);
675 } 683 }
676 684
677 let text = SmolStr::new(self.buf.as_str()); 685 self.inner.token(kind, self.buf.as_str());
678 self.buf.clear(); 686 self.buf.clear();
679 self.inner.token(kind, text);
680
681 // Add whitespace between adjoint puncts 687 // Add whitespace between adjoint puncts
682 let next = last.bump(); 688 let next = last.bump();
683 if let ( 689 if let (
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 9ff901e97..bd2977ebd 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -261,7 +261,6 @@ fn test_expr_order() {
261 261
262 let dump = format!("{:#?}", expanded); 262 let dump = format!("{:#?}", expanded);
263 assert_eq_text!( 263 assert_eq_text!(
264 dump.trim(),
265 r#"[email protected] 264 r#"[email protected]
266 [email protected] 265 [email protected]
267 [email protected] "fn" 266 [email protected] "fn"
@@ -285,6 +284,7 @@ fn test_expr_order() {
285 [email protected] "2" 284 [email protected] "2"
286 [email protected] ";" 285 [email protected] ";"
287 [email protected] "}""#, 286 [email protected] "}""#,
287 dump.trim()
288 ); 288 );
289} 289}
290 290
@@ -989,7 +989,6 @@ fn test_tt_composite2() {
989 989
990 let res = format!("{:#?}", &node); 990 let res = format!("{:#?}", &node);
991 assert_eq_text!( 991 assert_eq_text!(
992 res.trim(),
993 r###"[email protected] 992 r###"[email protected]
994 [email protected] 993 [email protected]
995 [email protected] 994 [email protected]
@@ -1003,7 +1002,8 @@ fn test_tt_composite2() {
1003 [email protected] ">" 1002 [email protected] ">"
1004 [email protected] " " 1003 [email protected] " "
1005 [email protected] "#" 1004 [email protected] "#"
1006 [email protected] ")""### 1005 [email protected] ")""###,
1006 res.trim()
1007 ); 1007 );
1008} 1008}
1009 1009
@@ -1742,7 +1742,7 @@ impl MacroFixture {
1742 fn assert_expand(&self, invocation: &str, expected: &str) { 1742 fn assert_expand(&self, invocation: &str, expected: &str) {
1743 let expansion = self.expand_tt(invocation); 1743 let expansion = self.expand_tt(invocation);
1744 let actual = format!("{:?}", expansion); 1744 let actual = format!("{:?}", expansion);
1745 test_utils::assert_eq_text!(&actual.trim(), &expected.trim()); 1745 test_utils::assert_eq_text!(&expected.trim(), &actual.trim());
1746 } 1746 }
1747 1747
1748 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { 1748 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture {
@@ -1941,7 +1941,6 @@ fn test_no_space_after_semi_colon() {
1941 1941
1942 let dump = format!("{:#?}", expanded); 1942 let dump = format!("{:#?}", expanded);
1943 assert_eq_text!( 1943 assert_eq_text!(
1944 dump.trim(),
1945 r###"[email protected] 1944 r###"[email protected]
1946 [email protected] 1945 [email protected]
1947 [email protected] 1946 [email protected]
@@ -1981,6 +1980,7 @@ fn test_no_space_after_semi_colon() {
1981 [email protected] 1980 [email protected]
1982 [email protected] "f" 1981 [email protected] "f"
1983 [email protected] ";""###, 1982 [email protected] ";""###,
1983 dump.trim()
1984 ); 1984 );
1985} 1985}
1986 1986
diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs
index a7d06a815..903668892 100644
--- a/crates/parser/src/event.rs
+++ b/crates/parser/src/event.rs
@@ -38,14 +38,16 @@ pub(crate) enum Event {
38 /// 38 ///
39 /// The events for it would look like this: 39 /// The events for it would look like this:
40 /// 40 ///
41 /// 41 /// ```text
42 /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH 42 /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH
43 /// | /\ 43 /// | /\
44 /// | | 44 /// | |
45 /// +------forward-parent------+ 45 /// +------forward-parent------+
46 /// ```
46 /// 47 ///
47 /// And the tree would look like this 48 /// And the tree would look like this
48 /// 49 ///
50 /// ```text
49 /// +--PATH---------+ 51 /// +--PATH---------+
50 /// | | | 52 /// | | |
51 /// | | | 53 /// | | |
@@ -54,6 +56,7 @@ pub(crate) enum Event {
54 /// PATH 56 /// PATH
55 /// | 57 /// |
56 /// 'foo' 58 /// 'foo'
59 /// ```
57 /// 60 ///
58 /// See also `CompletedMarker::precede`. 61 /// See also `CompletedMarker::precede`.
59 Start { 62 Start {
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index bb9ffea8b..6913e9ec2 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -190,17 +190,25 @@ fn opt_visibility(p: &mut Parser) -> bool {
190 // test crate_visibility 190 // test crate_visibility
191 // pub(crate) struct S; 191 // pub(crate) struct S;
192 // pub(self) struct S; 192 // pub(self) struct S;
193 // pub(self) struct S; 193 // pub(super) struct S;
194 // pub(self) struct S;
195 194
196 // test pub_parens_typepath 195 // test pub_parens_typepath
197 // struct B(pub (super::A)); 196 // struct B(pub (super::A));
198 // struct B(pub (crate::A,)); 197 // struct B(pub (crate::A,));
199 T![crate] | T![self] | T![super] if p.nth(2) != T![:] => { 198 T![crate] | T![self] | T![super] if p.nth(2) != T![:] => {
200 p.bump_any(); 199 p.bump_any();
200 let path_m = p.start();
201 let path_segment_m = p.start();
202 let name_ref_m = p.start();
201 p.bump_any(); 203 p.bump_any();
204 name_ref_m.complete(p, NAME_REF);
205 path_segment_m.complete(p, PATH_SEGMENT);
206 path_m.complete(p, PATH);
202 p.expect(T![')']); 207 p.expect(T![')']);
203 } 208 }
209 // test crate_visibility_in
210 // pub(in super::A) struct S;
211 // pub(in crate) struct S;
204 T![in] => { 212 T![in] => {
205 p.bump_any(); 213 p.bump_any();
206 p.bump_any(); 214 p.bump_any();
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 2070ce163..1d894e907 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -270,7 +270,9 @@ fn extern_crate(p: &mut Parser, m: Marker) {
270 p.bump(T![crate]); 270 p.bump(T![crate]);
271 271
272 if p.at(T![self]) { 272 if p.at(T![self]) {
273 let m = p.start();
273 p.bump(T![self]); 274 p.bump(T![self]);
275 m.complete(p, NAME_REF);
274 } else { 276 } else {
275 name_ref(p); 277 name_ref(p);
276 } 278 }
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index d076974ed..d3327271c 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -40,6 +40,10 @@ pub(super) fn impl_(p: &mut Parser) {
40 type_params::opt_generic_param_list(p); 40 type_params::opt_generic_param_list(p);
41 } 41 }
42 42
43 // test impl_def_const
44 // impl const Send for X {}
45 p.eat(T![const]);
46
43 // FIXME: never type 47 // FIXME: never type
44 // impl ! {} 48 // impl ! {}
45 49
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index 2d006a1d5..6a98d7368 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -156,7 +156,7 @@ fn variadic_param(p: &mut Parser) -> bool {
156fn opt_self_param(p: &mut Parser, m: Marker) { 156fn opt_self_param(p: &mut Parser, m: Marker) {
157 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { 157 if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
158 p.eat(T![mut]); 158 p.eat(T![mut]);
159 p.eat(T![self]); 159 self_as_name(p);
160 // test arb_self_types 160 // test arb_self_types
161 // impl S { 161 // impl S {
162 // fn a(self: &Self) {} 162 // fn a(self: &Self) {}
@@ -169,24 +169,29 @@ fn opt_self_param(p: &mut Parser, m: Marker) {
169 let la1 = p.nth(1); 169 let la1 = p.nth(1);
170 let la2 = p.nth(2); 170 let la2 = p.nth(2);
171 let la3 = p.nth(3); 171 let la3 = p.nth(3);
172 let mut n_toks = match (p.current(), la1, la2, la3) { 172 if !matches!((p.current(), la1, la2, la3),
173 (T![&], T![self], _, _) => 2, 173 (T![&], T![self], _, _)
174 (T![&], T![mut], T![self], _) => 3, 174 | (T![&], T![mut], T![self], _)
175 (T![&], LIFETIME_IDENT, T![self], _) => 3, 175 | (T![&], LIFETIME_IDENT, T![self], _)
176 (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4, 176 | (T![&], LIFETIME_IDENT, T![mut], T![self])
177 _ => return m.abandon(p), 177 ) {
178 }; 178 return m.abandon(p);
179 p.bump_any(); 179 }
180 p.bump(T![&]);
180 if p.at(LIFETIME_IDENT) { 181 if p.at(LIFETIME_IDENT) {
181 lifetime(p); 182 lifetime(p);
182 n_toks -= 1;
183 }
184 for _ in 1..n_toks {
185 p.bump_any();
186 } 183 }
184 p.eat(T![mut]);
185 self_as_name(p);
187 } 186 }
188 m.complete(p, SELF_PARAM); 187 m.complete(p, SELF_PARAM);
189 if !p.at(T![')']) { 188 if !p.at(T![')']) {
190 p.expect(T![,]); 189 p.expect(T![,]);
191 } 190 }
192} 191}
192
193fn self_as_name(p: &mut Parser) {
194 let m = p.start();
195 p.bump(T![self]);
196 m.complete(p, NAME);
197}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 7d53cc4cd..bcefd183a 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -143,6 +143,7 @@ pub enum SyntaxKind {
143 MACRO_DEF, 143 MACRO_DEF,
144 PAREN_TYPE, 144 PAREN_TYPE,
145 TUPLE_TYPE, 145 TUPLE_TYPE,
146 MACRO_TYPE,
146 NEVER_TYPE, 147 NEVER_TYPE,
147 PATH_TYPE, 148 PATH_TYPE,
148 PTR_TYPE, 149 PTR_TYPE,
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index 1af2bbca7..078568cb2 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
11 11
12[dependencies] 12[dependencies]
13serde = { version = "1.0", features = ["derive"] } 13serde = { version = "1.0", features = ["derive"] }
14serde_json = "1.0" 14serde_json = { version = "1.0", features = ["unbounded_depth"] }
15log = "0.4.8" 15log = "0.4.8"
16crossbeam-channel = "0.5.0" 16crossbeam-channel = "0.5.0"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
diff --git a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
index e6fd21610..ea34e688f 100644
--- a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
+++ b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
@@ -59,7 +59,7 @@ SUBTREE $
59 IDENT _serde 4294967295 59 IDENT _serde 4294967295
60 PUNCH : [joint] 4294967295 60 PUNCH : [joint] 4294967295
61 PUNCH : [alone] 4294967295 61 PUNCH : [alone] 4294967295
62 IDENT export 4294967295 62 IDENT __private 4294967295
63 PUNCH : [joint] 4294967295 63 PUNCH : [joint] 4294967295
64 PUNCH : [alone] 4294967295 64 PUNCH : [alone] 4294967295
65 IDENT Ok 4294967295 65 IDENT Ok 4294967295
@@ -72,7 +72,7 @@ SUBTREE $
72 IDENT _serde 4294967295 72 IDENT _serde 4294967295
73 PUNCH : [joint] 4294967295 73 PUNCH : [joint] 4294967295
74 PUNCH : [alone] 4294967295 74 PUNCH : [alone] 4294967295
75 IDENT export 4294967295 75 IDENT __private 4294967295
76 PUNCH : [joint] 4294967295 76 PUNCH : [joint] 4294967295
77 PUNCH : [alone] 4294967295 77 PUNCH : [alone] 4294967295
78 IDENT Err 4294967295 78 IDENT Err 4294967295
@@ -85,7 +85,7 @@ SUBTREE $
85 IDENT _serde 4294967295 85 IDENT _serde 4294967295
86 PUNCH : [joint] 4294967295 86 PUNCH : [joint] 4294967295
87 PUNCH : [alone] 4294967295 87 PUNCH : [alone] 4294967295
88 IDENT export 4294967295 88 IDENT __private 4294967295
89 PUNCH : [joint] 4294967295 89 PUNCH : [joint] 4294967295
90 PUNCH : [alone] 4294967295 90 PUNCH : [alone] 4294967295
91 IDENT Err 4294967295 91 IDENT Err 4294967295
@@ -120,7 +120,7 @@ SUBTREE $
120 IDENT _serde 4294967295 120 IDENT _serde 4294967295
121 PUNCH : [joint] 4294967295 121 PUNCH : [joint] 4294967295
122 PUNCH : [alone] 4294967295 122 PUNCH : [alone] 4294967295
123 IDENT export 4294967295 123 IDENT __private 4294967295
124 PUNCH : [joint] 4294967295 124 PUNCH : [joint] 4294967295
125 PUNCH : [alone] 4294967295 125 PUNCH : [alone] 4294967295
126 IDENT Result 4294967295 126 IDENT Result 4294967295
diff --git a/crates/proc_macro_srv/src/tests/mod.rs b/crates/proc_macro_srv/src/tests/mod.rs
index 1a827cbd7..fd54f8dfd 100644
--- a/crates/proc_macro_srv/src/tests/mod.rs
+++ b/crates/proc_macro_srv/src/tests/mod.rs
@@ -38,9 +38,9 @@ fn test_derive_proc_macro_list() {
38 let res = list("serde_derive", "1").join("\n"); 38 let res = list("serde_derive", "1").join("\n");
39 39
40 assert_eq_text!( 40 assert_eq_text!(
41 &res,
42 r#"Serialize [CustomDerive] 41 r#"Serialize [CustomDerive]
43Deserialize [CustomDerive]"# 42Deserialize [CustomDerive]"#,
43 &res
44 ); 44 );
45} 45}
46 46
@@ -50,9 +50,9 @@ fn list_test_macros() {
50 let res = list("proc_macro_test", "0.0.0").join("\n"); 50 let res = list("proc_macro_test", "0.0.0").join("\n");
51 51
52 assert_eq_text!( 52 assert_eq_text!(
53 &res,
54 r#"function_like_macro [FuncLike] 53 r#"function_like_macro [FuncLike]
55attribute_macro [Attr] 54attribute_macro [Attr]
56DummyTrait [CustomDerive]"# 55DummyTrait [CustomDerive]"#,
56 &res
57 ); 57 );
58} 58}
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs
index 36942147d..196abb8fc 100644
--- a/crates/proc_macro_srv/src/tests/utils.rs
+++ b/crates/proc_macro_srv/src/tests/utils.rs
@@ -52,7 +52,7 @@ pub fn assert_expand(
52 let fixture = parse_string(ra_fixture).unwrap(); 52 let fixture = parse_string(ra_fixture).unwrap();
53 53
54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); 54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
55 assert_eq_text!(&format!("{:?}", res), &expect.trim()); 55 assert_eq_text!(&expect.trim(), &format!("{:?}", res));
56} 56}
57 57
58pub fn list(crate_name: &str, version: &str) -> Vec<String> { 58pub fn list(crate_name: &str, version: &str) -> Vec<String> {
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 9c7ce26be..cc7da27f7 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -14,12 +14,15 @@ once_cell = "1.3.1"
14cfg-if = "1" 14cfg-if = "1"
15libc = "0.2.73" 15libc = "0.2.73"
16la-arena = { version = "0.2.0", path = "../../lib/arena" } 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17countme = { version = "2.0.0-pre.2", features = ["enable"] }
18jemalloc-ctl = { version = "0.3.3", optional = true }
17 19
18[target.'cfg(target_os = "linux")'.dependencies] 20[target.'cfg(target_os = "linux")'.dependencies]
19perf-event = "0.4" 21perf-event = "0.4"
20 22
21[features] 23[features]
22cpu_profiler = [] 24cpu_profiler = []
25jemalloc = ["jemalloc-ctl"]
23 26
24# Uncomment to enable for the whole crate graph 27# Uncomment to enable for the whole crate graph
25# default = [ "cpu_profiler" ] 28# default = [ "cpu_profiler" ]
diff --git a/crates/profile/src/hprof.rs b/crates/profile/src/hprof.rs
index 8957ea016..29d2ed518 100644
--- a/crates/profile/src/hprof.rs
+++ b/crates/profile/src/hprof.rs
@@ -3,6 +3,7 @@ use once_cell::sync::Lazy;
3use std::{ 3use std::{
4 cell::RefCell, 4 cell::RefCell,
5 collections::{BTreeMap, HashSet}, 5 collections::{BTreeMap, HashSet},
6 env,
6 io::{stderr, Write}, 7 io::{stderr, Write},
7 sync::{ 8 sync::{
8 atomic::{AtomicBool, Ordering}, 9 atomic::{AtomicBool, Ordering},
@@ -18,7 +19,8 @@ use crate::tree::{Idx, Tree};
18/// env RA_PROFILE=foo|bar|baz // enabled only selected entries 19/// env RA_PROFILE=foo|bar|baz // enabled only selected entries
19/// env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms 20/// env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
20pub fn init() { 21pub fn init() {
21 let spec = std::env::var("RA_PROFILE").unwrap_or_default(); 22 countme::enable(env::var("RA_COUNT").is_ok());
23 let spec = env::var("RA_PROFILE").unwrap_or_default();
22 init_from(&spec); 24 init_from(&spec);
23} 25}
24 26
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index aa6ccc36c..79dba47d5 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -15,6 +15,13 @@ pub use crate::{
15 stop_watch::{StopWatch, StopWatchSpan}, 15 stop_watch::{StopWatch, StopWatchSpan},
16}; 16};
17 17
18pub use countme;
19/// Include `_c: Count<Self>` field in important structs to count them.
20///
21/// To view the counts, run with `RA_COUNT=1`. The overhead of disabled count is
22/// almost zero.
23pub use countme::Count;
24
18thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false)); 25thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));
19 26
20/// Allows to check if the current code is withing some dynamic scope, can be 27/// Allows to check if the current code is withing some dynamic scope, can be
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs
index 83390212a..cb4e54447 100644
--- a/crates/profile/src/memory_usage.rs
+++ b/crates/profile/src/memory_usage.rs
@@ -24,7 +24,12 @@ impl std::ops::Sub for MemoryUsage {
24impl MemoryUsage { 24impl MemoryUsage {
25 pub fn current() -> MemoryUsage { 25 pub fn current() -> MemoryUsage {
26 cfg_if! { 26 cfg_if! {
27 if #[cfg(all(target_os = "linux", target_env = "gnu"))] { 27 if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] {
28 jemalloc_ctl::epoch::advance().unwrap();
29 MemoryUsage {
30 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
31 }
32 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
28 // Note: This is incredibly slow. 33 // Note: This is incredibly slow.
29 let alloc = unsafe { libc::mallinfo() }.uordblks as isize; 34 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
30 MemoryUsage { allocated: Bytes(alloc) } 35 MemoryUsage { allocated: Bytes(alloc) }
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index 436eff16b..293cb5bfe 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -25,3 +25,4 @@ toolchain = { path = "../toolchain", version = "0.0.0" }
25proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } 25proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
26paths = { path = "../paths", version = "0.0.0" } 26paths = { path = "../paths", version = "0.0.0" }
27stdx = { path = "../stdx", version = "0.0.0" } 27stdx = { path = "../stdx", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" }
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
new file mode 100644
index 000000000..cf32995e0
--- /dev/null
+++ b/crates/project_model/src/build_data.rs
@@ -0,0 +1,206 @@
1//! Handles build script specific information
2
3use std::{
4 ffi::OsStr,
5 io::BufReader,
6 path::{Path, PathBuf},
7 process::{Command, Stdio},
8};
9
10use anyhow::Result;
11use cargo_metadata::{BuildScript, Message, Package, PackageId};
12use itertools::Itertools;
13use paths::{AbsPath, AbsPathBuf};
14use rustc_hash::FxHashMap;
15use stdx::JodChild;
16
17use crate::{cfg_flag::CfgFlag, CargoConfig};
18
19#[derive(Debug, Clone, Default)]
20pub(crate) struct BuildDataMap {
21 data: FxHashMap<PackageId, BuildData>,
22}
23#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct BuildData {
25 /// List of config flags defined by this package's build script
26 pub cfgs: Vec<CfgFlag>,
27 /// List of cargo-related environment variables with their value
28 ///
29 /// If the package has a build script which defines environment variables,
30 /// they can also be found here.
31 pub envs: Vec<(String, String)>,
32 /// Directory where a build script might place its output
33 pub out_dir: Option<AbsPathBuf>,
34 /// Path to the proc-macro library file if this package exposes proc-macros
35 pub proc_macro_dylib_path: Option<AbsPathBuf>,
36}
37
38impl BuildDataMap {
39 pub(crate) fn new(
40 cargo_toml: &AbsPath,
41 cargo_features: &CargoConfig,
42 packages: &Vec<Package>,
43 progress: &dyn Fn(String),
44 ) -> Result<BuildDataMap> {
45 let mut cmd = Command::new(toolchain::cargo());
46 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
47 .arg(cargo_toml.as_ref());
48
49 // --all-targets includes tests, benches and examples in addition to the
50 // default lib and bins. This is an independent concept from the --targets
51 // flag below.
52 cmd.arg("--all-targets");
53
54 if let Some(target) = &cargo_features.target {
55 cmd.args(&["--target", target]);
56 }
57
58 if cargo_features.all_features {
59 cmd.arg("--all-features");
60 } else {
61 if cargo_features.no_default_features {
62 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
63 // https://github.com/oli-obk/cargo_metadata/issues/79
64 cmd.arg("--no-default-features");
65 }
66 if !cargo_features.features.is_empty() {
67 cmd.arg("--features");
68 cmd.arg(cargo_features.features.join(" "));
69 }
70 }
71
72 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
73
74 let mut child = cmd.spawn().map(JodChild)?;
75 let child_stdout = child.stdout.take().unwrap();
76 let stdout = BufReader::new(child_stdout);
77
78 let mut res = BuildDataMap::default();
79 for message in cargo_metadata::Message::parse_stream(stdout) {
80 if let Ok(message) = message {
81 match message {
82 Message::BuildScriptExecuted(BuildScript {
83 package_id,
84 out_dir,
85 cfgs,
86 env,
87 ..
88 }) => {
89 let cfgs = {
90 let mut acc = Vec::new();
91 for cfg in cfgs {
92 match cfg.parse::<CfgFlag>() {
93 Ok(it) => acc.push(it),
94 Err(err) => {
95 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
96 }
97 };
98 }
99 acc
100 };
101 let res = res.data.entry(package_id.clone()).or_default();
102 // cargo_metadata crate returns default (empty) path for
103 // older cargos, which is not absolute, so work around that.
104 if out_dir != PathBuf::default() {
105 let out_dir = AbsPathBuf::assert(out_dir);
106 res.out_dir = Some(out_dir);
107 res.cfgs = cfgs;
108 }
109
110 res.envs = env;
111 }
112 Message::CompilerArtifact(message) => {
113 progress(format!("metadata {}", message.target.name));
114
115 if message.target.kind.contains(&"proc-macro".to_string()) {
116 let package_id = message.package_id;
117 // Skip rmeta file
118 if let Some(filename) =
119 message.filenames.iter().find(|name| is_dylib(name))
120 {
121 let filename = AbsPathBuf::assert(filename.clone());
122 let res = res.data.entry(package_id.clone()).or_default();
123 res.proc_macro_dylib_path = Some(filename);
124 }
125 }
126 }
127 Message::CompilerMessage(message) => {
128 progress(message.target.name.clone());
129 }
130 Message::Unknown => (),
131 Message::BuildFinished(_) => {}
132 Message::TextLine(_) => {}
133 }
134 }
135 }
136 res.inject_cargo_env(packages);
137 Ok(res)
138 }
139
140 pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self {
141 let mut res = Self::default();
142 res.inject_cargo_env(packages);
143 res
144 }
145
146 pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> {
147 self.data.get(id)
148 }
149
150 fn inject_cargo_env(&mut self, packages: &Vec<Package>) {
151 for meta_pkg in packages {
152 let resource = self.data.entry(meta_pkg.id.clone()).or_default();
153 inject_cargo_env(meta_pkg, &mut resource.envs);
154
155 if let Some(out_dir) = &resource.out_dir {
156 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
157 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
158 resource.envs.push(("OUT_DIR".to_string(), out_dir));
159 }
160 }
161 }
162 }
163}
164
165// FIXME: File a better way to know if it is a dylib
166fn is_dylib(path: &Path) -> bool {
167 match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
168 None => false,
169 Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
170 }
171}
172
173/// Recreates the compile-time environment variables that Cargo sets.
174///
175/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
176fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
177 // FIXME: Missing variables:
178 // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
179
180 let mut manifest_dir = package.manifest_path.clone();
181 manifest_dir.pop();
182 if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
183 env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
184 }
185
186 env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
187 env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
188 env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
189 env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));
190
191 let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
192 env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));
193
194 let authors = package.authors.join(";");
195 env.push(("CARGO_PKG_AUTHORS".into(), authors));
196
197 env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
198 env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
199 //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
200 env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
201 env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));
202
203 let license_file =
204 package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
205 env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
206}
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index c0ed37fc1..c8a5333c4 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,24 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{convert::TryInto, ops, process::Command};
4 convert::TryInto,
5 ffi::OsStr,
6 io::BufReader,
7 ops,
8 path::{Path, PathBuf},
9 process::{Command, Stdio},
10};
11 4
12use anyhow::{Context, Result}; 5use anyhow::{Context, Result};
13use base_db::Edition; 6use base_db::Edition;
14use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 7use cargo_metadata::{CargoOpt, MetadataCommand};
15use itertools::Itertools;
16use la_arena::{Arena, Idx}; 8use la_arena::{Arena, Idx};
17use paths::{AbsPath, AbsPathBuf}; 9use paths::{AbsPath, AbsPathBuf};
18use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
19use stdx::JodChild;
20 11
21use crate::cfg_flag::CfgFlag; 12use crate::build_data::{BuildData, BuildDataMap};
22use crate::utf8_stdout; 13use crate::utf8_stdout;
23 14
24/// `CargoWorkspace` represents the logical structure of, well, a Cargo 15/// `CargoWorkspace` represents the logical structure of, well, a Cargo
@@ -99,19 +90,12 @@ pub struct PackageData {
99 pub dependencies: Vec<PackageDependency>, 90 pub dependencies: Vec<PackageDependency>,
100 /// Rust edition for this package 91 /// Rust edition for this package
101 pub edition: Edition, 92 pub edition: Edition,
102 /// List of features to activate 93 /// Features provided by the crate, mapped to the features required by that feature.
103 pub features: Vec<String>, 94 pub features: FxHashMap<String, Vec<String>>,
104 /// List of config flags defined by this package's build script 95 /// List of features enabled on this package
105 pub cfgs: Vec<CfgFlag>, 96 pub active_features: Vec<String>,
106 /// List of cargo-related environment variables with their value 97 /// Build script related data for this package
107 /// 98 pub build_data: BuildData,
108 /// If the package has a build script which defines environment variables,
109 /// they can also be found here.
110 pub envs: Vec<(String, String)>,
111 /// Directory where a build script might place its output
112 pub out_dir: Option<AbsPathBuf>,
113 /// Path to the proc-macro library file if this package exposes proc-macros
114 pub proc_macro_dylib_path: Option<AbsPathBuf>,
115} 99}
116 100
117#[derive(Debug, Clone, Eq, PartialEq)] 101#[derive(Debug, Clone, Eq, PartialEq)]
@@ -244,17 +228,11 @@ impl CargoWorkspace {
244 ) 228 )
245 })?; 229 })?;
246 230
247 let mut out_dir_by_id = FxHashMap::default(); 231 let resources = if config.load_out_dirs_from_check {
248 let mut cfgs = FxHashMap::default(); 232 BuildDataMap::new(cargo_toml, config, &meta.packages, progress)?
249 let mut envs = FxHashMap::default(); 233 } else {
250 let mut proc_macro_dylib_paths = FxHashMap::default(); 234 BuildDataMap::with_cargo_env(&meta.packages)
251 if config.load_out_dirs_from_check { 235 };
252 let resources = load_extern_resources(cargo_toml, config, progress)?;
253 out_dir_by_id = resources.out_dirs;
254 cfgs = resources.cfgs;
255 envs = resources.env;
256 proc_macro_dylib_paths = resources.proc_dylib_paths;
257 }
258 236
259 let mut pkg_by_id = FxHashMap::default(); 237 let mut pkg_by_id = FxHashMap::default();
260 let mut packages = Arena::default(); 238 let mut packages = Arena::default();
@@ -265,7 +243,7 @@ impl CargoWorkspace {
265 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 243 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
266 for meta_pkg in meta.packages { 244 for meta_pkg in meta.packages {
267 let id = meta_pkg.id.clone(); 245 let id = meta_pkg.id.clone();
268 inject_cargo_env(&meta_pkg, envs.entry(id).or_default()); 246 let build_data = resources.get(&id).cloned().unwrap_or_default();
269 247
270 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 248 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
271 meta_pkg; 249 meta_pkg;
@@ -281,11 +259,9 @@ impl CargoWorkspace {
281 is_member, 259 is_member,
282 edition, 260 edition,
283 dependencies: Vec::new(), 261 dependencies: Vec::new(),
284 features: Vec::new(), 262 features: meta_pkg.features.into_iter().collect(),
285 cfgs: cfgs.get(&id).cloned().unwrap_or_default(), 263 active_features: Vec::new(),
286 envs: envs.get(&id).cloned().unwrap_or_default(), 264 build_data,
287 out_dir: out_dir_by_id.get(&id).cloned(),
288 proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
289 }); 265 });
290 let pkg_data = &mut packages[pkg]; 266 let pkg_data = &mut packages[pkg];
291 pkg_by_id.insert(id, pkg); 267 pkg_by_id.insert(id, pkg);
@@ -328,7 +304,7 @@ impl CargoWorkspace {
328 let dep = PackageDependency { name: dep_node.name, pkg }; 304 let dep = PackageDependency { name: dep_node.name, pkg };
329 packages[source].dependencies.push(dep); 305 packages[source].dependencies.push(dep);
330 } 306 }
331 packages[source].features.extend(node.features); 307 packages[source].active_features.extend(node.features);
332 } 308 }
333 309
334 let workspace_root = AbsPathBuf::assert(meta.workspace_root); 310 let workspace_root = AbsPathBuf::assert(meta.workspace_root);
@@ -362,149 +338,3 @@ impl CargoWorkspace {
362 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 338 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
363 } 339 }
364} 340}
365
366#[derive(Debug, Clone, Default)]
367pub(crate) struct ExternResources {
368 out_dirs: FxHashMap<PackageId, AbsPathBuf>,
369 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
370 cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
371 env: FxHashMap<PackageId, Vec<(String, String)>>,
372}
373
374pub(crate) fn load_extern_resources(
375 cargo_toml: &Path,
376 cargo_features: &CargoConfig,
377 progress: &dyn Fn(String),
378) -> Result<ExternResources> {
379 let mut cmd = Command::new(toolchain::cargo());
380 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
381
382 // --all-targets includes tests, benches and examples in addition to the
383 // default lib and bins. This is an independent concept from the --targets
384 // flag below.
385 cmd.arg("--all-targets");
386
387 if let Some(target) = &cargo_features.target {
388 cmd.args(&["--target", target]);
389 }
390
391 if cargo_features.all_features {
392 cmd.arg("--all-features");
393 } else {
394 if cargo_features.no_default_features {
395 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
396 // https://github.com/oli-obk/cargo_metadata/issues/79
397 cmd.arg("--no-default-features");
398 }
399 if !cargo_features.features.is_empty() {
400 cmd.arg("--features");
401 cmd.arg(cargo_features.features.join(" "));
402 }
403 }
404
405 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
406
407 let mut child = cmd.spawn().map(JodChild)?;
408 let child_stdout = child.stdout.take().unwrap();
409 let stdout = BufReader::new(child_stdout);
410
411 let mut res = ExternResources::default();
412 for message in cargo_metadata::Message::parse_stream(stdout) {
413 if let Ok(message) = message {
414 match message {
415 Message::BuildScriptExecuted(BuildScript {
416 package_id,
417 out_dir,
418 cfgs,
419 env,
420 ..
421 }) => {
422 let cfgs = {
423 let mut acc = Vec::new();
424 for cfg in cfgs {
425 match cfg.parse::<CfgFlag>() {
426 Ok(it) => acc.push(it),
427 Err(err) => {
428 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
429 }
430 };
431 }
432 acc
433 };
434 // cargo_metadata crate returns default (empty) path for
435 // older cargos, which is not absolute, so work around that.
436 if out_dir != PathBuf::default() {
437 let out_dir = AbsPathBuf::assert(out_dir);
438 res.out_dirs.insert(package_id.clone(), out_dir);
439 res.cfgs.insert(package_id.clone(), cfgs);
440 }
441
442 res.env.insert(package_id, env);
443 }
444 Message::CompilerArtifact(message) => {
445 progress(format!("metadata {}", message.target.name));
446
447 if message.target.kind.contains(&"proc-macro".to_string()) {
448 let package_id = message.package_id;
449 // Skip rmeta file
450 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
451 {
452 let filename = AbsPathBuf::assert(filename.clone());
453 res.proc_dylib_paths.insert(package_id, filename);
454 }
455 }
456 }
457 Message::CompilerMessage(message) => {
458 progress(message.target.name.clone());
459 }
460 Message::Unknown => (),
461 Message::BuildFinished(_) => {}
462 Message::TextLine(_) => {}
463 }
464 }
465 }
466 Ok(res)
467}
468
469// FIXME: File a better way to know if it is a dylib
470fn is_dylib(path: &Path) -> bool {
471 match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
472 None => false,
473 Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
474 }
475}
476
477/// Recreates the compile-time environment variables that Cargo sets.
478///
479/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
480fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
481 // FIXME: Missing variables:
482 // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
483
484 let mut manifest_dir = package.manifest_path.clone();
485 manifest_dir.pop();
486 if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
487 env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
488 }
489
490 env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
491 env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
492 env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
493 env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));
494
495 let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
496 env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));
497
498 let authors = package.authors.join(";");
499 env.push(("CARGO_PKG_AUTHORS".into(), authors));
500
501 env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
502 env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
503 //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
504 env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
505 env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));
506
507 let license_file =
508 package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
509 env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
510}
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index aabb7a47d..525c336e6 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -5,6 +5,8 @@ mod cfg_flag;
5mod project_json; 5mod project_json;
6mod sysroot; 6mod sysroot;
7mod workspace; 7mod workspace;
8mod rustc_cfg;
9mod build_data;
8 10
9use std::{ 11use std::{
10 fs::{read_dir, ReadDir}, 12 fs::{read_dir, ReadDir},
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
new file mode 100644
index 000000000..4a7bd8ae3
--- /dev/null
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -0,0 +1,34 @@
1//! Runs `rustc --print cfg` to get built-in cfg flags.
2
3use std::process::Command;
4
5use crate::{cfg_flag::CfgFlag, utf8_stdout};
6
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> {
8 let _p = profile::span("rustc_cfg::get");
9 let mut res = Vec::new();
10
11 // Some nightly-only cfgs, which are required for stdlib
12 res.push(CfgFlag::Atom("target_thread_local".into()));
13 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
14 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
15 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
16 }
17 }
18
19 let rustc_cfgs = {
20 let mut cmd = Command::new(toolchain::rustc());
21 cmd.args(&["--print", "cfg", "-O"]);
22 if let Some(target) = target {
23 cmd.args(&["--target", target]);
24 }
25 utf8_stdout(cmd)
26 };
27
28 match rustc_cfgs {
29 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
30 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
31 }
32
33 res
34}
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 06a0be284..bc5041e5a 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -16,7 +16,7 @@ use proc_macro_api::ProcMacroClient;
16use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
17 17
18use crate::{ 18use crate::{
19 cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig, 19 cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig,
20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, 20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind,
21}; 21};
22 22
@@ -34,15 +34,25 @@ pub struct PackageRoot {
34#[derive(Clone, Eq, PartialEq)] 34#[derive(Clone, Eq, PartialEq)]
35pub enum ProjectWorkspace { 35pub enum ProjectWorkspace {
36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
37 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> }, 37 Cargo {
38 cargo: CargoWorkspace,
39 sysroot: Sysroot,
40 rustc: Option<CargoWorkspace>,
41 /// Holds cfg flags for the current target. We get those by running
42 /// `rustc --print cfg`.
43 ///
44 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
45 /// different target.
46 rustc_cfg: Vec<CfgFlag>,
47 },
38 /// Project workspace was manually specified using a `rust-project.json` file. 48 /// Project workspace was manually specified using a `rust-project.json` file.
39 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 49 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
40} 50}
41 51
42impl fmt::Debug for ProjectWorkspace { 52impl fmt::Debug for ProjectWorkspace {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self { 54 match self {
45 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f 55 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
46 .debug_struct("Cargo") 56 .debug_struct("Cargo")
47 .field("n_packages", &cargo.packages().len()) 57 .field("n_packages", &cargo.packages().len())
48 .field("n_sysroot_crates", &sysroot.crates().len()) 58 .field("n_sysroot_crates", &sysroot.crates().len())
@@ -50,13 +60,15 @@ impl fmt::Debug for ProjectWorkspace {
50 "n_rustc_compiler_crates", 60 "n_rustc_compiler_crates",
51 &rustc.as_ref().map_or(0, |rc| rc.packages().len()), 61 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
52 ) 62 )
63 .field("rustc_cfg", rustc_cfg)
53 .finish(), 64 .finish(),
54 ProjectWorkspace::Json { project, sysroot } => { 65 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
55 let mut debug_struct = f.debug_struct("Json"); 66 let mut debug_struct = f.debug_struct("Json");
56 debug_struct.field("n_crates", &project.n_crates()); 67 debug_struct.field("n_crates", &project.n_crates());
57 if let Some(sysroot) = sysroot { 68 if let Some(sysroot) = sysroot {
58 debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); 69 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
59 } 70 }
71 debug_struct.field("rustc_cfg", rustc_cfg);
60 debug_struct.finish() 72 debug_struct.finish()
61 } 73 }
62 } 74 }
@@ -79,7 +91,7 @@ impl ProjectWorkspace {
79 })?; 91 })?;
80 let project_location = project_json.parent().unwrap().to_path_buf(); 92 let project_location = project_json.parent().unwrap().to_path_buf();
81 let project_json = ProjectJson::new(&project_location, data); 93 let project_json = ProjectJson::new(&project_location, data);
82 ProjectWorkspace::load_inline(project_json)? 94 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
83 } 95 }
84 ProjectManifest::CargoToml(cargo_toml) => { 96 ProjectManifest::CargoToml(cargo_toml) => {
85 let cargo_version = utf8_stdout({ 97 let cargo_version = utf8_stdout({
@@ -117,21 +129,24 @@ impl ProjectWorkspace {
117 } else { 129 } else {
118 None 130 None
119 }; 131 };
120 132 let rustc_cfg = rustc_cfg::get(config.target.as_deref());
121 ProjectWorkspace::Cargo { cargo, sysroot, rustc } 133 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
122 } 134 }
123 }; 135 };
124 136
125 Ok(res) 137 Ok(res)
126 } 138 }
127 139
128 pub fn load_inline(project_json: ProjectJson) -> Result<ProjectWorkspace> { 140 pub fn load_inline(
141 project_json: ProjectJson,
142 target: Option<&str>,
143 ) -> Result<ProjectWorkspace> {
129 let sysroot = match &project_json.sysroot_src { 144 let sysroot = match &project_json.sysroot_src {
130 Some(path) => Some(Sysroot::load(path)?), 145 Some(path) => Some(Sysroot::load(path)?),
131 None => None, 146 None => None,
132 }; 147 };
133 148 let rustc_cfg = rustc_cfg::get(target);
134 Ok(ProjectWorkspace::Json { project: project_json, sysroot }) 149 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
135 } 150 }
136 151
137 /// Returns the roots for the current `ProjectWorkspace` 152 /// Returns the roots for the current `ProjectWorkspace`
@@ -139,7 +154,7 @@ impl ProjectWorkspace {
139 /// the root is a member of the current workspace 154 /// the root is a member of the current workspace
140 pub fn to_roots(&self) -> Vec<PackageRoot> { 155 pub fn to_roots(&self) -> Vec<PackageRoot> {
141 match self { 156 match self {
142 ProjectWorkspace::Json { project, sysroot } => project 157 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
143 .crates() 158 .crates()
144 .map(|(_, krate)| PackageRoot { 159 .map(|(_, krate)| PackageRoot {
145 is_member: krate.is_workspace_member, 160 is_member: krate.is_workspace_member,
@@ -156,14 +171,14 @@ impl ProjectWorkspace {
156 }) 171 })
157 })) 172 }))
158 .collect::<Vec<_>>(), 173 .collect::<Vec<_>>(),
159 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo 174 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo
160 .packages() 175 .packages()
161 .map(|pkg| { 176 .map(|pkg| {
162 let is_member = cargo[pkg].is_member; 177 let is_member = cargo[pkg].is_member;
163 let pkg_root = cargo[pkg].root().to_path_buf(); 178 let pkg_root = cargo[pkg].root().to_path_buf();
164 179
165 let mut include = vec![pkg_root.clone()]; 180 let mut include = vec![pkg_root.clone()];
166 include.extend(cargo[pkg].out_dir.clone()); 181 include.extend(cargo[pkg].build_data.out_dir.clone());
167 182
168 let mut exclude = vec![pkg_root.join(".git")]; 183 let mut exclude = vec![pkg_root.join(".git")];
169 if is_member { 184 if is_member {
@@ -194,7 +209,7 @@ impl ProjectWorkspace {
194 pub fn n_packages(&self) -> usize { 209 pub fn n_packages(&self) -> usize {
195 match self { 210 match self {
196 ProjectWorkspace::Json { project, .. } => project.n_crates(), 211 ProjectWorkspace::Json { project, .. } => project.n_crates(),
197 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 212 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
198 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); 213 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
199 cargo.packages().len() + sysroot.crates().len() + rustc_package_len 214 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
200 } 215 }
@@ -203,22 +218,31 @@ impl ProjectWorkspace {
203 218
204 pub fn to_crate_graph( 219 pub fn to_crate_graph(
205 &self, 220 &self,
206 target: Option<&str>,
207 proc_macro_client: Option<&ProcMacroClient>, 221 proc_macro_client: Option<&ProcMacroClient>,
208 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 222 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
209 ) -> CrateGraph { 223 ) -> CrateGraph {
224 let _p = profile::span("ProjectWorkspace::to_crate_graph");
210 let proc_macro_loader = |path: &Path| match proc_macro_client { 225 let proc_macro_loader = |path: &Path| match proc_macro_client {
211 Some(client) => client.by_dylib_path(path), 226 Some(client) => client.by_dylib_path(path),
212 None => Vec::new(), 227 None => Vec::new(),
213 }; 228 };
214 229
215 let mut crate_graph = match self { 230 let mut crate_graph = match self {
216 ProjectWorkspace::Json { project, sysroot } => { 231 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
217 project_json_to_crate_graph(target, &proc_macro_loader, load, project, sysroot) 232 rustc_cfg.clone(),
218 } 233 &proc_macro_loader,
219 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 234 load,
220 cargo_to_crate_graph(target, &proc_macro_loader, load, cargo, sysroot, rustc) 235 project,
221 } 236 sysroot,
237 ),
238 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph(
239 rustc_cfg.clone(),
240 &proc_macro_loader,
241 load,
242 cargo,
243 sysroot,
244 rustc,
245 ),
222 }; 246 };
223 if crate_graph.patch_cfg_if() { 247 if crate_graph.patch_cfg_if() {
224 log::debug!("Patched std to depend on cfg-if") 248 log::debug!("Patched std to depend on cfg-if")
@@ -230,7 +254,7 @@ impl ProjectWorkspace {
230} 254}
231 255
232fn project_json_to_crate_graph( 256fn project_json_to_crate_graph(
233 target: Option<&str>, 257 rustc_cfg: Vec<CfgFlag>,
234 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 258 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
235 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 259 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
236 project: &ProjectJson, 260 project: &ProjectJson,
@@ -239,9 +263,9 @@ fn project_json_to_crate_graph(
239 let mut crate_graph = CrateGraph::default(); 263 let mut crate_graph = CrateGraph::default();
240 let sysroot_deps = sysroot 264 let sysroot_deps = sysroot
241 .as_ref() 265 .as_ref()
242 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); 266 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
243 267
244 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); 268 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
245 let crates: FxHashMap<CrateId, CrateId> = project 269 let crates: FxHashMap<CrateId, CrateId> = project
246 .crates() 270 .crates()
247 .filter_map(|(crate_id, krate)| { 271 .filter_map(|(crate_id, krate)| {
@@ -253,9 +277,12 @@ fn project_json_to_crate_graph(
253 let env = krate.env.clone().into_iter().collect(); 277 let env = krate.env.clone().into_iter().collect();
254 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it)); 278 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
255 279
256 let target = krate.target.as_deref().or(target); 280 let target_cfgs = match krate.target.as_deref() {
257 let target_cfgs = 281 Some(target) => {
258 cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); 282 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target)))
283 }
284 None => &rustc_cfg,
285 };
259 286
260 let mut cfg_options = CfgOptions::default(); 287 let mut cfg_options = CfgOptions::default();
261 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); 288 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
@@ -292,19 +319,20 @@ fn project_json_to_crate_graph(
292} 319}
293 320
294fn cargo_to_crate_graph( 321fn cargo_to_crate_graph(
295 target: Option<&str>, 322 rustc_cfg: Vec<CfgFlag>,
296 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 323 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
297 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 324 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
298 cargo: &CargoWorkspace, 325 cargo: &CargoWorkspace,
299 sysroot: &Sysroot, 326 sysroot: &Sysroot,
300 rustc: &Option<CargoWorkspace>, 327 rustc: &Option<CargoWorkspace>,
301) -> CrateGraph { 328) -> CrateGraph {
329 let _p = profile::span("cargo_to_crate_graph");
302 let mut crate_graph = CrateGraph::default(); 330 let mut crate_graph = CrateGraph::default();
303 let (public_deps, libproc_macro) = 331 let (public_deps, libproc_macro) =
304 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 332 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
305 333
306 let mut cfg_options = CfgOptions::default(); 334 let mut cfg_options = CfgOptions::default();
307 cfg_options.extend(get_rustc_cfg_options(target)); 335 cfg_options.extend(rustc_cfg);
308 336
309 let mut pkg_to_lib_crate = FxHashMap::default(); 337 let mut pkg_to_lib_crate = FxHashMap::default();
310 338
@@ -453,26 +481,24 @@ fn add_target_crate_root(
453 let edition = pkg.edition; 481 let edition = pkg.edition;
454 let cfg_options = { 482 let cfg_options = {
455 let mut opts = cfg_options.clone(); 483 let mut opts = cfg_options.clone();
456 for feature in pkg.features.iter() { 484 for feature in pkg.active_features.iter() {
457 opts.insert_key_value("feature".into(), feature.into()); 485 opts.insert_key_value("feature".into(), feature.into());
458 } 486 }
459 opts.extend(pkg.cfgs.iter().cloned()); 487 opts.extend(pkg.build_data.cfgs.iter().cloned());
460 opts 488 opts
461 }; 489 };
462 490
463 let mut env = Env::default(); 491 let mut env = Env::default();
464 for (k, v) in &pkg.envs { 492 for (k, v) in &pkg.build_data.envs {
465 env.set(k, v.clone()); 493 env.set(k, v.clone());
466 } 494 }
467 if let Some(out_dir) = &pkg.out_dir {
468 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
469 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
470 env.set("OUT_DIR", out_dir);
471 }
472 }
473 495
474 let proc_macro = 496 let proc_macro = pkg
475 pkg.proc_macro_dylib_path.as_ref().map(|it| proc_macro_loader(&it)).unwrap_or_default(); 497 .build_data
498 .proc_macro_dylib_path
499 .as_ref()
500 .map(|it| proc_macro_loader(&it))
501 .unwrap_or_default();
476 502
477 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); 503 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
478 let crate_id = crate_graph.add_crate_root( 504 let crate_id = crate_graph.add_crate_root(
@@ -490,11 +516,12 @@ fn add_target_crate_root(
490fn sysroot_to_crate_graph( 516fn sysroot_to_crate_graph(
491 crate_graph: &mut CrateGraph, 517 crate_graph: &mut CrateGraph,
492 sysroot: &Sysroot, 518 sysroot: &Sysroot,
493 target: Option<&str>, 519 rustc_cfg: Vec<CfgFlag>,
494 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 520 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
495) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { 521) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
522 let _p = profile::span("sysroot_to_crate_graph");
496 let mut cfg_options = CfgOptions::default(); 523 let mut cfg_options = CfgOptions::default();
497 cfg_options.extend(get_rustc_cfg_options(target)); 524 cfg_options.extend(rustc_cfg);
498 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot 525 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
499 .crates() 526 .crates()
500 .filter_map(|krate| { 527 .filter_map(|krate| {
@@ -533,34 +560,6 @@ fn sysroot_to_crate_graph(
533 (public_deps, libproc_macro) 560 (public_deps, libproc_macro)
534} 561}
535 562
536fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
537 let mut res = Vec::new();
538
539 // Some nightly-only cfgs, which are required for stdlib
540 res.push(CfgFlag::Atom("target_thread_local".into()));
541 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
542 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
543 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
544 }
545 }
546
547 let rustc_cfgs = {
548 let mut cmd = Command::new(toolchain::rustc());
549 cmd.args(&["--print", "cfg", "-O"]);
550 if let Some(target) = target {
551 cmd.args(&["--target", target]);
552 }
553 utf8_stdout(cmd)
554 };
555
556 match rustc_cfgs {
557 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
558 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
559 }
560
561 res
562}
563
564fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { 563fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
565 if let Err(err) = graph.add_dep(from, name, to) { 564 if let Err(err) = graph.add_dep(from, name, to) {
566 log::error!("{}", err) 565 log::error!("{}", err)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index af7b86ead..268c00942 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.86.0", features = ["proposed"] } 25lsp-types = { version = "0.86.0", features = ["proposed"] }
26parking_lot = "0.11.0" 26parking_lot = "0.11.0"
27pico-args = "0.3.1" 27pico-args = "0.4.0"
28oorandom = "11.1.2" 28oorandom = "11.1.2"
29rustc-hash = "1.1.0" 29rustc-hash = "1.1.0"
30serde = { version = "1.0.106", features = ["derive"] } 30serde = { version = "1.0.106", features = ["derive"] }
@@ -61,8 +61,14 @@ proc_macro_srv = { path = "../proc_macro_srv", version = "0.0.0" }
61[target.'cfg(windows)'.dependencies] 61[target.'cfg(windows)'.dependencies]
62winapi = "0.3.8" 62winapi = "0.3.8"
63 63
64[target.'cfg(not(target_env = "msvc"))'.dependencies]
65jemallocator = { version = "0.3.2", optional = true }
66
64[dev-dependencies] 67[dev-dependencies]
65expect-test = "1.1" 68expect-test = "1.1"
66test_utils = { path = "../test_utils" } 69test_utils = { path = "../test_utils" }
67mbe = { path = "../mbe" } 70mbe = { path = "../mbe" }
68tt = { path = "../tt" } 71tt = { path = "../tt" }
72
73[features]
74jemalloc = ["jemallocator", "profile/jemalloc"]
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 0a471154e..7d917946e 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -109,7 +109,7 @@ impl Args {
109 let mut matches = Arguments::from_env(); 109 let mut matches = Arguments::from_env();
110 110
111 if matches.contains("--version") { 111 if matches.contains("--version") {
112 matches.finish()?; 112 finish_args(matches)?;
113 return Ok(Args { 113 return Ok(Args {
114 verbosity: Verbosity::Normal, 114 verbosity: Verbosity::Normal,
115 log_file: None, 115 log_file: None,
@@ -143,7 +143,7 @@ impl Args {
143 let subcommand = match matches.subcommand()? { 143 let subcommand = match matches.subcommand()? {
144 Some(it) => it, 144 Some(it) => it,
145 None => { 145 None => {
146 matches.finish()?; 146 finish_args(matches)?;
147 return Ok(Args { verbosity, log_file, command: Command::RunServer }); 147 return Ok(Args { verbosity, log_file, command: Command::RunServer });
148 } 148 }
149 }; 149 };
@@ -160,7 +160,7 @@ impl Args {
160 load_output_dirs: matches.contains("--load-output-dirs"), 160 load_output_dirs: matches.contains("--load-output-dirs"),
161 with_proc_macro: matches.contains("--with-proc-macro"), 161 with_proc_macro: matches.contains("--with-proc-macro"),
162 path: matches 162 path: matches
163 .free_from_str()? 163 .opt_free_from_str()?
164 .ok_or_else(|| format_err!("expected positional argument"))?, 164 .ok_or_else(|| format_err!("expected positional argument"))?,
165 }), 165 }),
166 "analysis-bench" => Command::Bench(BenchCmd { 166 "analysis-bench" => Command::Bench(BenchCmd {
@@ -187,21 +187,21 @@ impl Args {
187 load_output_dirs: matches.contains("--load-output-dirs"), 187 load_output_dirs: matches.contains("--load-output-dirs"),
188 with_proc_macro: matches.contains("--with-proc-macro"), 188 with_proc_macro: matches.contains("--with-proc-macro"),
189 path: matches 189 path: matches
190 .free_from_str()? 190 .opt_free_from_str()?
191 .ok_or_else(|| format_err!("expected positional argument"))?, 191 .ok_or_else(|| format_err!("expected positional argument"))?,
192 }), 192 }),
193 "diagnostics" => Command::Diagnostics { 193 "diagnostics" => Command::Diagnostics {
194 load_output_dirs: matches.contains("--load-output-dirs"), 194 load_output_dirs: matches.contains("--load-output-dirs"),
195 with_proc_macro: matches.contains("--with-proc-macro"), 195 with_proc_macro: matches.contains("--with-proc-macro"),
196 path: matches 196 path: matches
197 .free_from_str()? 197 .opt_free_from_str()?
198 .ok_or_else(|| format_err!("expected positional argument"))?, 198 .ok_or_else(|| format_err!("expected positional argument"))?,
199 }, 199 },
200 "proc-macro" => Command::ProcMacro, 200 "proc-macro" => Command::ProcMacro,
201 "ssr" => Command::Ssr { 201 "ssr" => Command::Ssr {
202 rules: { 202 rules: {
203 let mut acc = Vec::new(); 203 let mut acc = Vec::new();
204 while let Some(rule) = matches.free_from_str()? { 204 while let Some(rule) = matches.opt_free_from_str()? {
205 acc.push(rule); 205 acc.push(rule);
206 } 206 }
207 acc 207 acc
@@ -211,7 +211,7 @@ impl Args {
211 debug_snippet: matches.opt_value_from_str("--debug")?, 211 debug_snippet: matches.opt_value_from_str("--debug")?,
212 patterns: { 212 patterns: {
213 let mut acc = Vec::new(); 213 let mut acc = Vec::new();
214 while let Some(rule) = matches.free_from_str()? { 214 while let Some(rule) = matches.opt_free_from_str()? {
215 acc.push(rule); 215 acc.push(rule);
216 } 216 }
217 acc 217 acc
@@ -222,7 +222,14 @@ impl Args {
222 return Ok(Args { verbosity, log_file: None, command: Command::Help }); 222 return Ok(Args { verbosity, log_file: None, command: Command::Help });
223 } 223 }
224 }; 224 };
225 matches.finish()?; 225 finish_args(matches)?;
226 Ok(Args { verbosity, log_file, command }) 226 Ok(Args { verbosity, log_file, command })
227 } 227 }
228} 228}
229
230fn finish_args(args: Arguments) -> Result<()> {
231 if !args.finish().is_empty() {
232 bail!("Unused arguments.");
233 }
234 Ok(())
235}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index bf42654a8..2f7f94a39 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -15,6 +15,10 @@ use vfs::AbsPathBuf;
15#[global_allocator] 15#[global_allocator]
16static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; 16static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
17 17
18#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
19#[global_allocator]
20static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
21
18fn main() { 22fn main() {
19 if let Err(err) = try_main() { 23 if let Err(err) = try_main() {
20 eprintln!("{}", err); 24 eprintln!("{}", err);
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index a02c8327f..a01b49822 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -93,7 +93,7 @@ impl BenchCmd {
93 if is_completion { 93 if is_completion {
94 let options = CompletionConfig { 94 let options = CompletionConfig {
95 enable_postfix_completions: true, 95 enable_postfix_completions: true,
96 enable_autoimport_completions: true, 96 enable_imports_on_the_fly: true,
97 add_call_parenthesis: true, 97 add_call_parenthesis: true,
98 add_call_argument_snippets: true, 98 add_call_argument_snippets: true,
99 snippet_cap: SnippetCap::new(true), 99 snippet_cap: SnippetCap::new(true),
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index fd1407e60..66416f709 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -2,6 +2,7 @@
2//! errors. 2//! errors.
3 3
4use std::{ 4use std::{
5 env,
5 path::PathBuf, 6 path::PathBuf,
6 time::{SystemTime, UNIX_EPOCH}, 7 time::{SystemTime, UNIX_EPOCH},
7}; 8};
@@ -295,6 +296,10 @@ impl AnalysisStatsCmd {
295 report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); 296 report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
296 } 297 }
297 298
299 if env::var("RA_COUNT").is_ok() {
300 eprintln!("{}", profile::countme::get_all());
301 }
302
298 if self.memory_usage && verbosity.is_verbose() { 303 if self.memory_usage && verbosity.is_verbose() {
299 print_memory_usage(host, vfs); 304 print_memory_usage(host, vfs);
300 } 305 }
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 31a16ca46..16ccab781 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -39,7 +39,7 @@ pub fn load_cargo(
39 None 39 None
40 }; 40 };
41 41
42 let crate_graph = ws.to_crate_graph(None, proc_macro_client.as_ref(), &mut |path: &AbsPath| { 42 let crate_graph = ws.to_crate_graph(proc_macro_client.as_ref(), &mut |path: &AbsPath| {
43 let contents = loader.load_sync(path); 43 let contents = loader.load_sync(path);
44 let path = vfs::VfsPath::from(path.to_path_buf()); 44 let path = vfs::VfsPath::from(path.to_path_buf());
45 vfs.set_file_contents(path.clone(), contents); 45 vfs.set_file_contents(path.clone(), contents);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ce9655818..3ddb9e19a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -559,7 +559,7 @@ impl Config {
559 pub fn completion(&self) -> CompletionConfig { 559 pub fn completion(&self) -> CompletionConfig {
560 CompletionConfig { 560 CompletionConfig {
561 enable_postfix_completions: self.data.completion_postfix_enable, 561 enable_postfix_completions: self.data.completion_postfix_enable,
562 enable_autoimport_completions: self.data.completion_autoimport_enable 562 enable_imports_on_the_fly: self.data.completion_autoimport_enable
563 && completion_item_edit_resolve(&self.caps), 563 && completion_item_edit_resolve(&self.caps),
564 add_call_parenthesis: self.data.completion_addCallParenthesis, 564 add_call_parenthesis: self.data.completion_addCallParenthesis,
565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, 565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
@@ -581,18 +581,7 @@ impl Config {
581 AssistConfig { 581 AssistConfig {
582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), 582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
583 allowed: None, 583 allowed: None,
584 insert_use: InsertUseConfig { 584 insert_use: self.insert_use_config(),
585 merge: match self.data.assist_importMergeBehavior {
586 MergeBehaviorDef::None => None,
587 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
588 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
589 },
590 prefix_kind: match self.data.assist_importPrefix {
591 ImportPrefixDef::Plain => PrefixKind::Plain,
592 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
593 ImportPrefixDef::BySelf => PrefixKind::BySelf,
594 },
595 },
596 } 585 }
597 } 586 }
598 pub fn call_info_full(&self) -> bool { 587 pub fn call_info_full(&self) -> bool {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 1a4e0dd32..42451cd2d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -653,7 +653,7 @@ pub(crate) fn handle_completion(
653 let mut new_completion_items = 653 let mut new_completion_items =
654 to_proto::completion_item(&line_index, line_endings, item.clone()); 654 to_proto::completion_item(&line_index, line_endings, item.clone());
655 655
656 if completion_config.enable_autoimport_completions { 656 if completion_config.enable_imports_on_the_fly {
657 for new_item in &mut new_completion_items { 657 for new_item in &mut new_completion_items {
658 fill_resolve_data(&mut new_item.data, &item, &text_document_position); 658 fill_resolve_data(&mut new_item.data, &item, &text_document_position);
659 } 659 }
@@ -703,6 +703,7 @@ pub(crate) fn handle_completion_resolve(
703 FilePosition { file_id, offset }, 703 FilePosition { file_id, offset },
704 &resolve_data.full_import_path, 704 &resolve_data.full_import_path,
705 resolve_data.imported_name, 705 resolve_data.imported_name,
706 resolve_data.import_for_trait_assoc_item,
706 )? 707 )?
707 .into_iter() 708 .into_iter()
708 .flat_map(|edit| { 709 .flat_map(|edit| {
@@ -809,7 +810,7 @@ pub(crate) fn handle_rename(
809 810
810 let change = 811 let change =
811 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; 812 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
812 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 813 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
813 Ok(Some(workspace_edit)) 814 Ok(Some(workspace_edit))
814} 815}
815 816
@@ -857,6 +858,23 @@ pub(crate) fn handle_formatting(
857 RustfmtConfig::Rustfmt { extra_args } => { 858 RustfmtConfig::Rustfmt { extra_args } => {
858 let mut cmd = process::Command::new(toolchain::rustfmt()); 859 let mut cmd = process::Command::new(toolchain::rustfmt());
859 cmd.args(extra_args); 860 cmd.args(extra_args);
861 // try to chdir to the file so we can respect `rustfmt.toml`
862 // FIXME: use `rustfmt --config-path` once
863 // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
864 match params.text_document.uri.to_file_path() {
865 Ok(mut path) => {
866 // pop off file name
867 if path.pop() && path.is_dir() {
868 cmd.current_dir(path);
869 }
870 }
871 Err(_) => {
872 log::error!(
873 "Unable to get file path for {}, rustfmt.toml might be ignored",
874 params.text_document.uri
875 );
876 }
877 }
860 if let Some(&crate_id) = crate_ids.first() { 878 if let Some(&crate_id) = crate_ids.first() {
861 // Assume all crates are in the same edition 879 // Assume all crates are in the same edition
862 let edition = snap.analysis.crate_edition(crate_id)?; 880 let edition = snap.analysis.crate_edition(crate_id)?;
@@ -1694,6 +1712,7 @@ struct CompletionResolveData {
1694 position: lsp_types::TextDocumentPositionParams, 1712 position: lsp_types::TextDocumentPositionParams,
1695 full_import_path: String, 1713 full_import_path: String,
1696 imported_name: String, 1714 imported_name: String,
1715 import_for_trait_assoc_item: bool,
1697} 1716}
1698 1717
1699fn fill_resolve_data( 1718fn fill_resolve_data(
@@ -1710,6 +1729,7 @@ fn fill_resolve_data(
1710 position: position.to_owned(), 1729 position: position.to_owned(),
1711 full_import_path, 1730 full_import_path,
1712 imported_name, 1731 imported_name,
1732 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1713 }) 1733 })
1714 .unwrap(), 1734 .unwrap(),
1715 ); 1735 );
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 97e20362f..dabfb4241 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -135,7 +135,10 @@ impl GlobalState {
135 ) 135 )
136 } 136 }
137 LinkedProject::InlineJsonProject(it) => { 137 LinkedProject::InlineJsonProject(it) => {
138 project_model::ProjectWorkspace::load_inline(it.clone()) 138 project_model::ProjectWorkspace::load_inline(
139 it.clone(),
140 cargo_config.target.as_deref(),
141 )
139 } 142 }
140 }) 143 })
141 .collect::<Vec<_>>(); 144 .collect::<Vec<_>>();
@@ -253,11 +256,7 @@ impl GlobalState {
253 res 256 res
254 }; 257 };
255 for ws in workspaces.iter() { 258 for ws in workspaces.iter() {
256 crate_graph.extend(ws.to_crate_graph( 259 crate_graph.extend(ws.to_crate_graph(self.proc_macro_client.as_ref(), &mut load));
257 self.config.cargo().target.as_deref(),
258 self.proc_macro_client.as_ref(),
259 &mut load,
260 ));
261 } 260 }
262 261
263 crate_graph 262 crate_graph
@@ -274,6 +273,7 @@ impl GlobalState {
274 } 273 }
275 274
276 fn reload_flycheck(&mut self) { 275 fn reload_flycheck(&mut self) {
276 let _p = profile::span("GlobalState::reload_flycheck");
277 let config = match self.config.flycheck() { 277 let config = match self.config.flycheck() {
278 Some(it) => it, 278 Some(it) => it,
279 None => { 279 None => {
@@ -288,9 +288,7 @@ impl GlobalState {
288 .iter() 288 .iter()
289 .enumerate() 289 .enumerate()
290 .filter_map(|(id, w)| match w { 290 .filter_map(|(id, w)| match w {
291 ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => { 291 ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
292 Some((id, cargo.workspace_root()))
293 }
294 ProjectWorkspace::Json { project, .. } => { 292 ProjectWorkspace::Json { project, .. } => {
295 // Enable flychecks for json projects if a custom flycheck command was supplied 293 // Enable flychecks for json projects if a custom flycheck command was supplied
296 // in the workspace configuration. 294 // in the workspace configuration.
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1ff2d3fea..0e3550002 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -884,7 +884,7 @@ mod tests {
884 .completions( 884 .completions(
885 &ide::CompletionConfig { 885 &ide::CompletionConfig {
886 enable_postfix_completions: true, 886 enable_postfix_completions: true,
887 enable_autoimport_completions: true, 887 enable_imports_on_the_fly: true,
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),
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs
index 42d313f91..df013bae9 100644
--- a/crates/ssr/src/matching.rs
+++ b/crates/ssr/src/matching.rs
@@ -10,8 +10,11 @@ use hir::Semantics;
10use ide_db::base_db::FileRange; 10use ide_db::base_db::FileRange;
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12use std::{cell::Cell, iter::Peekable}; 12use std::{cell::Cell, iter::Peekable};
13use syntax::ast::{AstNode, AstToken};
14use syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken}; 13use syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
14use syntax::{
15 ast::{AstNode, AstToken},
16 SmolStr,
17};
15use test_utils::mark; 18use test_utils::mark;
16 19
17// Creates a match error. If we're currently attempting to match some code that we thought we were 20// Creates a match error. If we're currently attempting to match some code that we thought we were
@@ -398,11 +401,11 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
398 code: &SyntaxNode, 401 code: &SyntaxNode,
399 ) -> Result<(), MatchFailed> { 402 ) -> Result<(), MatchFailed> {
400 // Build a map keyed by field name. 403 // Build a map keyed by field name.
401 let mut fields_by_name = FxHashMap::default(); 404 let mut fields_by_name: FxHashMap<SmolStr, SyntaxNode> = FxHashMap::default();
402 for child in code.children() { 405 for child in code.children() {
403 if let Some(record) = ast::RecordExprField::cast(child.clone()) { 406 if let Some(record) = ast::RecordExprField::cast(child.clone()) {
404 if let Some(name) = record.field_name() { 407 if let Some(name) = record.field_name() {
405 fields_by_name.insert(name.text().clone(), child.clone()); 408 fields_by_name.insert(name.text().into(), child.clone());
406 } 409 }
407 } 410 }
408 } 411 }
@@ -473,9 +476,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
473 } 476 }
474 SyntaxElement::Node(n) => { 477 SyntaxElement::Node(n) => {
475 if let Some(first_token) = n.first_token() { 478 if let Some(first_token) = n.first_token() {
476 if Some(first_token.text().as_str()) 479 if Some(first_token.text()) == next_pattern_token.as_deref() {
477 == next_pattern_token.as_deref()
478 {
479 if let Some(SyntaxElement::Node(p)) = pattern.next() { 480 if let Some(SyntaxElement::Node(p)) = pattern.next() {
480 // We have a subtree that starts with the next token in our pattern. 481 // We have a subtree that starts with the next token in our pattern.
481 self.attempt_match_token_tree(phase, &p, &n)?; 482 self.attempt_match_token_tree(phase, &p, &n)?;
diff --git a/crates/ssr/src/replacing.rs b/crates/ssr/src/replacing.rs
index 7e7ce37bd..06a94a46c 100644
--- a/crates/ssr/src/replacing.rs
+++ b/crates/ssr/src/replacing.rs
@@ -173,7 +173,7 @@ impl ReplacementRenderer<'_> {
173 ); 173 );
174 } 174 }
175 } else { 175 } else {
176 self.out.push_str(token.text().as_str()); 176 self.out.push_str(token.text());
177 } 177 }
178 } 178 }
179 179
diff --git a/crates/ssr/src/resolving.rs b/crates/ssr/src/resolving.rs
index f5ceb5729..14e5a3b69 100644
--- a/crates/ssr/src/resolving.rs
+++ b/crates/ssr/src/resolving.rs
@@ -228,7 +228,7 @@ impl<'db> ResolutionScope<'db> {
228 None, 228 None,
229 |_ty, assoc_item| { 229 |_ty, assoc_item| {
230 let item_name = assoc_item.name(self.scope.db)?; 230 let item_name = assoc_item.name(self.scope.db)?;
231 if item_name.to_string().as_str() == name.text().as_str() { 231 if item_name.to_string().as_str() == name.text() {
232 Some(hir::PathResolution::AssocItem(assoc_item)) 232 Some(hir::PathResolution::AssocItem(assoc_item))
233 } else { 233 } else {
234 None 234 None
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 1ff2559bb..73b121f8a 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -134,8 +134,10 @@ impl<'a> Iterator for LinesWithEnds<'a> {
134 134
135/// Returns `idx` such that: 135/// Returns `idx` such that:
136/// 136///
137/// ```text
137/// ∀ x in slice[..idx]: pred(x) 138/// ∀ x in slice[..idx]: pred(x)
138/// && ∀ x in slice[idx..]: !pred(x) 139/// && ∀ x in slice[idx..]: !pred(x)
140/// ```
139/// 141///
140/// https://github.com/rust-lang/rust/issues/73831 142/// https://github.com/rust-lang/rust/issues/73831
141pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize 143pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 52394b337..24298fbfa 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -12,15 +12,12 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.10.0" 14itertools = "0.10.0"
15rowan = "0.10.3" 15rowan = "0.12"
16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "700.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"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
20indexmap = "1.4.0" 20indexmap = "1.4.0"
21# This crate transitively depends on `smol_str` via `rowan`.
22# ideally, `serde` should be enabled by `rust-analyzer`, but we enable it here
23# to reduce number of compilations
24smol_str = { version = "0.1.15", features = ["serde"] } 21smol_str = { version = "0.1.15", features = ["serde"] }
25serde = { version = "1.0.106", features = ["derive"] } 22serde = { version = "1.0.106", features = ["derive"] }
26 23
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 827ae78f9..2ff92f9f6 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -4,6 +4,7 @@ use std::{
4 fmt, 4 fmt,
5 hash::BuildHasherDefault, 5 hash::BuildHasherDefault,
6 ops::{self, RangeInclusive}, 6 ops::{self, RangeInclusive},
7 ptr,
7}; 8};
8 9
9use indexmap::IndexMap; 10use indexmap::IndexMap;
@@ -171,7 +172,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
171 && lhs.text_range().len() == rhs.text_range().len() 172 && lhs.text_range().len() == rhs.text_range().len()
172 && match (&lhs, &rhs) { 173 && match (&lhs, &rhs) {
173 (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { 174 (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
174 lhs.green() == rhs.green() || lhs.text() == rhs.text() 175 ptr::eq(lhs.green(), rhs.green()) || lhs.text() == rhs.text()
175 } 176 }
176 (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), 177 (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
177 _ => false, 178 _ => false,
@@ -566,7 +567,7 @@ impl<'a> SyntaxRewriter<'a> {
566 567
567fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 568fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
568 match element { 569 match element {
569 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), 570 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().to_owned()),
570 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 571 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
571 } 572 }
572} 573}
@@ -624,7 +625,7 @@ fn position_of_child(parent: &SyntaxNode, child: SyntaxElement) -> usize {
624 625
625fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 626fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
626 match element { 627 match element {
627 NodeOrToken::Node(it) => it.green().clone().into(), 628 NodeOrToken::Node(it) => it.green().to_owned().into(),
628 NodeOrToken::Token(it) => it.green().clone().into(), 629 NodeOrToken::Token(it) => it.green().clone().into(),
629 } 630 }
630} 631}
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 83de067d9..a25ff655e 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -12,7 +12,7 @@ use std::marker::PhantomData;
12 12
13use crate::{ 13use crate::{
14 syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken}, 14 syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
15 SmolStr, SyntaxKind, 15 SyntaxKind,
16}; 16};
17 17
18pub use self::{ 18pub use self::{
@@ -54,7 +54,7 @@ pub trait AstToken {
54 54
55 fn syntax(&self) -> &SyntaxToken; 55 fn syntax(&self) -> &SyntaxToken;
56 56
57 fn text(&self) -> &SmolStr { 57 fn text(&self) -> &str {
58 self.syntax().text() 58 self.syntax().text()
59 } 59 }
60} 60}
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 1d722db3c..5baa54a3f 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -11,6 +11,7 @@ pub struct Name {
11} 11}
12impl Name { 12impl Name {
13 pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) } 13 pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
14 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
14} 15}
15#[derive(Debug, Clone, PartialEq, Eq, Hash)] 16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct NameRef { 17pub struct NameRef {
@@ -238,7 +239,6 @@ impl ExternCrate {
238 pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) } 239 pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) }
239 pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) } 240 pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
240 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) } 241 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
241 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
242 pub fn rename(&self) -> Option<Rename> { support::child(&self.syntax) } 242 pub fn rename(&self) -> Option<Rename> { support::child(&self.syntax) }
243 pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } 243 pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
244} 244}
@@ -406,9 +406,6 @@ pub struct Visibility {
406impl Visibility { 406impl Visibility {
407 pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) } 407 pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) }
408 pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } 408 pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
409 pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
410 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
411 pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
412 pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) } 409 pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
413 pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } 410 pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
414 pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } 411 pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
@@ -492,11 +489,11 @@ pub struct SelfParam {
492 pub(crate) syntax: SyntaxNode, 489 pub(crate) syntax: SyntaxNode,
493} 490}
494impl ast::AttrsOwner for SelfParam {} 491impl ast::AttrsOwner for SelfParam {}
492impl ast::NameOwner for SelfParam {}
495impl SelfParam { 493impl SelfParam {
496 pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) } 494 pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) }
497 pub fn lifetime(&self) -> Option<Lifetime> { support::child(&self.syntax) } 495 pub fn lifetime(&self) -> Option<Lifetime> { support::child(&self.syntax) }
498 pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) } 496 pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
499 pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
500 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } 497 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
501 pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } 498 pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
502} 499}
@@ -1075,6 +1072,13 @@ impl InferType {
1075 pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) } 1072 pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) }
1076} 1073}
1077#[derive(Debug, Clone, PartialEq, Eq, Hash)] 1074#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1075pub struct MacroType {
1076 pub(crate) syntax: SyntaxNode,
1077}
1078impl MacroType {
1079 pub fn macro_call(&self) -> Option<MacroCall> { support::child(&self.syntax) }
1080}
1081#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1078pub struct NeverType { 1082pub struct NeverType {
1079 pub(crate) syntax: SyntaxNode, 1083 pub(crate) syntax: SyntaxNode,
1080} 1084}
@@ -1303,6 +1307,7 @@ pub enum Type {
1303 ForType(ForType), 1307 ForType(ForType),
1304 ImplTraitType(ImplTraitType), 1308 ImplTraitType(ImplTraitType),
1305 InferType(InferType), 1309 InferType(InferType),
1310 MacroType(MacroType),
1306 NeverType(NeverType), 1311 NeverType(NeverType),
1307 ParenType(ParenType), 1312 ParenType(ParenType),
1308 PathType(PathType), 1313 PathType(PathType),
@@ -2561,6 +2566,17 @@ impl AstNode for InferType {
2561 } 2566 }
2562 fn syntax(&self) -> &SyntaxNode { &self.syntax } 2567 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2563} 2568}
2569impl AstNode for MacroType {
2570 fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_TYPE }
2571 fn cast(syntax: SyntaxNode) -> Option<Self> {
2572 if Self::can_cast(syntax.kind()) {
2573 Some(Self { syntax })
2574 } else {
2575 None
2576 }
2577 }
2578 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2579}
2564impl AstNode for NeverType { 2580impl AstNode for NeverType {
2565 fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE } 2581 fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE }
2566 fn cast(syntax: SyntaxNode) -> Option<Self> { 2582 fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2892,6 +2908,9 @@ impl From<ImplTraitType> for Type {
2892impl From<InferType> for Type { 2908impl From<InferType> for Type {
2893 fn from(node: InferType) -> Type { Type::InferType(node) } 2909 fn from(node: InferType) -> Type { Type::InferType(node) }
2894} 2910}
2911impl From<MacroType> for Type {
2912 fn from(node: MacroType) -> Type { Type::MacroType(node) }
2913}
2895impl From<NeverType> for Type { 2914impl From<NeverType> for Type {
2896 fn from(node: NeverType) -> Type { Type::NeverType(node) } 2915 fn from(node: NeverType) -> Type { Type::NeverType(node) }
2897} 2916}
@@ -2917,8 +2936,8 @@ impl AstNode for Type {
2917 fn can_cast(kind: SyntaxKind) -> bool { 2936 fn can_cast(kind: SyntaxKind) -> bool {
2918 match kind { 2937 match kind {
2919 ARRAY_TYPE | DYN_TRAIT_TYPE | FN_PTR_TYPE | FOR_TYPE | IMPL_TRAIT_TYPE | INFER_TYPE 2938 ARRAY_TYPE | DYN_TRAIT_TYPE | FN_PTR_TYPE | FOR_TYPE | IMPL_TRAIT_TYPE | INFER_TYPE
2920 | NEVER_TYPE | PAREN_TYPE | PATH_TYPE | PTR_TYPE | REF_TYPE | SLICE_TYPE 2939 | MACRO_TYPE | NEVER_TYPE | PAREN_TYPE | PATH_TYPE | PTR_TYPE | REF_TYPE
2921 | TUPLE_TYPE => true, 2940 | SLICE_TYPE | TUPLE_TYPE => true,
2922 _ => false, 2941 _ => false,
2923 } 2942 }
2924 } 2943 }
@@ -2930,6 +2949,7 @@ impl AstNode for Type {
2930 FOR_TYPE => Type::ForType(ForType { syntax }), 2949 FOR_TYPE => Type::ForType(ForType { syntax }),
2931 IMPL_TRAIT_TYPE => Type::ImplTraitType(ImplTraitType { syntax }), 2950 IMPL_TRAIT_TYPE => Type::ImplTraitType(ImplTraitType { syntax }),
2932 INFER_TYPE => Type::InferType(InferType { syntax }), 2951 INFER_TYPE => Type::InferType(InferType { syntax }),
2952 MACRO_TYPE => Type::MacroType(MacroType { syntax }),
2933 NEVER_TYPE => Type::NeverType(NeverType { syntax }), 2953 NEVER_TYPE => Type::NeverType(NeverType { syntax }),
2934 PAREN_TYPE => Type::ParenType(ParenType { syntax }), 2954 PAREN_TYPE => Type::ParenType(ParenType { syntax }),
2935 PATH_TYPE => Type::PathType(PathType { syntax }), 2955 PATH_TYPE => Type::PathType(PathType { syntax }),
@@ -2949,6 +2969,7 @@ impl AstNode for Type {
2949 Type::ForType(it) => &it.syntax, 2969 Type::ForType(it) => &it.syntax,
2950 Type::ImplTraitType(it) => &it.syntax, 2970 Type::ImplTraitType(it) => &it.syntax,
2951 Type::InferType(it) => &it.syntax, 2971 Type::InferType(it) => &it.syntax,
2972 Type::MacroType(it) => &it.syntax,
2952 Type::NeverType(it) => &it.syntax, 2973 Type::NeverType(it) => &it.syntax,
2953 Type::ParenType(it) => &it.syntax, 2974 Type::ParenType(it) => &it.syntax,
2954 Type::PathType(it) => &it.syntax, 2975 Type::PathType(it) => &it.syntax,
@@ -4085,6 +4106,11 @@ impl std::fmt::Display for InferType {
4085 std::fmt::Display::fmt(self.syntax(), f) 4106 std::fmt::Display::fmt(self.syntax(), f)
4086 } 4107 }
4087} 4108}
4109impl std::fmt::Display for MacroType {
4110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4111 std::fmt::Display::fmt(self.syntax(), f)
4112 }
4113}
4088impl std::fmt::Display for NeverType { 4114impl std::fmt::Display for NeverType {
4089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 4115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4090 std::fmt::Display::fmt(self.syntax(), f) 4116 std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 9ffc3ae11..b755c9692 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -478,7 +478,7 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
478} 478}
479 479
480fn unroot(n: SyntaxNode) -> SyntaxNode { 480fn unroot(n: SyntaxNode) -> SyntaxNode {
481 SyntaxNode::new_root(n.green().clone()) 481 SyntaxNode::new_root(n.green().to_owned())
482} 482}
483 483
484pub mod tokens { 484pub mod tokens {
@@ -495,7 +495,7 @@ pub mod tokens {
495 .syntax() 495 .syntax()
496 .descendants_with_tokens() 496 .descendants_with_tokens()
497 .filter_map(|it| it.into_token()) 497 .filter_map(|it| it.into_token())
498 .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ") 498 .find(|it| it.kind() == WHITESPACE && it.text() == " ")
499 .unwrap() 499 .unwrap()
500 } 500 }
501 501
@@ -523,7 +523,7 @@ pub mod tokens {
523 .syntax() 523 .syntax()
524 .descendants_with_tokens() 524 .descendants_with_tokens()
525 .filter_map(|it| it.into_token()) 525 .filter_map(|it| it.into_token())
526 .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n") 526 .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
527 .unwrap() 527 .unwrap()
528 } 528 }
529 529
@@ -533,7 +533,7 @@ pub mod tokens {
533 .syntax() 533 .syntax()
534 .descendants_with_tokens() 534 .descendants_with_tokens()
535 .filter_map(|it| it.into_token()) 535 .filter_map(|it| it.into_token())
536 .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n\n") 536 .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
537 .unwrap() 537 .unwrap()
538 } 538 }
539 539
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index b8ce71d27..5c8cf900f 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -13,19 +13,19 @@ use crate::{
13}; 13};
14 14
15impl ast::Lifetime { 15impl ast::Lifetime {
16 pub fn text(&self) -> &SmolStr { 16 pub fn text(&self) -> &str {
17 text_of_first_token(self.syntax()) 17 text_of_first_token(self.syntax())
18 } 18 }
19} 19}
20 20
21impl ast::Name { 21impl ast::Name {
22 pub fn text(&self) -> &SmolStr { 22 pub fn text(&self) -> &str {
23 text_of_first_token(self.syntax()) 23 text_of_first_token(self.syntax())
24 } 24 }
25} 25}
26 26
27impl ast::NameRef { 27impl ast::NameRef {
28 pub fn text(&self) -> &SmolStr { 28 pub fn text(&self) -> &str {
29 text_of_first_token(self.syntax()) 29 text_of_first_token(self.syntax())
30 } 30 }
31 31
@@ -34,7 +34,7 @@ impl ast::NameRef {
34 } 34 }
35} 35}
36 36
37fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { 37fn text_of_first_token(node: &SyntaxNode) -> &str {
38 node.green().children().next().and_then(|it| it.into_token()).unwrap().text() 38 node.green().children().next().and_then(|it| it.into_token()).unwrap().text()
39} 39}
40 40
@@ -121,7 +121,7 @@ impl ast::Attr {
121 pub fn simple_name(&self) -> Option<SmolStr> { 121 pub fn simple_name(&self) -> Option<SmolStr> {
122 let path = self.path()?; 122 let path = self.path()?;
123 match (path.segment(), path.qualifier()) { 123 match (path.segment(), path.qualifier()) {
124 (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()), 124 (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
125 _ => None, 125 _ => None,
126 } 126 }
127 } 127 }
@@ -198,6 +198,13 @@ impl ast::Path {
198 pub fn parent_path(&self) -> Option<ast::Path> { 198 pub fn parent_path(&self) -> Option<ast::Path> {
199 self.syntax().parent().and_then(ast::Path::cast) 199 self.syntax().parent().and_then(ast::Path::cast)
200 } 200 }
201
202 pub fn as_single_segment(&self) -> Option<ast::PathSegment> {
203 match self.qualifier() {
204 Some(_) => None,
205 None => self.segment(),
206 }
207 }
201} 208}
202 209
203impl ast::UseTreeList { 210impl ast::UseTreeList {
@@ -448,16 +455,22 @@ pub enum VisibilityKind {
448 455
449impl ast::Visibility { 456impl ast::Visibility {
450 pub fn kind(&self) -> VisibilityKind { 457 pub fn kind(&self) -> VisibilityKind {
451 if let Some(path) = support::children(self.syntax()).next() { 458 match self.path() {
452 VisibilityKind::In(path) 459 Some(path) => {
453 } else if self.crate_token().is_some() { 460 if let Some(segment) =
454 VisibilityKind::PubCrate 461 path.as_single_segment().filter(|it| it.coloncolon_token().is_none())
455 } else if self.super_token().is_some() { 462 {
456 VisibilityKind::PubSuper 463 if segment.crate_token().is_some() {
457 } else if self.self_token().is_some() { 464 return VisibilityKind::PubCrate;
458 VisibilityKind::PubSelf 465 } else if segment.super_token().is_some() {
459 } else { 466 return VisibilityKind::PubSuper;
460 VisibilityKind::Pub 467 } else if segment.self_token().is_some() {
468 return VisibilityKind::PubSelf;
469 }
470 }
471 VisibilityKind::In(path)
472 }
473 None => VisibilityKind::Pub,
461 } 474 }
462 } 475 }
463} 476}
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 5e9620a40..5e07ec7d1 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -41,7 +41,7 @@ impl ast::Comment {
41 match kind { 41 match kind {
42 CommentKind { shape, doc: Some(_) } => { 42 CommentKind { shape, doc: Some(_) } => {
43 let prefix = kind.prefix(); 43 let prefix = kind.prefix();
44 let text = &self.text().as_str()[prefix.len()..]; 44 let text = &self.text()[prefix.len()..];
45 let ws = text.chars().next().filter(|c| c.is_whitespace()); 45 let ws = text.chars().next().filter(|c| c.is_whitespace());
46 let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); 46 let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]);
47 match shape { 47 match shape {
@@ -156,13 +156,13 @@ impl ast::String {
156 156
157 pub fn value(&self) -> Option<Cow<'_, str>> { 157 pub fn value(&self) -> Option<Cow<'_, str>> {
158 if self.is_raw() { 158 if self.is_raw() {
159 let text = self.text().as_str(); 159 let text = self.text();
160 let text = 160 let text =
161 &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; 161 &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
162 return Some(Cow::Borrowed(text)); 162 return Some(Cow::Borrowed(text));
163 } 163 }
164 164
165 let text = self.text().as_str(); 165 let text = self.text();
166 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; 166 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
167 167
168 let mut buf = String::new(); 168 let mut buf = String::new();
@@ -190,7 +190,7 @@ impl ast::String {
190 } 190 }
191 191
192 pub fn quote_offsets(&self) -> Option<QuoteOffsets> { 192 pub fn quote_offsets(&self) -> Option<QuoteOffsets> {
193 let text = self.text().as_str(); 193 let text = self.text();
194 let offsets = QuoteOffsets::new(text)?; 194 let offsets = QuoteOffsets::new(text)?;
195 let o = self.syntax().text_range().start(); 195 let o = self.syntax().text_range().start();
196 let offsets = QuoteOffsets { 196 let offsets = QuoteOffsets {
@@ -560,7 +560,7 @@ impl HasFormatSpecifier for ast::String {
560 fn char_ranges( 560 fn char_ranges(
561 &self, 561 &self,
562 ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { 562 ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
563 let text = self.text().as_str(); 563 let text = self.text();
564 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; 564 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
565 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); 565 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
566 566
@@ -590,7 +590,7 @@ impl ast::IntNumber {
590 pub fn value(&self) -> Option<u128> { 590 pub fn value(&self) -> Option<u128> {
591 let token = self.syntax(); 591 let token = self.syntax();
592 592
593 let mut text = token.text().as_str(); 593 let mut text = token.text();
594 if let Some(suffix) = self.suffix() { 594 if let Some(suffix) = self.suffix() {
595 text = &text[..text.len() - suffix.len()] 595 text = &text[..text.len() - suffix.len()]
596 } 596 }
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index ea7482bb1..11294c5b2 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -56,9 +56,9 @@ pub use crate::{
56}; 56};
57pub use parser::{SyntaxKind, T}; 57pub use parser::{SyntaxKind, T};
58pub use rowan::{ 58pub use rowan::{
59 Direction, GreenNode, NodeOrToken, SmolStr, SyntaxText, TextRange, TextSize, TokenAtOffset, 59 Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
60 WalkEvent,
61}; 60};
61pub use smol_str::SmolStr;
62 62
63/// `Parse` is the result of the parsing: a syntax tree and a collection of 63/// `Parse` is the result of the parsing: a syntax tree and a collection of
64/// errors. 64/// errors.
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 76f01084c..3d637bf91 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -73,8 +73,7 @@ fn reparse_token<'node>(
73 new_text.pop(); 73 new_text.pop();
74 } 74 }
75 75
76 let new_token = 76 let new_token = GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), &new_text);
77 GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
78 Some(( 77 Some((
79 prev_token.replace_with(new_token), 78 prev_token.replace_with(new_token),
80 new_err.into_iter().collect(), 79 new_err.into_iter().collect(),
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index ce27c3dd9..d5ddc076f 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -8,7 +8,7 @@ use crate::{
8 ast, 8 ast,
9 parsing::Token, 9 parsing::Token,
10 syntax_node::GreenNode, 10 syntax_node::GreenNode,
11 SmolStr, SyntaxError, 11 SyntaxError,
12 SyntaxKind::{self, *}, 12 SyntaxKind::{self, *},
13 SyntaxTreeBuilder, TextRange, TextSize, 13 SyntaxTreeBuilder, TextRange, TextSize,
14}; 14};
@@ -135,7 +135,7 @@ impl<'a> TextTreeSink<'a> {
135 135
136 fn do_token(&mut self, kind: SyntaxKind, len: TextSize, n_tokens: usize) { 136 fn do_token(&mut self, kind: SyntaxKind, len: TextSize, n_tokens: usize) {
137 let range = TextRange::at(self.text_pos, len); 137 let range = TextRange::at(self.text_pos, len);
138 let text: SmolStr = self.text[range].into(); 138 let text = &self.text[range];
139 self.text_pos += len; 139 self.text_pos += len;
140 self.token_pos += n_tokens; 140 self.token_pos += n_tokens;
141 self.inner.token(kind, text); 141 self.inner.token(kind, text);
diff --git a/crates/syntax/src/syntax_node.rs b/crates/syntax/src/syntax_node.rs
index cc30138fa..8f643b228 100644
--- a/crates/syntax/src/syntax_node.rs
+++ b/crates/syntax/src/syntax_node.rs
@@ -8,7 +8,7 @@
8 8
9use rowan::{GreenNodeBuilder, Language}; 9use rowan::{GreenNodeBuilder, Language};
10 10
11use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextSize}; 11use crate::{Parse, SyntaxError, SyntaxKind, TextSize};
12 12
13pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken}; 13pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken};
14 14
@@ -53,7 +53,7 @@ impl SyntaxTreeBuilder {
53 Parse::new(green, errors) 53 Parse::new(green, errors)
54 } 54 }
55 55
56 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { 56 pub fn token(&mut self, kind: SyntaxKind, text: &str) {
57 let kind = RustLanguage::kind_to_raw(kind); 57 let kind = RustLanguage::kind_to_raw(kind);
58 self.inner.token(kind, text) 58 self.inner.token(kind, text)
59 } 59 }
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 7901580ee..7694e8834 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -116,7 +116,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
116 } 116 }
117 117
118 let token = literal.token(); 118 let token = literal.token();
119 let text = token.text().as_str(); 119 let text = token.text();
120 120
121 // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205) 121 // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
122 let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { 122 let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
diff --git a/crates/syntax/test_data/parser/err/0037_visibility_in_traits.rast b/crates/syntax/test_data/parser/err/0037_visibility_in_traits.rast
index faf87d6e5..ae4dd2f3b 100644
--- a/crates/syntax/test_data/parser/err/0037_visibility_in_traits.rast
+++ b/crates/syntax/test_data/parser/err/0037_visibility_in_traits.rast
@@ -50,7 +50,10 @@ [email protected]
50 [email protected] 50 [email protected]
51 [email protected] "pub" 51 [email protected] "pub"
52 [email protected] "(" 52 [email protected] "("
53 [email protected] "crate" 53 [email protected]
54 [email protected]
55 [email protected]
56 [email protected] "crate"
54 [email protected] ")" 57 [email protected] ")"
55 [email protected] " " 58 [email protected] " "
56 [email protected] "type" 59 [email protected] "type"
@@ -69,7 +72,10 @@ [email protected]
69 [email protected] 72 [email protected]
70 [email protected] "pub" 73 [email protected] "pub"
71 [email protected] "(" 74 [email protected] "("
72 [email protected] "crate" 75 [email protected]
76 [email protected]
77 [email protected]
78 [email protected] "crate"
73 [email protected] ")" 79 [email protected] ")"
74 [email protected] " " 80 [email protected] " "
75 [email protected] "const" 81 [email protected] "const"
diff --git a/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast b/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
index 8048f5fad..f0d152d33 100644
--- a/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
@@ -19,7 +19,8 @@ [email protected]
19 [email protected] 19 [email protected]
20 [email protected] "(" 20 [email protected] "("
21 [email protected] 21 [email protected]
22 [email protected] "self" 22 [email protected]
23 [email protected] "self"
23 [email protected] ")" 24 [email protected] ")"
24 [email protected] " " 25 [email protected] " "
25 [email protected] 26 [email protected]
@@ -35,7 +36,8 @@ [email protected]
35 [email protected] "(" 36 [email protected] "("
36 [email protected] 37 [email protected]
37 [email protected] "&" 38 [email protected] "&"
38 [email protected] "self" 39 [email protected]
40 [email protected] "self"
39 [email protected] "," 41 [email protected] ","
40 [email protected] ")" 42 [email protected] ")"
41 [email protected] " " 43 [email protected] " "
@@ -55,7 +57,8 @@ [email protected]
55 [email protected] 57 [email protected]
56 [email protected] "\'a" 58 [email protected] "\'a"
57 [email protected] " " 59 [email protected] " "
58 [email protected] "self" 60 [email protected]
61 [email protected] "self"
59 [email protected] "," 62 [email protected] ","
60 [email protected] ")" 63 [email protected] ")"
61 [email protected] " " 64 [email protected] " "
@@ -77,7 +80,8 @@ [email protected]
77 [email protected] " " 80 [email protected] " "
78 [email protected] "mut" 81 [email protected] "mut"
79 [email protected] " " 82 [email protected] " "
80 [email protected] "self" 83 [email protected]
84 [email protected] "self"
81 [email protected] "," 85 [email protected] ","
82 [email protected] " " 86 [email protected] " "
83 [email protected] 87 [email protected]
@@ -107,7 +111,8 @@ [email protected]
107 [email protected] 111 [email protected]
108 [email protected] "mut" 112 [email protected] "mut"
109 [email protected] " " 113 [email protected] " "
110 [email protected] "self" 114 [email protected]
115 [email protected] "self"
111 [email protected] ")" 116 [email protected] ")"
112 [email protected] " " 117 [email protected] " "
113 [email protected] 118 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0018_arb_self_types.rast b/crates/syntax/test_data/parser/inline/ok/0018_arb_self_types.rast
index ddbd66588..df59f37a2 100644
--- a/crates/syntax/test_data/parser/inline/ok/0018_arb_self_types.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0018_arb_self_types.rast
@@ -19,7 +19,8 @@ [email protected]
19 [email protected] 19 [email protected]
20 [email protected] "(" 20 [email protected] "("
21 [email protected] 21 [email protected]
22 [email protected] "self" 22 [email protected]
23 [email protected] "self"
23 [email protected] ":" 24 [email protected] ":"
24 [email protected] " " 25 [email protected] " "
25 [email protected] 26 [email protected]
@@ -45,7 +46,8 @@ [email protected]
45 [email protected] 46 [email protected]
46 [email protected] "mut" 47 [email protected] "mut"
47 [email protected] " " 48 [email protected] " "
48 [email protected] "self" 49 [email protected]
50 [email protected] "self"
49 [email protected] ":" 51 [email protected] ":"
50 [email protected] " " 52 [email protected] " "
51 [email protected] 53 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast b/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast
index ca0702aba..dc7f6295b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0021_impl_item_list.rast
@@ -67,7 +67,8 @@ [email protected]
67 [email protected] "(" 67 [email protected] "("
68 [email protected] 68 [email protected]
69 [email protected] "&" 69 [email protected] "&"
70 [email protected] "self" 70 [email protected]
71 [email protected] "self"
71 [email protected] ")" 72 [email protected] ")"
72 [email protected] " " 73 [email protected] " "
73 [email protected] 74 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rast b/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rast
index 50742cbcf..f2ead8a62 100644
--- a/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rast
@@ -1,9 +1,12 @@
1SOURCE_FILE@0..81 1SOURCE_FILE@0..62
2 [email protected] 2 [email protected]
3 [email protected] 3 [email protected]
4 [email protected] "pub" 4 [email protected] "pub"
5 [email protected] "(" 5 [email protected] "("
6 [email protected] "crate" 6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "crate"
7 [email protected] ")" 10 [email protected] ")"
8 [email protected] " " 11 [email protected] " "
9 [email protected] "struct" 12 [email protected] "struct"
@@ -16,7 +19,10 @@ [email protected]
16 [email protected] 19 [email protected]
17 [email protected] "pub" 20 [email protected] "pub"
18 [email protected] "(" 21 [email protected] "("
19 [email protected] "self" 22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected] "self"
20 [email protected] ")" 26 [email protected] ")"
21 [email protected] " " 27 [email protected] " "
22 [email protected] "struct" 28 [email protected] "struct"
@@ -25,29 +31,19 @@ [email protected]
25 [email protected] "S" 31 [email protected] "S"
26 [email protected] ";" 32 [email protected] ";"
27 [email protected] "\n" 33 [email protected] "\n"
28 [email protected]0 34 [email protected]1
29 [email protected]0 35 [email protected]1
30 [email protected] "pub" 36 [email protected] "pub"
31 [email protected] "(" 37 [email protected] "("
32 [email protected] "self" 38 [email protected]
33 [email protected] ")" 39 [email protected]
34 [email protected] " " 40 [email protected]
35 [email protected] "struct" 41 [email protected] "super"
36 [email protected] " " 42 [email protected] ")"
37 [email protected] 43 [email protected] " "
38 [email protected] "S" 44 [email protected] "struct"
39 [email protected] ";" 45 [email protected] " "
40 [email protected] "\n" 46 [email protected]
41 [email protected] 47 [email protected] "S"
42 [email protected] 48 [email protected] ";"
43 [email protected] "pub" 49 [email protected] "\n"
44 [email protected] "("
45 [email protected] "self"
46 [email protected] ")"
47 [email protected] " "
48 [email protected] "struct"
49 [email protected] " "
50 [email protected]
51 [email protected] "S"
52 [email protected] ";"
53 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rs b/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rs
index faeefde94..a790a485f 100644
--- a/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0022_crate_visibility.rs
@@ -1,4 +1,3 @@
1pub(crate) struct S; 1pub(crate) struct S;
2pub(self) struct S; 2pub(self) struct S;
3pub(self) struct S; 3pub(super) struct S;
4pub(self) struct S;
diff --git a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
index d3219f0b2..c54e64e3f 100644
--- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
@@ -16,7 +16,8 @@ [email protected]
16 [email protected] "must_use" 16 [email protected] "must_use"
17 [email protected] "]" 17 [email protected] "]"
18 [email protected] " " 18 [email protected] " "
19 [email protected] "self" 19 [email protected]
20 [email protected] "self"
20 [email protected] ")" 21 [email protected] ")"
21 [email protected] " " 22 [email protected] " "
22 [email protected] 23 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rast b/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rast
new file mode 100644
index 000000000..3d855fc6b
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rast
@@ -0,0 +1,42 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected] "pub"
5 [email protected] "("
6 [email protected] "in"
7 [email protected] " "
8 [email protected]
9 [email protected]
10 [email protected]
11 [email protected]
12 [email protected] "super"
13 [email protected] "::"
14 [email protected]
15 [email protected]
16 [email protected] "A"
17 [email protected] ")"
18 [email protected] " "
19 [email protected] "struct"
20 [email protected] " "
21 [email protected]
22 [email protected] "S"
23 [email protected] ";"
24 [email protected] "\n"
25 [email protected]
26 [email protected]
27 [email protected] "pub"
28 [email protected] "("
29 [email protected] "in"
30 [email protected] " "
31 [email protected]
32 [email protected]
33 [email protected]
34 [email protected] "crate"
35 [email protected] ")"
36 [email protected] " "
37 [email protected] "struct"
38 [email protected] " "
39 [email protected]
40 [email protected] "S"
41 [email protected] ";"
42 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rs b/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rs
new file mode 100644
index 000000000..2856dbd84
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0160_crate_visibility_in.rs
@@ -0,0 +1,2 @@
1pub(in super::A) struct S;
2pub(in crate) struct S;
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast
new file mode 100644
index 000000000..dcd39535b
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast
@@ -0,0 +1,24 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected] "const"
6 [email protected] " "
7 [email protected]
8 [email protected]
9 [email protected]
10 [email protected]
11 [email protected] "Send"
12 [email protected] " "
13 [email protected] "for"
14 [email protected] " "
15 [email protected]
16 [email protected]
17 [email protected]
18 [email protected]
19 [email protected] "X"
20 [email protected] " "
21 [email protected]
22 [email protected] "{"
23 [email protected] "}"
24 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs
new file mode 100644
index 000000000..8d6886469
--- /dev/null
+++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs
@@ -0,0 +1 @@
impl const Send for X {}
diff --git a/crates/syntax/test_data/parser/ok/0007_extern_crate.rast b/crates/syntax/test_data/parser/ok/0007_extern_crate.rast
index 594c2f8f2..4babdba92 100644
--- a/crates/syntax/test_data/parser/ok/0007_extern_crate.rast
+++ b/crates/syntax/test_data/parser/ok/0007_extern_crate.rast
@@ -28,7 +28,8 @@ [email protected]
28 [email protected] " " 28 [email protected] " "
29 [email protected] "crate" 29 [email protected] "crate"
30 [email protected] " " 30 [email protected] " "
31 [email protected] "self" 31 [email protected]
32 [email protected] "self"
32 [email protected] " " 33 [email protected] " "
33 [email protected] 34 [email protected]
34 [email protected] "as" 35 [email protected] "as"
diff --git a/crates/syntax/test_data/parser/ok/0012_visibility.rast b/crates/syntax/test_data/parser/ok/0012_visibility.rast
index 83a93b5a9..c5dbfb702 100644
--- a/crates/syntax/test_data/parser/ok/0012_visibility.rast
+++ b/crates/syntax/test_data/parser/ok/0012_visibility.rast
@@ -32,7 +32,10 @@ [email protected]
32 [email protected] 32 [email protected]
33 [email protected] "pub" 33 [email protected] "pub"
34 [email protected] "(" 34 [email protected] "("
35 [email protected] "crate" 35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected] "crate"
36 [email protected] ")" 39 [email protected] ")"
37 [email protected] " " 40 [email protected] " "
38 [email protected] "fn" 41 [email protected] "fn"
@@ -51,7 +54,10 @@ [email protected]
51 [email protected] 54 [email protected]
52 [email protected] "pub" 55 [email protected] "pub"
53 [email protected] "(" 56 [email protected] "("
54 [email protected] "super" 57 [email protected]
58 [email protected]
59 [email protected]
60 [email protected] "super"
55 [email protected] ")" 61 [email protected] ")"
56 [email protected] " " 62 [email protected] " "
57 [email protected] "fn" 63 [email protected] "fn"
diff --git a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast
index 0ac56df6d..6afed5f05 100644
--- a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast
@@ -110,7 +110,8 @@ [email protected]
110 [email protected] "(" 110 [email protected] "("
111 [email protected] 111 [email protected]
112 [email protected] "&" 112 [email protected] "&"
113 [email protected] "self" 113 [email protected]
114 [email protected] "self"
114 [email protected] "," 115 [email protected] ","
115 [email protected] " " 116 [email protected] " "
116 [email protected] 117 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index 3fed11838..e10521d85 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -281,7 +281,8 @@ [email protected]
281 [email protected] "must_use" 281 [email protected] "must_use"
282 [email protected] "]" 282 [email protected] "]"
283 [email protected] " " 283 [email protected] " "
284 [email protected] "self" 284 [email protected]
285 [email protected] "self"
285 [email protected] ")" 286 [email protected] ")"
286 [email protected] " " 287 [email protected] " "
287 [email protected] 288 [email protected]
@@ -305,7 +306,8 @@ [email protected]
305 [email protected] "attr" 306 [email protected] "attr"
306 [email protected] "]" 307 [email protected] "]"
307 [email protected] " " 308 [email protected] " "
308 [email protected] "self" 309 [email protected]
310 [email protected] "self"
309 [email protected] ")" 311 [email protected] ")"
310 [email protected] " " 312 [email protected] " "
311 [email protected] 313 [email protected]
@@ -330,7 +332,8 @@ [email protected]
330 [email protected] "]" 332 [email protected] "]"
331 [email protected] " " 333 [email protected] " "
332 [email protected] "&" 334 [email protected] "&"
333 [email protected] "self" 335 [email protected]
336 [email protected] "self"
334 [email protected] ")" 337 [email protected] ")"
335 [email protected] " " 338 [email protected] " "
336 [email protected] 339 [email protected]
@@ -363,7 +366,8 @@ [email protected]
363 [email protected] "&" 366 [email protected] "&"
364 [email protected] "mut" 367 [email protected] "mut"
365 [email protected] " " 368 [email protected] " "
366 [email protected] "self" 369 [email protected]
370 [email protected] "self"
367 [email protected] ")" 371 [email protected] ")"
368 [email protected] " " 372 [email protected] " "
369 [email protected] 373 [email protected]
@@ -397,7 +401,8 @@ [email protected]
397 [email protected] 401 [email protected]
398 [email protected] "\'a" 402 [email protected] "\'a"
399 [email protected] " " 403 [email protected] " "
400 [email protected] "self" 404 [email protected]
405 [email protected] "self"
401 [email protected] ")" 406 [email protected] ")"
402 [email protected] " " 407 [email protected] " "
403 [email protected] 408 [email protected]
@@ -433,7 +438,8 @@ [email protected]
433 [email protected] " " 438 [email protected] " "
434 [email protected] "mut" 439 [email protected] "mut"
435 [email protected] " " 440 [email protected] " "
436 [email protected] "self" 441 [email protected]
442 [email protected] "self"
437 [email protected] ")" 443 [email protected] ")"
438 [email protected] " " 444 [email protected] " "
439 [email protected] 445 [email protected]
@@ -457,7 +463,8 @@ [email protected]
457 [email protected] "attr" 463 [email protected] "attr"
458 [email protected] "]" 464 [email protected] "]"
459 [email protected] " " 465 [email protected] " "
460 [email protected] "self" 466 [email protected]
467 [email protected] "self"
461 [email protected] ":" 468 [email protected] ":"
462 [email protected] " " 469 [email protected] " "
463 [email protected] 470 [email protected]
@@ -488,7 +495,8 @@ [email protected]
488 [email protected] "attr" 495 [email protected] "attr"
489 [email protected] "]" 496 [email protected] "]"
490 [email protected] " " 497 [email protected] " "
491 [email protected] "self" 498 [email protected]
499 [email protected] "self"
492 [email protected] ":" 500 [email protected] ":"
493 [email protected] " " 501 [email protected] " "
494 [email protected] 502 [email protected]
diff --git a/docs/dev/README.md b/docs/dev/README.md
index dd2bfc493..24197b332 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -251,6 +251,9 @@ RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more tha
251 251
252In particular, I have `export RA_PROFILE='*>10'` in my shell profile. 252In particular, I have `export RA_PROFILE='*>10'` in my shell profile.
253 253
254We also have a "counting" profiler which counts number of instances of popular structs.
255It is enabled by `RA_COUNT=1`.
256
254To measure time for from-scratch analysis, use something like this: 257To measure time for from-scratch analysis, use something like this:
255 258
256``` 259```
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 21330948b..7e6cd49e0 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -6,6 +6,9 @@ Our approach to "clean code" is two-fold:
6It is explicitly OK for a reviewer to flag only some nits in the PR, and then send a follow-up cleanup PR for things which are easier to explain by example, cc-ing the original author. 6It is explicitly OK for a reviewer to flag only some nits in the PR, and then send a follow-up cleanup PR for things which are easier to explain by example, cc-ing the original author.
7Sending small cleanup PRs (like renaming a single local variable) is encouraged. 7Sending small cleanup PRs (like renaming a single local variable) is encouraged.
8 8
9When reviewing pull requests prefer extending this document to leaving
10non-reusable comments on the pull request itself.
11
9# General 12# General
10 13
11## Scale of Changes 14## Scale of Changes
@@ -280,6 +283,9 @@ Prefer `Default` even it has to be implemented manually.
280 283
281**Rationale:** less typing in the common case, uniformity. 284**Rationale:** less typing in the common case, uniformity.
282 285
286Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength
287reduction.
288
283## Functions Over Objects 289## Functions Over Objects
284 290
285Avoid creating "doer" objects. 291Avoid creating "doer" objects.
@@ -372,6 +378,15 @@ This allows for exceptionally good performance, but leads to increased compile t
372Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot. 378Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot.
373Compile time **does not** obey this rule -- all code has to be compiled. 379Compile time **does not** obey this rule -- all code has to be compiled.
374 380
381## Appropriate String Types
382
383When interfacing with OS APIs, use `OsString`, even if the original source of
384data is utf-8 encoded. **Rationale:** cleanly delineates the boundary when the
385data goes into the OS-land.
386
387Use `AbsPathBuf` and `AbsPath` over `std::Path`. **Rationale:** rust-analyzer is
388a long-lived process which handles several projects at the same time. It is
389important not to leak cwd by accident.
375 390
376# Premature Pessimization 391# Premature Pessimization
377 392
@@ -418,12 +433,44 @@ fn frobnicate(s: &str) {
418**Rationale:** reveals the costs. 433**Rationale:** reveals the costs.
419It is also more efficient when the caller already owns the allocation. 434It is also more efficient when the caller already owns the allocation.
420 435
421## Collection types 436## Collection Types
422 437
423Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`. 438Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
424 439
425**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount. 440**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount.
426 441
442## Avoid Intermediate Collections
443
444When writing a recursive function to compute a sets of things, use an accumulator parameter instead of returning a fresh collection.
445Accumulator goes first in the list of arguments.
446
447```rust
448// GOOD
449pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
450 let mut res = FxHashSet::default();
451 go(&mut res, node);
452 res
453}
454fn go(acc: &mut FxHashSet<Node>, node: Node) {
455 acc.insert(node);
456 for n in node.neighbors() {
457 go(acc, n);
458 }
459}
460
461// BAD
462pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
463 let mut res = FxHashSet::default();
464 res.insert(node);
465 for n in node.neighbors() {
466 res.extend(reachable_nodes(n));
467 }
468 res
469}
470```
471
472**Rational:** re-use allocations, accumulator style is more concise for complex cases.
473
427# Style 474# Style
428 475
429## Order of Imports 476## Order of Imports
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 1bb9222d7..4e6b439fd 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -12,10 +12,10 @@ doctest = false
12[dependencies] 12[dependencies]
13anyhow = "1.0.26" 13anyhow = "1.0.26"
14flate2 = "1.0" 14flate2 = "1.0"
15pico-args = "0.3.1" 15pico-args = "0.4.0"
16proc-macro2 = "1.0.8" 16proc-macro2 = "1.0.8"
17quote = "1.0.2" 17quote = "1.0.2"
18ungrammar = "1.8" 18ungrammar = "1.9"
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 046d68f52..0fd1d13e6 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -104,6 +104,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
104 "MACRO_DEF", 104 "MACRO_DEF",
105 "PAREN_TYPE", 105 "PAREN_TYPE",
106 "TUPLE_TYPE", 106 "TUPLE_TYPE",
107 "MACRO_TYPE",
107 "NEVER_TYPE", 108 "NEVER_TYPE",
108 "PATH_TYPE", 109 "PATH_TYPE",
109 "PTR_TYPE", 110 "PTR_TYPE",
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 6e18a50a6..51f58180c 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -4,7 +4,7 @@ use std::{fmt, path::Path};
4 4
5use crate::{ 5use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE}, 6 codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE},
7 project_root, rust_files, Result, 7 project_root, rust_files_in, Result,
8}; 8};
9 9
10pub fn generate_assists_tests(mode: Mode) -> Result<()> { 10pub fn generate_assists_tests(mode: Mode) -> Result<()> {
@@ -32,7 +32,7 @@ struct Assist {
32impl Assist { 32impl Assist {
33 fn collect() -> Result<Vec<Assist>> { 33 fn collect() -> Result<Vec<Assist>> {
34 let mut res = Vec::new(); 34 let mut res = Vec::new();
35 for path in rust_files(&project_root().join("crates/assists/src/handlers")) { 35 for path in rust_files_in(&project_root().join("crates/assists/src/handlers")) {
36 collect_file(&mut res, path.as_path())?; 36 collect_file(&mut res, path.as_path())?;
37 } 37 }
38 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); 38 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
diff --git a/xtask/src/codegen/gen_diagnostic_docs.rs b/xtask/src/codegen/gen_diagnostic_docs.rs
index 00aaea5b7..7c14d4a07 100644
--- a/xtask/src/codegen/gen_diagnostic_docs.rs
+++ b/xtask/src/codegen/gen_diagnostic_docs.rs
@@ -27,7 +27,7 @@ struct Diagnostic {
27impl Diagnostic { 27impl Diagnostic {
28 fn collect() -> Result<Vec<Diagnostic>> { 28 fn collect() -> Result<Vec<Diagnostic>> {
29 let mut res = Vec::new(); 29 let mut res = Vec::new();
30 for path in rust_files(&project_root()) { 30 for path in rust_files() {
31 collect_file(&mut res, path)?; 31 collect_file(&mut res, path)?;
32 } 32 }
33 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); 33 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 065dd33f1..61081063b 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -26,7 +26,7 @@ struct Feature {
26impl Feature { 26impl Feature {
27 fn collect() -> Result<Vec<Feature>> { 27 fn collect() -> Result<Vec<Feature>> {
28 let mut res = Vec::new(); 28 let mut res = Vec::new();
29 for path in rust_files(&project_root()) { 29 for path in rust_files() {
30 collect_file(&mut res, path)?; 30 collect_file(&mut res, path)?;
31 } 31 }
32 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); 32 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 12962bcfa..202c74426 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -67,6 +67,7 @@ pub struct ServerOpt {
67pub enum Malloc { 67pub enum Malloc {
68 System, 68 System,
69 Mimalloc, 69 Mimalloc,
70 Jemalloc,
70} 71}
71 72
72impl InstallCmd { 73impl InstallCmd {
@@ -176,6 +177,7 @@ fn install_server(opts: ServerOpt) -> Result<()> {
176 let features = match opts.malloc { 177 let features = match opts.malloc {
177 Malloc::System => &[][..], 178 Malloc::System => &[][..],
178 Malloc::Mimalloc => &["--features", "mimalloc"], 179 Malloc::Mimalloc => &["--features", "mimalloc"],
180 Malloc::Jemalloc => &["--features", "jemalloc"],
179 }; 181 };
180 182
181 let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}"); 183 let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}");
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index babec2dbd..16b06b853 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -34,7 +34,11 @@ pub fn project_root() -> PathBuf {
34 .to_path_buf() 34 .to_path_buf()
35} 35}
36 36
37pub fn rust_files(path: &Path) -> impl Iterator<Item = PathBuf> { 37pub fn rust_files() -> impl Iterator<Item = PathBuf> {
38 rust_files_in(&project_root().join("crates"))
39}
40
41pub fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> {
38 let iter = WalkDir::new(path); 42 let iter = WalkDir::new(path);
39 return iter 43 return iter
40 .into_iter() 44 .into_iter()
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index dec48629c..5a99f4a76 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -10,6 +10,7 @@
10 10
11use std::env; 11use std::env;
12 12
13use anyhow::bail;
13use codegen::CodegenCmd; 14use codegen::CodegenCmd;
14use pico_args::Arguments; 15use pico_args::Arguments;
15use xshell::{cmd, cp, pushd}; 16use xshell::{cmd, cp, pushd};
@@ -49,7 +50,8 @@ FLAGS:
49 --client[=CLIENT] Install only VS Code plugin. 50 --client[=CLIENT] Install only VS Code plugin.
50 CLIENT is one of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss' 51 CLIENT is one of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'
51 --server Install only the language server 52 --server Install only the language server
52 --mimalloc Use mimalloc for server 53 --mimalloc Use mimalloc allocator for server
54 --jemalloc Use jemalloc allocator for server
53 -h, --help Prints help information 55 -h, --help Prints help information
54 " 56 "
55 ); 57 );
@@ -65,12 +67,17 @@ FLAGS:
65 return Ok(()); 67 return Ok(());
66 } 68 }
67 69
68 let malloc = 70 let malloc = if args.contains("--mimalloc") {
69 if args.contains("--mimalloc") { Malloc::Mimalloc } else { Malloc::System }; 71 Malloc::Mimalloc
72 } else if args.contains("--jemalloc") {
73 Malloc::Jemalloc
74 } else {
75 Malloc::System
76 };
70 77
71 let client_opt = args.opt_value_from_str("--client")?; 78 let client_opt = args.opt_value_from_str("--client")?;
72 79
73 args.finish()?; 80 finish_args(args)?;
74 81
75 InstallCmd { 82 InstallCmd {
76 client: if server { None } else { Some(client_opt.unwrap_or_default()) }, 83 client: if server { None } else { Some(client_opt.unwrap_or_default()) },
@@ -80,53 +87,53 @@ FLAGS:
80 } 87 }
81 "codegen" => { 88 "codegen" => {
82 let features = args.contains("--features"); 89 let features = args.contains("--features");
83 args.finish()?; 90 finish_args(args)?;
84 CodegenCmd { features }.run() 91 CodegenCmd { features }.run()
85 } 92 }
86 "format" => { 93 "format" => {
87 args.finish()?; 94 finish_args(args)?;
88 run_rustfmt(Mode::Overwrite) 95 run_rustfmt(Mode::Overwrite)
89 } 96 }
90 "install-pre-commit-hook" => { 97 "install-pre-commit-hook" => {
91 args.finish()?; 98 finish_args(args)?;
92 pre_commit::install_hook() 99 pre_commit::install_hook()
93 } 100 }
94 "lint" => { 101 "lint" => {
95 args.finish()?; 102 finish_args(args)?;
96 run_clippy() 103 run_clippy()
97 } 104 }
98 "fuzz-tests" => { 105 "fuzz-tests" => {
99 args.finish()?; 106 finish_args(args)?;
100 run_fuzzer() 107 run_fuzzer()
101 } 108 }
102 "pre-cache" => { 109 "pre-cache" => {
103 args.finish()?; 110 finish_args(args)?;
104 PreCacheCmd.run() 111 PreCacheCmd.run()
105 } 112 }
106 "release" => { 113 "release" => {
107 let dry_run = args.contains("--dry-run"); 114 let dry_run = args.contains("--dry-run");
108 args.finish()?; 115 finish_args(args)?;
109 ReleaseCmd { dry_run }.run() 116 ReleaseCmd { dry_run }.run()
110 } 117 }
111 "promote" => { 118 "promote" => {
112 let dry_run = args.contains("--dry-run"); 119 let dry_run = args.contains("--dry-run");
113 args.finish()?; 120 finish_args(args)?;
114 PromoteCmd { dry_run }.run() 121 PromoteCmd { dry_run }.run()
115 } 122 }
116 "dist" => { 123 "dist" => {
117 let nightly = args.contains("--nightly"); 124 let nightly = args.contains("--nightly");
118 let client_version: Option<String> = args.opt_value_from_str("--client")?; 125 let client_version: Option<String> = args.opt_value_from_str("--client")?;
119 args.finish()?; 126 finish_args(args)?;
120 DistCmd { nightly, client_version }.run() 127 DistCmd { nightly, client_version }.run()
121 } 128 }
122 "metrics" => { 129 "metrics" => {
123 let dry_run = args.contains("--dry-run"); 130 let dry_run = args.contains("--dry-run");
124 args.finish()?; 131 finish_args(args)?;
125 MetricsCmd { dry_run }.run() 132 MetricsCmd { dry_run }.run()
126 } 133 }
127 "bb" => { 134 "bb" => {
128 let suffix: String = args.free_from_str()?.unwrap(); 135 let suffix: String = args.free_from_str()?;
129 args.finish()?; 136 finish_args(args)?;
130 cmd!("cargo build --release").run()?; 137 cmd!("cargo build --release").run()?;
131 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?; 138 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?;
132 Ok(()) 139 Ok(())
@@ -155,3 +162,10 @@ SUBCOMMANDS:
155 } 162 }
156 } 163 }
157} 164}
165
166fn finish_args(args: Arguments) -> Result<()> {
167 if !args.finish().is_empty() {
168 bail!("Unused arguments.");
169 }
170 Ok(())
171}
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 6abad189a..9a6933b09 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -82,7 +82,7 @@ Please adjust docs/dev/lsp-extensions.md.
82#[test] 82#[test]
83fn rust_files_are_tidy() { 83fn rust_files_are_tidy() {
84 let mut tidy_docs = TidyDocs::default(); 84 let mut tidy_docs = TidyDocs::default();
85 for path in rust_files(&project_root().join("crates")) { 85 for path in rust_files() {
86 let text = read_file(&path).unwrap(); 86 let text = read_file(&path).unwrap();
87 check_todo(&path, &text); 87 check_todo(&path, &text);
88 check_dbg(&path, &text); 88 check_dbg(&path, &text);