aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml7
-rw-r--r--Cargo.lock121
-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/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/hir/src/attrs.rs6
-rw-r--r--crates/hir/src/code_model.rs6
-rw-r--r--crates/hir/src/db.rs14
-rw-r--r--crates/hir_def/src/attr.rs1
-rw-r--r--crates/hir_def/src/body.rs2
-rw-r--r--crates/hir_def/src/body/tests.rs41
-rw-r--r--crates/hir_def/src/db.rs7
-rw-r--r--crates/hir_def/src/find_path.rs18
-rw-r--r--crates/hir_def/src/import_map.rs2
-rw-r--r--crates/hir_def/src/item_tree.rs31
-rw-r--r--crates/hir_def/src/item_tree/lower.rs45
-rw-r--r--crates/hir_def/src/lang_item.rs2
-rw-r--r--crates/hir_def/src/nameres.rs140
-rw-r--r--crates/hir_def/src/nameres/collector.rs58
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs37
-rw-r--r--crates/hir_def/src/nameres/tests.rs20
-rw-r--r--crates/hir_def/src/nameres/tests/block.rs97
-rw-r--r--crates/hir_def/src/resolver.rs23
-rw-r--r--crates/hir_def/src/test_db.rs6
-rw-r--r--crates/hir_expand/src/ast_id_map.rs20
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/name.rs8
-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/simple.rs24
-rw-r--r--crates/ide/src/display/navigation_target.rs4
-rw-r--r--crates/ide/src/display/short_label.rs10
-rw-r--r--crates/ide/src/doc_links.rs23
-rw-r--r--crates/ide/src/extend_selection.rs4
-rw-r--r--crates/ide/src/goto_definition.rs90
-rw-r--r--crates/ide/src/hover.rs30
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/runnables.rs68
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/defs.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs2
-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.rs6
-rw-r--r--crates/profile/Cargo.toml1
-rw-r--r--crates/profile/src/hprof.rs4
-rw-r--r--crates/profile/src/lib.rs7
-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.rs1
-rw-r--r--crates/project_model/src/workspace.rs22
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/args.rs23
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs5
-rw-r--r--crates/rust-analyzer/src/handlers.rs17
-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/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/make.rs8
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
-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--docs/dev/README.md3
-rw-r--r--docs/dev/style.md59
-rw-r--r--xtask/Cargo.toml2
-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/lib.rs6
-rw-r--r--xtask/src/main.rs34
-rw-r--r--xtask/tests/tidy.rs2
89 files changed, 1189 insertions, 591 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a97ed24ba..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,10 +109,12 @@ 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:
diff --git a/Cargo.lock b/Cargo.lock
index 901784bec..bf1b9e417 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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
@@ -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",
@@ -274,6 +274,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" 274checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
275 275
276[[package]] 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]
286
287[[package]]
277name = "crc32fast" 288name = "crc32fast"
278version = "1.2.1" 289version = "1.2.1"
279source = "registry+https://github.com/rust-lang/crates.io-index" 290source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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
@@ -942,15 +963,6 @@ dependencies = [
942 963
943[[package]] 964[[package]]
944name = "memoffset" 965name = "memoffset"
945version = "0.5.6"
946source = "registry+https://github.com/rust-lang/crates.io-index"
947checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
948dependencies = [
949 "autocfg",
950]
951
952[[package]]
953name = "memoffset"
954version = "0.6.1" 966version = "0.6.1"
955source = "registry+https://github.com/rust-lang/crates.io-index" 967source = "registry+https://github.com/rust-lang/crates.io-index"
956checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 968checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
@@ -1082,12 +1094,6 @@ dependencies = [
1082 1094
1083[[package]] 1095[[package]]
1084name = "object" 1096name = "object"
1085version = "0.22.0"
1086source = "registry+https://github.com/rust-lang/crates.io-index"
1087checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
1088
1089[[package]]
1090name = "object"
1091version = "0.23.0" 1097version = "0.23.0"
1092source = "registry+https://github.com/rust-lang/crates.io-index" 1098source = "registry+https://github.com/rust-lang/crates.io-index"
1093checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" 1099checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
@@ -1124,7 +1130,7 @@ dependencies = [
1124 "cfg-if 1.0.0", 1130 "cfg-if 1.0.0",
1125 "instant", 1131 "instant",
1126 "libc", 1132 "libc",
1127 "redox_syscall", 1133 "redox_syscall 0.1.57",
1128 "smallvec", 1134 "smallvec",
1129 "winapi 0.3.9", 1135 "winapi 0.3.9",
1130] 1136]
@@ -1205,9 +1211,9 @@ dependencies = [
1205 1211
1206[[package]] 1212[[package]]
1207name = "pico-args" 1213name = "pico-args"
1208version = "0.3.4" 1214version = "0.4.0"
1209source = "registry+https://github.com/rust-lang/crates.io-index" 1215source = "registry+https://github.com/rust-lang/crates.io-index"
1210checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" 1216checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6"
1211 1217
1212[[package]] 1218[[package]]
1213name = "pin-project-lite" 1219name = "pin-project-lite"
@@ -1251,7 +1257,7 @@ dependencies = [
1251 "libloading", 1257 "libloading",
1252 "mbe", 1258 "mbe",
1253 "memmap", 1259 "memmap",
1254 "object 0.23.0", 1260 "object",
1255 "proc_macro_api", 1261 "proc_macro_api",
1256 "proc_macro_test", 1262 "proc_macro_test",
1257 "serde_derive", 1263 "serde_derive",
@@ -1269,6 +1275,7 @@ name = "profile"
1269version = "0.0.0" 1275version = "0.0.0"
1270dependencies = [ 1276dependencies = [
1271 "cfg-if 1.0.0", 1277 "cfg-if 1.0.0",
1278 "countme",
1272 "jemalloc-ctl", 1279 "jemalloc-ctl",
1273 "la-arena", 1280 "la-arena",
1274 "libc", 1281 "libc",
@@ -1358,6 +1365,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1358checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1365checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1359 1366
1360[[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]]
1361name = "regex" 1377name = "regex"
1362version = "1.4.3" 1378version = "1.4.3"
1363source = "registry+https://github.com/rust-lang/crates.io-index" 1379source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1384,15 +1400,15 @@ checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
1384 1400
1385[[package]] 1401[[package]]
1386name = "rowan" 1402name = "rowan"
1387version = "0.10.6" 1403version = "0.12.1"
1388source = "registry+https://github.com/rust-lang/crates.io-index" 1404source = "registry+https://github.com/rust-lang/crates.io-index"
1389checksum = "8a0734142c18710f7214dc21908e2f054e973b908dbb1a602a3e6691615aaaae" 1405checksum = "24c2d78254049413f9d73495f883e7fa0b7a7d4b88468cd72a3bbbd0ad585cd1"
1390dependencies = [ 1406dependencies = [
1407 "countme",
1391 "hashbrown", 1408 "hashbrown",
1409 "memoffset",
1392 "rustc-hash", 1410 "rustc-hash",
1393 "smol_str",
1394 "text-size", 1411 "text-size",
1395 "triomphe",
1396] 1412]
1397 1413
1398[[package]] 1414[[package]]
@@ -1448,9 +1464,9 @@ dependencies = [
1448 1464
1449[[package]] 1465[[package]]
1450name = "rustc-ap-rustc_lexer" 1466name = "rustc-ap-rustc_lexer"
1451version = "697.0.0" 1467version = "700.0.0"
1452source = "registry+https://github.com/rust-lang/crates.io-index" 1468source = "registry+https://github.com/rust-lang/crates.io-index"
1453checksum = "67adbe260a0a11910624d6d28c0304fcf7b063e666682111005c83b09f73429d" 1469checksum = "5ed36784376b69c941d7aa36e960a52ac712e2663960357121a4d9f2cc58e225"
1454dependencies = [ 1470dependencies = [
1455 "unicode-xid", 1471 "unicode-xid",
1456] 1472]
@@ -1544,18 +1560,18 @@ dependencies = [
1544 1560
1545[[package]] 1561[[package]]
1546name = "serde" 1562name = "serde"
1547version = "1.0.119" 1563version = "1.0.120"
1548source = "registry+https://github.com/rust-lang/crates.io-index" 1564source = "registry+https://github.com/rust-lang/crates.io-index"
1549checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" 1565checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
1550dependencies = [ 1566dependencies = [
1551 "serde_derive", 1567 "serde_derive",
1552] 1568]
1553 1569
1554[[package]] 1570[[package]]
1555name = "serde_derive" 1571name = "serde_derive"
1556version = "1.0.119" 1572version = "1.0.120"
1557source = "registry+https://github.com/rust-lang/crates.io-index" 1573source = "registry+https://github.com/rust-lang/crates.io-index"
1558checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" 1574checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
1559dependencies = [ 1575dependencies = [
1560 "proc-macro2", 1576 "proc-macro2",
1561 "quote", 1577 "quote",
@@ -1639,12 +1655,6 @@ dependencies = [
1639] 1655]
1640 1656
1641[[package]] 1657[[package]]
1642name = "stable_deref_trait"
1643version = "1.2.0"
1644source = "registry+https://github.com/rust-lang/crates.io-index"
1645checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1646
1647[[package]]
1648name = "stdx" 1658name = "stdx"
1649version = "0.0.0" 1659version = "0.0.0"
1650dependencies = [ 1660dependencies = [
@@ -1872,17 +1882,6 @@ dependencies = [
1872] 1882]
1873 1883
1874[[package]] 1884[[package]]
1875name = "triomphe"
1876version = "0.1.2"
1877source = "registry+https://github.com/rust-lang/crates.io-index"
1878checksum = "6e9d872053cf9e5a833d8c1dd772cdc38ab66a908129d6f73c049c986161d07c"
1879dependencies = [
1880 "memoffset 0.5.6",
1881 "serde",
1882 "stable_deref_trait",
1883]
1884
1885[[package]]
1886name = "tt" 1885name = "tt"
1887version = "0.0.0" 1886version = "0.0.0"
1888dependencies = [ 1887dependencies = [
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/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/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 a4141e111..aaa7013b6 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -90,7 +90,7 @@ impl Crate {
90 } 90 }
91 91
92 pub fn root_module(self, db: &dyn HirDatabase) -> Module { 92 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
93 let module_id = db.crate_def_map(self.id).root; 93 let module_id = db.crate_def_map(self.id).root();
94 Module::new(self, module_id) 94 Module::new(self, module_id)
95 } 95 }
96 96
@@ -302,7 +302,7 @@ impl Module {
302 /// in the module tree of any target in `Cargo.toml`. 302 /// in the module tree of any target in `Cargo.toml`.
303 pub fn crate_root(self, db: &dyn HirDatabase) -> Module { 303 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
304 let def_map = db.crate_def_map(self.id.krate); 304 let def_map = db.crate_def_map(self.id.krate);
305 self.with_module_id(def_map.root) 305 self.with_module_id(def_map.root())
306 } 306 }
307 307
308 /// Iterates over all child modules. 308 /// Iterates over all child modules.
@@ -1000,7 +1000,7 @@ impl MacroDef {
1000 /// early, in `hir_expand`, where modules simply do not exist yet. 1000 /// early, in `hir_expand`, where modules simply do not exist yet.
1001 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { 1001 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1002 let krate = self.id.krate; 1002 let krate = self.id.krate;
1003 let module_id = db.crate_def_map(krate).root; 1003 let module_id = db.crate_def_map(krate).root();
1004 Some(Module::new(Crate { id: krate }, module_id)) 1004 Some(Module::new(Crate { id: krate }, module_id))
1005 } 1005 }
1006 1006
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_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 1b09ff816..c72649c41 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -207,6 +207,7 @@ impl Attrs {
207 mod_data.definition_source(db).as_ref().map(|src| match src { 207 mod_data.definition_source(db).as_ref().map(|src| match src {
208 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner, 208 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
209 ModuleSource::Module(module) => module as &dyn AttrsOwner, 209 ModuleSource::Module(module) => module as &dyn AttrsOwner,
210 ModuleSource::BlockExpr(block) => block as &dyn AttrsOwner,
210 }), 211 }),
211 ), 212 ),
212 } 213 }
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 3b2dd0f6e..2c2c999dd 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -122,7 +122,7 @@ impl Expander {
122 122
123 let mut err = None; 123 let mut err = None;
124 let call_id = 124 let call_id =
125 macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| { 125 macro_call.as_call_id_with_errors(db, self.crate_def_map.krate(), resolver, &mut |e| {
126 err.get_or_insert(e); 126 err.get_or_insert(e);
127 }); 127 });
128 let call_id = match call_id { 128 let call_id = match call_id {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index de77d5fc9..2e5d0a01e 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -6,18 +6,24 @@ use crate::{test_db::TestDB, ModuleDefId};
6use super::*; 6use super::*;
7 7
8fn lower(ra_fixture: &str) -> Arc<Body> { 8fn lower(ra_fixture: &str) -> Arc<Body> {
9 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); 9 let db = crate::test_db::TestDB::with_files(ra_fixture);
10 10
11 let krate = db.crate_graph().iter().next().unwrap(); 11 let krate = db.crate_graph().iter().next().unwrap();
12 let def_map = db.crate_def_map(krate); 12 let def_map = db.crate_def_map(krate);
13 let module = def_map.modules_for_file(file_id).next().unwrap(); 13 let mut fn_def = None;
14 let module = &def_map[module]; 14 'outer: for (_, module) in def_map.modules() {
15 let fn_def = match module.scope.declarations().next().unwrap() { 15 for decl in module.scope.declarations() {
16 ModuleDefId::FunctionId(it) => it, 16 match decl {
17 _ => panic!(), 17 ModuleDefId::FunctionId(it) => {
18 }; 18 fn_def = Some(it);
19 break 'outer;
20 }
21 _ => {}
22 }
23 }
24 }
19 25
20 db.body(fn_def.into()) 26 db.body(fn_def.unwrap().into())
21} 27}
22 28
23fn check_diagnostics(ra_fixture: &str) { 29fn check_diagnostics(ra_fixture: &str) {
@@ -42,6 +48,25 @@ fn main() { n_nuple!(1,2,3); }
42} 48}
43 49
44#[test] 50#[test]
51fn macro_resolve() {
52 // Regression test for a path resolution bug introduced with inner item handling.
53 lower(
54 r"
55macro_rules! vec {
56 () => { () };
57 ($elem:expr; $n:expr) => { () };
58 ($($x:expr),+ $(,)?) => { () };
59}
60mod m {
61 fn outer() {
62 let _ = vec![FileSet::default(); self.len()];
63 }
64}
65 ",
66 );
67}
68
69#[test]
45fn cfg_diagnostics() { 70fn cfg_diagnostics() {
46 check_diagnostics( 71 check_diagnostics(
47 r" 72 r"
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 91c8d45cd..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},
@@ -55,6 +55,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
55 #[salsa::invoke(DefMap::crate_def_map_query)] 55 #[salsa::invoke(DefMap::crate_def_map_query)]
56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; 56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
57 57
58 #[salsa::invoke(DefMap::block_def_map_query)]
59 fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>;
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>;
60 #[salsa::invoke(StructData::union_data_query)] 63 #[salsa::invoke(StructData::union_data_query)]
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 422a6eeb4..db2d125ae 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -51,7 +51,7 @@ fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<
51 if item == ItemInNs::Types(from.into()) { 51 if item == ItemInNs::Types(from.into()) {
52 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
54 } else if let Some(parent_id) = def_map.modules[from.local_id].parent { 54 } else if let Some(parent_id) = def_map[from.local_id].parent {
55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
56 if item 56 if item
57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
@@ -111,7 +111,7 @@ fn find_path_inner(
111 111
112 // - if the item is already in scope, return the name under which it is 112 // - if the item is already in scope, return the name under which it is
113 let def_map = db.crate_def_map(from.krate); 113 let def_map = db.crate_def_map(from.krate);
114 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 114 let from_scope: &crate::item_scope::ItemScope = &def_map[from.local_id].scope;
115 let scope_name = 115 let scope_name =
116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
117 if prefixed.is_none() && scope_name.is_some() { 117 if prefixed.is_none() && scope_name.is_some() {
@@ -123,7 +123,7 @@ fn find_path_inner(
123 if item 123 if item
124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
125 krate: from.krate, 125 krate: from.krate,
126 local_id: def_map.root, 126 local_id: def_map.root(),
127 })) 127 }))
128 { 128 {
129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
@@ -136,7 +136,7 @@ fn find_path_inner(
136 } 136 }
137 137
138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
139 for (name, def_id) in &def_map.extern_prelude { 139 for (name, def_id) in def_map.extern_prelude() {
140 if item == ItemInNs::Types(*def_id) { 140 if item == ItemInNs::Types(*def_id) {
141 let name = scope_name.unwrap_or_else(|| name.clone()); 141 let name = scope_name.unwrap_or_else(|| name.clone());
142 return Some(ModPath::from_segments(PathKind::Plain, vec![name])); 142 return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -144,10 +144,10 @@ fn find_path_inner(
144 } 144 }
145 145
146 // - if the item is in the prelude, return the name from there 146 // - if the item is in the prelude, return the name from there
147 if let Some(prelude_module) = def_map.prelude { 147 if let Some(prelude_module) = def_map.prelude() {
148 let prelude_def_map = db.crate_def_map(prelude_module.krate); 148 let prelude_def_map = db.crate_def_map(prelude_module.krate);
149 let prelude_scope: &crate::item_scope::ItemScope = 149 let prelude_scope: &crate::item_scope::ItemScope =
150 &prelude_def_map.modules[prelude_module.local_id].scope; 150 &prelude_def_map[prelude_module.local_id].scope;
151 if let Some((name, vis)) = prelude_scope.name_of(item) { 151 if let Some((name, vis)) = prelude_scope.name_of(item) {
152 if vis.is_visible_from(db, from) { 152 if vis.is_visible_from(db, from) {
153 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); 153 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
@@ -175,7 +175,7 @@ fn find_path_inner(
175 175
176 // - otherwise, look for modules containing (reexporting) it and import it from one of those 176 // - otherwise, look for modules containing (reexporting) it and import it from one of those
177 177
178 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 178 let crate_root = ModuleId { local_id: def_map.root(), krate: from.krate };
179 let crate_attrs = db.attrs(crate_root.into()); 179 let crate_attrs = db.attrs(crate_root.into());
180 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 180 let prefer_no_std = crate_attrs.by_key("no_std").exists();
181 let mut best_path = None; 181 let mut best_path = None;
@@ -287,7 +287,7 @@ fn find_local_import_locations(
287 287
288 // Compute the initial worklist. We start with all direct child modules of `from` as well as all 288 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
289 // of its (recursive) parent modules. 289 // of its (recursive) parent modules.
290 let data = &def_map.modules[from.local_id]; 290 let data = &def_map[from.local_id];
291 let mut worklist = data 291 let mut worklist = data
292 .children 292 .children
293 .values() 293 .values()
@@ -296,7 +296,7 @@ fn find_local_import_locations(
296 let mut parent = data.parent; 296 let mut parent = data.parent;
297 while let Some(p) = parent { 297 while let Some(p) = parent {
298 worklist.push(ModuleId { krate: from.krate, local_id: p }); 298 worklist.push(ModuleId { krate: from.krate, local_id: p });
299 parent = def_map.modules[p].parent; 299 parent = def_map[p].parent;
300 } 300 }
301 301
302 let mut seen: FxHashSet<_> = FxHashSet::default(); 302 let mut seen: FxHashSet<_> = FxHashSet::default();
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index fac0de90c..0251d016b 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -75,7 +75,7 @@ impl ImportMap {
75 75
76 // We look only into modules that are public(ly reexported), starting with the crate root. 76 // We look only into modules that are public(ly reexported), starting with the crate root.
77 let empty = ImportPath { segments: vec![] }; 77 let empty = ImportPath { segments: vec![] };
78 let root = ModuleId { krate, local_id: def_map.root }; 78 let root = ModuleId { krate, local_id: def_map.root() };
79 let mut worklist = vec![(root, empty)]; 79 let mut worklist = vec![(root, empty)];
80 while let Some((module, mod_path)) = worklist.pop() { 80 while let Some((module, mod_path)) = worklist.pop() {
81 let ext_def_map; 81 let ext_def_map;
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index ff62928df..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 }
@@ -147,6 +149,7 @@ impl ItemTree {
147 macro_defs, 149 macro_defs,
148 vis, 150 vis,
149 generics, 151 generics,
152 inner_items,
150 } = &mut **data; 153 } = &mut **data;
151 154
152 imports.shrink_to_fit(); 155 imports.shrink_to_fit();
@@ -169,6 +172,8 @@ impl ItemTree {
169 172
170 vis.arena.shrink_to_fit(); 173 vis.arena.shrink_to_fit();
171 generics.arena.shrink_to_fit(); 174 generics.arena.shrink_to_fit();
175
176 inner_items.shrink_to_fit();
172 } 177 }
173 } 178 }
174 179
@@ -191,16 +196,18 @@ impl ItemTree {
191 self.raw_attrs(of).clone().filter(db, krate) 196 self.raw_attrs(of).clone().filter(db, krate)
192 } 197 }
193 198
194 /// Returns the lowered inner items that `ast` corresponds to. 199 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
195 /// 200 match &self.data {
196 /// 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(),
197 /// to multiple items in the `ItemTree`. 202 None => None.into_iter().flatten(),
198 pub fn inner_items(&self, ast: FileAstId<ast::Item>) -> &[ModItem] { 203 }
199 &self.inner_items[&ast]
200 } 204 }
201 205
202 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { 206 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
203 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 }
204 } 211 }
205 212
206 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 {
@@ -297,6 +304,8 @@ struct ItemTreeData {
297 304
298 vis: ItemVisibilities, 305 vis: ItemVisibilities,
299 generics: GenericParamsStorage, 306 generics: GenericParamsStorage,
307
308 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
300} 309}
301 310
302#[derive(Debug, Eq, PartialEq, Hash)] 311#[derive(Debug, Eq, PartialEq, Hash)]
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 5e71ca42c..ce470fc3b 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -6,7 +6,7 @@ use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}
6use smallvec::SmallVec; 6use smallvec::SmallVec;
7use syntax::{ 7use syntax::{
8 ast::{self, ModuleItemOwner}, 8 ast::{self, ModuleItemOwner},
9 SyntaxNode, 9 SyntaxNode, WalkEvent,
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -37,7 +37,6 @@ pub(super) struct Ctx {
37 file: HirFileId, 37 file: HirFileId,
38 source_ast_id_map: Arc<AstIdMap>, 38 source_ast_id_map: Arc<AstIdMap>,
39 body_ctx: crate::body::LowerCtx, 39 body_ctx: crate::body::LowerCtx,
40 inner_items: Vec<ModItem>,
41 forced_visibility: Option<RawVisibilityId>, 40 forced_visibility: Option<RawVisibilityId>,
42} 41}
43 42
@@ -49,7 +48,6 @@ impl Ctx {
49 file, 48 file,
50 source_ast_id_map: db.ast_id_map(file), 49 source_ast_id_map: db.ast_id_map(file),
51 body_ctx: crate::body::LowerCtx::new(db, file), 50 body_ctx: crate::body::LowerCtx::new(db, file),
52 inner_items: Vec::new(),
53 forced_visibility: None, 51 forced_visibility: None,
54 } 52 }
55 } 53 }
@@ -73,8 +71,6 @@ impl Ctx {
73 } 71 }
74 72
75 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> { 73 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> {
76 assert!(inner || self.inner_items.is_empty());
77
78 // Collect inner items for 1-to-1-lowered items. 74 // Collect inner items for 1-to-1-lowered items.
79 match item { 75 match item {
80 ast::Item::Struct(_) 76 ast::Item::Struct(_)
@@ -150,14 +146,37 @@ impl Ctx {
150 146
151 fn collect_inner_items(&mut self, container: &SyntaxNode) { 147 fn collect_inner_items(&mut self, container: &SyntaxNode) {
152 let forced_vis = self.forced_visibility.take(); 148 let forced_vis = self.forced_visibility.take();
153 let mut inner_items = mem::take(&mut self.tree.inner_items); 149
154 inner_items.extend(container.descendants().skip(1).filter_map(ast::Item::cast).filter_map( 150 let mut block_stack = Vec::new();
155 |item| { 151 for event in container.preorder().skip(1) {
156 let ast_id = self.source_ast_id_map.ast_id(&item); 152 match event {
157 Some((ast_id, self.lower_mod_item(&item, true)?.0)) 153 WalkEvent::Enter(node) => {
158 }, 154 match_ast! {
159 )); 155 match node {
160 self.tree.inner_items = inner_items; 156 ast::BlockExpr(block) => {
157 block_stack.push(self.source_ast_id_map.ast_id(&block));
158 },
159 ast::Item(item) => {
160 let mod_items = self.lower_mod_item(&item, true);
161 let current_block = block_stack.last();
162 if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
163 if !mod_items.0.is_empty() {
164 self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
165 }
166 }
167 },
168 _ => {}
169 }
170 }
171 }
172 WalkEvent::Leave(node) => {
173 if ast::BlockExpr::cast(node).is_some() {
174 block_stack.pop();
175 }
176 }
177 }
178 }
179
161 self.forced_visibility = forced_vis; 180 self.forced_visibility = forced_vis;
162 } 181 }
163 182
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs
index 30188b740..9e90f745c 100644
--- a/crates/hir_def/src/lang_item.rs
+++ b/crates/hir_def/src/lang_item.rs
@@ -84,7 +84,7 @@ impl LangItems {
84 84
85 let crate_def_map = db.crate_def_map(krate); 85 let crate_def_map = db.crate_def_map(krate);
86 86
87 for (_, module_data) in crate_def_map.modules.iter() { 87 for (_, module_data) in crate_def_map.modules() {
88 for impl_def in module_data.scope.impls() { 88 for impl_def in module_data.scope.impls() {
89 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId) 89 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId)
90 } 90 }
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 769a557ad..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,
@@ -75,14 +76,16 @@ use crate::{
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 DefMap { 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>,
@@ -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}
@@ -174,24 +184,51 @@ impl 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 DefMap {
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 DefMap {
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,7 +281,12 @@ impl DefMap {
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: &DefMap, path: &str, module: LocalModuleId) { 292 fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
@@ -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 61da56340..cd68efbe6 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -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: DefMap) -> DefMap { 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: DefMap) -> DefMap
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}
@@ -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 {
@@ -1470,7 +1506,6 @@ 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
@@ -1489,6 +1524,7 @@ 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 }
@@ -1497,20 +1533,8 @@ mod tests {
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 DefMap {
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 096a7d0ac..ec90f4e65 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -104,6 +104,43 @@ impl DefMap {
104 path: &ModPath, 104 path: &ModPath,
105 shadow: BuiltinShadowMode, 105 shadow: BuiltinShadowMode,
106 ) -> ResolvePathResult { 106 ) -> ResolvePathResult {
107 let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
108 result.segment_index = Some(usize::max_value());
109
110 let mut current_map = self;
111 loop {
112 let new = current_map.resolve_path_fp_with_macro_single(
113 db,
114 mode,
115 original_module,
116 path,
117 shadow,
118 );
119
120 // Merge `new` into `result`.
121 result.resolved_def = result.resolved_def.or(new.resolved_def);
122 if result.reached_fixedpoint == ReachedFixedPoint::No {
123 result.reached_fixedpoint = new.reached_fixedpoint;
124 }
125 // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
126 result.krate = result.krate.or(new.krate);
127 result.segment_index = result.segment_index.min(new.segment_index);
128
129 match &current_map.parent {
130 Some(map) => current_map = map,
131 None => return result,
132 }
133 }
134 }
135
136 pub(super) fn resolve_path_fp_with_macro_single(
137 &self,
138 db: &dyn DefDatabase,
139 mode: ResolveMode,
140 original_module: LocalModuleId,
141 path: &ModPath,
142 shadow: BuiltinShadowMode,
143 ) -> ResolvePathResult {
107 let mut segments = path.segments.iter().enumerate(); 144 let mut segments = path.segments.iter().enumerate();
108 let mut curr_per_ns: PerNs = match path.kind { 145 let mut curr_per_ns: PerNs = match path.kind {
109 PathKind::DollarCrate(krate) => { 146 PathKind::DollarCrate(krate) => {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 723481c36..73e3a4702 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -4,11 +4,13 @@ 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};
@@ -19,12 +21,30 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
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/resolver.rs b/crates/hir_def/src/resolver.rs
index a505bf2be..b2f577649 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -146,6 +146,19 @@ impl Resolver {
146 self.resolve_module_path(db, path, BuiltinShadowMode::Module) 146 self.resolve_module_path(db, path, BuiltinShadowMode::Module)
147 } 147 }
148 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
149 pub fn resolve_path_in_type_ns( 162 pub fn resolve_path_in_type_ns(
150 &self, 163 &self,
151 db: &dyn DefDatabase, 164 db: &dyn DefDatabase,
@@ -416,7 +429,7 @@ impl Resolver {
416 let mut traits = FxHashSet::default(); 429 let mut traits = FxHashSet::default();
417 for scope in &self.scopes { 430 for scope in &self.scopes {
418 if let Scope::ModuleScope(m) = scope { 431 if let Scope::ModuleScope(m) = scope {
419 if let Some(prelude) = m.crate_def_map.prelude { 432 if let Some(prelude) = m.crate_def_map.prelude() {
420 let prelude_def_map = db.crate_def_map(prelude.krate); 433 let prelude_def_map = db.crate_def_map(prelude.krate);
421 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 434 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
422 } 435 }
@@ -446,11 +459,11 @@ impl Resolver {
446 459
447 pub fn module(&self) -> Option<ModuleId> { 460 pub fn module(&self) -> Option<ModuleId> {
448 let (def_map, local_id) = self.module_scope()?; 461 let (def_map, local_id) = self.module_scope()?;
449 Some(ModuleId { krate: def_map.krate, local_id }) 462 Some(ModuleId { krate: def_map.krate(), local_id })
450 } 463 }
451 464
452 pub fn krate(&self) -> Option<CrateId> { 465 pub fn krate(&self) -> Option<CrateId> {
453 self.module_scope().map(|t| t.0.krate) 466 self.module_scope().map(|t| t.0.krate())
454 } 467 }
455 468
456 pub fn where_predicates_in_scope<'a>( 469 pub fn where_predicates_in_scope<'a>(
@@ -509,13 +522,13 @@ impl Scope {
509 seen.insert((name.clone(), scope)); 522 seen.insert((name.clone(), scope));
510 f(name.clone(), ScopeDef::PerNs(scope)); 523 f(name.clone(), ScopeDef::PerNs(scope));
511 }); 524 });
512 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 525 m.crate_def_map.extern_prelude().for_each(|(name, &def)| {
513 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 526 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
514 }); 527 });
515 BUILTIN_SCOPE.iter().for_each(|(name, &def)| { 528 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
516 f(name.clone(), ScopeDef::PerNs(def)); 529 f(name.clone(), ScopeDef::PerNs(def));
517 }); 530 });
518 if let Some(prelude) = m.crate_def_map.prelude { 531 if let Some(prelude) = m.crate_def_map.prelude() {
519 let prelude_def_map = db.crate_def_map(prelude.krate); 532 let prelude_def_map = db.crate_def_map(prelude.krate);
520 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)| {
521 let seen_tuple = (name.clone(), def); 534 let seen_tuple = (name.clone(), def);
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_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/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_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/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/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 8d08e4763..16fa828ad 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -130,8 +130,7 @@ impl NavigationTarget {
130 node: InFile<&dyn ast::NameOwner>, 130 node: InFile<&dyn ast::NameOwner>,
131 kind: SymbolKind, 131 kind: SymbolKind,
132 ) -> NavigationTarget { 132 ) -> NavigationTarget {
133 let name = 133 let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
134 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
135 let focus_range = 134 let focus_range =
136 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);
137 let frange = node.map(|it| it.syntax()).original_file_range(db); 136 let frange = node.map(|it| it.syntax()).original_file_range(db);
@@ -272,6 +271,7 @@ impl ToNav for hir::Module {
272 ModuleSource::Module(node) => { 271 ModuleSource::Module(node) => {
273 (node.syntax(), node.name().map(|it| it.syntax().text_range())) 272 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
274 } 273 }
274 ModuleSource::BlockExpr(node) => (node.syntax(), None),
275 }; 275 };
276 let frange = src.with_value(syntax).original_file_range(db); 276 let frange = src.with_value(syntax).original_file_range(db);
277 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)
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 1f08d7810..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}
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 17a540972..2d722dee0 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -213,8 +213,8 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange
213 let ws_text = ws.text(); 213 let ws_text = ws.text();
214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start(); 214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start();
215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); 215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start();
216 let ws_suffix = &ws_text.as_str()[suffix]; 216 let ws_suffix = &ws_text[suffix];
217 let ws_prefix = &ws_text.as_str()[prefix]; 217 let ws_prefix = &ws_text[prefix];
218 if ws_text.contains('\n') && !ws_suffix.contains('\n') { 218 if ws_text.contains('\n') && !ws_suffix.contains('\n') {
219 if let Some(node) = ws.next_sibling_or_token() { 219 if let Some(node) = ws.next_sibling_or_token() {
220 let start = match ws_prefix.rfind('\n') { 220 let start = match ws_prefix.rfind('\n') {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index a1d2bce1d..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,28 +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 }, 53 },
58 _ => return None, 54 _ => return None,
59 } 55 }
60 }; 56 };
61 57
62 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 58 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
63} 59}
64 60
65fn def_for_doc_comment( 61fn def_for_doc_comment(
@@ -120,42 +116,16 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
120 } 116 }
121} 117}
122 118
123#[derive(Debug)]
124pub(crate) enum ReferenceResult {
125 Exact(NavigationTarget),
126 Approximate(Vec<NavigationTarget>),
127}
128
129impl ReferenceResult {
130 fn to_vec(self) -> Vec<NavigationTarget> {
131 match self {
132 ReferenceResult::Exact(target) => vec![target],
133 ReferenceResult::Approximate(vec) => vec,
134 }
135 }
136}
137
138pub(crate) fn reference_definition( 119pub(crate) fn reference_definition(
139 sema: &Semantics<RootDatabase>, 120 sema: &Semantics<RootDatabase>,
140 name_ref: Either<&ast::Lifetime, &ast::NameRef>, 121 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
141) -> ReferenceResult { 122) -> Option<NavigationTarget> {
142 let name_kind = name_ref.either( 123 let name_kind = name_ref.either(
143 |lifetime| NameRefClass::classify_lifetime(sema, lifetime), 124 |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
144 |name_ref| NameRefClass::classify(sema, name_ref), 125 |name_ref| NameRefClass::classify(sema, name_ref),
145 ); 126 )?;
146 if let Some(def) = name_kind { 127 let def = name_kind.referenced(sema.db);
147 let def = def.referenced(sema.db); 128 def.try_to_nav(sema.db)
148 return match def.try_to_nav(sema.db) {
149 Some(nav) => ReferenceResult::Exact(nav),
150 None => ReferenceResult::Approximate(Vec::new()),
151 };
152 }
153
154 // Fallback index based approach:
155 let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
156 let navs =
157 symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
158 ReferenceResult::Approximate(navs)
159} 129}
160 130
161#[cfg(test)] 131#[cfg(test)]
@@ -192,12 +162,12 @@ mod tests {
192 fn goto_def_for_extern_crate() { 162 fn goto_def_for_extern_crate() {
193 check( 163 check(
194 r#" 164 r#"
195 //- /main.rs crate:main deps:std 165//- /main.rs crate:main deps:std
196 extern crate std$0; 166extern crate std$0;
197 //- /std/lib.rs crate:std 167//- /std/lib.rs crate:std
198 // empty 168// empty
199 //^ file 169//^ file
200 "#, 170"#,
201 ) 171 )
202 } 172 }
203 173
@@ -205,12 +175,12 @@ mod tests {
205 fn goto_def_for_renamed_extern_crate() { 175 fn goto_def_for_renamed_extern_crate() {
206 check( 176 check(
207 r#" 177 r#"
208 //- /main.rs crate:main deps:std 178//- /main.rs crate:main deps:std
209 extern crate std as abc$0; 179extern crate std as abc$0;
210 //- /std/lib.rs crate:std 180//- /std/lib.rs crate:std
211 // empty 181// empty
212 //^ file 182//^ file
213 "#, 183"#,
214 ) 184 )
215 } 185 }
216 186
@@ -297,13 +267,13 @@ fn bar() {
297 fn goto_def_for_macros_from_other_crates() { 267 fn goto_def_for_macros_from_other_crates() {
298 check( 268 check(
299 r#" 269 r#"
300//- /lib.rs 270//- /lib.rs crate:main deps:foo
301use foo::foo; 271use foo::foo;
302fn bar() { 272fn bar() {
303 $0foo!(); 273 $0foo!();
304} 274}
305 275
306//- /foo/lib.rs 276//- /foo/lib.rs crate:foo
307#[macro_export] 277#[macro_export]
308macro_rules! foo { () => { () } } 278macro_rules! foo { () => { () } }
309 //^^^ 279 //^^^
@@ -315,10 +285,10 @@ macro_rules! foo { () => { () } }
315 fn goto_def_for_macros_in_use_tree() { 285 fn goto_def_for_macros_in_use_tree() {
316 check( 286 check(
317 r#" 287 r#"
318//- /lib.rs 288//- /lib.rs crate:main deps:foo
319use foo::foo$0; 289use foo::foo$0;
320 290
321//- /foo/lib.rs 291//- /foo/lib.rs crate:foo
322#[macro_export] 292#[macro_export]
323macro_rules! foo { () => { () } } 293macro_rules! foo { () => { () } }
324 //^^^ 294 //^^^
@@ -976,10 +946,10 @@ type Alias<T> = T$0;
976 fn goto_def_for_macro_container() { 946 fn goto_def_for_macro_container() {
977 check( 947 check(
978 r#" 948 r#"
979//- /lib.rs 949//- /lib.rs crate:main deps:foo
980foo::module$0::mac!(); 950foo::module$0::mac!();
981 951
982//- /foo/lib.rs 952//- /foo/lib.rs crate:foo
983pub mod module { 953pub mod module {
984 //^^^^^^ 954 //^^^^^^
985 #[macro_export] 955 #[macro_export]
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 44ebdbd35..d47a4cb0f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -321,6 +321,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
321 match it.definition_source(db).value { 321 match it.definition_source(db).value {
322 ModuleSource::Module(it) => it.short_label(), 322 ModuleSource::Module(it) => it.short_label(),
323 ModuleSource::SourceFile(it) => it.short_label(), 323 ModuleSource::SourceFile(it) => it.short_label(),
324 ModuleSource::BlockExpr(it) => it.short_label(),
324 }, 325 },
325 mod_path, 326 mod_path,
326 ), 327 ),
@@ -1825,6 +1826,35 @@ pub struct B$0ar
1825 "#]], 1826 "#]],
1826 ); 1827 );
1827 } 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 }
1828 1858
1829 #[test] 1859 #[test]
1830 fn test_hover_external_url() { 1860 fn test_hover_external_url() {
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/runnables.rs b/crates/ide/src/runnables.rs
index e282b31af..33170906d 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,6 +9,7 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 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},
@@ -96,28 +97,26 @@ impl Runnable {
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 module = match sema.to_module_def(file_id) { 99 let module = match sema.to_module_def(file_id) {
99 None => return vec![], 100 None => return Vec::new(),
100 Some(it) => it, 101 Some(it) => it,
101 }; 102 };
102 103
103 runnables_mod(&sema, module) 104 let mut res = Vec::new();
105 runnables_mod(&sema, &mut res, module);
106 res
104} 107}
105 108
106fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> { 109fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
107 let mut res: Vec<Runnable> = module 110 acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
108 .declarations(sema.db) 111 let runnable = match def {
109 .into_iter() 112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
110 .filter_map(|def| { 113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
111 let runnable = match def { 114 _ => None,
112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it), 115 };
113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it), 116 runnable.or_else(|| module_def_doctest(&sema, def))
114 _ => None, 117 }));
115 };
116 runnable.or_else(|| module_def_doctest(&sema, def))
117 })
118 .collect();
119 118
120 res.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map( 119 acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
121 |def| match def { 120 |def| match def {
122 hir::AssocItem::Function(it) => { 121 hir::AssocItem::Function(it) => {
123 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) 122 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
@@ -127,12 +126,15 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Run
127 }, 126 },
128 )); 127 ));
129 128
130 res.extend(module.declarations(sema.db).into_iter().flat_map(|def| match def { 129 for def in module.declarations(sema.db) {
131 hir::ModuleDef::Module(it) => runnables_mod(sema, it), 130 if let hir::ModuleDef::Module(submodule) = def {
132 _ => vec![], 131 match submodule.definition_source(sema.db).value {
133 })); 132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
134 133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules),
135 res 134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 }
136 }
137 }
136} 138}
137 139
138pub(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> {
@@ -326,6 +328,7 @@ fn has_test_function_or_multiple_test_submodules(
326#[cfg(test)] 328#[cfg(test)]
327mod tests { 329mod tests {
328 use expect_test::{expect, Expect}; 330 use expect_test::{expect, Expect};
331 use test_utils::mark;
329 332
330 use crate::fixture; 333 use crate::fixture;
331 334
@@ -1050,4 +1053,25 @@ mod tests {
1050 "#]], 1053 "#]],
1051 ); 1054 );
1052 } 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 }
1053} 1077}
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index e10d7c3a4..137c38c0d 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -38,6 +38,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
38 format_to!(buf, "{}\n", syntax_tree_stats(db)); 38 format_to!(buf, "{}\n", syntax_tree_stats(db));
39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db)); 39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db));
40 format_to!(buf, "{} total\n", memory_usage()); 40 format_to!(buf, "{} total\n", memory_usage());
41 format_to!(buf, "\ncounts:\n{}", profile::countme::get_all());
41 42
42 if let Some(file_id) = file_id { 43 if let Some(file_id) = file_id {
43 format_to!(buf, "\nfile info:\n"); 44 format_to!(buf, "\nfile info:\n");
@@ -60,6 +61,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
60 None => format_to!(buf, "does not belong to any crate"), 61 None => format_to!(buf, "does not belong to any crate"),
61 } 62 }
62 } 63 }
64
63 buf 65 buf
64} 66}
65 67
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 63bff6376..8c67a0863 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -31,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
31 let parent = string.syntax().parent(); 31 let parent = string.syntax().parent();
32 32
33 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()?;
34 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { 34 if !matches!(name.text(), "format_args" | "format_args_nl") {
35 return None; 35 return None;
36 } 36 }
37 37
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_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 d9875ffef..a8091dbee 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -343,7 +343,7 @@ impl NameRefClass {
343 hir::AssocItem::TypeAlias(it) => Some(*it), 343 hir::AssocItem::TypeAlias(it) => Some(*it),
344 _ => None, 344 _ => None,
345 }) 345 })
346 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) 346 .find(|alias| &alias.name(sema.db).to_string() == name_ref.text())
347 { 347 {
348 return Some(NameRefClass::Definition(Definition::ModuleDef( 348 return Some(NameRefClass::Definition(Definition::ModuleDef(
349 ModuleDef::TypeAlias(ty), 349 ModuleDef::TypeAlias(ty),
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/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 51002e7b8..0cdc175be 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -507,7 +507,7 @@ impl SrcToken for SynToken {
507 } 507 }
508 } 508 }
509 fn to_text(&self) -> SmolStr { 509 fn to_text(&self) -> SmolStr {
510 self.token().text().clone() 510 self.token().text().into()
511 } 511 }
512} 512}
513 513
@@ -682,10 +682,8 @@ impl<'a> TreeSink for TtTreeSink<'a> {
682 self.text_pos += TextSize::of(text); 682 self.text_pos += TextSize::of(text);
683 } 683 }
684 684
685 let text = SmolStr::new(self.buf.as_str()); 685 self.inner.token(kind, self.buf.as_str());
686 self.buf.clear(); 686 self.buf.clear();
687 self.inner.token(kind, text);
688
689 // Add whitespace between adjoint puncts 687 // Add whitespace between adjoint puncts
690 let next = last.bump(); 688 let next = last.bump();
691 if let ( 689 if let (
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index f7231c2b8..cc7da27f7 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -14,6 +14,7 @@ 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"] }
17jemalloc-ctl = { version = "0.3.3", optional = true } 18jemalloc-ctl = { version = "0.3.3", optional = true }
18 19
19[target.'cfg(target_os = "linux")'.dependencies] 20[target.'cfg(target_os = "linux")'.dependencies]
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/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 970a7e140..525c336e6 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -6,6 +6,7 @@ mod project_json;
6mod sysroot; 6mod sysroot;
7mod workspace; 7mod workspace;
8mod rustc_cfg; 8mod rustc_cfg;
9mod build_data;
9 10
10use std::{ 11use std::{
11 fs::{read_dir, ReadDir}, 12 fs::{read_dir, ReadDir},
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 8e0481ae9..bc5041e5a 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -178,7 +178,7 @@ impl ProjectWorkspace {
178 let pkg_root = cargo[pkg].root().to_path_buf(); 178 let pkg_root = cargo[pkg].root().to_path_buf();
179 179
180 let mut include = vec![pkg_root.clone()]; 180 let mut include = vec![pkg_root.clone()];
181 include.extend(cargo[pkg].out_dir.clone()); 181 include.extend(cargo[pkg].build_data.out_dir.clone());
182 182
183 let mut exclude = vec![pkg_root.join(".git")]; 183 let mut exclude = vec![pkg_root.join(".git")];
184 if is_member { 184 if is_member {
@@ -481,26 +481,24 @@ fn add_target_crate_root(
481 let edition = pkg.edition; 481 let edition = pkg.edition;
482 let cfg_options = { 482 let cfg_options = {
483 let mut opts = cfg_options.clone(); 483 let mut opts = cfg_options.clone();
484 for feature in pkg.features.iter() { 484 for feature in pkg.active_features.iter() {
485 opts.insert_key_value("feature".into(), feature.into()); 485 opts.insert_key_value("feature".into(), feature.into());
486 } 486 }
487 opts.extend(pkg.cfgs.iter().cloned()); 487 opts.extend(pkg.build_data.cfgs.iter().cloned());
488 opts 488 opts
489 }; 489 };
490 490
491 let mut env = Env::default(); 491 let mut env = Env::default();
492 for (k, v) in &pkg.envs { 492 for (k, v) in &pkg.build_data.envs {
493 env.set(k, v.clone()); 493 env.set(k, v.clone());
494 } 494 }
495 if let Some(out_dir) = &pkg.out_dir {
496 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
497 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
498 env.set("OUT_DIR", out_dir);
499 }
500 }
501 495
502 let proc_macro = 496 let proc_macro = pkg
503 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();
504 502
505 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); 503 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
506 let crate_id = crate_graph.add_crate_root( 504 let crate_id = crate_graph.add_crate_root(
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 3cb45b030..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"] }
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/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/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 10cbd7eeb..809452e6d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -859,6 +859,23 @@ pub(crate) fn handle_formatting(
859 RustfmtConfig::Rustfmt { extra_args } => { 859 RustfmtConfig::Rustfmt { extra_args } => {
860 let mut cmd = process::Command::new(toolchain::rustfmt()); 860 let mut cmd = process::Command::new(toolchain::rustfmt());
861 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 }
862 if let Some(&crate_id) = crate_ids.first() { 879 if let Some(&crate_id) = crate_ids.first() {
863 // Assume all crates are in the same edition 880 // Assume all crates are in the same edition
864 let edition = snap.analysis.crate_edition(crate_id)?; 881 let edition = snap.analysis.crate_edition(crate_id)?;
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/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/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 738c92a5b..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 }
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/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/xtask/Cargo.toml b/xtask/Cargo.toml
index 4abc7b053..4e6b439fd 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -12,7 +12,7 @@ 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.9" 18ungrammar = "1.9"
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/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 c3e5c7326..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};
@@ -76,7 +77,7 @@ FLAGS:
76 77
77 let client_opt = args.opt_value_from_str("--client")?; 78 let client_opt = args.opt_value_from_str("--client")?;
78 79
79 args.finish()?; 80 finish_args(args)?;
80 81
81 InstallCmd { 82 InstallCmd {
82 client: if server { None } else { Some(client_opt.unwrap_or_default()) }, 83 client: if server { None } else { Some(client_opt.unwrap_or_default()) },
@@ -86,53 +87,53 @@ FLAGS:
86 } 87 }
87 "codegen" => { 88 "codegen" => {
88 let features = args.contains("--features"); 89 let features = args.contains("--features");
89 args.finish()?; 90 finish_args(args)?;
90 CodegenCmd { features }.run() 91 CodegenCmd { features }.run()
91 } 92 }
92 "format" => { 93 "format" => {
93 args.finish()?; 94 finish_args(args)?;
94 run_rustfmt(Mode::Overwrite) 95 run_rustfmt(Mode::Overwrite)
95 } 96 }
96 "install-pre-commit-hook" => { 97 "install-pre-commit-hook" => {
97 args.finish()?; 98 finish_args(args)?;
98 pre_commit::install_hook() 99 pre_commit::install_hook()
99 } 100 }
100 "lint" => { 101 "lint" => {
101 args.finish()?; 102 finish_args(args)?;
102 run_clippy() 103 run_clippy()
103 } 104 }
104 "fuzz-tests" => { 105 "fuzz-tests" => {
105 args.finish()?; 106 finish_args(args)?;
106 run_fuzzer() 107 run_fuzzer()
107 } 108 }
108 "pre-cache" => { 109 "pre-cache" => {
109 args.finish()?; 110 finish_args(args)?;
110 PreCacheCmd.run() 111 PreCacheCmd.run()
111 } 112 }
112 "release" => { 113 "release" => {
113 let dry_run = args.contains("--dry-run"); 114 let dry_run = args.contains("--dry-run");
114 args.finish()?; 115 finish_args(args)?;
115 ReleaseCmd { dry_run }.run() 116 ReleaseCmd { dry_run }.run()
116 } 117 }
117 "promote" => { 118 "promote" => {
118 let dry_run = args.contains("--dry-run"); 119 let dry_run = args.contains("--dry-run");
119 args.finish()?; 120 finish_args(args)?;
120 PromoteCmd { dry_run }.run() 121 PromoteCmd { dry_run }.run()
121 } 122 }
122 "dist" => { 123 "dist" => {
123 let nightly = args.contains("--nightly"); 124 let nightly = args.contains("--nightly");
124 let client_version: Option<String> = args.opt_value_from_str("--client")?; 125 let client_version: Option<String> = args.opt_value_from_str("--client")?;
125 args.finish()?; 126 finish_args(args)?;
126 DistCmd { nightly, client_version }.run() 127 DistCmd { nightly, client_version }.run()
127 } 128 }
128 "metrics" => { 129 "metrics" => {
129 let dry_run = args.contains("--dry-run"); 130 let dry_run = args.contains("--dry-run");
130 args.finish()?; 131 finish_args(args)?;
131 MetricsCmd { dry_run }.run() 132 MetricsCmd { dry_run }.run()
132 } 133 }
133 "bb" => { 134 "bb" => {
134 let suffix: String = args.free_from_str()?.unwrap(); 135 let suffix: String = args.free_from_str()?;
135 args.finish()?; 136 finish_args(args)?;
136 cmd!("cargo build --release").run()?; 137 cmd!("cargo build --release").run()?;
137 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?; 138 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?;
138 Ok(()) 139 Ok(())
@@ -161,3 +162,10 @@ SUBCOMMANDS:
161 } 162 }
162 } 163 }
163} 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);