aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml44
-rw-r--r--Cargo.lock252
-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/fn_param.rs4
-rw-r--r--crates/completion/src/completions/mod_.rs10
-rw-r--r--crates/completion/src/completions/record.rs8
-rw-r--r--crates/completion/src/completions/trait_impl.rs8
-rw-r--r--crates/completion/src/completions/unqualified_path.rs60
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/item.rs99
-rw-r--r--crates/completion/src/lib.rs11
-rw-r--r--crates/completion/src/render.rs191
-rw-r--r--crates/completion/src/render/const_.rs10
-rw-r--r--crates/completion/src/render/enum_variant.rs5
-rw-r--r--crates/completion/src/render/function.rs7
-rw-r--r--crates/completion/src/render/macro_.rs5
-rw-r--r--crates/completion/src/render/type_alias.rs10
-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.rs41
-rw-r--r--crates/hir/src/db.rs14
-rw-r--r--crates/hir/src/has_source.rs6
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/hir/src/semantics/source_to_def.rs3
-rw-r--r--crates/hir_def/src/attr.rs3
-rw-r--r--crates/hir_def/src/body.rs8
-rw-r--r--crates/hir_def/src/body/tests.rs41
-rw-r--r--crates/hir_def/src/child_by_source.rs2
-rw-r--r--crates/hir_def/src/db.rs17
-rw-r--r--crates/hir_def/src/find_path.rs32
-rw-r--r--crates/hir_def/src/import_map.rs70
-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/lib.rs12
-rw-r--r--crates/hir_def/src/nameres.rs150
-rw-r--r--crates/hir_def/src/nameres/collector.rs70
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs45
-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.rs66
-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.rs8
-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/infer/expr.rs5
-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.rs16
-rw-r--r--crates/hir_ty/src/tests/simple.rs24
-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.rs43
-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/file_structure.rs3
-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.rs23
-rw-r--r--crates/ide/src/references.rs72
-rw-r--r--crates/ide/src/references/rename.rs676
-rw-r--r--crates/ide/src/runnables.rs234
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/ide/src/syntax_highlighting.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs5
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs26
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/tags.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/lib.rs24
-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.rs3
-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.rs27
-rw-r--r--crates/rust-analyzer/src/reload.rs16
-rw-r--r--crates/rust-analyzer/src/to_proto.rs47
-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.md59
-rw-r--r--editors/code/src/main.ts1
-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
176 files changed, 3566 insertions, 1863 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ece1fd364..94b6aa7c4 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -94,6 +94,7 @@ jobs:
94 toolchain: stable 94 toolchain: stable
95 profile: minimal 95 profile: minimal
96 override: true 96 override: true
97 components: rust-src
97 98
98 - name: Install Nodejs 99 - name: Install Nodejs
99 uses: actions/setup-node@v1 100 uses: actions/setup-node@v1
@@ -108,16 +109,49 @@ jobs:
108 if: github.ref != 'refs/heads/release' 109 if: github.ref != 'refs/heads/release'
109 run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly 110 run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly
110 111
111 - name: Nightly analysis-stats check 112 - name: Run analysis-stats on rust-analyzer
112 if: github.ref != 'refs/heads/release'
113 run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats . 113 run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats .
114 114
115 - name: Run analysis-stats on rust std library
116 run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
117
115 - name: Upload artifacts 118 - name: Upload artifacts
116 uses: actions/upload-artifact@v1 119 uses: actions/upload-artifact@v1
117 with: 120 with:
118 name: dist-x86_64-unknown-linux-gnu 121 name: dist-x86_64-unknown-linux-gnu
119 path: ./dist 122 path: ./dist
120 123
124 dist-aarch64-unknown-linux-gnu:
125 name: dist (aarch64-unknown-linux-gnu)
126 runs-on: ubuntu-16.04
127 env:
128 RA_TARGET: aarch64-unknown-linux-gnu
129 CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
130
131 steps:
132 - name: Checkout repository
133 uses: actions/checkout@v2
134
135 - name: Install Rust toolchain
136 uses: actions-rs/toolchain@v1
137 with:
138 toolchain: stable
139 target: aarch64-unknown-linux-gnu
140 profile: minimal
141 override: true
142
143 - name: Install target toolchain
144 run: sudo apt-get install gcc-aarch64-linux-gnu
145
146 - name: Dist
147 run: cargo xtask dist
148
149 - name: Upload artifacts
150 uses: actions/upload-artifact@v1
151 with:
152 name: dist-aarch64-unknown-linux-gnu
153 path: ./dist
154
121 dist-x86_64-apple-darwin: 155 dist-x86_64-apple-darwin:
122 name: dist (x86_64-apple-darwin) 156 name: dist (x86_64-apple-darwin)
123 runs-on: macos-latest 157 runs-on: macos-latest
@@ -174,7 +208,7 @@ jobs:
174 publish: 208 publish:
175 name: publish 209 name: publish
176 runs-on: ubuntu-16.04 210 runs-on: ubuntu-16.04
177 needs: ['dist-x86_64-pc-windows-msvc', 'dist-aarch64-pc-windows-msvc', 'dist-x86_64-unknown-linux-gnu', 'dist-x86_64-apple-darwin', 'dist-aarch64-apple-darwin'] 211 needs: ['dist-x86_64-pc-windows-msvc', 'dist-aarch64-pc-windows-msvc', 'dist-x86_64-unknown-linux-gnu', 'dist-aarch64-unknown-linux-gnu', 'dist-x86_64-apple-darwin', 'dist-aarch64-apple-darwin']
178 steps: 212 steps:
179 - name: Install Nodejs 213 - name: Install Nodejs
180 uses: actions/setup-node@v1 214 uses: actions/setup-node@v1
@@ -207,6 +241,10 @@ jobs:
207 path: dist 241 path: dist
208 - uses: actions/download-artifact@v1 242 - uses: actions/download-artifact@v1
209 with: 243 with:
244 name: dist-aarch64-unknown-linux-gnu
245 path: dist
246 - uses: actions/download-artifact@v1
247 with:
210 name: dist-x86_64-pc-windows-msvc 248 name: dist-x86_64-pc-windows-msvc
211 path: dist 249 path: dist
212 - uses: actions/download-artifact@v1 250 - uses: actions/download-artifact@v1
diff --git a/Cargo.lock b/Cargo.lock
index dd1d599e3..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" 273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" 274checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
275
276[[package]]
277name = "countme"
278version = "2.0.0-pre.2"
279source = "registry+https://github.com/rust-lang/crates.io-index"
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"
@@ -512,9 +539,9 @@ dependencies = [
512 539
513[[package]] 540[[package]]
514name = "hermit-abi" 541name = "hermit-abi"
515version = "0.1.17" 542version = "0.1.18"
516source = "registry+https://github.com/rust-lang/crates.io-index" 543source = "registry+https://github.com/rust-lang/crates.io-index"
517checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 544checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
518dependencies = [ 545dependencies = [
519 "libc", 546 "libc",
520] 547]
@@ -702,9 +729,9 @@ dependencies = [
702 729
703[[package]] 730[[package]]
704name = "inotify-sys" 731name = "inotify-sys"
705version = "0.1.4" 732version = "0.1.5"
706source = "registry+https://github.com/rust-lang/crates.io-index" 733source = "registry+https://github.com/rust-lang/crates.io-index"
707checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" 734checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
708dependencies = [ 735dependencies = [
709 "libc", 736 "libc",
710] 737]
@@ -752,6 +779,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
752checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 779checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
753 780
754[[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]]
755name = "jod-thread" 814name = "jod-thread"
756version = "0.1.2" 815version = "0.1.2"
757source = "registry+https://github.com/rust-lang/crates.io-index" 816source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -785,15 +844,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
785 844
786[[package]] 845[[package]]
787name = "libc" 846name = "libc"
788version = "0.2.81" 847version = "0.2.82"
789source = "registry+https://github.com/rust-lang/crates.io-index" 848source = "registry+https://github.com/rust-lang/crates.io-index"
790checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 849checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
791 850
792[[package]] 851[[package]]
793name = "libloading" 852name = "libloading"
794version = "0.6.6" 853version = "0.6.7"
795source = "registry+https://github.com/rust-lang/crates.io-index" 854source = "registry+https://github.com/rust-lang/crates.io-index"
796checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc" 855checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
797dependencies = [ 856dependencies = [
798 "cfg-if 1.0.0", 857 "cfg-if 1.0.0",
799 "winapi 0.3.9", 858 "winapi 0.3.9",
@@ -819,9 +878,9 @@ dependencies = [
819 878
820[[package]] 879[[package]]
821name = "log" 880name = "log"
822version = "0.4.11" 881version = "0.4.13"
823source = "registry+https://github.com/rust-lang/crates.io-index" 882source = "registry+https://github.com/rust-lang/crates.io-index"
824checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 883checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
825dependencies = [ 884dependencies = [
826 "cfg-if 0.1.10", 885 "cfg-if 0.1.10",
827] 886]
@@ -904,15 +963,6 @@ dependencies = [
904 963
905[[package]] 964[[package]]
906name = "memoffset" 965name = "memoffset"
907version = "0.5.6"
908source = "registry+https://github.com/rust-lang/crates.io-index"
909checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
910dependencies = [
911 "autocfg",
912]
913
914[[package]]
915name = "memoffset"
916version = "0.6.1" 966version = "0.6.1"
917source = "registry+https://github.com/rust-lang/crates.io-index" 967source = "registry+https://github.com/rust-lang/crates.io-index"
918checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 968checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
@@ -1044,12 +1094,6 @@ dependencies = [
1044 1094
1045[[package]] 1095[[package]]
1046name = "object" 1096name = "object"
1047version = "0.22.0"
1048source = "registry+https://github.com/rust-lang/crates.io-index"
1049checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
1050
1051[[package]]
1052name = "object"
1053version = "0.23.0" 1097version = "0.23.0"
1054source = "registry+https://github.com/rust-lang/crates.io-index" 1098source = "registry+https://github.com/rust-lang/crates.io-index"
1055checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" 1099checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
@@ -1086,7 +1130,7 @@ dependencies = [
1086 "cfg-if 1.0.0", 1130 "cfg-if 1.0.0",
1087 "instant", 1131 "instant",
1088 "libc", 1132 "libc",
1089 "redox_syscall", 1133 "redox_syscall 0.1.57",
1090 "smallvec", 1134 "smallvec",
1091 "winapi 0.3.9", 1135 "winapi 0.3.9",
1092] 1136]
@@ -1099,6 +1143,25 @@ dependencies = [
1099] 1143]
1100 1144
1101[[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]]
1102name = "paths" 1165name = "paths"
1103version = "0.0.0" 1166version = "0.0.0"
1104 1167
@@ -1148,15 +1211,21 @@ dependencies = [
1148 1211
1149[[package]] 1212[[package]]
1150name = "pico-args" 1213name = "pico-args"
1151version = "0.3.4" 1214version = "0.4.0"
1152source = "registry+https://github.com/rust-lang/crates.io-index" 1215source = "registry+https://github.com/rust-lang/crates.io-index"
1153checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" 1216checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6"
1154 1217
1155[[package]] 1218[[package]]
1156name = "pin-project-lite" 1219name = "pin-project-lite"
1157version = "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"
1158source = "registry+https://github.com/rust-lang/crates.io-index" 1227source = "registry+https://github.com/rust-lang/crates.io-index"
1159checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 1228checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
1160 1229
1161[[package]] 1230[[package]]
1162name = "proc-macro2" 1231name = "proc-macro2"
@@ -1188,7 +1257,7 @@ dependencies = [
1188 "libloading", 1257 "libloading",
1189 "mbe", 1258 "mbe",
1190 "memmap", 1259 "memmap",
1191 "object 0.23.0", 1260 "object",
1192 "proc_macro_api", 1261 "proc_macro_api",
1193 "proc_macro_test", 1262 "proc_macro_test",
1194 "serde_derive", 1263 "serde_derive",
@@ -1206,6 +1275,8 @@ name = "profile"
1206version = "0.0.0" 1275version = "0.0.0"
1207dependencies = [ 1276dependencies = [
1208 "cfg-if 1.0.0", 1277 "cfg-if 1.0.0",
1278 "countme",
1279 "jemalloc-ctl",
1209 "la-arena", 1280 "la-arena",
1210 "libc", 1281 "libc",
1211 "once_cell", 1282 "once_cell",
@@ -1225,6 +1296,7 @@ dependencies = [
1225 "log", 1296 "log",
1226 "paths", 1297 "paths",
1227 "proc_macro_api", 1298 "proc_macro_api",
1299 "profile",
1228 "rustc-hash", 1300 "rustc-hash",
1229 "serde", 1301 "serde",
1230 "serde_json", 1302 "serde_json",
@@ -1293,10 +1365,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1293checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1365checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1294 1366
1295[[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]]
1296name = "regex" 1377name = "regex"
1297version = "1.4.2" 1378version = "1.4.3"
1298source = "registry+https://github.com/rust-lang/crates.io-index" 1379source = "registry+https://github.com/rust-lang/crates.io-index"
1299checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1380checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
1300dependencies = [ 1381dependencies = [
1301 "regex-syntax", 1382 "regex-syntax",
1302] 1383]
@@ -1313,20 +1394,21 @@ dependencies = [
1313 1394
1314[[package]] 1395[[package]]
1315name = "regex-syntax" 1396name = "regex-syntax"
1316version = "0.6.21" 1397version = "0.6.22"
1317source = "registry+https://github.com/rust-lang/crates.io-index" 1398source = "registry+https://github.com/rust-lang/crates.io-index"
1318checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1399checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
1319 1400
1320[[package]] 1401[[package]]
1321name = "rowan" 1402name = "rowan"
1322version = "0.10.3" 1403version = "0.12.1"
1323source = "registry+https://github.com/rust-lang/crates.io-index" 1404source = "registry+https://github.com/rust-lang/crates.io-index"
1324checksum = "d55d358c5fda3d5c4484f71a4808f5eeb39a0aff93bf3acebc680a6d15376f3c" 1405checksum = "24c2d78254049413f9d73495f883e7fa0b7a7d4b88468cd72a3bbbd0ad585cd1"
1325dependencies = [ 1406dependencies = [
1407 "countme",
1408 "hashbrown",
1409 "memoffset",
1326 "rustc-hash", 1410 "rustc-hash",
1327 "smol_str",
1328 "text-size", 1411 "text-size",
1329 "triomphe",
1330] 1412]
1331 1413
1332[[package]] 1414[[package]]
@@ -1346,6 +1428,7 @@ dependencies = [
1346 "ide", 1428 "ide",
1347 "ide_db", 1429 "ide_db",
1348 "itertools 0.10.0", 1430 "itertools 0.10.0",
1431 "jemallocator",
1349 "jod-thread", 1432 "jod-thread",
1350 "log", 1433 "log",
1351 "lsp-server", 1434 "lsp-server",
@@ -1381,9 +1464,9 @@ dependencies = [
1381 1464
1382[[package]] 1465[[package]]
1383name = "rustc-ap-rustc_lexer" 1466name = "rustc-ap-rustc_lexer"
1384version = "697.0.0" 1467version = "700.0.0"
1385source = "registry+https://github.com/rust-lang/crates.io-index" 1468source = "registry+https://github.com/rust-lang/crates.io-index"
1386checksum = "67adbe260a0a11910624d6d28c0304fcf7b063e666682111005c83b09f73429d" 1469checksum = "5ed36784376b69c941d7aa36e960a52ac712e2663960357121a4d9f2cc58e225"
1387dependencies = [ 1470dependencies = [
1388 "unicode-xid", 1471 "unicode-xid",
1389] 1472]
@@ -1477,18 +1560,18 @@ dependencies = [
1477 1560
1478[[package]] 1561[[package]]
1479name = "serde" 1562name = "serde"
1480version = "1.0.118" 1563version = "1.0.120"
1481source = "registry+https://github.com/rust-lang/crates.io-index" 1564source = "registry+https://github.com/rust-lang/crates.io-index"
1482checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1565checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
1483dependencies = [ 1566dependencies = [
1484 "serde_derive", 1567 "serde_derive",
1485] 1568]
1486 1569
1487[[package]] 1570[[package]]
1488name = "serde_derive" 1571name = "serde_derive"
1489version = "1.0.118" 1572version = "1.0.120"
1490source = "registry+https://github.com/rust-lang/crates.io-index" 1573source = "registry+https://github.com/rust-lang/crates.io-index"
1491checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1574checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
1492dependencies = [ 1575dependencies = [
1493 "proc-macro2", 1576 "proc-macro2",
1494 "quote", 1577 "quote",
@@ -1544,9 +1627,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1544 1627
1545[[package]] 1628[[package]]
1546name = "smallvec" 1629name = "smallvec"
1547version = "1.6.0" 1630version = "1.6.1"
1548source = "registry+https://github.com/rust-lang/crates.io-index" 1631source = "registry+https://github.com/rust-lang/crates.io-index"
1549checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" 1632checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
1550 1633
1551[[package]] 1634[[package]]
1552name = "smol_str" 1635name = "smol_str"
@@ -1572,12 +1655,6 @@ dependencies = [
1572] 1655]
1573 1656
1574[[package]] 1657[[package]]
1575name = "stable_deref_trait"
1576version = "1.2.0"
1577source = "registry+https://github.com/rust-lang/crates.io-index"
1578checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1579
1580[[package]]
1581name = "stdx" 1658name = "stdx"
1582version = "0.0.0" 1659version = "0.0.0"
1583dependencies = [ 1660dependencies = [
@@ -1586,9 +1663,9 @@ dependencies = [
1586 1663
1587[[package]] 1664[[package]]
1588name = "syn" 1665name = "syn"
1589version = "1.0.57" 1666version = "1.0.58"
1590source = "registry+https://github.com/rust-lang/crates.io-index" 1667source = "registry+https://github.com/rust-lang/crates.io-index"
1591checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 1668checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
1592dependencies = [ 1669dependencies = [
1593 "proc-macro2", 1670 "proc-macro2",
1594 "quote", 1671 "quote",
@@ -1665,9 +1742,9 @@ dependencies = [
1665 1742
1666[[package]] 1743[[package]]
1667name = "thread_local" 1744name = "thread_local"
1668version = "1.0.1" 1745version = "1.1.0"
1669source = "registry+https://github.com/rust-lang/crates.io-index" 1746source = "registry+https://github.com/rust-lang/crates.io-index"
1670checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1747checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
1671dependencies = [ 1748dependencies = [
1672 "lazy_static", 1749 "lazy_static",
1673] 1750]
@@ -1805,17 +1882,6 @@ dependencies = [
1805] 1882]
1806 1883
1807[[package]] 1884[[package]]
1808name = "triomphe"
1809version = "0.1.2"
1810source = "registry+https://github.com/rust-lang/crates.io-index"
1811checksum = "6e9d872053cf9e5a833d8c1dd772cdc38ab66a908129d6f73c049c986161d07c"
1812dependencies = [
1813 "memoffset 0.5.6",
1814 "serde",
1815 "stable_deref_trait",
1816]
1817
1818[[package]]
1819name = "tt" 1885name = "tt"
1820version = "0.0.0" 1886version = "0.0.0"
1821dependencies = [ 1887dependencies = [
@@ -1831,9 +1897,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1831 1897
1832[[package]] 1898[[package]]
1833name = "ungrammar" 1899name = "ungrammar"
1834version = "1.8.0" 1900version = "1.9.3"
1835source = "registry+https://github.com/rust-lang/crates.io-index" 1901source = "registry+https://github.com/rust-lang/crates.io-index"
1836checksum = "e33a2183403af89252547c4219a06a6cc8aef6302fee67e10e8431866af3ee72" 1902checksum = "f5901372c0f3a6a1a9d880aef134c8eaf5e54409343637508c0a344270b42d7b"
1837 1903
1838[[package]] 1904[[package]]
1839name = "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/fn_param.rs b/crates/completion/src/completions/fn_param.rs
index 5505c3559..38e33a93e 100644
--- a/crates/completion/src/completions/fn_param.rs
+++ b/crates/completion/src/completions/fn_param.rs
@@ -6,7 +6,7 @@ use syntax::{
6 match_ast, AstNode, 6 match_ast, AstNode,
7}; 7};
8 8
9use crate::{CompletionContext, CompletionItem, CompletionKind, Completions}; 9use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
10 10
11/// Complete repeated parameters, both name and type. For example, if all 11/// Complete repeated parameters, both name and type. For example, if all
12/// functions in a file have a `spam: &mut Spam` parameter, a completion with 12/// functions in a file have a `spam: &mut Spam` parameter, a completion with
@@ -58,7 +58,7 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
58 }) 58 })
59 .for_each(|(label, lookup)| { 59 .for_each(|(label, lookup)| {
60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 60 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
61 .kind(crate::CompletionItemKind::Binding) 61 .kind(CompletionItemKind::Binding)
62 .lookup_by(lookup) 62 .lookup_by(lookup)
63 .add_to(acc) 63 .add_to(acc)
64 }); 64 });
diff --git a/crates/completion/src/completions/mod_.rs b/crates/completion/src/completions/mod_.rs
index 00e951ca9..352fc7c77 100644
--- a/crates/completion/src/completions/mod_.rs
+++ b/crates/completion/src/completions/mod_.rs
@@ -3,11 +3,13 @@
3use std::iter; 3use std::iter;
4 4
5use hir::{Module, ModuleSource}; 5use hir::{Module, ModuleSource};
6use ide_db::base_db::{SourceDatabaseExt, VfsPath}; 6use ide_db::{
7use ide_db::RootDatabase; 7 base_db::{SourceDatabaseExt, VfsPath},
8 RootDatabase, SymbolKind,
9};
8use rustc_hash::FxHashSet; 10use rustc_hash::FxHashSet;
9 11
10use crate::{CompletionItem, CompletionItemKind}; 12use crate::CompletionItem;
11 13
12use crate::{context::CompletionContext, item::CompletionKind, Completions}; 14use crate::{context::CompletionContext, item::CompletionKind, Completions};
13 15
@@ -79,7 +81,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
79 label.push(';'); 81 label.push(';');
80 } 82 }
81 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
82 .kind(CompletionItemKind::Module) 84 .kind(SymbolKind::Module)
83 .add_to(acc) 85 .add_to(acc)
84 }); 86 });
85 87
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index bb6354ded..0a7927eb8 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -1,10 +1,8 @@
1//! Complete fields in record literals and patterns. 1//! Complete fields in record literals and patterns.
2use ide_db::helpers::FamousDefs; 2use ide_db::{helpers::FamousDefs, SymbolKind};
3use syntax::ast::Expr; 3use syntax::ast::Expr;
4 4
5use crate::{ 5use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions};
6 item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
7};
8 6
9pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 7pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
10 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { 8 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
@@ -31,7 +29,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
31 "..Default::default()", 29 "..Default::default()",
32 ) 30 )
33 .insert_text(completion_text) 31 .insert_text(completion_text)
34 .kind(CompletionItemKind::Field) 32 .kind(SymbolKind::Field)
35 .build(), 33 .build(),
36 ); 34 );
37 } 35 }
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index 135ae49dc..f258ad9c3 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -32,7 +32,7 @@
32//! ``` 32//! ```
33 33
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::traits::get_missing_assoc_items; 35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit, Impl},
38 display::function_declaration, 38 display::function_declaration,
@@ -152,7 +152,7 @@ fn add_function_impl(
152 let completion_kind = if func.self_param(ctx.db).is_some() { 152 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 153 CompletionItemKind::Method
154 } else { 154 } else {
155 CompletionItemKind::Function 155 CompletionItemKind::SymbolKind(SymbolKind::Function)
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158 158
@@ -188,7 +188,7 @@ fn add_type_alias_impl(
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
189 .text_edit(TextEdit::replace(range, snippet)) 189 .text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 190 .lookup_by(alias_name)
191 .kind(CompletionItemKind::TypeAlias) 191 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 192 .set_documentation(type_alias.docs(ctx.db))
193 .add_to(acc); 193 .add_to(acc);
194} 194}
@@ -211,7 +211,7 @@ fn add_const_impl(
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
212 .text_edit(TextEdit::replace(range, snippet)) 212 .text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(CompletionItemKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db))
216 .add_to(acc); 216 .add_to(acc);
217 } 217 }
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index ac5596ca4..a289efc34 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
@@ -161,8 +165,8 @@ fn quux(x: i32) {
161} 165}
162"#, 166"#,
163 expect![[r#" 167 expect![[r#"
164 bn y i32 168 lc y i32
165 bn x i32 169 lc x i32
166 fn quux(…) fn quux(x: i32) 170 fn quux(…) fn quux(x: i32)
167 "#]], 171 "#]],
168 ); 172 );
@@ -183,8 +187,8 @@ fn quux() {
183} 187}
184"#, 188"#,
185 expect![[r#" 189 expect![[r#"
186 bn b i32 190 lc b i32
187 bn a 191 lc a
188 fn quux() fn quux() 192 fn quux() fn quux()
189 "#]], 193 "#]],
190 ); 194 );
@@ -199,7 +203,7 @@ fn quux() {
199} 203}
200"#, 204"#,
201 expect![[r#" 205 expect![[r#"
202 bn x 206 lc x
203 fn quux() fn quux() 207 fn quux() fn quux()
204 "#]], 208 "#]],
205 ); 209 );
@@ -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 cp 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]
@@ -241,7 +263,7 @@ fn main() {
241 check( 263 check(
242 r#"struct S<T> { x: $0}"#, 264 r#"struct S<T> { x: $0}"#,
243 expect![[r#" 265 expect![[r#"
244 tp Self 266 sp Self
245 tp T 267 tp T
246 st S<…> 268 st S<…>
247 "#]], 269 "#]],
@@ -253,7 +275,7 @@ fn main() {
253 check( 275 check(
254 r#"enum X { Y($0) }"#, 276 r#"enum X { Y($0) }"#,
255 expect![[r#" 277 expect![[r#"
256 tp Self 278 sp Self
257 en X 279 en X
258 "#]], 280 "#]],
259 ); 281 );
@@ -356,8 +378,8 @@ fn foo() {
356"#, 378"#,
357 // FIXME: should be only one bar here 379 // FIXME: should be only one bar here
358 expect![[r#" 380 expect![[r#"
359 bn bar i32 381 lc bar i32
360 bn bar i32 382 lc bar i32
361 fn foo() fn foo() 383 fn foo() fn foo()
362 "#]], 384 "#]],
363 ); 385 );
@@ -368,8 +390,8 @@ fn foo() {
368 check( 390 check(
369 r#"impl S { fn foo(&self) { $0 } }"#, 391 r#"impl S { fn foo(&self) { $0 } }"#,
370 expect![[r#" 392 expect![[r#"
371 bn self &{unknown} 393 lc self &{unknown}
372 tp Self 394 sp Self
373 "#]], 395 "#]],
374 ); 396 );
375 } 397 }
@@ -553,8 +575,8 @@ fn quux(x: i32) {
553} 575}
554"#, 576"#,
555 expect![[r#" 577 expect![[r#"
556 bn y i32 578 lc y i32
557 bn x i32 579 lc x i32
558 fn quux(…) fn quux(x: i32) 580 fn quux(…) fn quux(x: i32)
559 ma m!(…) macro_rules! m 581 ma m!(…) macro_rules! m
560 "#]], 582 "#]],
@@ -572,8 +594,8 @@ fn quux(x: i32) {
572} 594}
573", 595",
574 expect![[r#" 596 expect![[r#"
575 bn y i32 597 lc y i32
576 bn x i32 598 lc x i32
577 fn quux(…) fn quux(x: i32) 599 fn quux(…) fn quux(x: i32)
578 ma m!(…) macro_rules! m 600 ma m!(…) macro_rules! m
579 "#]], 601 "#]],
@@ -591,8 +613,8 @@ fn quux(x: i32) {
591} 613}
592"#, 614"#,
593 expect![[r#" 615 expect![[r#"
594 bn y i32 616 lc y i32
595 bn x i32 617 lc x i32
596 fn quux(…) fn quux(x: i32) 618 fn quux(…) fn quux(x: i32)
597 ma m!(…) macro_rules! m 619 ma m!(…) macro_rules! m
598 "#]], 620 "#]],
@@ -728,7 +750,7 @@ struct MyStruct {}
728impl My$0 750impl My$0
729"#, 751"#,
730 expect![[r#" 752 expect![[r#"
731 tp Self 753 sp Self
732 tt MyTrait 754 tt MyTrait
733 st MyStruct 755 st MyStruct
734 "#]], 756 "#]],
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..d2e6a6aeb 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -3,11 +3,14 @@
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::helpers::{ 6use ide_db::{
7 insert_use::{self, ImportScope, MergeBehavior}, 7 helpers::{
8 mod_path_to_ast, SnippetCap, 8 insert_use::{self, ImportScope, MergeBehavior},
9 mod_path_to_ast, SnippetCap,
10 },
11 SymbolKind,
9}; 12};
10use stdx::assert_never; 13use stdx::{assert_never, impl_from};
11use syntax::{algo, TextRange}; 14use syntax::{algo, TextRange};
12use text_edit::TextEdit; 15use text_edit::TextEdit;
13 16
@@ -117,49 +120,50 @@ pub enum CompletionScore {
117 120
118#[derive(Debug, Clone, Copy, PartialEq, Eq)] 121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum CompletionItemKind { 122pub enum CompletionItemKind {
120 Snippet, 123 SymbolKind(SymbolKind),
121 Keyword, 124 Attribute,
122 Module,
123 Function,
124 BuiltinType,
125 Struct,
126 Enum,
127 EnumVariant,
128 Binding, 125 Binding,
129 Field, 126 BuiltinType,
130 Static, 127 Keyword,
131 Const,
132 Trait,
133 TypeAlias,
134 Method, 128 Method,
135 TypeParam, 129 Snippet,
136 Macro,
137 Attribute,
138 UnresolvedReference, 130 UnresolvedReference,
139} 131}
140 132
133impl_from!(SymbolKind for CompletionItemKind);
134
141impl CompletionItemKind { 135impl CompletionItemKind {
142 #[cfg(test)] 136 #[cfg(test)]
143 pub(crate) fn tag(&self) -> &'static str { 137 pub(crate) fn tag(&self) -> &'static str {
144 match self { 138 match self {
139 CompletionItemKind::SymbolKind(kind) => match kind {
140 SymbolKind::Const => "ct",
141 SymbolKind::ConstParam => "cp",
142 SymbolKind::Enum => "en",
143 SymbolKind::Field => "fd",
144 SymbolKind::Function => "fn",
145 SymbolKind::Impl => "im",
146 SymbolKind::Label => "lb",
147 SymbolKind::LifetimeParam => "lt",
148 SymbolKind::Local => "lc",
149 SymbolKind::Macro => "ma",
150 SymbolKind::Module => "md",
151 SymbolKind::SelfParam => "sp",
152 SymbolKind::Static => "sc",
153 SymbolKind::Struct => "st",
154 SymbolKind::Trait => "tt",
155 SymbolKind::TypeAlias => "ta",
156 SymbolKind::TypeParam => "tp",
157 SymbolKind::Union => "un",
158 SymbolKind::ValueParam => "vp",
159 SymbolKind::Variant => "ev",
160 },
145 CompletionItemKind::Attribute => "at", 161 CompletionItemKind::Attribute => "at",
146 CompletionItemKind::Binding => "bn", 162 CompletionItemKind::Binding => "bn",
147 CompletionItemKind::BuiltinType => "bt", 163 CompletionItemKind::BuiltinType => "bt",
148 CompletionItemKind::Const => "ct",
149 CompletionItemKind::Enum => "en",
150 CompletionItemKind::EnumVariant => "ev",
151 CompletionItemKind::Field => "fd",
152 CompletionItemKind::Function => "fn",
153 CompletionItemKind::Keyword => "kw", 164 CompletionItemKind::Keyword => "kw",
154 CompletionItemKind::Macro => "ma",
155 CompletionItemKind::Method => "me", 165 CompletionItemKind::Method => "me",
156 CompletionItemKind::Module => "md",
157 CompletionItemKind::Snippet => "sn", 166 CompletionItemKind::Snippet => "sn",
158 CompletionItemKind::Static => "sc",
159 CompletionItemKind::Struct => "st",
160 CompletionItemKind::Trait => "tt",
161 CompletionItemKind::TypeAlias => "ta",
162 CompletionItemKind::TypeParam => "tp",
163 CompletionItemKind::UnresolvedReference => "??", 167 CompletionItemKind::UnresolvedReference => "??",
164 } 168 }
165 } 169 }
@@ -270,6 +274,7 @@ impl CompletionItem {
270pub struct ImportEdit { 274pub struct ImportEdit {
271 pub import_path: ModPath, 275 pub import_path: ModPath,
272 pub import_scope: ImportScope, 276 pub import_scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
273} 278}
274 279
275impl ImportEdit { 280impl ImportEdit {
@@ -321,17 +326,19 @@ impl Builder {
321 let mut insert_text = self.insert_text; 326 let mut insert_text = self.insert_text;
322 327
323 if let Some(import_to_add) = self.import_to_add.as_ref() { 328 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(); 329 if import_to_add.import_for_trait_assoc_item {
325 let _ = import_path_without_last_segment.segments.pop(); 330 lookup = lookup.or_else(|| Some(label.clone()));
326 331 insert_text = insert_text.or_else(|| Some(label.clone()));
327 if !import_path_without_last_segment.segments.is_empty() { 332 label = format!("{} ({})", label, import_to_add.import_path);
328 if lookup.is_none() { 333 } else {
329 lookup = Some(label.clone()); 334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
330 } 335 let _ = import_path_without_last_segment.segments.pop();
331 if insert_text.is_none() { 336
332 insert_text = Some(label.clone()); 337 if !import_path_without_last_segment.segments.is_empty() {
338 lookup = lookup.or_else(|| Some(label.clone()));
339 insert_text = insert_text.or_else(|| Some(label.clone()));
340 label = format!("{}::{}", import_path_without_last_segment, label);
333 } 341 }
334 label = format!("{}::{}", import_path_without_last_segment, label);
335 } 342 }
336 } 343 }
337 344
@@ -379,8 +386,8 @@ impl Builder {
379 self.insert_text_format = InsertTextFormat::Snippet; 386 self.insert_text_format = InsertTextFormat::Snippet;
380 self.insert_text(snippet) 387 self.insert_text(snippet)
381 } 388 }
382 pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder { 389 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder {
383 self.kind = Some(kind); 390 self.kind = Some(kind.into());
384 self 391 self
385 } 392 }
386 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 393 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
@@ -398,7 +405,9 @@ impl Builder {
398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 405 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
399 self.detail = detail.map(Into::into); 406 self.detail = detail.map(Into::into);
400 if let Some(detail) = &self.detail { 407 if let Some(detail) = &self.detail {
401 assert_never!(detail.contains('\n'), "multiline detail: {}", detail); 408 if assert_never!(detail.contains('\n'), "multiline detail: {}", detail) {
409 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
410 }
402 } 411 }
403 self 412 self
404 } 413 }
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..fa594b5e5 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -10,8 +10,10 @@ 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::{
14use ide_db::{helpers::SnippetCap, RootDatabase}; 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind};
15use syntax::TextRange; 17use syntax::TextRange;
16use test_utils::mark; 18use test_utils::mark;
17 19
@@ -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> {
@@ -127,7 +146,7 @@ impl<'a> Render<'a> {
127 self.ctx.source_range(), 146 self.ctx.source_range(),
128 name.to_string(), 147 name.to_string(),
129 ) 148 )
130 .kind(CompletionItemKind::Field) 149 .kind(SymbolKind::Field)
131 .detail(ty.display(self.ctx.db()).to_string()) 150 .detail(ty.display(self.ctx.db()).to_string())
132 .set_documentation(field.docs(self.ctx.db())) 151 .set_documentation(field.docs(self.ctx.db()))
133 .set_deprecated(is_deprecated); 152 .set_deprecated(is_deprecated);
@@ -141,7 +160,7 @@ impl<'a> Render<'a> {
141 160
142 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 161 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
143 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 162 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string())
144 .kind(CompletionItemKind::Field) 163 .kind(SymbolKind::Field)
145 .detail(ty.display(self.ctx.db()).to_string()) 164 .detail(ty.display(self.ctx.db()).to_string())
146 .build() 165 .build()
147 } 166 }
@@ -168,7 +187,7 @@ impl<'a> Render<'a> {
168 if self.ctx.completion.is_pat_binding_or_const 187 if self.ctx.completion.is_pat_binding_or_const
169 | self.ctx.completion.is_irrefutable_pat_binding => 188 | self.ctx.completion.is_irrefutable_pat_binding =>
170 { 189 {
171 CompletionItemKind::EnumVariant 190 CompletionItemKind::SymbolKind(SymbolKind::Variant)
172 } 191 }
173 ScopeDef::ModuleDef(Variant(var)) => { 192 ScopeDef::ModuleDef(Variant(var)) => {
174 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); 193 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
@@ -179,20 +198,29 @@ impl<'a> Render<'a> {
179 return item; 198 return item;
180 } 199 }
181 200
182 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, 201 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
183 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, 202 ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
184 // FIXME: add CompletionItemKind::Union 203 hir::Adt::Struct(_) => SymbolKind::Struct,
185 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, 204 // FIXME: add CompletionItemKind::Union
186 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, 205 hir::Adt::Union(_) => SymbolKind::Struct,
187 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, 206 hir::Adt::Enum(_) => SymbolKind::Enum,
188 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, 207 }),
189 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, 208 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
190 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, 209 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
210 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
211 ScopeDef::ModuleDef(TypeAlias(..)) => {
212 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
213 }
191 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, 214 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
192 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, 215 ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
193 ScopeDef::Local(..) => CompletionItemKind::Binding, 216 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
194 // (does this need its own kind?) 217 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
195 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, 218 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
219 }),
220 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
221 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
222 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
223 }
196 ScopeDef::Unknown => { 224 ScopeDef::Unknown => {
197 let item = CompletionItem::new( 225 let item = CompletionItem::new(
198 CompletionKind::Reference, 226 CompletionKind::Reference,
@@ -206,8 +234,6 @@ impl<'a> Render<'a> {
206 } 234 }
207 }; 235 };
208 236
209 let docs = self.docs(resolution);
210
211 let mut item = 237 let mut item =
212 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); 238 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
213 if let ScopeDef::Local(local) = resolution { 239 if let ScopeDef::Local(local) = resolution {
@@ -253,13 +279,14 @@ impl<'a> Render<'a> {
253 } 279 }
254 } 280 }
255 281
256 let item = item 282 Some(
257 .kind(kind) 283 item.kind(kind)
258 .add_import(import_to_add) 284 .add_import(import_to_add)
259 .set_documentation(docs) 285 .set_ref_match(ref_match)
260 .set_ref_match(ref_match) 286 .set_documentation(self.docs(resolution))
261 .build(); 287 .set_deprecated(self.is_deprecated(resolution))
262 Some(item) 288 .build(),
289 )
263 } 290 }
264 291
265 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 292 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -275,6 +302,16 @@ impl<'a> Render<'a> {
275 _ => None, 302 _ => None,
276 } 303 }
277 } 304 }
305
306 fn is_deprecated(&self, resolution: &ScopeDef) -> bool {
307 match resolution {
308 ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it),
309 ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it),
310 ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it),
311 ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it),
312 _ => false,
313 }
314 }
278} 315}
279 316
280fn compute_score_from_active( 317fn compute_score_from_active(
@@ -372,7 +409,9 @@ fn main() { Foo::Fo$0 }
372 source_range: 54..56, 409 source_range: 54..56,
373 delete: 54..56, 410 delete: 54..56,
374 insert: "Foo", 411 insert: "Foo",
375 kind: EnumVariant, 412 kind: SymbolKind(
413 Variant,
414 ),
376 detail: "{ x: i32, y: i32 }", 415 detail: "{ x: i32, y: i32 }",
377 }, 416 },
378 ] 417 ]
@@ -395,7 +434,9 @@ fn main() { Foo::Fo$0 }
395 source_range: 46..48, 434 source_range: 46..48,
396 delete: 46..48, 435 delete: 46..48,
397 insert: "Foo($0)", 436 insert: "Foo($0)",
398 kind: EnumVariant, 437 kind: SymbolKind(
438 Variant,
439 ),
399 lookup: "Foo", 440 lookup: "Foo",
400 detail: "(i32, i32)", 441 detail: "(i32, i32)",
401 trigger_call_info: true, 442 trigger_call_info: true,
@@ -420,7 +461,9 @@ fn main() { Foo::Fo$0 }
420 source_range: 35..37, 461 source_range: 35..37,
421 delete: 35..37, 462 delete: 35..37,
422 insert: "Foo", 463 insert: "Foo",
423 kind: EnumVariant, 464 kind: SymbolKind(
465 Variant,
466 ),
424 detail: "()", 467 detail: "()",
425 }, 468 },
426 ] 469 ]
@@ -444,7 +487,9 @@ fn main() { let _: m::Spam = S$0 }
444 source_range: 75..76, 487 source_range: 75..76,
445 delete: 75..76, 488 delete: 75..76,
446 insert: "Spam::Bar($0)", 489 insert: "Spam::Bar($0)",
447 kind: EnumVariant, 490 kind: SymbolKind(
491 Variant,
492 ),
448 lookup: "Spam::Bar", 493 lookup: "Spam::Bar",
449 detail: "(i32)", 494 detail: "(i32)",
450 trigger_call_info: true, 495 trigger_call_info: true,
@@ -454,14 +499,18 @@ fn main() { let _: m::Spam = S$0 }
454 source_range: 75..76, 499 source_range: 75..76,
455 delete: 75..76, 500 delete: 75..76,
456 insert: "m", 501 insert: "m",
457 kind: Module, 502 kind: SymbolKind(
503 Module,
504 ),
458 }, 505 },
459 CompletionItem { 506 CompletionItem {
460 label: "m::Spam::Foo", 507 label: "m::Spam::Foo",
461 source_range: 75..76, 508 source_range: 75..76,
462 delete: 75..76, 509 delete: 75..76,
463 insert: "m::Spam::Foo", 510 insert: "m::Spam::Foo",
464 kind: EnumVariant, 511 kind: SymbolKind(
512 Variant,
513 ),
465 lookup: "Spam::Foo", 514 lookup: "Spam::Foo",
466 detail: "()", 515 detail: "()",
467 }, 516 },
@@ -470,7 +519,9 @@ fn main() { let _: m::Spam = S$0 }
470 source_range: 75..76, 519 source_range: 75..76,
471 delete: 75..76, 520 delete: 75..76,
472 insert: "main()$0", 521 insert: "main()$0",
473 kind: Function, 522 kind: SymbolKind(
523 Function,
524 ),
474 lookup: "main", 525 lookup: "main",
475 detail: "fn main()", 526 detail: "fn main()",
476 }, 527 },
@@ -485,7 +536,7 @@ fn main() { let _: m::Spam = S$0 }
485 r#" 536 r#"
486#[deprecated] 537#[deprecated]
487fn something_deprecated() {} 538fn something_deprecated() {}
488#[deprecated(since = "1.0.0")] 539#[rustc_deprecated(since = "1.0.0")]
489fn something_else_deprecated() {} 540fn something_else_deprecated() {}
490 541
491fn main() { som$0 } 542fn main() { som$0 }
@@ -494,29 +545,35 @@ fn main() { som$0 }
494 [ 545 [
495 CompletionItem { 546 CompletionItem {
496 label: "main()", 547 label: "main()",
497 source_range: 121..124, 548 source_range: 127..130,
498 delete: 121..124, 549 delete: 127..130,
499 insert: "main()$0", 550 insert: "main()$0",
500 kind: Function, 551 kind: SymbolKind(
552 Function,
553 ),
501 lookup: "main", 554 lookup: "main",
502 detail: "fn main()", 555 detail: "fn main()",
503 }, 556 },
504 CompletionItem { 557 CompletionItem {
505 label: "something_deprecated()", 558 label: "something_deprecated()",
506 source_range: 121..124, 559 source_range: 127..130,
507 delete: 121..124, 560 delete: 127..130,
508 insert: "something_deprecated()$0", 561 insert: "something_deprecated()$0",
509 kind: Function, 562 kind: SymbolKind(
563 Function,
564 ),
510 lookup: "something_deprecated", 565 lookup: "something_deprecated",
511 detail: "fn something_deprecated()", 566 detail: "fn something_deprecated()",
512 deprecated: true, 567 deprecated: true,
513 }, 568 },
514 CompletionItem { 569 CompletionItem {
515 label: "something_else_deprecated()", 570 label: "something_else_deprecated()",
516 source_range: 121..124, 571 source_range: 127..130,
517 delete: 121..124, 572 delete: 127..130,
518 insert: "something_else_deprecated()$0", 573 insert: "something_else_deprecated()$0",
519 kind: Function, 574 kind: SymbolKind(
575 Function,
576 ),
520 lookup: "something_else_deprecated", 577 lookup: "something_else_deprecated",
521 detail: "fn something_else_deprecated()", 578 detail: "fn something_else_deprecated()",
522 deprecated: true, 579 deprecated: true,
@@ -537,7 +594,9 @@ fn foo() { A { the$0 } }
537 source_range: 57..60, 594 source_range: 57..60,
538 delete: 57..60, 595 delete: 57..60,
539 insert: "the_field", 596 insert: "the_field",
540 kind: Field, 597 kind: SymbolKind(
598 Field,
599 ),
541 detail: "u32", 600 detail: "u32",
542 deprecated: true, 601 deprecated: true,
543 }, 602 },
@@ -577,7 +636,9 @@ impl S {
577 source_range: 94..94, 636 source_range: 94..94,
578 delete: 94..94, 637 delete: 94..94,
579 insert: "foo", 638 insert: "foo",
580 kind: Field, 639 kind: SymbolKind(
640 Field,
641 ),
581 detail: "{unknown}", 642 detail: "{unknown}",
582 documentation: Documentation( 643 documentation: Documentation(
583 "Field docs", 644 "Field docs",
@@ -608,7 +669,9 @@ use self::E::*;
608 source_range: 10..12, 669 source_range: 10..12,
609 delete: 10..12, 670 delete: 10..12,
610 insert: "E", 671 insert: "E",
611 kind: Enum, 672 kind: SymbolKind(
673 Enum,
674 ),
612 documentation: Documentation( 675 documentation: Documentation(
613 "enum docs", 676 "enum docs",
614 ), 677 ),
@@ -618,7 +681,9 @@ use self::E::*;
618 source_range: 10..12, 681 source_range: 10..12,
619 delete: 10..12, 682 delete: 10..12,
620 insert: "V", 683 insert: "V",
621 kind: EnumVariant, 684 kind: SymbolKind(
685 Variant,
686 ),
622 detail: "()", 687 detail: "()",
623 documentation: Documentation( 688 documentation: Documentation(
624 "variant docs", 689 "variant docs",
@@ -629,7 +694,9 @@ use self::E::*;
629 source_range: 10..12, 694 source_range: 10..12,
630 delete: 10..12, 695 delete: 10..12,
631 insert: "my", 696 insert: "my",
632 kind: Module, 697 kind: SymbolKind(
698 Module,
699 ),
633 documentation: Documentation( 700 documentation: Documentation(
634 "mod docs", 701 "mod docs",
635 ), 702 ),
@@ -855,7 +922,7 @@ struct WorldSnapshot { _f: () };
855fn go(world: &WorldSnapshot) { go(w$0) } 922fn go(world: &WorldSnapshot) { go(w$0) }
856"#, 923"#,
857 expect![[r#" 924 expect![[r#"
858 bn world [type+name] 925 lc world [type+name]
859 st WorldSnapshot [] 926 st WorldSnapshot []
860 fn go(…) [] 927 fn go(…) []
861 "#]], 928 "#]],
@@ -872,7 +939,7 @@ fn f(foo: &Foo) { f(foo, w$0) }
872 expect![[r#" 939 expect![[r#"
873 st Foo [] 940 st Foo []
874 fn f(…) [] 941 fn f(…) []
875 bn foo [] 942 lc foo []
876 "#]], 943 "#]],
877 ); 944 );
878 } 945 }
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index ce924f309..5010b642a 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -1,13 +1,14 @@
1//! Renderer for `const` fields. 1//! Renderer for `const` fields.
2 2
3use hir::HasSource; 3use hir::HasSource;
4use ide_db::SymbolKind;
4use syntax::{ 5use syntax::{
5 ast::{Const, NameOwner}, 6 ast::{Const, NameOwner},
6 display::const_label, 7 display::const_label,
7}; 8};
8 9
9use crate::{ 10use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind}, 11 item::{CompletionItem, CompletionKind},
11 render::RenderContext, 12 render::RenderContext,
12}; 13};
13 14
@@ -36,9 +37,12 @@ impl<'a> ConstRender<'a> {
36 let detail = self.detail(); 37 let detail = self.detail();
37 38
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::Const) 40 .kind(SymbolKind::Const)
40 .set_documentation(self.ctx.docs(self.const_)) 41 .set_documentation(self.ctx.docs(self.const_))
41 .set_deprecated(self.ctx.is_deprecated(self.const_)) 42 .set_deprecated(
43 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_),
45 )
42 .detail(detail) 46 .detail(detail)
43 .build(); 47 .build();
44 48
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 89fb49773..adcddebd1 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -1,11 +1,12 @@
1//! Renderer for `enum` variants. 1//! Renderer for `enum` variants.
2 2
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use ide_db::SymbolKind;
4use itertools::Itertools; 5use itertools::Itertools;
5use test_utils::mark; 6use test_utils::mark;
6 7
7use crate::{ 8use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 10 render::{builder_ext::Params, RenderContext},
10}; 11};
11 12
@@ -60,7 +61,7 @@ impl<'a> EnumRender<'a> {
60 self.ctx.source_range(), 61 self.ctx.source_range(),
61 self.qualified_name.clone(), 62 self.qualified_name.clone(),
62 ) 63 )
63 .kind(CompletionItemKind::EnumVariant) 64 .kind(SymbolKind::Variant)
64 .set_documentation(self.variant.docs(self.ctx.db())) 65 .set_documentation(self.variant.docs(self.ctx.db()))
65 .set_deprecated(self.ctx.is_deprecated(self.variant)) 66 .set_deprecated(self.ctx.is_deprecated(self.variant))
66 .add_import(import_to_add) 67 .add_import(import_to_add)
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index f5b0ce3e3..2d616b1fb 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -1,6 +1,7 @@
1//! Renderer for function calls. 1//! Renderer for function calls.
2 2
3use hir::{HasSource, Type}; 3use hir::{HasSource, Type};
4use ide_db::SymbolKind;
4use syntax::{ast::Fn, display::function_declaration}; 5use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark; 6use test_utils::mark;
6 7
@@ -44,7 +45,9 @@ impl<'a> FunctionRender<'a> {
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 45 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind()) 46 .kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func)) 47 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated(self.ctx.is_deprecated(self.func)) 48 .set_deprecated(
49 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
50 )
48 .detail(self.detail()) 51 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params) 52 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add) 53 .add_import(import_to_add)
@@ -103,7 +106,7 @@ impl<'a> FunctionRender<'a> {
103 if self.func.self_param(self.ctx.db()).is_some() { 106 if self.func.self_param(self.ctx.db()).is_some() {
104 CompletionItemKind::Method 107 CompletionItemKind::Method
105 } else { 108 } else {
106 CompletionItemKind::Function 109 SymbolKind::Function.into()
107 } 110 }
108 } 111 }
109} 112}
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index f893e420a..a4535786f 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -1,11 +1,12 @@
1//! Renderer for macro invocations. 1//! Renderer for macro invocations.
2 2
3use hir::{Documentation, HasSource}; 3use hir::{Documentation, HasSource};
4use ide_db::SymbolKind;
4use syntax::display::macro_label; 5use syntax::display::macro_label;
5use test_utils::mark; 6use test_utils::mark;
6 7
7use crate::{ 8use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::RenderContext, 10 render::RenderContext,
10}; 11};
11 12
@@ -41,7 +42,7 @@ impl<'a> MacroRender<'a> {
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 42 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 let mut builder = 43 let mut builder =
43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
44 .kind(CompletionItemKind::Macro) 45 .kind(SymbolKind::Macro)
45 .set_documentation(self.docs.clone()) 46 .set_documentation(self.docs.clone())
46 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 47 .set_deprecated(self.ctx.is_deprecated(self.macro_))
47 .add_import(import_to_add) 48 .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..bd97c3692 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -1,13 +1,14 @@
1//! Renderer for type aliases. 1//! Renderer for type aliases.
2 2
3use hir::HasSource; 3use hir::HasSource;
4use ide_db::SymbolKind;
4use syntax::{ 5use syntax::{
5 ast::{NameOwner, TypeAlias}, 6 ast::{NameOwner, TypeAlias},
6 display::type_label, 7 display::type_label,
7}; 8};
8 9
9use crate::{ 10use crate::{
10 item::{CompletionItem, CompletionItemKind, CompletionKind}, 11 item::{CompletionItem, CompletionKind},
11 render::RenderContext, 12 render::RenderContext,
12}; 13};
13 14
@@ -36,9 +37,12 @@ impl<'a> TypeAliasRender<'a> {
36 let detail = self.detail(); 37 let detail = self.detail();
37 38
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::TypeAlias) 40 .kind(SymbolKind::TypeAlias)
40 .set_documentation(self.ctx.docs(self.type_alias)) 41 .set_documentation(self.ctx.docs(self.type_alias))
41 .set_deprecated(self.ctx.is_deprecated(self.type_alias)) 42 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 )
42 .detail(detail) 46 .detail(detail)
43 .build(); 47 .build();
44 48
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..c34a99d90 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
@@ -281,7 +281,7 @@ impl Module {
281 281
282 /// Name of this module. 282 /// Name of this module.
283 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 283 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
284 let def_map = db.crate_def_map(self.id.krate); 284 let def_map = self.id.def_map(db.upcast());
285 let parent = def_map[self.id.local_id].parent?; 285 let parent = def_map[self.id.local_id].parent?;
286 def_map[parent].children.iter().find_map(|(name, module_id)| { 286 def_map[parent].children.iter().find_map(|(name, module_id)| {
287 if *module_id == self.id.local_id { 287 if *module_id == self.id.local_id {
@@ -302,12 +302,12 @@ 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.
309 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { 309 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
310 let def_map = db.crate_def_map(self.id.krate); 310 let def_map = self.id.def_map(db.upcast());
311 let children = def_map[self.id.local_id] 311 let children = def_map[self.id.local_id]
312 .children 312 .children
313 .iter() 313 .iter()
@@ -318,7 +318,7 @@ impl Module {
318 318
319 /// Finds a parent module. 319 /// Finds a parent module.
320 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { 320 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
321 let def_map = db.crate_def_map(self.id.krate); 321 let def_map = self.id.def_map(db.upcast());
322 let parent_id = def_map[self.id.local_id].parent?; 322 let parent_id = def_map[self.id.local_id].parent?;
323 Some(self.with_module_id(parent_id)) 323 Some(self.with_module_id(parent_id))
324 } 324 }
@@ -339,7 +339,7 @@ impl Module {
339 db: &dyn HirDatabase, 339 db: &dyn HirDatabase,
340 visible_from: Option<Module>, 340 visible_from: Option<Module>,
341 ) -> Vec<(Name, ScopeDef)> { 341 ) -> Vec<(Name, ScopeDef)> {
342 db.crate_def_map(self.id.krate)[self.id.local_id] 342 self.id.def_map(db.upcast())[self.id.local_id]
343 .scope 343 .scope
344 .entries() 344 .entries()
345 .filter_map(|(name, def)| { 345 .filter_map(|(name, def)| {
@@ -362,14 +362,14 @@ impl Module {
362 } 362 }
363 363
364 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { 364 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
365 db.crate_def_map(self.id.krate)[self.id.local_id].scope.visibility_of(def.clone().into()) 365 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
366 } 366 }
367 367
368 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 368 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
369 let _p = profile::span("Module::diagnostics").detail(|| { 369 let _p = profile::span("Module::diagnostics").detail(|| {
370 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) 370 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
371 }); 371 });
372 let crate_def_map = db.crate_def_map(self.id.krate); 372 let crate_def_map = self.id.def_map(db.upcast());
373 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); 373 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
374 for decl in self.declarations(db) { 374 for decl in self.declarations(db) {
375 match decl { 375 match decl {
@@ -396,12 +396,12 @@ impl Module {
396 } 396 }
397 397
398 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { 398 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
399 let def_map = db.crate_def_map(self.id.krate); 399 let def_map = self.id.def_map(db.upcast());
400 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() 400 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect()
401 } 401 }
402 402
403 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { 403 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
404 let def_map = db.crate_def_map(self.id.krate); 404 let def_map = self.id.def_map(db.upcast());
405 def_map[self.id.local_id].scope.impls().map(Impl::from).collect() 405 def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
406 } 406 }
407 407
@@ -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/has_source.rs b/crates/hir/src/has_source.rs
index 7c57d8378..262002671 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -24,12 +24,12 @@ pub trait HasSource {
24impl Module { 24impl Module {
25 /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. 25 /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
26 pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> { 26 pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> {
27 let def_map = db.crate_def_map(self.id.krate); 27 let def_map = self.id.def_map(db.upcast());
28 def_map[self.id.local_id].definition_source(db.upcast()) 28 def_map[self.id.local_id].definition_source(db.upcast())
29 } 29 }
30 30
31 pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool { 31 pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
32 let def_map = db.crate_def_map(self.id.krate); 32 let def_map = self.id.def_map(db.upcast());
33 match def_map[self.id.local_id].origin { 33 match def_map[self.id.local_id].origin {
34 ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs, 34 ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
35 _ => false, 35 _ => false,
@@ -39,7 +39,7 @@ impl Module {
39 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. 39 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
40 /// `None` for the crate root. 40 /// `None` for the crate root.
41 pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> { 41 pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
42 let def_map = db.crate_def_map(self.id.krate); 42 let def_map = self.id.def_map(db.upcast());
43 def_map[self.id.local_id].declaration_source(db.upcast()) 43 def_map[self.id.local_id].declaration_source(db.upcast())
44 } 44 }
45} 45}
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/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 9bf60c72a..775f7ec8b 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -31,6 +31,7 @@ impl SourceToDefCtx<'_, '_> {
31 pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { 31 pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> {
32 let _p = profile::span("SourceBinder::to_module_def"); 32 let _p = profile::span("SourceBinder::to_module_def");
33 let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { 33 let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| {
34 // FIXME: inner items
34 let crate_def_map = self.db.crate_def_map(crate_id); 35 let crate_def_map = self.db.crate_def_map(crate_id);
35 let local_id = crate_def_map.modules_for_file(file).next()?; 36 let local_id = crate_def_map.modules_for_file(file).next()?;
36 Some((crate_id, local_id)) 37 Some((crate_id, local_id))
@@ -60,7 +61,7 @@ impl SourceToDefCtx<'_, '_> {
60 }?; 61 }?;
61 62
62 let child_name = src.value.name()?.as_name(); 63 let child_name = src.value.name()?.as_name();
63 let def_map = self.db.crate_def_map(parent_module.krate); 64 let def_map = parent_module.def_map(self.db.upcast());
64 let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; 65 let child_id = *def_map[parent_module.local_id].children.get(&child_name)?;
65 Some(ModuleId { krate: parent_module.krate, local_id: child_id }) 66 Some(ModuleId { krate: parent_module.krate, local_id: child_id })
66 } 67 }
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 1b09ff816..6513daec8 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -196,7 +196,7 @@ impl Attrs {
196 pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { 196 pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
197 let raw_attrs = match def { 197 let raw_attrs = match def {
198 AttrDefId::ModuleId(module) => { 198 AttrDefId::ModuleId(module) => {
199 let def_map = db.crate_def_map(module.krate); 199 let def_map = module.def_map(db);
200 let mod_data = &def_map[module.local_id]; 200 let mod_data = &def_map[module.local_id];
201 match mod_data.declaration_source(db) { 201 match mod_data.declaration_source(db) {
202 Some(it) => { 202 Some(it) => {
@@ -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..d0c84ab0b 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,
@@ -86,7 +86,7 @@ impl Expander {
86 module: ModuleId, 86 module: ModuleId,
87 ) -> Expander { 87 ) -> Expander {
88 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 88 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
89 let crate_def_map = db.crate_def_map(module.krate); 89 let crate_def_map = module.def_map(db);
90 let ast_id_map = db.ast_id_map(current_file_id); 90 let ast_id_map = db.ast_id_map(current_file_id);
91 Expander { 91 Expander {
92 cfg_expander, 92 cfg_expander,
@@ -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/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index dcb00a1d9..65d85c86a 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -74,7 +74,7 @@ impl ChildBySource for ImplId {
74 74
75impl ChildBySource for ModuleId { 75impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
77 let crate_def_map = db.crate_def_map(self.krate); 77 let crate_def_map = self.def_map(db);
78 let module_data = &crate_def_map[self.local_id]; 78 let module_data = &crate_def_map[self.local_id];
79 module_data.scope.child_by_source(db) 79 module_data.scope.child_by_source(db)
80 } 80 }
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..c01b6daf2 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 {
@@ -110,8 +110,8 @@ fn find_path_inner(
110 // Base cases: 110 // Base cases:
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 = from.def_map(db);
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 = prelude_module.def_map(db);
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;
@@ -283,11 +283,11 @@ fn find_local_import_locations(
283 // above `from` with any visibility. That means we do not need to descend into private siblings 283 // above `from` with any visibility. That means we do not need to descend into private siblings
284 // of `from` (and similar). 284 // of `from` (and similar).
285 285
286 let def_map = db.crate_def_map(from.krate); 286 let def_map = from.def_map(db);
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();
@@ -312,7 +312,7 @@ fn find_local_import_locations(
312 &def_map[module.local_id] 312 &def_map[module.local_id]
313 } else { 313 } else {
314 // The crate might reexport a module defined in another crate. 314 // The crate might reexport a module defined in another crate.
315 ext_def_map = db.crate_def_map(module.krate); 315 ext_def_map = module.def_map(db);
316 &ext_def_map[module.local_id] 316 &ext_def_map[module.local_id]
317 }; 317 };
318 318
@@ -375,7 +375,7 @@ mod tests {
375 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); 375 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
376 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); 376 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
377 377
378 let crate_def_map = db.crate_def_map(module.krate); 378 let crate_def_map = module.def_map(&db);
379 let resolved = crate_def_map 379 let resolved = crate_def_map
380 .resolve_path( 380 .resolve_path(
381 &db, 381 &db,
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index e5368b293..0b7830445 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;
@@ -83,7 +83,7 @@ impl ImportMap {
83 &def_map[module.local_id] 83 &def_map[module.local_id]
84 } else { 84 } else {
85 // The crate might reexport a module defined in another crate. 85 // The crate might reexport a module defined in another crate.
86 ext_def_map = db.crate_def_map(module.krate); 86 ext_def_map = module.def_map(db);
87 &ext_def_map[module.local_id] 87 &ext_def_map[module.local_id]
88 }; 88 };
89 89
@@ -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/lib.rs b/crates/hir_def/src/lib.rs
index 08ed920c6..2f9261a7f 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -50,7 +50,10 @@ pub mod import_map;
50#[cfg(test)] 50#[cfg(test)]
51mod test_db; 51mod test_db;
52 52
53use std::hash::{Hash, Hasher}; 53use std::{
54 hash::{Hash, Hasher},
55 sync::Arc,
56};
54 57
55use base_db::{impl_intern_key, salsa, CrateId}; 58use base_db::{impl_intern_key, salsa, CrateId};
56use hir_expand::{ 59use hir_expand::{
@@ -58,6 +61,7 @@ use hir_expand::{
58 MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 61 MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
59}; 62};
60use la_arena::Idx; 63use la_arena::Idx;
64use nameres::DefMap;
61use syntax::ast; 65use syntax::ast;
62 66
63use crate::builtin_type::BuiltinType; 67use crate::builtin_type::BuiltinType;
@@ -73,6 +77,12 @@ pub struct ModuleId {
73 pub local_id: LocalModuleId, 77 pub local_id: LocalModuleId,
74} 78}
75 79
80impl ModuleId {
81 pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
82 db.crate_def_map(self.krate)
83 }
84}
85
76/// An ID of a module, **local** to a specific crate 86/// An ID of a module, **local** to a specific crate
77pub type LocalModuleId = Idx<nameres::ModuleData>; 87pub type LocalModuleId = Idx<nameres::ModuleData>;
78 88
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..adfcf879a 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 {
@@ -542,7 +578,7 @@ impl DefCollector<'_> {
542 } else if m.krate != self.def_map.krate { 578 } else if m.krate != self.def_map.krate {
543 mark::hit!(glob_across_crates); 579 mark::hit!(glob_across_crates);
544 // glob import from other crate => we can just import everything once 580 // glob import from other crate => we can just import everything once
545 let item_map = self.db.crate_def_map(m.krate); 581 let item_map = m.def_map(self.db);
546 let scope = &item_map[m.local_id].scope; 582 let scope = &item_map[m.local_id].scope;
547 583
548 // Module scoped macros is included 584 // Module scoped macros is included
@@ -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..82528b792 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) => {
@@ -206,7 +243,7 @@ impl CrateDefMap {
206 kind: PathKind::Super(0), 243 kind: PathKind::Super(0),
207 }; 244 };
208 log::debug!("resolving {:?} in other crate", path); 245 log::debug!("resolving {:?} in other crate", path);
209 let defp_map = db.crate_def_map(module.krate); 246 let defp_map = module.def_map(db);
210 let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow); 247 let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
211 return ResolvePathResult::with( 248 return ResolvePathResult::with(
212 def, 249 def,
@@ -319,7 +356,7 @@ impl CrateDefMap {
319 self 356 self
320 } else { 357 } else {
321 // Extend lifetime 358 // Extend lifetime
322 keep = db.crate_def_map(prelude.krate); 359 keep = prelude.def_map(db);
323 &keep 360 &keep
324 }; 361 };
325 def_map[prelude.local_id].scope.get(name) 362 def_map[prelude.local_id].scope.get(name)
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..130c074f0 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,8 +429,8 @@ 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 = prelude.def_map(db);
420 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 434 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
421 } 435 }
422 traits.extend(m.crate_def_map[m.module_id].scope.traits()); 436 traits.extend(m.crate_def_map[m.module_id].scope.traits());
@@ -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,14 +522,14 @@ 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 = prelude.def_map(db);
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);
521 if !seen.contains(&seen_tuple) { 535 if !seen.contains(&seen_tuple) {
@@ -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
@@ -617,7 +633,7 @@ pub trait HasResolver: Copy {
617 633
618impl HasResolver for ModuleId { 634impl HasResolver for ModuleId {
619 fn resolver(self, db: &dyn DefDatabase) -> Resolver { 635 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
620 let def_map = db.crate_def_map(self.krate); 636 let def_map = self.def_map(db);
621 Resolver::default().push_module_scope(def_map, self.local_id) 637 Resolver::default().push_module_scope(def_map, self.local_id)
622 } 638 }
623} 639}
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..e79a91102 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};
@@ -103,7 +103,7 @@ impl Visibility {
103 if from_module.krate != to_module.krate { 103 if from_module.krate != to_module.krate {
104 return false; 104 return false;
105 } 105 }
106 let def_map = db.crate_def_map(from_module.krate); 106 let def_map = from_module.def_map(db);
107 self.is_visible_from_def_map(&def_map, from_module.local_id) 107 self.is_visible_from_def_map(&def_map, from_module.local_id)
108 } 108 }
109 109
@@ -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/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 9bf3b51b0..d7351d212 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -491,7 +491,10 @@ impl<'a> InferenceContext<'a> {
491 Expr::Box { expr } => { 491 Expr::Box { expr } => {
492 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); 492 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
493 if let Some(box_) = self.resolve_boxed_box() { 493 if let Some(box_) = self.resolve_boxed_box() {
494 Ty::apply_one(TypeCtor::Adt(box_), inner_ty) 494 let mut sb = Substs::build_for_type_ctor(self.db, TypeCtor::Adt(box_));
495 sb = sb.push(inner_ty);
496 sb = sb.fill(repeat_with(|| self.table.new_type_var()));
497 Ty::apply(TypeCtor::Adt(box_), sb.build())
495 } else { 498 } else {
496 Ty::Unknown 499 Ty::Unknown
497 } 500 }
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..25ee664d6 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 };
@@ -188,10 +188,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
188 }; 188 };
189 189
190 let module = db.module_for_file(file_id); 190 let module = db.module_for_file(file_id);
191 let crate_def_map = db.crate_def_map(module.krate); 191 let def_map = module.def_map(&db);
192 192
193 let mut defs: Vec<DefWithBodyId> = Vec::new(); 193 let mut defs: Vec<DefWithBodyId> = Vec::new();
194 visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it)); 194 visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it));
195 defs.sort_by_key(|def| match def { 195 defs.sort_by_key(|def| match def {
196 DefWithBodyId::FunctionId(it) => { 196 DefWithBodyId::FunctionId(it) => {
197 let loc = it.lookup(&db); 197 let loc = it.lookup(&db);
@@ -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 ) {
@@ -321,7 +321,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
321 { 321 {
322 let events = db.log_executed(|| { 322 let events = db.log_executed(|| {
323 let module = db.module_for_file(pos.file_id); 323 let module = db.module_for_file(pos.file_id);
324 let crate_def_map = db.crate_def_map(module.krate); 324 let crate_def_map = module.def_map(&db);
325 visit_module(&db, &crate_def_map, module.local_id, &mut |def| { 325 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
326 db.infer(def); 326 db.infer(def);
327 }); 327 });
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 8d431b920..16682f76f 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -29,6 +29,30 @@ mod boxed {
29} 29}
30 30
31#[test] 31#[test]
32fn infer_box_with_allocator() {
33 check_types(
34 r#"
35//- /main.rs crate:main deps:std
36fn test() {
37 let x = box 1;
38 let t = (x, box x, box &1, box [1]);
39 t;
40} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; _], {unknown}>)
41
42//- /std.rs crate:std
43#[prelude_import] use prelude::*;
44mod boxed {
45 #[lang = "owned_box"]
46 pub struct Box<T: ?Sized, A: Allocator> {
47 inner: *mut T,
48 allocator: A,
49 }
50}
51"#,
52 );
53}
54
55#[test]
32fn infer_adt_self() { 56fn infer_adt_self() {
33 check_types( 57 check_types(
34 r#" 58 r#"
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..16fa828ad 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -7,6 +7,7 @@ use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, Mo
7use ide_db::{ 7use ide_db::{
8 base_db::{FileId, FileRange, SourceDatabase}, 8 base_db::{FileId, FileRange, SourceDatabase},
9 symbol_index::FileSymbolKind, 9 symbol_index::FileSymbolKind,
10 SymbolKind,
10}; 11};
11use ide_db::{defs::Definition, RootDatabase}; 12use ide_db::{defs::Definition, RootDatabase};
12use syntax::{ 13use syntax::{
@@ -18,30 +19,6 @@ use crate::FileSymbol;
18 19
19use super::short_label::ShortLabel; 20use super::short_label::ShortLabel;
20 21
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub enum SymbolKind {
23 Module,
24 Impl,
25 Field,
26 TypeParam,
27 ConstParam,
28 LifetimeParam,
29 ValueParam,
30 SelfParam,
31 Local,
32 Label,
33 Function,
34 Const,
35 Static,
36 Struct,
37 Enum,
38 Variant,
39 Union,
40 TypeAlias,
41 Trait,
42 Macro,
43}
44
45/// `NavigationTarget` represents and element in the editor's UI which you can 22/// `NavigationTarget` represents and element in the editor's UI which you can
46/// click on to navigate to a particular piece of code. 23/// click on to navigate to a particular piece of code.
47/// 24///
@@ -153,8 +130,7 @@ impl NavigationTarget {
153 node: InFile<&dyn ast::NameOwner>, 130 node: InFile<&dyn ast::NameOwner>,
154 kind: SymbolKind, 131 kind: SymbolKind,
155 ) -> NavigationTarget { 132 ) -> NavigationTarget {
156 let name = 133 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 = 134 let focus_range =
159 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); 135 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); 136 let frange = node.map(|it| it.syntax()).original_file_range(db);
@@ -295,6 +271,7 @@ impl ToNav for hir::Module {
295 ModuleSource::Module(node) => { 271 ModuleSource::Module(node) => {
296 (node.syntax(), node.name().map(|it| it.syntax().text_range())) 272 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
297 } 273 }
274 ModuleSource::BlockExpr(node) => (node.syntax(), None),
298 }; 275 };
299 let frange = src.with_value(syntax).original_file_range(db); 276 let frange = src.with_value(syntax).original_file_range(db);
300 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) 277 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module)
@@ -400,15 +377,13 @@ impl TryToNav for hir::GenericParam {
400impl ToNav for hir::Local { 377impl ToNav for hir::Local {
401 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 378 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
402 let src = self.source(db); 379 let src = self.source(db);
403 let (node, focus_range) = match &src.value { 380 let (node, name) = match &src.value {
404 Either::Left(bind_pat) => ( 381 Either::Left(bind_pat) => (bind_pat.syntax().clone(), bind_pat.name()),
405 bind_pat.syntax().clone(), 382 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 }; 383 };
384 let focus_range =
385 name.map(|it| src.with_value(&it.syntax().clone()).original_file_range(db).range);
386
412 let full_range = src.with_value(&node).original_file_range(db); 387 let full_range = src.with_value(&node).original_file_range(db);
413 let name = match self.name(db) { 388 let name = match self.name(db) {
414 Some(it) => it.to_string().into(), 389 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/file_structure.rs b/crates/ide/src/file_structure.rs
index 32556dad3..26793bdb4 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -1,10 +1,9 @@
1use ide_db::SymbolKind;
1use syntax::{ 2use syntax::{
2 ast::{self, AttrsOwner, GenericParamsOwner, NameOwner}, 3 ast::{self, AttrsOwner, GenericParamsOwner, NameOwner},
3 match_ast, AstNode, SourceFile, SyntaxNode, TextRange, WalkEvent, 4 match_ast, AstNode, SourceFile, SyntaxNode, TextRange, WalkEvent,
4}; 5};
5 6
6use crate::SymbolKind;
7
8#[derive(Debug, Clone)] 7#[derive(Debug, Clone)]
9pub struct StructureNode { 8pub struct StructureNode {
10 pub parent: Option<usize>, 9 pub parent: Option<usize>,
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..989e94a31 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -65,7 +65,7 @@ use crate::display::ToNav;
65pub use crate::{ 65pub use crate::{
66 call_hierarchy::CallItem, 66 call_hierarchy::CallItem,
67 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 67 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
68 display::navigation_target::{NavigationTarget, SymbolKind}, 68 display::navigation_target::NavigationTarget,
69 expand_macro::ExpandedMacro, 69 expand_macro::ExpandedMacro,
70 file_structure::StructureNode, 70 file_structure::StructureNode,
71 folding_ranges::{Fold, FoldKind}, 71 folding_ranges::{Fold, FoldKind},
@@ -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..33170906d 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -3,16 +3,17 @@ use std::fmt;
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::{defs::Definition, RootDatabase}; 6use ide_db::{defs::Definition, RootDatabase, SymbolKind};
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},
15 FileId, NavigationTarget, SymbolKind, 16 FileId, NavigationTarget,
16}; 17};
17 18
18#[derive(Debug, Clone)] 19#[derive(Debug, Clone)]
@@ -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.rs b/crates/ide/src/syntax_highlighting.rs
index f2d4da78d..a3d4e4f77 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -13,7 +13,7 @@ mod html;
13mod tests; 13mod tests;
14 14
15use hir::{Name, Semantics}; 15use hir::{Name, Semantics};
16use ide_db::RootDatabase; 16use ide_db::{RootDatabase, SymbolKind};
17use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
18use syntax::{ 18use syntax::{
19 ast::{self, HasFormatSpecifier}, 19 ast::{self, HasFormatSpecifier},
@@ -27,7 +27,7 @@ use crate::{
27 format::highlight_format_string, highlights::Highlights, 27 format::highlight_format_string, highlights::Highlights,
28 macro_rules::MacroRulesHighlighter, tags::Highlight, 28 macro_rules::MacroRulesHighlighter, tags::Highlight,
29 }, 29 },
30 FileId, HlMod, HlTag, SymbolKind, 30 FileId, HlMod, HlTag,
31}; 31};
32 32
33pub(crate) use html::highlight_as_html; 33pub(crate) use html::highlight_as_html;
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index a74ca844b..8c67a0863 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -1,10 +1,11 @@
1//! Syntax highlighting for format macro strings. 1//! Syntax highlighting for format macro strings.
2use ide_db::SymbolKind;
2use syntax::{ 3use syntax::{
3 ast::{self, FormatSpecifier, HasFormatSpecifier}, 4 ast::{self, FormatSpecifier, HasFormatSpecifier},
4 AstNode, AstToken, TextRange, 5 AstNode, AstToken, TextRange,
5}; 6};
6 7
7use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag, SymbolKind}; 8use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag};
8 9
9pub(super) fn highlight_format_string( 10pub(super) fn highlight_format_string(
10 stack: &mut Highlights, 11 stack: &mut Highlights,
@@ -30,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
30 let parent = string.syntax().parent(); 31 let parent = string.syntax().parent();
31 32
32 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; 33 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") { 34 if !matches!(name.text(), "format_args" | "format_args_nl") {
34 return None; 35 return None;
35 } 36 }
36 37
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 87578e70a..24fcbb584 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -3,7 +3,7 @@
3use hir::{AsAssocItem, Semantics, VariantDef}; 3use hir::{AsAssocItem, Semantics, VariantDef};
4use ide_db::{ 4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase, 6 RootDatabase, SymbolKind,
7}; 7};
8use rustc_hash::FxHashMap; 8use rustc_hash::FxHashMap;
9use syntax::{ 9use syntax::{
@@ -12,7 +12,7 @@ use syntax::{
12 SyntaxNode, SyntaxToken, T, 12 SyntaxNode, SyntaxToken, T,
13}; 13};
14 14
15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag, SymbolKind}; 15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag};
16 16
17pub(super) fn element( 17pub(super) fn element(
18 sema: &Semantics<RootDatabase>, 18 sema: &Semantics<RootDatabase>,
@@ -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_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 8dd05ac52..3c02fdb11 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -3,7 +3,7 @@
3 3
4use std::{fmt, ops}; 4use std::{fmt, ops};
5 5
6use crate::SymbolKind; 6use ide_db::SymbolKind;
7 7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub struct Highlight { 9pub struct Highlight {
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/lib.rs b/crates/ide_db/src/lib.rs
index 118c090d7..6eb34b06b 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -134,3 +134,27 @@ fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
134 let text = db.file_text(file_id); 134 let text = db.file_text(file_id);
135 Arc::new(LineIndex::new(&*text)) 135 Arc::new(LineIndex::new(&*text))
136} 136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
139pub enum SymbolKind {
140 Const,
141 ConstParam,
142 Enum,
143 Field,
144 Function,
145 Impl,
146 Label,
147 LifetimeParam,
148 Local,
149 Macro,
150 Module,
151 SelfParam,
152 Static,
153 Struct,
154 Trait,
155 TypeAlias,
156 TypeParam,
157 Union,
158 ValueParam,
159 Variant,
160}
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.rs b/crates/parser/src/syntax_kind.rs
index 63204436c..9ea0e4f9b 100644
--- a/crates/parser/src/syntax_kind.rs
+++ b/crates/parser/src/syntax_kind.rs
@@ -6,6 +6,7 @@ mod generated;
6pub use self::generated::SyntaxKind; 6pub use self::generated::SyntaxKind;
7 7
8impl From<u16> for SyntaxKind { 8impl From<u16> for SyntaxKind {
9 #[inline]
9 fn from(d: u16) -> SyntaxKind { 10 fn from(d: u16) -> SyntaxKind {
10 assert!(d <= (SyntaxKind::__LAST as u16)); 11 assert!(d <= (SyntaxKind::__LAST as u16));
11 unsafe { std::mem::transmute::<u16, SyntaxKind>(d) } 12 unsafe { std::mem::transmute::<u16, SyntaxKind>(d) }
@@ -13,12 +14,14 @@ impl From<u16> for SyntaxKind {
13} 14}
14 15
15impl From<SyntaxKind> for u16 { 16impl From<SyntaxKind> for u16 {
17 #[inline]
16 fn from(k: SyntaxKind) -> u16 { 18 fn from(k: SyntaxKind) -> u16 {
17 k as u16 19 k as u16
18 } 20 }
19} 21}
20 22
21impl SyntaxKind { 23impl SyntaxKind {
24 #[inline]
22 pub fn is_trivia(self) -> bool { 25 pub fn is_trivia(self) -> bool {
23 matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT) 26 matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
24 } 27 }
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..809452e6d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -10,8 +10,9 @@ use std::{
10 10
11use ide::{ 11use ide::{
12 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, 12 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget,
13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, SymbolKind, TextEdit, 13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
14}; 14};
15use ide_db::SymbolKind;
15use itertools::Itertools; 16use itertools::Itertools;
16use lsp_server::ErrorCode; 17use lsp_server::ErrorCode;
17use lsp_types::{ 18use lsp_types::{
@@ -653,7 +654,7 @@ pub(crate) fn handle_completion(
653 let mut new_completion_items = 654 let mut new_completion_items =
654 to_proto::completion_item(&line_index, line_endings, item.clone()); 655 to_proto::completion_item(&line_index, line_endings, item.clone());
655 656
656 if completion_config.enable_autoimport_completions { 657 if completion_config.enable_imports_on_the_fly {
657 for new_item in &mut new_completion_items { 658 for new_item in &mut new_completion_items {
658 fill_resolve_data(&mut new_item.data, &item, &text_document_position); 659 fill_resolve_data(&mut new_item.data, &item, &text_document_position);
659 } 660 }
@@ -703,6 +704,7 @@ pub(crate) fn handle_completion_resolve(
703 FilePosition { file_id, offset }, 704 FilePosition { file_id, offset },
704 &resolve_data.full_import_path, 705 &resolve_data.full_import_path,
705 resolve_data.imported_name, 706 resolve_data.imported_name,
707 resolve_data.import_for_trait_assoc_item,
706 )? 708 )?
707 .into_iter() 709 .into_iter()
708 .flat_map(|edit| { 710 .flat_map(|edit| {
@@ -809,7 +811,7 @@ pub(crate) fn handle_rename(
809 811
810 let change = 812 let change =
811 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; 813 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
812 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 814 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
813 Ok(Some(workspace_edit)) 815 Ok(Some(workspace_edit))
814} 816}
815 817
@@ -857,6 +859,23 @@ pub(crate) fn handle_formatting(
857 RustfmtConfig::Rustfmt { extra_args } => { 859 RustfmtConfig::Rustfmt { extra_args } => {
858 let mut cmd = process::Command::new(toolchain::rustfmt()); 860 let mut cmd = process::Command::new(toolchain::rustfmt());
859 cmd.args(extra_args); 861 cmd.args(extra_args);
862 // try to chdir to the file so we can respect `rustfmt.toml`
863 // FIXME: use `rustfmt --config-path` once
864 // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
865 match params.text_document.uri.to_file_path() {
866 Ok(mut path) => {
867 // pop off file name
868 if path.pop() && path.is_dir() {
869 cmd.current_dir(path);
870 }
871 }
872 Err(_) => {
873 log::error!(
874 "Unable to get file path for {}, rustfmt.toml might be ignored",
875 params.text_document.uri
876 );
877 }
878 }
860 if let Some(&crate_id) = crate_ids.first() { 879 if let Some(&crate_id) = crate_ids.first() {
861 // Assume all crates are in the same edition 880 // Assume all crates are in the same edition
862 let edition = snap.analysis.crate_edition(crate_id)?; 881 let edition = snap.analysis.crate_edition(crate_id)?;
@@ -1694,6 +1713,7 @@ struct CompletionResolveData {
1694 position: lsp_types::TextDocumentPositionParams, 1713 position: lsp_types::TextDocumentPositionParams,
1695 full_import_path: String, 1714 full_import_path: String,
1696 imported_name: String, 1715 imported_name: String,
1716 import_for_trait_assoc_item: bool,
1697} 1717}
1698 1718
1699fn fill_resolve_data( 1719fn fill_resolve_data(
@@ -1710,6 +1730,7 @@ fn fill_resolve_data(
1710 position: position.to_owned(), 1730 position: position.to_owned(),
1711 full_import_path, 1731 full_import_path,
1712 imported_name, 1732 imported_name,
1733 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1713 }) 1734 })
1714 .unwrap(), 1735 .unwrap(),
1715 ); 1736 );
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..96f915f1c 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -8,8 +8,9 @@ use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, 10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
11 RenameError, Runnable, Severity, SourceChange, SymbolKind, TextEdit, TextRange, TextSize, 11 RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize,
12}; 12};
13use ide_db::SymbolKind;
13use itertools::Itertools; 14use itertools::Itertools;
14 15
15use crate::{ 16use crate::{
@@ -87,25 +88,35 @@ pub(crate) fn completion_item_kind(
87 completion_item_kind: CompletionItemKind, 88 completion_item_kind: CompletionItemKind,
88) -> lsp_types::CompletionItemKind { 89) -> lsp_types::CompletionItemKind {
89 match completion_item_kind { 90 match completion_item_kind {
90 CompletionItemKind::Keyword => lsp_types::CompletionItemKind::Keyword, 91 CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember,
91 CompletionItemKind::Snippet => lsp_types::CompletionItemKind::Snippet,
92 CompletionItemKind::Module => lsp_types::CompletionItemKind::Module,
93 CompletionItemKind::Function => lsp_types::CompletionItemKind::Function,
94 CompletionItemKind::Struct => lsp_types::CompletionItemKind::Struct,
95 CompletionItemKind::Enum => lsp_types::CompletionItemKind::Enum,
96 CompletionItemKind::EnumVariant => lsp_types::CompletionItemKind::EnumMember,
97 CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::Struct,
98 CompletionItemKind::Binding => lsp_types::CompletionItemKind::Variable, 92 CompletionItemKind::Binding => lsp_types::CompletionItemKind::Variable,
99 CompletionItemKind::Field => lsp_types::CompletionItemKind::Field, 93 CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::Struct,
100 CompletionItemKind::Trait => lsp_types::CompletionItemKind::Interface, 94 CompletionItemKind::Keyword => lsp_types::CompletionItemKind::Keyword,
101 CompletionItemKind::TypeAlias => lsp_types::CompletionItemKind::Struct,
102 CompletionItemKind::Const => lsp_types::CompletionItemKind::Constant,
103 CompletionItemKind::Static => lsp_types::CompletionItemKind::Value,
104 CompletionItemKind::Method => lsp_types::CompletionItemKind::Method, 95 CompletionItemKind::Method => lsp_types::CompletionItemKind::Method,
105 CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter, 96 CompletionItemKind::Snippet => lsp_types::CompletionItemKind::Snippet,
106 CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method,
107 CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember,
108 CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::Reference, 97 CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::Reference,
98 CompletionItemKind::SymbolKind(symbol) => match symbol {
99 SymbolKind::Const => lsp_types::CompletionItemKind::Constant,
100 SymbolKind::ConstParam => lsp_types::CompletionItemKind::TypeParameter,
101 SymbolKind::Enum => lsp_types::CompletionItemKind::Enum,
102 SymbolKind::Field => lsp_types::CompletionItemKind::Field,
103 SymbolKind::Function => lsp_types::CompletionItemKind::Function,
104 SymbolKind::Impl => lsp_types::CompletionItemKind::Text,
105 SymbolKind::Label => lsp_types::CompletionItemKind::Variable,
106 SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TypeParameter,
107 SymbolKind::Local => lsp_types::CompletionItemKind::Variable,
108 SymbolKind::Macro => lsp_types::CompletionItemKind::Method,
109 SymbolKind::Module => lsp_types::CompletionItemKind::Module,
110 SymbolKind::SelfParam => lsp_types::CompletionItemKind::Value,
111 SymbolKind::Static => lsp_types::CompletionItemKind::Value,
112 SymbolKind::Struct => lsp_types::CompletionItemKind::Struct,
113 SymbolKind::Trait => lsp_types::CompletionItemKind::Interface,
114 SymbolKind::TypeAlias => lsp_types::CompletionItemKind::Struct,
115 SymbolKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter,
116 SymbolKind::Union => lsp_types::CompletionItemKind::Struct,
117 SymbolKind::ValueParam => lsp_types::CompletionItemKind::Value,
118 SymbolKind::Variant => lsp_types::CompletionItemKind::EnumMember,
119 },
109 } 120 }
110} 121}
111 122
@@ -884,7 +895,7 @@ mod tests {
884 .completions( 895 .completions(
885 &ide::CompletionConfig { 896 &ide::CompletionConfig {
886 enable_postfix_completions: true, 897 enable_postfix_completions: true,
887 enable_autoimport_completions: true, 898 enable_imports_on_the_fly: true,
888 add_call_parenthesis: true, 899 add_call_parenthesis: true,
889 add_call_argument_snippets: true, 900 add_call_argument_snippets: true,
890 snippet_cap: SnippetCap::new(true), 901 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..428cee3ad 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
@@ -139,6 +142,17 @@ There are many benefits to this:
139 142
140Formatting ensures that you can use your editor's "number of selected characters" feature to correlate offsets with test's source code. 143Formatting ensures that you can use your editor's "number of selected characters" feature to correlate offsets with test's source code.
141 144
145## Marked Tests
146
147Use
148[`mark::hit! / mark::check!`](https://github.com/rust-analyzer/rust-analyzer/blob/71fe719dd5247ed8615641d9303d7ca1aa201c2f/crates/test_utils/src/mark.rs)
149when testing specific conditions.
150Do not place several marks into a single test or condition.
151Do not reuse marks between several tests.
152
153**Rationale:** marks provide an easy way to find the canonical test for each bit of code.
154This makes it much easier to understand.
155
142## Function Preconditions 156## Function Preconditions
143 157
144Express function preconditions in types and force the caller to provide them (rather than checking in callee): 158Express function preconditions in types and force the caller to provide them (rather than checking in callee):
@@ -280,6 +294,9 @@ Prefer `Default` even it has to be implemented manually.
280 294
281**Rationale:** less typing in the common case, uniformity. 295**Rationale:** less typing in the common case, uniformity.
282 296
297Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength
298reduction.
299
283## Functions Over Objects 300## Functions Over Objects
284 301
285Avoid creating "doer" objects. 302Avoid creating "doer" objects.
@@ -372,6 +389,14 @@ 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. 389Runtime 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. 390Compile time **does not** obey this rule -- all code has to be compiled.
374 391
392## Appropriate String Types
393
394When interfacing with OS APIs, use `OsString`, even if the original source of data is utf-8 encoded.
395**Rationale:** cleanly delineates the boundary when the data goes into the OS-land.
396
397Use `AbsPathBuf` and `AbsPath` over `std::Path`.
398**Rationale:** rust-analyzer is a long-lived process which handles several projects at the same time.
399It is important not to leak cwd by accident.
375 400
376# Premature Pessimization 401# Premature Pessimization
377 402
@@ -418,12 +443,44 @@ fn frobnicate(s: &str) {
418**Rationale:** reveals the costs. 443**Rationale:** reveals the costs.
419It is also more efficient when the caller already owns the allocation. 444It is also more efficient when the caller already owns the allocation.
420 445
421## Collection types 446## Collection Types
422 447
423Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`. 448Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
424 449
425**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount. 450**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount.
426 451
452## Avoid Intermediate Collections
453
454When writing a recursive function to compute a sets of things, use an accumulator parameter instead of returning a fresh collection.
455Accumulator goes first in the list of arguments.
456
457```rust
458// GOOD
459pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
460 let mut res = FxHashSet::default();
461 go(&mut res, node);
462 res
463}
464fn go(acc: &mut FxHashSet<Node>, node: Node) {
465 acc.insert(node);
466 for n in node.neighbors() {
467 go(acc, n);
468 }
469}
470
471// BAD
472pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
473 let mut res = FxHashSet::default();
474 res.insert(node);
475 for n in node.neighbors() {
476 res.extend(reachable_nodes(n));
477 }
478 res
479}
480```
481
482**Rational:** re-use allocations, accumulator style is more concise for complex cases.
483
427# Style 484# Style
428 485
429## Order of Imports 486## Order of Imports
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index e598c2c84..1edb7713d 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -294,6 +294,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
294 "x64 linux": "x86_64-unknown-linux-gnu", 294 "x64 linux": "x86_64-unknown-linux-gnu",
295 "x64 darwin": "x86_64-apple-darwin", 295 "x64 darwin": "x86_64-apple-darwin",
296 "arm64 win32": "aarch64-pc-windows-msvc", 296 "arm64 win32": "aarch64-pc-windows-msvc",
297 "arm64 linux": "aarch64-unknown-linux-gnu",
297 "arm64 darwin": "aarch64-apple-darwin", 298 "arm64 darwin": "aarch64-apple-darwin",
298 }; 299 };
299 const platform = platforms[`${process.arch} ${process.platform}`]; 300 const platform = platforms[`${process.arch} ${process.platform}`];
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);