aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml72
-rw-r--r--Cargo.lock100
-rw-r--r--crates/assists/src/handlers/early_return.rs2
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs12
-rw-r--r--crates/assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/assists/src/handlers/infer_function_return_type.rs4
-rw-r--r--crates/assists/src/handlers/inline_function.rs202
-rw-r--r--crates/assists/src/handlers/move_guard.rs4
-rw-r--r--crates/assists/src/handlers/pull_assignment_up.rs (renamed from crates/assists/src/handlers/extract_assignment.rs)151
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs8
-rw-r--r--crates/assists/src/lib.rs10
-rw-r--r--crates/assists/src/tests/generated.rs81
-rw-r--r--crates/assists/src/utils.rs2
-rw-r--r--crates/completion/src/completions/unqualified_path.rs3
-rw-r--r--crates/completion/src/context.rs2
-rw-r--r--crates/hir/src/code_model.rs57
-rw-r--r--crates/hir_def/src/attr.rs56
-rw-r--r--crates/hir_def/src/body/lower.rs2
-rw-r--r--crates/hir_def/src/db.rs11
-rw-r--r--crates/hir_def/src/diagnostics.rs4
-rw-r--r--crates/hir_def/src/import_map.rs287
-rw-r--r--crates/hir_def/src/item_scope.rs3
-rw-r--r--crates/hir_def/src/nameres/collector.rs33
-rw-r--r--crates/hir_expand/src/proc_macro.rs10
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/display.rs4
-rw-r--r--crates/ide/src/display/short_label.rs11
-rw-r--r--crates/ide/src/hover.rs268
-rw-r--r--crates/ide_db/src/imports_locator.rs21
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs6
-rw-r--r--crates/mbe/src/subtree_source.rs152
-rw-r--r--crates/mbe/src/syntax_bridge.rs51
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs159
-rw-r--r--crates/rust-analyzer/src/handlers.rs21
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs6
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs24
-rw-r--r--crates/ssr/src/parsing.rs15
-rw-r--r--crates/ssr/src/tests.rs91
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/lib.rs9
-rw-r--r--crates/syntax/src/tests.rs9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast69
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs5
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast12
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast22
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs3
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast10
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs1
-rw-r--r--crates/tt/src/buffer.rs105
-rw-r--r--docs/user/generated_config.adoc106
-rw-r--r--docs/user/manual.adoc3
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/config.ts4
-rw-r--r--editors/code/src/main.ts19
-rw-r--r--xtask/Cargo.toml2
82 files changed, 1745 insertions, 694 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 889ada401..ece1fd364 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -15,8 +15,8 @@ env:
15 RUSTUP_MAX_RETRIES: 10 15 RUSTUP_MAX_RETRIES: 10
16 16
17jobs: 17jobs:
18 dist-windows: 18 dist-x86_64-pc-windows-msvc:
19 name: dist (Windows) 19 name: dist (x86_64-pc-windows-msvc)
20 runs-on: windows-latest 20 runs-on: windows-latest
21 env: 21 env:
22 RA_TARGET: x86_64-pc-windows-msvc 22 RA_TARGET: x86_64-pc-windows-msvc
@@ -45,11 +45,41 @@ jobs:
45 - name: Upload artifacts 45 - name: Upload artifacts
46 uses: actions/upload-artifact@v1 46 uses: actions/upload-artifact@v1
47 with: 47 with:
48 name: dist-windows-latest 48 name: dist-x86_64-pc-windows-msvc
49 path: ./dist 49 path: ./dist
50 50
51 dist-ubuntu: 51 dist-aarch64-pc-windows-msvc:
52 name: dist (Ubuntu 16.04) 52 name: dist (aarch64-pc-windows-msvc)
53 runs-on: windows-latest
54 env:
55 RA_TARGET: aarch64-pc-windows-msvc
56
57 steps:
58 - name: Checkout repository
59 uses: actions/checkout@v2
60
61 - name: Rename existing rust toolchain
62 run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old
63
64 - name: Install Rust toolchain
65 uses: actions-rs/toolchain@v1
66 with:
67 toolchain: stable
68 target: aarch64-pc-windows-msvc
69 profile: minimal
70 override: true
71
72 - name: Dist
73 run: cargo xtask dist
74
75 - name: Upload artifacts
76 uses: actions/upload-artifact@v1
77 with:
78 name: dist-aarch64-pc-windows-msvc
79 path: ./dist
80
81 dist-x86_64-unknown-linux-gnu:
82 name: dist (x86_64-unknown-linux-gnu)
53 runs-on: ubuntu-16.04 83 runs-on: ubuntu-16.04
54 env: 84 env:
55 RA_TARGET: x86_64-unknown-linux-gnu 85 RA_TARGET: x86_64-unknown-linux-gnu
@@ -85,11 +115,11 @@ jobs:
85 - name: Upload artifacts 115 - name: Upload artifacts
86 uses: actions/upload-artifact@v1 116 uses: actions/upload-artifact@v1
87 with: 117 with:
88 name: dist-ubuntu-16.04 118 name: dist-x86_64-unknown-linux-gnu
89 path: ./dist 119 path: ./dist
90 120
91 dist-macos-latest: 121 dist-x86_64-apple-darwin:
92 name: dist (MacOS latest) 122 name: dist (x86_64-apple-darwin)
93 runs-on: macos-latest 123 runs-on: macos-latest
94 env: 124 env:
95 RA_TARGET: x86_64-apple-darwin 125 RA_TARGET: x86_64-apple-darwin
@@ -111,12 +141,12 @@ jobs:
111 - name: Upload artifacts 141 - name: Upload artifacts
112 uses: actions/upload-artifact@v1 142 uses: actions/upload-artifact@v1
113 with: 143 with:
114 name: dist-macos-latest 144 name: dist-x86_64-apple-darwin
115 path: ./dist 145 path: ./dist
116 146
117 dist-macos-11: 147 dist-aarch64-apple-darwin:
118 name: dist (MacOS 11.0) 148 name: dist (aarch64-apple-darwin)
119 runs-on: macos-11.0 149 runs-on: macos-latest
120 env: 150 env:
121 RA_TARGET: aarch64-apple-darwin 151 RA_TARGET: aarch64-apple-darwin
122 152
@@ -133,18 +163,18 @@ jobs:
133 override: true 163 override: true
134 164
135 - name: Dist 165 - name: Dist
136 run: cargo xtask dist 166 run: SDKROOT=$(xcrun -sdk macosx11.0 --show-sdk-path) MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx11.0 --show-sdk-platform-version) cargo xtask dist
137 167
138 - name: Upload artifacts 168 - name: Upload artifacts
139 uses: actions/upload-artifact@v1 169 uses: actions/upload-artifact@v1
140 with: 170 with:
141 name: dist-macos-11.0 171 name: dist-aarch64-apple-darwin
142 path: ./dist 172 path: ./dist
143 173
144 publish: 174 publish:
145 name: publish 175 name: publish
146 runs-on: ubuntu-16.04 176 runs-on: ubuntu-16.04
147 needs: ['dist-windows', 'dist-ubuntu', 'dist-macos-latest', 'dist-macos-11'] 177 needs: ['dist-x86_64-pc-windows-msvc', 'dist-aarch64-pc-windows-msvc', 'dist-x86_64-unknown-linux-gnu', 'dist-x86_64-apple-darwin', 'dist-aarch64-apple-darwin']
148 steps: 178 steps:
149 - name: Install Nodejs 179 - name: Install Nodejs
150 uses: actions/setup-node@v1 180 uses: actions/setup-node@v1
@@ -165,19 +195,23 @@ jobs:
165 195
166 - uses: actions/download-artifact@v1 196 - uses: actions/download-artifact@v1
167 with: 197 with:
168 name: dist-macos-11.0 198 name: dist-aarch64-apple-darwin
199 path: dist
200 - uses: actions/download-artifact@v1
201 with:
202 name: dist-x86_64-apple-darwin
169 path: dist 203 path: dist
170 - uses: actions/download-artifact@v1 204 - uses: actions/download-artifact@v1
171 with: 205 with:
172 name: dist-macos-latest 206 name: dist-x86_64-unknown-linux-gnu
173 path: dist 207 path: dist
174 - uses: actions/download-artifact@v1 208 - uses: actions/download-artifact@v1
175 with: 209 with:
176 name: dist-ubuntu-16.04 210 name: dist-x86_64-pc-windows-msvc
177 path: dist 211 path: dist
178 - uses: actions/download-artifact@v1 212 - uses: actions/download-artifact@v1
179 with: 213 with:
180 name: dist-windows-latest 214 name: dist-aarch64-pc-windows-msvc
181 path: dist 215 path: dist
182 - run: ls -al ./dist 216 - run: ls -al ./dist
183 217
diff --git a/Cargo.lock b/Cargo.lock
index 4aaee3ab1..fe45f9dec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,9 +2,9 @@
2# It is not intended for manual editing. 2# It is not intended for manual editing.
3[[package]] 3[[package]]
4name = "addr2line" 4name = "addr2line"
5version = "0.14.0" 5version = "0.14.1"
6source = "registry+https://github.com/rust-lang/crates.io-index" 6source = "registry+https://github.com/rust-lang/crates.io-index"
7checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" 7checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7"
8dependencies = [ 8dependencies = [
9 "gimli", 9 "gimli",
10] 10]
@@ -132,7 +132,7 @@ version = "0.12.0"
132source = "registry+https://github.com/rust-lang/crates.io-index" 132source = "registry+https://github.com/rust-lang/crates.io-index"
133checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" 133checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345"
134dependencies = [ 134dependencies = [
135 "semver 0.11.0", 135 "semver",
136 "serde", 136 "serde",
137 "serde_json", 137 "serde_json",
138] 138]
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.45.0" 171version = "0.47.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "ec7dacf94958d1a930b95d049d9443860859af59eadc77849392093eb577bcee" 173checksum = "3f00f6342a387edc822002d36a381e117afcac9f744951ff75fbf4a218edea5c"
174dependencies = [ 174dependencies = [
175 "proc-macro2", 175 "proc-macro2",
176 "quote", 176 "quote",
@@ -180,9 +180,9 @@ dependencies = [
180 180
181[[package]] 181[[package]]
182name = "chalk-ir" 182name = "chalk-ir"
183version = "0.45.0" 183version = "0.47.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "a1a5b38ede247def17da87f4badb62396a5753db6048e2011d3089d8b3796c67" 185checksum = "c686e69913591ae753e5526e73cbee39db3d9b0a92cc9078ab780cabf1c70aa9"
186dependencies = [ 186dependencies = [
187 "bitflags", 187 "bitflags",
188 "chalk-derive", 188 "chalk-derive",
@@ -191,9 +191,9 @@ dependencies = [
191 191
192[[package]] 192[[package]]
193name = "chalk-recursive" 193name = "chalk-recursive"
194version = "0.45.0" 194version = "0.47.0"
195source = "registry+https://github.com/rust-lang/crates.io-index" 195source = "registry+https://github.com/rust-lang/crates.io-index"
196checksum = "7a18db146d7a023edc20ad094e8c2284451f7888719645004979617d1f17c041" 196checksum = "310fdcac0340dab4163b766baa8067266e3b909108d1ac1b5246c033bde63975"
197dependencies = [ 197dependencies = [
198 "chalk-derive", 198 "chalk-derive",
199 "chalk-ir", 199 "chalk-ir",
@@ -204,9 +204,9 @@ dependencies = [
204 204
205[[package]] 205[[package]]
206name = "chalk-solve" 206name = "chalk-solve"
207version = "0.45.0" 207version = "0.47.0"
208source = "registry+https://github.com/rust-lang/crates.io-index" 208source = "registry+https://github.com/rust-lang/crates.io-index"
209checksum = "7f73e0de04a0f394e47ed8118e00541bcf681d7c3c2ef500fa743eb4cf3a4850" 209checksum = "c3c3252116111c3548f1164ab8d98c67c49848b3bde10dd11b650fd023e91c72"
210dependencies = [ 210dependencies = [
211 "chalk-derive", 211 "chalk-derive",
212 "chalk-ir", 212 "chalk-ir",
@@ -488,19 +488,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
488checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 488checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
489 489
490[[package]] 490[[package]]
491name = "generator"
492version = "0.6.23"
493source = "registry+https://github.com/rust-lang/crates.io-index"
494checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc"
495dependencies = [
496 "cc",
497 "libc",
498 "log",
499 "rustc_version",
500 "winapi 0.3.9",
501]
502
503[[package]]
504name = "gimli" 491name = "gimli"
505version = "0.23.0" 492version = "0.23.0"
506source = "registry+https://github.com/rust-lang/crates.io-index" 493source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -834,19 +821,6 @@ dependencies = [
834] 821]
835 822
836[[package]] 823[[package]]
837name = "loom"
838version = "0.3.6"
839source = "registry+https://github.com/rust-lang/crates.io-index"
840checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed"
841dependencies = [
842 "cfg-if 0.1.10",
843 "generator",
844 "scoped-tls",
845 "serde",
846 "serde_json",
847]
848
849[[package]]
850name = "lsp-server" 824name = "lsp-server"
851version = "0.5.0" 825version = "0.5.0"
852source = "registry+https://github.com/rust-lang/crates.io-index" 826source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1394,9 +1368,9 @@ dependencies = [
1394 1368
1395[[package]] 1369[[package]]
1396name = "rustc-ap-rustc_lexer" 1370name = "rustc-ap-rustc_lexer"
1397version = "695.0.0" 1371version = "697.0.0"
1398source = "registry+https://github.com/rust-lang/crates.io-index" 1372source = "registry+https://github.com/rust-lang/crates.io-index"
1399checksum = "390bad134705b0bff02cd9541ac66df751a91c3cc734c3369cd6151ca269caed" 1373checksum = "67adbe260a0a11910624d6d28c0304fcf7b063e666682111005c83b09f73429d"
1400dependencies = [ 1374dependencies = [
1401 "unicode-xid", 1375 "unicode-xid",
1402] 1376]
@@ -1414,15 +1388,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1414checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1388checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
1415 1389
1416[[package]] 1390[[package]]
1417name = "rustc_version"
1418version = "0.2.3"
1419source = "registry+https://github.com/rust-lang/crates.io-index"
1420checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
1421dependencies = [
1422 "semver 0.9.0",
1423]
1424
1425[[package]]
1426name = "ryu" 1391name = "ryu"
1427version = "1.0.5" 1392version = "1.0.5"
1428source = "registry+https://github.com/rust-lang/crates.io-index" 1393source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1480,31 +1445,16 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1480 1445
1481[[package]] 1446[[package]]
1482name = "semver" 1447name = "semver"
1483version = "0.9.0"
1484source = "registry+https://github.com/rust-lang/crates.io-index"
1485checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1486dependencies = [
1487 "semver-parser 0.7.0",
1488]
1489
1490[[package]]
1491name = "semver"
1492version = "0.11.0" 1448version = "0.11.0"
1493source = "registry+https://github.com/rust-lang/crates.io-index" 1449source = "registry+https://github.com/rust-lang/crates.io-index"
1494checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 1450checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
1495dependencies = [ 1451dependencies = [
1496 "semver-parser 0.10.1", 1452 "semver-parser",
1497 "serde", 1453 "serde",
1498] 1454]
1499 1455
1500[[package]] 1456[[package]]
1501name = "semver-parser" 1457name = "semver-parser"
1502version = "0.7.0"
1503source = "registry+https://github.com/rust-lang/crates.io-index"
1504checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1505
1506[[package]]
1507name = "semver-parser"
1508version = "0.10.1" 1458version = "0.10.1"
1509source = "registry+https://github.com/rust-lang/crates.io-index" 1459source = "registry+https://github.com/rust-lang/crates.io-index"
1510checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" 1460checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428"
@@ -1566,12 +1516,11 @@ dependencies = [
1566 1516
1567[[package]] 1517[[package]]
1568name = "sharded-slab" 1518name = "sharded-slab"
1569version = "0.1.0" 1519version = "0.1.1"
1570source = "registry+https://github.com/rust-lang/crates.io-index" 1520source = "registry+https://github.com/rust-lang/crates.io-index"
1571checksum = "7b4921be914e16899a80adefb821f8ddb7974e3f1250223575a44ed994882127" 1521checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3"
1572dependencies = [ 1522dependencies = [
1573 "lazy_static", 1523 "lazy_static",
1574 "loom",
1575] 1524]
1576 1525
1577[[package]] 1526[[package]]
@@ -1582,9 +1531,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1582 1531
1583[[package]] 1532[[package]]
1584name = "smallvec" 1533name = "smallvec"
1585version = "1.5.1" 1534version = "1.6.0"
1586source = "registry+https://github.com/rust-lang/crates.io-index" 1535source = "registry+https://github.com/rust-lang/crates.io-index"
1587checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" 1536checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
1588 1537
1589[[package]] 1538[[package]]
1590name = "smol_str" 1539name = "smol_str"
@@ -1615,9 +1564,9 @@ version = "0.0.0"
1615 1564
1616[[package]] 1565[[package]]
1617name = "syn" 1566name = "syn"
1618version = "1.0.56" 1567version = "1.0.57"
1619source = "registry+https://github.com/rust-lang/crates.io-index" 1568source = "registry+https://github.com/rust-lang/crates.io-index"
1620checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" 1569checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
1621dependencies = [ 1570dependencies = [
1622 "proc-macro2", 1571 "proc-macro2",
1623 "quote", 1572 "quote",
@@ -1826,15 +1775,16 @@ dependencies = [
1826 1775
1827[[package]] 1776[[package]]
1828name = "tracing-tree" 1777name = "tracing-tree"
1829version = "0.1.6" 1778version = "0.1.7"
1830source = "registry+https://github.com/rust-lang/crates.io-index" 1779source = "registry+https://github.com/rust-lang/crates.io-index"
1831checksum = "43aac8afb493b08e1e1904956f7407c1e671b9c83b26a17e1bd83d6a3520e350" 1780checksum = "023e80cdb7c8468b7aade1d756afa2acbe2ae0a6142a25ec664b5239d6ef2794"
1832dependencies = [ 1781dependencies = [
1833 "ansi_term", 1782 "ansi_term",
1834 "atty", 1783 "atty",
1835 "chrono", 1784 "chrono",
1836 "termcolor", 1785 "termcolor",
1837 "tracing", 1786 "tracing",
1787 "tracing-log",
1838 "tracing-subscriber", 1788 "tracing-subscriber",
1839] 1789]
1840 1790
@@ -1854,9 +1804,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1854 1804
1855[[package]] 1805[[package]]
1856name = "ungrammar" 1806name = "ungrammar"
1857version = "1.5.0" 1807version = "1.6.0"
1858source = "registry+https://github.com/rust-lang/crates.io-index" 1808source = "registry+https://github.com/rust-lang/crates.io-index"
1859checksum = "c11bffada52edc8f2a56160b286ea4640acf90ffcb21bded361ccb8ed43a1457" 1809checksum = "f96cc1b6938f7c548fbcc630bac5c896ae77a130909829ab18b8eab78c51b7ee"
1860 1810
1861[[package]] 1811[[package]]
1862name = "unicase" 1812name = "unicase"
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs
index 7bcc318a9..2c48f32bf 100644
--- a/crates/assists/src/handlers/early_return.rs
+++ b/crates/assists/src/handlers/early_return.rs
@@ -69,7 +69,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
69 69
70 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; 70 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
71 71
72 if parent_block.expr()? != if_expr.clone().into() { 72 if parent_block.tail_expr()? != if_expr.clone().into() {
73 return None; 73 return None;
74 } 74 }
75 75
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 030b9cd0c..6f35a061c 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -117,10 +117,14 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
117 .into_iter() 117 .into_iter()
118 .filter(|(_, def)| match def { 118 .filter(|(_, def)| match def {
119 // only check type-namespace 119 // only check type-namespace
120 hir::ScopeDef::ModuleDef(def) => matches!(def, 120 hir::ScopeDef::ModuleDef(def) => matches!(
121 ModuleDef::Module(_) | ModuleDef::Adt(_) | 121 def,
122 ModuleDef::Variant(_) | ModuleDef::Trait(_) | 122 ModuleDef::Module(_)
123 ModuleDef::TypeAlias(_) | ModuleDef::BuiltinType(_) 123 | ModuleDef::Adt(_)
124 | ModuleDef::Variant(_)
125 | ModuleDef::Trait(_)
126 | ModuleDef::TypeAlias(_)
127 | ModuleDef::BuiltinType(_)
124 ), 128 ),
125 _ => false, 129 _ => false,
126 }) 130 })
diff --git a/crates/assists/src/handlers/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs
index 9957012fe..291809205 100644
--- a/crates/assists/src/handlers/extract_variable.rs
+++ b/crates/assists/src/handlers/extract_variable.rs
@@ -139,7 +139,7 @@ impl Anchor {
139 fn from(to_extract: &ast::Expr) -> Option<Anchor> { 139 fn from(to_extract: &ast::Expr) -> Option<Anchor> {
140 to_extract.syntax().ancestors().find_map(|node| { 140 to_extract.syntax().ancestors().find_map(|node| {
141 if let Some(expr) = 141 if let Some(expr) =
142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) 142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr())
143 { 143 {
144 if expr.syntax() == &node { 144 if expr.syntax() == &node {
145 mark::hit!(test_extract_var_last_expr); 145 mark::hit!(test_extract_var_last_expr);
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs
index aa584eb03..f499cdfdc 100644
--- a/crates/assists/src/handlers/infer_function_return_type.rs
+++ b/crates/assists/src/handlers/infer_function_return_type.rs
@@ -89,7 +89,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
89 let body = closure.body()?; 89 let body = closure.body()?;
90 let body_start = body.syntax().first_token()?.text_range().start(); 90 let body_start = body.syntax().first_token()?.text_range().start();
91 let (tail_expr, wrap_expr) = match body { 91 let (tail_expr, wrap_expr) = match body {
92 ast::Expr::BlockExpr(block) => (block.expr()?, false), 92 ast::Expr::BlockExpr(block) => (block.tail_expr()?, false),
93 body => (body, true), 93 body => (body, true),
94 }; 94 };
95 95
@@ -101,7 +101,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
101 let action = ret_ty_to_action(func.ret_type(), rparen_pos)?; 101 let action = ret_ty_to_action(func.ret_type(), rparen_pos)?;
102 102
103 let body = func.body()?; 103 let body = func.body()?;
104 let tail_expr = body.expr()?; 104 let tail_expr = body.tail_expr()?;
105 105
106 let ret_range_end = body.l_curly_token()?.text_range().start(); 106 let ret_range_end = body.l_curly_token()?.text_range().start();
107 let ret_range = TextRange::new(rparen_pos, ret_range_end); 107 let ret_range = TextRange::new(rparen_pos, ret_range_end);
diff --git a/crates/assists/src/handlers/inline_function.rs b/crates/assists/src/handlers/inline_function.rs
new file mode 100644
index 000000000..6e351bdcd
--- /dev/null
+++ b/crates/assists/src/handlers/inline_function.rs
@@ -0,0 +1,202 @@
1use ast::make;
2use hir::{HasSource, PathResolution};
3use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode,
6};
7use test_utils::mark;
8
9use crate::{
10 assist_context::{AssistContext, Assists},
11 AssistId, AssistKind,
12};
13
14// Assist: inline_function
15//
16// Inlines a function body.
17//
18// ```
19// fn add(a: u32, b: u32) -> u32 { a + b }
20// fn main() {
21// let x = add<|>(1, 2);
22// }
23// ```
24// ->
25// ```
26// fn add(a: u32, b: u32) -> u32 { a + b }
27// fn main() {
28// let x = {
29// let a = 1;
30// let b = 2;
31// a + b
32// };
33// }
34// ```
35pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
37 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
38 let path = path_expr.path()?;
39
40 let function = match ctx.sema.resolve_path(&path)? {
41 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
42 _ => return None,
43 };
44
45 let function_source = function.source(ctx.db())?;
46 let arguments: Vec<_> = call.arg_list()?.args().collect();
47 let parameters = function_parameter_patterns(&function_source.value)?;
48
49 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None;
54 }
55
56 let new_bindings = parameters.into_iter().zip(arguments);
57
58 let body = function_source.value.body()?;
59
60 acc.add(
61 AssistId("inline_function", AssistKind::RefactorInline),
62 format!("Inline `{}`", path),
63 call.syntax().text_range(),
64 |builder| {
65 let mut statements: Vec<ast::Stmt> = Vec::new();
66
67 for (pattern, value) in new_bindings {
68 statements.push(make::let_stmt(pattern, Some(value)).into());
69 }
70
71 statements.extend(body.statements());
72
73 let original_indentation = call.indent_level();
74 let replacement = make::block_expr(statements, body.tail_expr())
75 .reset_indent()
76 .indent(original_indentation);
77
78 builder.replace_ast(ast::Expr::CallExpr(call), ast::Expr::BlockExpr(replacement));
79 },
80 )
81}
82
83fn function_parameter_patterns(value: &ast::Fn) -> Option<Vec<ast::Pat>> {
84 let mut patterns = Vec::new();
85
86 for param in value.param_list()?.params() {
87 let pattern = param.pat()?;
88 patterns.push(pattern);
89 }
90
91 Some(patterns)
92}
93
94#[cfg(test)]
95mod tests {
96 use crate::tests::{check_assist, check_assist_not_applicable};
97
98 use super::*;
99
100 #[test]
101 fn no_args_or_return_value_gets_inlined_without_block() {
102 check_assist(
103 inline_function,
104 r#"
105fn foo() { println!("Hello, World!"); }
106fn main() {
107 fo<|>o();
108}
109"#,
110 r#"
111fn foo() { println!("Hello, World!"); }
112fn main() {
113 {
114 println!("Hello, World!");
115 };
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn args_with_side_effects() {
123 check_assist(
124 inline_function,
125 r#"
126fn foo(name: String) { println!("Hello, {}!", name); }
127fn main() {
128 foo<|>(String::from("Michael"));
129}
130"#,
131 r#"
132fn foo(name: String) { println!("Hello, {}!", name); }
133fn main() {
134 {
135 let name = String::from("Michael");
136 println!("Hello, {}!", name);
137 };
138}
139"#,
140 );
141 }
142
143 #[test]
144 fn method_inlining_isnt_supported() {
145 check_assist_not_applicable(
146 inline_function,
147 r"
148struct Foo;
149impl Foo { fn bar(&self) {} }
150
151fn main() { Foo.bar<|>(); }
152",
153 );
154 }
155
156 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable(
160 inline_function,
161 r#"
162fn add(a: u32, b: u32) -> u32 { a + b }
163fn main() { let x = add<|>(42); }
164"#,
165 );
166 }
167
168 #[test]
169 fn function_with_multiple_statements() {
170 check_assist(
171 inline_function,
172 r#"
173fn foo(a: u32, b: u32) -> u32 {
174 let x = a + b;
175 let y = x - b;
176 x * y
177}
178
179fn main() {
180 let x = foo<|>(1, 2);
181}
182"#,
183 r#"
184fn foo(a: u32, b: u32) -> u32 {
185 let x = a + b;
186 let y = x - b;
187 x * y
188}
189
190fn main() {
191 let x = {
192 let a = 1;
193 let b = 2;
194 let x = a + b;
195 let y = x - b;
196 x * y
197 };
198}
199"#,
200 );
201 }
202}
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs
index eaffd80ce..4318ca6dc 100644
--- a/crates/assists/src/handlers/move_guard.rs
+++ b/crates/assists/src/handlers/move_guard.rs
@@ -98,7 +98,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
98 let mut replace_node = None; 98 let mut replace_node = None;
99 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { 99 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
100 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; 100 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
101 if let Expr::IfExpr(e) = block_expr.expr()? { 101 if let Expr::IfExpr(e) = block_expr.tail_expr()? {
102 replace_node = Some(block_expr.syntax().clone()); 102 replace_node = Some(block_expr.syntax().clone());
103 Some(e) 103 Some(e)
104 } else { 104 } else {
@@ -128,7 +128,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
128 |edit| { 128 |edit| {
129 let then_only_expr = then_block.statements().next().is_none(); 129 let then_only_expr = then_block.statements().next().is_none();
130 130
131 match &then_block.expr() { 131 match &then_block.tail_expr() {
132 Some(then_expr) if then_only_expr => { 132 Some(then_expr) if then_only_expr => {
133 edit.replace(replace_node.text_range(), then_expr.syntax().text()) 133 edit.replace(replace_node.text_range(), then_expr.syntax().text())
134 } 134 }
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/pull_assignment_up.rs
index 281cf5d24..63b662fad 100644
--- a/crates/assists/src/handlers/extract_assignment.rs
+++ b/crates/assists/src/handlers/pull_assignment_up.rs
@@ -1,4 +1,3 @@
1use hir::AsName;
2use syntax::{ 1use syntax::{
3 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, edit::AstNodeEdit, make},
4 AstNode, 3 AstNode,
@@ -10,9 +9,9 @@ use crate::{
10 AssistId, AssistKind, 9 AssistId, AssistKind,
11}; 10};
12 11
13// Assist: extract_assignment 12// Assist: pull_assignment_up
14// 13//
15// Extracts variable assigment to outside an if or match statement. 14// Extracts variable assignment to outside an if or match statement.
16// 15//
17// ``` 16// ```
18// fn main() { 17// fn main() {
@@ -37,16 +36,24 @@ use crate::{
37// }; 36// };
38// } 37// }
39// ``` 38// ```
40pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let name = ctx.find_node_at_offset::<ast::NameRef>()?.as_name(); 40 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
41 let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment {
42 assign_expr.lhs()?
43 } else {
44 return None;
45 };
42 46
43 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { 47 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
44 ( 48 (
45 ast::Expr::cast(if_expr.syntax().to_owned())?, 49 ast::Expr::cast(if_expr.syntax().to_owned())?,
46 exprify_if(&if_expr, &name)?.indent(if_expr.indent_level()), 50 exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()),
47 ) 51 )
48 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { 52 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
49 (ast::Expr::cast(match_expr.syntax().to_owned())?, exprify_match(&match_expr, &name)?) 53 (
54 ast::Expr::cast(match_expr.syntax().to_owned())?,
55 exprify_match(&match_expr, &ctx.sema, &name_expr)?,
56 )
50 } else { 57 } else {
51 return None; 58 return None;
52 }; 59 };
@@ -54,22 +61,26 @@ pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Optio
54 let expr_stmt = make::expr_stmt(new_stmt); 61 let expr_stmt = make::expr_stmt(new_stmt);
55 62
56 acc.add( 63 acc.add(
57 AssistId("extract_assignment", AssistKind::RefactorExtract), 64 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
58 "Extract assignment", 65 "Pull assignment up",
59 old_stmt.syntax().text_range(), 66 old_stmt.syntax().text_range(),
60 move |edit| { 67 move |edit| {
61 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name, expr_stmt)); 68 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt));
62 }, 69 },
63 ) 70 )
64} 71}
65 72
66fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> { 73fn exprify_match(
74 match_expr: &ast::MatchExpr,
75 sema: &hir::Semantics<ide_db::RootDatabase>,
76 name: &ast::Expr,
77) -> Option<ast::Expr> {
67 let new_arm_list = match_expr 78 let new_arm_list = match_expr
68 .match_arm_list()? 79 .match_arm_list()?
69 .arms() 80 .arms()
70 .map(|arm| { 81 .map(|arm| {
71 if let ast::Expr::BlockExpr(block) = arm.expr()? { 82 if let ast::Expr::BlockExpr(block) = arm.expr()? {
72 let new_block = exprify_block(&block, name)?.indent(block.indent_level()); 83 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level());
73 Some(arm.replace_descendant(block, new_block)) 84 Some(arm.replace_descendant(block, new_block))
74 } else { 85 } else {
75 None 86 None
@@ -82,22 +93,32 @@ fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::E
82 Some(make::expr_match(match_expr.expr()?, new_arm_list)) 93 Some(make::expr_match(match_expr.expr()?, new_arm_list))
83} 94}
84 95
85fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> { 96fn exprify_if(
86 let then_branch = exprify_block(&statement.then_branch()?, name)?; 97 statement: &ast::IfExpr,
98 sema: &hir::Semantics<ide_db::RootDatabase>,
99 name: &ast::Expr,
100) -> Option<ast::Expr> {
101 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
87 let else_branch = match statement.else_branch()? { 102 let else_branch = match statement.else_branch()? {
88 ast::ElseBranch::Block(ref block) => ast::ElseBranch::Block(exprify_block(block, name)?), 103 ast::ElseBranch::Block(ref block) => {
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 }
89 ast::ElseBranch::IfExpr(expr) => { 106 ast::ElseBranch::IfExpr(expr) => {
90 mark::hit!(test_extract_assigment_chained_if); 107 mark::hit!(test_pull_assignment_up_chained_if);
91 ast::ElseBranch::IfExpr(ast::IfExpr::cast( 108 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
92 exprify_if(&expr, name)?.syntax().to_owned(), 109 exprify_if(&expr, sema, name)?.syntax().to_owned(),
93 )?) 110 )?)
94 } 111 }
95 }; 112 };
96 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) 113 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
97} 114}
98 115
99fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> { 116fn exprify_block(
100 if block.expr().is_some() { 117 block: &ast::BlockExpr,
118 sema: &hir::Semantics<ide_db::RootDatabase>,
119 name: &ast::Expr,
120) -> Option<ast::BlockExpr> {
121 if block.tail_expr().is_some() {
101 return None; 122 return None;
102 } 123 }
103 124
@@ -106,8 +127,7 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE
106 127
107 if let ast::Stmt::ExprStmt(stmt) = stmt { 128 if let ast::Stmt::ExprStmt(stmt) = stmt {
108 if let ast::Expr::BinExpr(expr) = stmt.expr()? { 129 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
109 if expr.op_kind()? == ast::BinOp::Assignment 130 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name)
110 && &expr.lhs()?.name_ref()?.as_name() == name
111 { 131 {
112 // The last statement in the block is an assignment to the name we want 132 // The last statement in the block is an assignment to the name we want
113 return Some(make::block_expr(stmts, Some(expr.rhs()?))); 133 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
@@ -117,6 +137,29 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE
117 None 137 None
118} 138}
119 139
140fn is_equivalent(
141 sema: &hir::Semantics<ide_db::RootDatabase>,
142 expr0: &ast::Expr,
143 expr1: &ast::Expr,
144) -> bool {
145 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
151 let path0 = path0.path();
152 let path1 = path1.path();
153 if let (Some(path0), Some(path1)) = (path0, path1) {
154 sema.resolve_path(&path0) == sema.resolve_path(&path1)
155 } else {
156 false
157 }
158 }
159 _ => false,
160 }
161}
162
120#[cfg(test)] 163#[cfg(test)]
121mod tests { 164mod tests {
122 use super::*; 165 use super::*;
@@ -124,9 +167,9 @@ mod tests {
124 use crate::tests::{check_assist, check_assist_not_applicable}; 167 use crate::tests::{check_assist, check_assist_not_applicable};
125 168
126 #[test] 169 #[test]
127 fn test_extract_assignment_if() { 170 fn test_pull_assignment_up_if() {
128 check_assist( 171 check_assist(
129 extract_assigment, 172 pull_assignment_up,
130 r#" 173 r#"
131fn foo() { 174fn foo() {
132 let mut a = 1; 175 let mut a = 1;
@@ -151,9 +194,9 @@ fn foo() {
151 } 194 }
152 195
153 #[test] 196 #[test]
154 fn test_extract_assignment_match() { 197 fn test_pull_assignment_up_match() {
155 check_assist( 198 check_assist(
156 extract_assigment, 199 pull_assignment_up,
157 r#" 200 r#"
158fn foo() { 201fn foo() {
159 let mut a = 1; 202 let mut a = 1;
@@ -190,9 +233,9 @@ fn foo() {
190 } 233 }
191 234
192 #[test] 235 #[test]
193 fn test_extract_assignment_not_last_not_applicable() { 236 fn test_pull_assignment_up_not_last_not_applicable() {
194 check_assist_not_applicable( 237 check_assist_not_applicable(
195 extract_assigment, 238 pull_assignment_up,
196 r#" 239 r#"
197fn foo() { 240fn foo() {
198 let mut a = 1; 241 let mut a = 1;
@@ -208,10 +251,10 @@ fn foo() {
208 } 251 }
209 252
210 #[test] 253 #[test]
211 fn test_extract_assignment_chained_if() { 254 fn test_pull_assignment_up_chained_if() {
212 mark::check!(test_extract_assigment_chained_if); 255 mark::check!(test_pull_assignment_up_chained_if);
213 check_assist( 256 check_assist(
214 extract_assigment, 257 pull_assignment_up,
215 r#" 258 r#"
216fn foo() { 259fn foo() {
217 let mut a = 1; 260 let mut a = 1;
@@ -240,9 +283,9 @@ fn foo() {
240 } 283 }
241 284
242 #[test] 285 #[test]
243 fn test_extract_assigment_retains_stmts() { 286 fn test_pull_assignment_up_retains_stmts() {
244 check_assist( 287 check_assist(
245 extract_assigment, 288 pull_assignment_up,
246 r#" 289 r#"
247fn foo() { 290fn foo() {
248 let mut a = 1; 291 let mut a = 1;
@@ -271,9 +314,9 @@ fn foo() {
271 } 314 }
272 315
273 #[test] 316 #[test]
274 fn extract_assignment_let_stmt_not_applicable() { 317 fn pull_assignment_up_let_stmt_not_applicable() {
275 check_assist_not_applicable( 318 check_assist_not_applicable(
276 extract_assigment, 319 pull_assignment_up,
277 r#" 320 r#"
278fn foo() { 321fn foo() {
279 let mut a = 1; 322 let mut a = 1;
@@ -288,9 +331,9 @@ fn foo() {
288 } 331 }
289 332
290 #[test] 333 #[test]
291 fn extract_assignment_if_missing_assigment_not_applicable() { 334 fn pull_assignment_up_if_missing_assigment_not_applicable() {
292 check_assist_not_applicable( 335 check_assist_not_applicable(
293 extract_assigment, 336 pull_assignment_up,
294 r#" 337 r#"
295fn foo() { 338fn foo() {
296 let mut a = 1; 339 let mut a = 1;
@@ -303,9 +346,9 @@ fn foo() {
303 } 346 }
304 347
305 #[test] 348 #[test]
306 fn extract_assignment_match_missing_assigment_not_applicable() { 349 fn pull_assignment_up_match_missing_assigment_not_applicable() {
307 check_assist_not_applicable( 350 check_assist_not_applicable(
308 extract_assigment, 351 pull_assignment_up,
309 r#" 352 r#"
310fn foo() { 353fn foo() {
311 let mut a = 1; 354 let mut a = 1;
@@ -322,4 +365,36 @@ fn foo() {
322}"#, 365}"#,
323 ) 366 )
324 } 367 }
368
369 #[test]
370 fn test_pull_assignment_up_field_assignment() {
371 mark::check!(test_pull_assignment_up_field_assignment);
372 check_assist(
373 pull_assignment_up,
374 r#"
375struct A(usize);
376
377fn foo() {
378 let mut a = A(1);
379
380 if true {
381 <|>a.0 = 2;
382 } else {
383 a.0 = 3;
384 }
385}"#,
386 r#"
387struct A(usize);
388
389fn foo() {
390 let mut a = A(1);
391
392 a.0 = if true {
393 2
394 } else {
395 3
396 };
397}"#,
398 )
399 }
325} 400}
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs
index 4a355c66f..b67219222 100644
--- a/crates/assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/assists/src/handlers/replace_if_let_with_match.rs
@@ -138,7 +138,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
138 }; 138 };
139 let else_expr = match else_expr { 139 let else_expr = match else_expr {
140 ast::Expr::BlockExpr(block) 140 ast::Expr::BlockExpr(block)
141 if block.statements().count() == 0 && block.expr().is_none() => 141 if block.statements().count() == 0 && block.tail_expr().is_none() =>
142 { 142 {
143 None 143 None
144 } 144 }
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
index 59e5debb1..358b61046 100644
--- a/crates/assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs
@@ -98,7 +98,7 @@ impl TailReturnCollector {
98 } 98 }
99 99
100 // Browse tail expressions for each block 100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() { 101 if let Some(expr) = block_expr.tail_expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) { 102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs { 103 for last_expr in last_exprs {
104 let last_expr = match last_expr { 104 let last_expr = match last_expr {
@@ -170,7 +170,7 @@ impl TailReturnCollector {
170 } 170 }
171 171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) { 172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() { 173 if let Some(expr) = block.tail_expr() {
174 self.handle_exprs(&expr, true); 174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr); 175 self.fetch_tail_exprs(&expr);
176 } 176 }
@@ -206,7 +206,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
206 Expr::IfExpr(if_expr) => { 206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![]; 207 let mut nodes = vec![];
208 for block in if_expr.blocks() { 208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() { 209 if let Some(block_expr) = block.tail_expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { 210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs); 211 nodes.extend(tail_exprs);
212 } 212 }
@@ -228,7 +228,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) 228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 } 229 }
230 Expr::BlockExpr(block_expr) => { 230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) 231 block_expr.tail_expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 } 232 }
233 Expr::MatchExpr(match_expr) => { 233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?; 234 let arm_list = match_expr.match_arm_list()?;
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 212464f85..9c2a95735 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -116,7 +116,6 @@ mod handlers {
116 mod convert_integer_literal; 116 mod convert_integer_literal;
117 mod early_return; 117 mod early_return;
118 mod expand_glob_import; 118 mod expand_glob_import;
119 mod extract_assignment;
120 mod extract_module_to_file; 119 mod extract_module_to_file;
121 mod extract_struct_from_enum_variant; 120 mod extract_struct_from_enum_variant;
122 mod extract_variable; 121 mod extract_variable;
@@ -125,13 +124,14 @@ mod handlers {
125 mod flip_binexpr; 124 mod flip_binexpr;
126 mod flip_comma; 125 mod flip_comma;
127 mod flip_trait_bound; 126 mod flip_trait_bound;
128 mod generate_derive;
129 mod generate_default_from_enum_variant; 127 mod generate_default_from_enum_variant;
128 mod generate_derive;
130 mod generate_from_impl_for_enum; 129 mod generate_from_impl_for_enum;
131 mod generate_function; 130 mod generate_function;
132 mod generate_impl; 131 mod generate_impl;
133 mod generate_new; 132 mod generate_new;
134 mod infer_function_return_type; 133 mod infer_function_return_type;
134 mod inline_function;
135 mod inline_local_variable; 135 mod inline_local_variable;
136 mod introduce_named_lifetime; 136 mod introduce_named_lifetime;
137 mod invert_if; 137 mod invert_if;
@@ -139,6 +139,7 @@ mod handlers {
139 mod merge_match_arms; 139 mod merge_match_arms;
140 mod move_bounds; 140 mod move_bounds;
141 mod move_guard; 141 mod move_guard;
142 mod pull_assignment_up;
142 mod qualify_path; 143 mod qualify_path;
143 mod raw_string; 144 mod raw_string;
144 mod remove_dbg; 145 mod remove_dbg;
@@ -168,7 +169,6 @@ mod handlers {
168 convert_integer_literal::convert_integer_literal, 169 convert_integer_literal::convert_integer_literal,
169 early_return::convert_to_guarded_return, 170 early_return::convert_to_guarded_return,
170 expand_glob_import::expand_glob_import, 171 expand_glob_import::expand_glob_import,
171 extract_assignment::extract_assigment,
172 extract_module_to_file::extract_module_to_file, 172 extract_module_to_file::extract_module_to_file,
173 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 173 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
174 extract_variable::extract_variable, 174 extract_variable::extract_variable,
@@ -177,13 +177,14 @@ mod handlers {
177 flip_binexpr::flip_binexpr, 177 flip_binexpr::flip_binexpr,
178 flip_comma::flip_comma, 178 flip_comma::flip_comma,
179 flip_trait_bound::flip_trait_bound, 179 flip_trait_bound::flip_trait_bound,
180 generate_derive::generate_derive,
181 generate_default_from_enum_variant::generate_default_from_enum_variant, 180 generate_default_from_enum_variant::generate_default_from_enum_variant,
181 generate_derive::generate_derive,
182 generate_from_impl_for_enum::generate_from_impl_for_enum, 182 generate_from_impl_for_enum::generate_from_impl_for_enum,
183 generate_function::generate_function, 183 generate_function::generate_function,
184 generate_impl::generate_impl, 184 generate_impl::generate_impl,
185 generate_new::generate_new, 185 generate_new::generate_new,
186 infer_function_return_type::infer_function_return_type, 186 infer_function_return_type::infer_function_return_type,
187 inline_function::inline_function,
187 inline_local_variable::inline_local_variable, 188 inline_local_variable::inline_local_variable,
188 introduce_named_lifetime::introduce_named_lifetime, 189 introduce_named_lifetime::introduce_named_lifetime,
189 invert_if::invert_if, 190 invert_if::invert_if,
@@ -192,6 +193,7 @@ mod handlers {
192 move_bounds::move_bounds_to_where_clause, 193 move_bounds::move_bounds_to_where_clause,
193 move_guard::move_arm_cond_to_match_guard, 194 move_guard::move_arm_cond_to_match_guard,
194 move_guard::move_guard_to_arm_body, 195 move_guard::move_guard_to_arm_body,
196 pull_assignment_up::pull_assignment_up,
195 qualify_path::qualify_path, 197 qualify_path::qualify_path,
196 raw_string::add_hash, 198 raw_string::add_hash,
197 raw_string::make_usual_string, 199 raw_string::make_usual_string,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index b91a816e8..b15352cf3 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -238,35 +238,6 @@ fn qux(bar: Bar, baz: Baz) {}
238} 238}
239 239
240#[test] 240#[test]
241fn doctest_extract_assignment() {
242 check_doc_test(
243 "extract_assignment",
244 r#####"
245fn main() {
246 let mut foo = 6;
247
248 if true {
249 <|>foo = 5;
250 } else {
251 foo = 4;
252 }
253}
254"#####,
255 r#####"
256fn main() {
257 let mut foo = 6;
258
259 foo = if true {
260 5
261 } else {
262 4
263 };
264}
265"#####,
266 )
267}
268
269#[test]
270fn doctest_extract_module_to_file() { 241fn doctest_extract_module_to_file() {
271 check_doc_test( 242 check_doc_test(
272 "extract_module_to_file", 243 "extract_module_to_file",
@@ -560,6 +531,29 @@ fn foo() -> i32 { 42i32 }
560} 531}
561 532
562#[test] 533#[test]
534fn doctest_inline_function() {
535 check_doc_test(
536 "inline_function",
537 r#####"
538fn add(a: u32, b: u32) -> u32 { a + b }
539fn main() {
540 let x = add<|>(1, 2);
541}
542"#####,
543 r#####"
544fn add(a: u32, b: u32) -> u32 { a + b }
545fn main() {
546 let x = {
547 let a = 1;
548 let b = 2;
549 a + b
550 };
551}
552"#####,
553 )
554}
555
556#[test]
563fn doctest_inline_local_variable() { 557fn doctest_inline_local_variable() {
564 check_doc_test( 558 check_doc_test(
565 "inline_local_variable", 559 "inline_local_variable",
@@ -767,6 +761,35 @@ fn handle(action: Action) {
767} 761}
768 762
769#[test] 763#[test]
764fn doctest_pull_assignment_up() {
765 check_doc_test(
766 "pull_assignment_up",
767 r#####"
768fn main() {
769 let mut foo = 6;
770
771 if true {
772 <|>foo = 5;
773 } else {
774 foo = 4;
775 }
776}
777"#####,
778 r#####"
779fn main() {
780 let mut foo = 6;
781
782 foo = if true {
783 5
784 } else {
785 4
786 };
787}
788"#####,
789 )
790}
791
792#[test]
770fn doctest_qualify_path() { 793fn doctest_qualify_path() {
771 check_doc_test( 794 check_doc_test(
772 "qualify_path", 795 "qualify_path",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index b05596446..8212cd129 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -37,7 +37,7 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
37 non_trivial_children.next().is_some() 37 non_trivial_children.next().is_some()
38 }; 38 };
39 39
40 if let Some(expr) = block.expr() { 40 if let Some(expr) = block.tail_expr() {
41 if has_anything_else(expr.syntax()) { 41 if has_anything_else(expr.syntax()) {
42 return None; 42 return None;
43 } 43 }
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 81a6d00e2..2f41a3f96 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -124,8 +124,8 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
125// capability enabled. 125// capability enabled.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
127 let _p = profile::span("fuzzy_completion");
128 let potential_import_name = ctx.token.to_string(); 127 let potential_import_name = ctx.token.to_string();
128 let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone());
129 129
130 if potential_import_name.len() < 2 { 130 if potential_import_name.len() < 2 {
131 return None; 131 return None;
@@ -142,6 +142,7 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
142 Some(40), 142 Some(40),
143 potential_import_name, 143 potential_import_name,
144 true, 144 true,
145 true,
145 ) 146 )
146 .filter_map(|import_candidate| { 147 .filter_map(|import_candidate| {
147 Some(match import_candidate { 148 Some(match import_candidate {
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index 41de324d8..f979697ab 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -458,7 +458,7 @@ impl<'a> CompletionContext<'a> {
458 } 458 }
459 if let Some(block) = ast::BlockExpr::cast(node) { 459 if let Some(block) = ast::BlockExpr::cast(node) {
460 return Some( 460 return Some(
461 block.expr().map(|e| e.syntax().text_range()) 461 block.tail_expr().map(|e| e.syntax().text_range())
462 == Some(name_ref.syntax().text_range()), 462 == Some(name_ref.syntax().text_range()),
463 ); 463 );
464 } 464 }
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 62eccf475..cc1938333 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -5,9 +5,7 @@ use arrayvec::ArrayVec;
5use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 5use base_db::{CrateDisplayName, CrateId, Edition, FileId};
6use either::Either; 6use either::Either;
7use hir_def::{ 7use hir_def::{
8 adt::ReprKind, 8 adt::{ReprKind, StructKind, VariantData},
9 adt::StructKind,
10 adt::VariantData,
11 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
12 expr::{BindingAnnotation, LabelId, Pat, PatId}, 10 expr::{BindingAnnotation, LabelId, Pat, PatId},
13 import_map, 11 import_map,
@@ -31,7 +29,7 @@ use hir_expand::{
31}; 29};
32use hir_ty::{ 30use hir_ty::{
33 autoderef, 31 autoderef,
34 display::{HirDisplayError, HirFormatter}, 32 display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter},
35 method_resolution, 33 method_resolution,
36 traits::{FnTrait, Solution, SolutionVariables}, 34 traits::{FnTrait, Solution, SolutionVariables},
37 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, 35 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
@@ -745,6 +743,18 @@ impl Function {
745 db.function_data(self.id).name.clone() 743 db.function_data(self.id).name.clone()
746 } 744 }
747 745
746 /// Get this function's return type
747 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
748 let resolver = self.id.resolver(db.upcast());
749 let ret_type = &db.function_data(self.id).ret_type;
750 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
751 let environment = TraitEnvironment::lower(db, &resolver);
752 Type {
753 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
754 ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment },
755 }
756 }
757
748 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { 758 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
749 if !db.function_data(self.id).has_self_param { 759 if !db.function_data(self.id).has_self_param {
750 return None; 760 return None;
@@ -1278,6 +1288,18 @@ impl TypeParam {
1278 } 1288 }
1279 } 1289 }
1280 1290
1291 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1292 db.generic_predicates_for_param(self.id)
1293 .into_iter()
1294 .filter_map(|pred| match &pred.value {
1295 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1296 Some(Trait::from(trait_ref.trait_))
1297 }
1298 _ => None,
1299 })
1300 .collect()
1301 }
1302
1281 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { 1303 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1282 let params = db.generic_defaults(self.id.parent); 1304 let params = db.generic_defaults(self.id.parent);
1283 let local_idx = hir_ty::param_idx(db, self.id)?; 1305 let local_idx = hir_ty::param_idx(db, self.id)?;
@@ -1293,6 +1315,20 @@ impl TypeParam {
1293 } 1315 }
1294} 1316}
1295 1317
1318impl HirDisplay for TypeParam {
1319 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1320 write!(f, "{}", self.name(f.db))?;
1321 let bounds = f.db.generic_predicates_for_param(self.id);
1322 let substs = Substs::type_params(f.db, self.id.parent);
1323 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1324 if !(predicates.is_empty() || f.omit_verbose_types()) {
1325 write!(f, ": ")?;
1326 write_bounds_like_dyn_trait(&predicates, f)?;
1327 }
1328 Ok(())
1329 }
1330}
1331
1296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1332#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1297pub struct LifetimeParam { 1333pub struct LifetimeParam {
1298 pub(crate) id: LifetimeParamId, 1334 pub(crate) id: LifetimeParamId,
@@ -1331,6 +1367,12 @@ impl ConstParam {
1331 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { 1367 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1332 self.id.parent.into() 1368 self.id.parent.into()
1333 } 1369 }
1370
1371 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1372 let def = self.id.parent;
1373 let krate = def.module(db.upcast()).krate;
1374 Type::new(db, krate, def, db.const_param_ty(self.id))
1375 }
1334} 1376}
1335 1377
1336#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1378#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -1600,9 +1642,10 @@ impl Type {
1600 } 1642 }
1601 1643
1602 pub fn is_fn(&self) -> bool { 1644 pub fn is_fn(&self) -> bool {
1603 matches!(&self.ty.value, 1645 matches!(
1604 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) | 1646 &self.ty.value,
1605 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. }) 1647 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. })
1648 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. })
1606 ) 1649 )
1607 } 1650 }
1608 1651
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 6b79e7bad..9e6426b31 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -2,6 +2,7 @@
2 2
3use std::{ops, sync::Arc}; 3use std::{ops, sync::Arc};
4 4
5use arena::map::ArenaMap;
5use base_db::CrateId; 6use base_db::CrateId;
6use cfg::{CfgExpr, CfgOptions}; 7use cfg::{CfgExpr, CfgOptions};
7use either::Either; 8use either::Either;
@@ -21,7 +22,8 @@ use crate::{
21 nameres::ModuleSource, 22 nameres::ModuleSource,
22 path::{ModPath, PathKind}, 23 path::{ModPath, PathKind},
23 src::HasChildSource, 24 src::HasChildSource,
24 AdtId, AttrDefId, GenericParamId, Lookup, 25 AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup,
26 VariantId,
25}; 27};
26 28
27/// Holds documentation 29/// Holds documentation
@@ -210,16 +212,10 @@ impl Attrs {
210 } 212 }
211 } 213 }
212 AttrDefId::FieldId(it) => { 214 AttrDefId::FieldId(it) => {
213 let src = it.parent.child_source(db); 215 return db.fields_attrs(it.parent)[it.local_id].clone();
214 match &src.value[it.local_id] {
215 Either::Left(_tuple) => RawAttrs::default(),
216 Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)),
217 }
218 } 216 }
219 AttrDefId::EnumVariantId(var_id) => { 217 AttrDefId::EnumVariantId(it) => {
220 let src = var_id.parent.child_source(db); 218 return db.variants_attrs(it.parent)[it.local_id].clone();
221 let src = src.as_ref().map(|it| &it[var_id.local_id]);
222 RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
223 } 219 }
224 AttrDefId::AdtId(it) => match it { 220 AttrDefId::AdtId(it) => match it {
225 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), 221 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
@@ -259,6 +255,46 @@ impl Attrs {
259 raw_attrs.filter(db, def.krate(db)) 255 raw_attrs.filter(db, def.krate(db))
260 } 256 }
261 257
258 pub(crate) fn variants_attrs_query(
259 db: &dyn DefDatabase,
260 e: EnumId,
261 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
262 let krate = e.lookup(db).container.module(db).krate;
263 let src = e.child_source(db);
264 let mut res = ArenaMap::default();
265
266 for (id, var) in src.value.iter() {
267 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner))
268 .filter(db, krate);
269
270 res.insert(id, attrs)
271 }
272
273 Arc::new(res)
274 }
275
276 pub(crate) fn fields_attrs_query(
277 db: &dyn DefDatabase,
278 v: VariantId,
279 ) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
280 let krate = v.module(db).krate;
281 let src = v.child_source(db);
282 let mut res = ArenaMap::default();
283
284 for (id, fld) in src.value.iter() {
285 let attrs = match fld {
286 Either::Left(_tuple) => Attrs::default(),
287 Either::Right(record) => {
288 RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
289 }
290 };
291
292 res.insert(id, attrs);
293 }
294
295 Arc::new(res)
296 }
297
262 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 298 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
263 AttrQuery { attrs: self, key } 299 AttrQuery { attrs: self, key }
264 } 300 }
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 17c72779b..6be1eaade 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -695,7 +695,7 @@ impl ExprCollector<'_> {
695 self.collect_stmts_items(block.statements()); 695 self.collect_stmts_items(block.statements());
696 let statements = 696 let statements =
697 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); 697 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
698 let tail = block.expr().map(|e| self.collect_expr(e)); 698 let tail = block.tail_expr().map(|e| self.collect_expr(e));
699 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) 699 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
700 } 700 }
701 701
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index d1a459066..d3bf5b34c 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -1,6 +1,7 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use arena::map::ArenaMap;
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 6use hir_expand::{db::AstDatabase, HirFileId};
6use syntax::SmolStr; 7use syntax::SmolStr;
@@ -16,8 +17,8 @@ use crate::{
16 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
17 nameres::CrateDefMap, 18 nameres::CrateDefMap,
18 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
19 GenericDefId, ImplId, ImplLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, 20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId,
20 TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
21}; 22};
22 23
23#[salsa::query_group(InternDatabaseStorage)] 24#[salsa::query_group(InternDatabaseStorage)]
@@ -92,6 +93,12 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
92 #[salsa::invoke(GenericParams::generic_params_query)] 93 #[salsa::invoke(GenericParams::generic_params_query)]
93 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; 94 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>;
94 95
96 #[salsa::invoke(Attrs::variants_attrs_query)]
97 fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
98
99 #[salsa::invoke(Attrs::fields_attrs_query)]
100 fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
101
95 #[salsa::invoke(Attrs::attrs_query)] 102 #[salsa::invoke(Attrs::attrs_query)]
96 fn attrs(&self, def: AttrDefId) -> Attrs; 103 fn attrs(&self, def: AttrDefId) -> Attrs;
97 104
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index c71266dc0..ab3f059ce 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -133,6 +133,10 @@ impl Diagnostic for InactiveCode {
133// This diagnostic is shown when a procedural macro can not be found. This usually means that 133// This diagnostic is shown when a procedural macro can not be found. This usually means that
134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error), 134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
135// but can also indicate project setup problems. 135// but can also indicate project setup problems.
136//
137// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
138// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
139// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
136#[derive(Debug, Clone, Eq, PartialEq)] 140#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct UnresolvedProcMacro { 141pub struct UnresolvedProcMacro {
138 pub file: HirFileId, 142 pub file: HirFileId,
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 30b22f51d..e5368b293 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -7,9 +7,8 @@ use fst::{self, Streamer};
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use smallvec::SmallVec; 11use test_utils::mark;
12use syntax::SmolStr;
13 12
14use crate::{ 13use crate::{
15 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, 14 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@@ -25,6 +24,8 @@ pub struct ImportInfo {
25 pub path: ImportPath, 24 pub path: ImportPath,
26 /// The module containing this item. 25 /// The module containing this item.
27 pub container: ModuleId, 26 pub container: ModuleId,
27 /// Whether the import is a trait associated item or not.
28 pub is_trait_assoc_item: bool,
28} 29}
29 30
30#[derive(Debug, Clone, Eq, PartialEq)] 31#[derive(Debug, Clone, Eq, PartialEq)]
@@ -64,10 +65,6 @@ pub struct ImportMap {
64 /// the index of the first one. 65 /// the index of the first one.
65 importables: Vec<ItemInNs>, 66 importables: Vec<ItemInNs>,
66 fst: fst::Map<Vec<u8>>, 67 fst: fst::Map<Vec<u8>>,
67
68 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
69 /// exported.
70 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
71} 68}
72 69
73impl ImportMap { 70impl ImportMap {
@@ -108,14 +105,27 @@ impl ImportMap {
108 105
109 for item in per_ns.iter_items() { 106 for item in per_ns.iter_items() {
110 let path = mk_path(); 107 let path = mk_path();
108 let path_len = path.len();
109 let import_info =
110 ImportInfo { path, container: module, is_trait_assoc_item: false };
111
112 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
113 import_map.collect_trait_assoc_items(
114 db,
115 tr,
116 matches!(item, ItemInNs::Types(_)),
117 &import_info,
118 );
119 }
120
111 match import_map.map.entry(item) { 121 match import_map.map.entry(item) {
112 Entry::Vacant(entry) => { 122 Entry::Vacant(entry) => {
113 entry.insert(ImportInfo { path, container: module }); 123 entry.insert(import_info);
114 } 124 }
115 Entry::Occupied(mut entry) => { 125 Entry::Occupied(mut entry) => {
116 // If the new path is shorter, prefer that one. 126 // If the new path is shorter, prefer that one.
117 if path.len() < entry.get().path.len() { 127 if path_len < entry.get().path.len() {
118 *entry.get_mut() = ImportInfo { path, container: module }; 128 *entry.get_mut() = import_info;
119 } else { 129 } else {
120 continue; 130 continue;
121 } 131 }
@@ -128,11 +138,6 @@ impl ImportMap {
128 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { 138 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
129 worklist.push((mod_id, mk_path())); 139 worklist.push((mod_id, mk_path()));
130 } 140 }
131
132 // If we've added a path to a trait, add the trait's methods to the method map.
133 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
134 import_map.collect_trait_methods(db, tr);
135 }
136 } 141 }
137 } 142 }
138 } 143 }
@@ -153,12 +158,10 @@ impl ImportMap {
153 } 158 }
154 } 159 }
155 160
156 let start = last_batch_start; 161 let key = fst_path(&importables[last_batch_start].1.path);
157 last_batch_start = idx + 1; 162 builder.insert(key, last_batch_start as u64).unwrap();
158
159 let key = fst_path(&importables[start].1.path);
160 163
161 builder.insert(key, start as u64).unwrap(); 164 last_batch_start = idx + 1;
162 } 165 }
163 166
164 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); 167 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
@@ -176,10 +179,34 @@ impl ImportMap {
176 self.map.get(&item) 179 self.map.get(&item)
177 } 180 }
178 181
179 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { 182 fn collect_trait_assoc_items(
180 let data = db.trait_data(tr); 183 &mut self,
181 for (name, item) in data.items.iter() { 184 db: &dyn DefDatabase,
182 self.assoc_map.entry(name.to_string().into()).or_default().push(*item); 185 tr: TraitId,
186 is_type_in_ns: bool,
187 original_import_info: &ImportInfo,
188 ) {
189 for (assoc_item_name, item) in &db.trait_data(tr).items {
190 let module_def_id = match item {
191 AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
192 AssocItemId::ConstId(c) => ModuleDefId::from(*c),
193 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
194 // qualifier, ergo no need to store it for imports in import_map
195 AssocItemId::TypeAliasId(_) => {
196 mark::hit!(type_aliases_ignored);
197 continue;
198 }
199 };
200 let assoc_item = if is_type_in_ns {
201 ItemInNs::Types(module_def_id)
202 } else {
203 ItemInNs::Values(module_def_id)
204 };
205
206 let mut assoc_item_info = original_import_info.clone();
207 assoc_item_info.path.segments.push(assoc_item_name.to_owned());
208 assoc_item_info.is_trait_assoc_item = true;
209 self.map.insert(assoc_item, assoc_item_info);
183 } 210 }
184 } 211 }
185} 212}
@@ -302,38 +329,38 @@ impl Query {
302 self.exclude_import_kinds.insert(import_kind); 329 self.exclude_import_kinds.insert(import_kind);
303 self 330 self
304 } 331 }
305}
306 332
307fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { 333 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
308 let mut input = if query.name_only { 334 let mut input = if import.is_trait_assoc_item || self.name_only {
309 input_path.segments.last().unwrap().to_string() 335 import.path.segments.last().unwrap().to_string()
310 } else { 336 } else {
311 input_path.to_string() 337 import.path.to_string()
312 }; 338 };
313 if enforce_lowercase || !query.case_sensitive { 339 if enforce_lowercase || !self.case_sensitive {
314 input.make_ascii_lowercase(); 340 input.make_ascii_lowercase();
315 } 341 }
316 342
317 let query_string = 343 let query_string =
318 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; 344 if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased };
319 345
320 match query.search_mode { 346 match self.search_mode {
321 SearchMode::Equals => &input == query_string, 347 SearchMode::Equals => &input == query_string,
322 SearchMode::Contains => input.contains(query_string), 348 SearchMode::Contains => input.contains(query_string),
323 SearchMode::Fuzzy => { 349 SearchMode::Fuzzy => {
324 let mut unchecked_query_chars = query_string.chars(); 350 let mut unchecked_query_chars = query_string.chars();
325 let mut mismatching_query_char = unchecked_query_chars.next(); 351 let mut mismatching_query_char = unchecked_query_chars.next();
326 352
327 for input_char in input.chars() { 353 for input_char in input.chars() {
328 match mismatching_query_char { 354 match mismatching_query_char {
329 None => return true, 355 None => return true,
330 Some(matching_query_char) if matching_query_char == input_char => { 356 Some(matching_query_char) if matching_query_char == input_char => {
331 mismatching_query_char = unchecked_query_chars.next(); 357 mismatching_query_char = unchecked_query_chars.next();
358 }
359 _ => (),
332 } 360 }
333 _ => (),
334 } 361 }
362 mismatching_query_char.is_none()
335 } 363 }
336 mismatching_query_char.is_none()
337 } 364 }
338 } 365 }
339} 366}
@@ -366,13 +393,13 @@ pub fn search_dependencies<'a>(
366 let import_map = &import_maps[indexed_value.index]; 393 let import_map = &import_maps[indexed_value.index];
367 let importables = &import_map.importables[indexed_value.value as usize..]; 394 let importables = &import_map.importables[indexed_value.value as usize..];
368 395
369 // Path shared by the importable items in this group. 396 let common_importable_data = &import_map.map[&importables[0]];
370 let common_importables_path = &import_map.map[&importables[0]].path; 397 if !query.import_matches(common_importable_data, true) {
371 if !contains_query(&query, common_importables_path, true) {
372 continue; 398 continue;
373 } 399 }
374 400
375 let common_importables_path_fst = fst_path(common_importables_path); 401 // Path shared by the importable items in this group.
402 let common_importables_path_fst = fst_path(&common_importable_data.path);
376 // Add the items from this `ModPath` group. Those are all subsequent items in 403 // Add the items from this `ModPath` group. Those are all subsequent items in
377 // `importables` whose paths match `path`. 404 // `importables` whose paths match `path`.
378 let iter = importables 405 let iter = importables
@@ -387,7 +414,7 @@ pub fn search_dependencies<'a>(
387 }) 414 })
388 .filter(|item| { 415 .filter(|item| {
389 !query.case_sensitive // we've already checked the common importables path case-insensitively 416 !query.case_sensitive // we've already checked the common importables path case-insensitively
390 || contains_query(&query, &import_map.map[item].path, false) 417 || query.import_matches(&import_map.map[item], false)
391 }); 418 });
392 res.extend(iter); 419 res.extend(iter);
393 420
@@ -398,19 +425,6 @@ pub fn search_dependencies<'a>(
398 } 425 }
399 } 426 }
400 427
401 // Add all exported associated items whose names match the query (exactly).
402 for map in &import_maps {
403 if let Some(v) = map.assoc_map.get(&*query.query) {
404 res.extend(v.iter().map(|&assoc| {
405 ItemInNs::Types(match assoc {
406 AssocItemId::FunctionId(it) => it.into(),
407 AssocItemId::ConstId(it) => it.into(),
408 AssocItemId::TypeAliasId(it) => it.into(),
409 })
410 }));
411 }
412 }
413
414 res 428 res
415} 429}
416 430
@@ -432,9 +446,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
432mod tests { 446mod tests {
433 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 447 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
434 use expect_test::{expect, Expect}; 448 use expect_test::{expect, Expect};
435 use stdx::format_to; 449 use test_utils::mark;
436 450
437 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; 451 use crate::{test_db::TestDB, AssocContainerId, Lookup};
438 452
439 use super::*; 453 use super::*;
440 454
@@ -451,46 +465,66 @@ mod tests {
451 465
452 let actual = search_dependencies(db.upcast(), krate, query) 466 let actual = search_dependencies(db.upcast(), krate, query)
453 .into_iter() 467 .into_iter()
454 .filter_map(|item| { 468 .filter_map(|dependency| {
455 let mark = match item { 469 let dependency_krate = dependency.krate(db.upcast())?;
456 ItemInNs::Types(ModuleDefId::FunctionId(_)) 470 let dependency_imports = db.import_map(dependency_krate);
457 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", 471
458 ItemInNs::Types(_) => "t", 472 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
459 ItemInNs::Values(_) => "v", 473 Some(assoc_item_path) => (assoc_item_path, "a"),
460 ItemInNs::Macros(_) => "m", 474 None => (
475 dependency_imports.path_of(dependency)?.to_string(),
476 match dependency {
477 ItemInNs::Types(ModuleDefId::FunctionId(_))
478 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
479 ItemInNs::Types(_) => "t",
480 ItemInNs::Values(_) => "v",
481 ItemInNs::Macros(_) => "m",
482 },
483 ),
461 }; 484 };
462 item.krate(db.upcast()).map(|krate| { 485
463 let map = db.import_map(krate); 486 Some(format!(
464 487 "{}::{} ({})\n",
465 let path = match assoc_to_trait(&db, item) { 488 crate_graph[dependency_krate].display_name.as_ref()?,
466 Some(trait_) => { 489 path,
467 let mut full_path = map.path_of(trait_).unwrap().to_string(); 490 mark
468 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) 491 ))
469 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
470 {
471 format_to!(
472 full_path,
473 "::{}",
474 FunctionData::fn_data_query(&db, function_id).name,
475 );
476 }
477 full_path
478 }
479 None => map.path_of(item).unwrap().to_string(),
480 };
481
482 format!(
483 "{}::{} ({})\n",
484 crate_graph[krate].display_name.as_ref().unwrap(),
485 path,
486 mark
487 )
488 })
489 }) 492 })
490 .collect::<String>(); 493 .collect::<String>();
491 expect.assert_eq(&actual) 494 expect.assert_eq(&actual)
492 } 495 }
493 496
497 fn assoc_item_path(
498 db: &dyn DefDatabase,
499 dependency_imports: &ImportMap,
500 dependency: ItemInNs,
501 ) -> Option<String> {
502 let dependency_assoc_item_id = match dependency {
503 ItemInNs::Types(ModuleDefId::FunctionId(id))
504 | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id),
505 ItemInNs::Types(ModuleDefId::ConstId(id))
506 | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id),
507 ItemInNs::Types(ModuleDefId::TypeAliasId(id))
508 | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id),
509 _ => return None,
510 };
511
512 let trait_ = assoc_to_trait(db, dependency)?;
513 if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? {
514 let trait_data = db.trait_data(tr);
515 let assoc_item_name =
516 trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| {
517 if &dependency_assoc_item_id == assoc_item_id {
518 Some(assoc_item_name)
519 } else {
520 None
521 }
522 })?;
523 return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name));
524 }
525 None
526 }
527
494 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { 528 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
495 let assoc: AssocItemId = match item { 529 let assoc: AssocItemId = match item {
496 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 530 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
@@ -749,6 +783,37 @@ mod tests {
749 } 783 }
750 784
751 #[test] 785 #[test]
786 fn fuzzy_import_trait_and_assoc_items() {
787 mark::check!(type_aliases_ignored);
788 let ra_fixture = r#"
789 //- /main.rs crate:main deps:dep
790 //- /dep.rs crate:dep
791 pub mod fmt {
792 pub trait Display {
793 type FmtTypeAlias;
794 const FMT_CONST: bool;
795
796 fn format_function();
797 fn format_method(&self);
798 }
799 }
800 "#;
801
802 check_search(
803 ra_fixture,
804 "main",
805 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
806 expect![[r#"
807 dep::fmt (t)
808 dep::fmt::Display (t)
809 dep::fmt::Display::FMT_CONST (a)
810 dep::fmt::Display::format_function (a)
811 dep::fmt::Display::format_method (a)
812 "#]],
813 );
814 }
815
816 #[test]
752 fn search_mode() { 817 fn search_mode() {
753 let ra_fixture = r#" 818 let ra_fixture = r#"
754 //- /main.rs crate:main deps:dep 819 //- /main.rs crate:main deps:dep
@@ -784,8 +849,8 @@ mod tests {
784 dep::Fmt (v) 849 dep::Fmt (v)
785 dep::Fmt (m) 850 dep::Fmt (m)
786 dep::fmt::Display (t) 851 dep::fmt::Display (t)
852 dep::fmt::Display::fmt (a)
787 dep::format (f) 853 dep::format (f)
788 dep::fmt::Display::fmt (f)
789 "#]], 854 "#]],
790 ); 855 );
791 856
@@ -798,7 +863,7 @@ mod tests {
798 dep::Fmt (t) 863 dep::Fmt (t)
799 dep::Fmt (v) 864 dep::Fmt (v)
800 dep::Fmt (m) 865 dep::Fmt (m)
801 dep::fmt::Display::fmt (f) 866 dep::fmt::Display::fmt (a)
802 "#]], 867 "#]],
803 ); 868 );
804 869
@@ -812,7 +877,7 @@ mod tests {
812 dep::Fmt (v) 877 dep::Fmt (v)
813 dep::Fmt (m) 878 dep::Fmt (m)
814 dep::fmt::Display (t) 879 dep::fmt::Display (t)
815 dep::fmt::Display::fmt (f) 880 dep::fmt::Display::fmt (a)
816 "#]], 881 "#]],
817 ); 882 );
818 } 883 }
@@ -853,7 +918,7 @@ mod tests {
853 dep::Fmt (v) 918 dep::Fmt (v)
854 dep::Fmt (m) 919 dep::Fmt (m)
855 dep::fmt::Display (t) 920 dep::fmt::Display (t)
856 dep::fmt::Display::fmt (f) 921 dep::fmt::Display::fmt (a)
857 "#]], 922 "#]],
858 ); 923 );
859 924
@@ -866,7 +931,7 @@ mod tests {
866 dep::Fmt (t) 931 dep::Fmt (t)
867 dep::Fmt (v) 932 dep::Fmt (v)
868 dep::Fmt (m) 933 dep::Fmt (m)
869 dep::fmt::Display::fmt (f) 934 dep::fmt::Display::fmt (a)
870 "#]], 935 "#]],
871 ); 936 );
872 } 937 }
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 62ab3b2bd..2750e1c91 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -10,10 +10,9 @@ use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use test_utils::mark; 11use test_utils::mark;
12 12
13use crate::ModuleId;
14use crate::{ 13use crate::{
15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
16 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, 15 LocalModuleId, Lookup, MacroDefId, ModuleDefId, ModuleId, TraitId,
17}; 16};
18 17
19#[derive(Copy, Clone)] 18#[derive(Copy, Clone)]
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index a636ec77d..77017e4ea 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,7 +13,7 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 16 HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
@@ -860,6 +860,37 @@ impl DefCollector<'_> {
860 } 860 }
861 861
862 fn finish(mut self) -> CrateDefMap { 862 fn finish(mut self) -> CrateDefMap {
863 // Emit diagnostics for all remaining unexpanded macros.
864
865 for directive in &self.unexpanded_macros {
866 let mut error = None;
867 directive.ast_id.as_call_id_with_errors(
868 self.db,
869 self.def_map.krate,
870 |path| {
871 let resolved_res = self.def_map.resolve_path_fp_with_macro(
872 self.db,
873 ResolveMode::Other,
874 directive.module_id,
875 &path,
876 BuiltinShadowMode::Module,
877 );
878 resolved_res.resolved_def.take_macros()
879 },
880 &mut |e| {
881 error.get_or_insert(e);
882 },
883 );
884
885 if let Some(err) = error {
886 self.def_map.diagnostics.push(DefDiagnostic::macro_error(
887 directive.module_id,
888 MacroCallKind::FnLike(directive.ast_id.ast_id),
889 err.to_string(),
890 ));
891 }
892 }
893
863 // Emit diagnostics for all remaining unresolved imports. 894 // Emit diagnostics for all remaining unresolved imports.
864 895
865 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't 896 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7c77f6ce0..1923daca5 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -58,7 +58,7 @@ impl ProcMacroExpander {
58} 58}
59 59
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool { 60fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
61 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() { 61 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
62 if punct.char == c { 62 if punct.char == c {
63 *cursor = cursor.bump(); 63 *cursor = cursor.bump();
64 return true; 64 return true;
@@ -68,7 +68,7 @@ fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
68} 68}
69 69
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { 70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
71 if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() { 71 if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
72 if Some(kind) == subtree.delimiter_kind() { 72 if Some(kind) == subtree.delimiter_kind() {
73 *cursor = cursor.bump_subtree(); 73 *cursor = cursor.bump_subtree();
74 return true; 74 return true;
@@ -78,7 +78,7 @@ fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
78} 78}
79 79
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { 80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
81 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() { 81 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
82 if t == ident.text.as_str() { 82 if t == ident.text.as_str() {
83 *cursor = cursor.bump(); 83 *cursor = cursor.bump();
84 return true; 84 return true;
@@ -88,7 +88,7 @@ fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
88} 88}
89 89
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { 90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
91 let buffer = TokenBuffer::new(&tt.token_trees); 91 let buffer = TokenBuffer::from_tokens(&tt.token_trees);
92 let mut p = buffer.begin(); 92 let mut p = buffer.begin();
93 let mut result = tt::Subtree::default(); 93 let mut result = tt::Subtree::default();
94 94
@@ -106,7 +106,7 @@ fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
106 } 106 }
107 } 107 }
108 108
109 result.token_trees.push(curr.token_tree()?.clone()); 109 result.token_trees.push(curr.token_tree()?.cloned());
110 p = curr.bump(); 110 p = curr.bump();
111 } 111 }
112 112
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 3d1778590..b0a453961 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.45", default-features = false } 20chalk-solve = { version = "0.47", default-features = false }
21chalk-ir = "0.45" 21chalk-ir = "0.47"
22chalk-recursive = "0.45" 22chalk-recursive = "0.47"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 0e827a29e..e9e949c47 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -168,7 +168,7 @@ pub enum DisplayTarget {
168 168
169impl DisplayTarget { 169impl DisplayTarget {
170 fn is_source_code(&self) -> bool { 170 fn is_source_code(&self) -> bool {
171 matches!(self, Self::SourceCode {..}) 171 matches!(self, Self::SourceCode { .. })
172 } 172 }
173 fn is_test(&self) -> bool { 173 fn is_test(&self) -> bool {
174 matches!(self, Self::Test) 174 matches!(self, Self::Test)
@@ -595,7 +595,7 @@ impl HirDisplay for FnSig {
595 } 595 }
596} 596}
597 597
598fn write_bounds_like_dyn_trait( 598pub fn write_bounds_like_dyn_trait(
599 predicates: &[GenericPredicate], 599 predicates: &[GenericPredicate],
600 f: &mut HirFormatter, 600 f: &mut HirFormatter,
601) -> Result<(), HirDisplayError> { 601) -> Result<(), HirDisplayError> {
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index ea49d9f97..990f740b8 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -87,6 +87,17 @@ impl ShortLabel for ast::Variant {
87 } 87 }
88} 88}
89 89
90impl ShortLabel for ast::ConstParam {
91 fn short_label(&self) -> Option<String> {
92 let mut buf = "const ".to_owned();
93 buf.push_str(self.name()?.text().as_str());
94 if let Some(type_ref) = self.ty() {
95 format_to!(buf, ": {}", type_ref.syntax());
96 }
97 Some(buf)
98 }
99}
100
90fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String> 101fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String>
91where 102where
92 T: NameOwner + VisibilityOwner, 103 T: NameOwner + VisibilityOwner,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2737c900f..f2ad95cb6 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -70,7 +70,7 @@ impl HoverConfig {
70#[derive(Debug, Clone)] 70#[derive(Debug, Clone)]
71pub enum HoverAction { 71pub enum HoverAction {
72 Runnable(Runnable), 72 Runnable(Runnable),
73 Implementaion(FilePosition), 73 Implementation(FilePosition),
74 GoToType(Vec<HoverGotoTypeData>), 74 GoToType(Vec<HoverGotoTypeData>),
75} 75}
76 76
@@ -116,12 +116,13 @@ pub(crate) fn hover(
116 }; 116 };
117 if let Some(definition) = definition { 117 if let Some(definition) = definition {
118 if let Some(markup) = hover_for_definition(db, definition) { 118 if let Some(markup) = hover_for_definition(db, definition) {
119 let markup = markup.as_str();
119 let markup = if !markdown { 120 let markup = if !markdown {
120 remove_markdown(&markup.as_str()) 121 remove_markdown(markup)
121 } else if links_in_hover { 122 } else if links_in_hover {
122 rewrite_links(db, &markup.as_str(), &definition) 123 rewrite_links(db, markup, &definition)
123 } else { 124 } else {
124 remove_links(&markup.as_str()) 125 remove_links(markup)
125 }; 126 };
126 res.markup = Markup::from(markup); 127 res.markup = Markup::from(markup);
127 if let Some(action) = show_implementations_action(db, definition) { 128 if let Some(action) = show_implementations_action(db, definition) {
@@ -175,22 +176,24 @@ pub(crate) fn hover(
175 176
176fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 177fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
177 fn to_action(nav_target: NavigationTarget) -> HoverAction { 178 fn to_action(nav_target: NavigationTarget) -> HoverAction {
178 HoverAction::Implementaion(FilePosition { 179 HoverAction::Implementation(FilePosition {
179 file_id: nav_target.file_id, 180 file_id: nav_target.file_id,
180 offset: nav_target.focus_or_full_range().start(), 181 offset: nav_target.focus_or_full_range().start(),
181 }) 182 })
182 } 183 }
183 184
184 match def { 185 let adt = match def {
185 Definition::ModuleDef(it) => match it { 186 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
186 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)), 187 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
187 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)), 188 Definition::SelfType(it) => it.target_ty(db).as_adt(),
188 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
189 ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
190 _ => None,
191 },
192 _ => None, 189 _ => None,
190 }?;
191 match adt {
192 Adt::Struct(it) => it.try_to_nav(db),
193 Adt::Union(it) => it.try_to_nav(db),
194 Adt::Enum(it) => it.try_to_nav(db),
193 } 195 }
196 .map(to_action)
194} 197}
195 198
196fn runnable_action( 199fn runnable_action(
@@ -225,45 +228,46 @@ fn runnable_action(
225} 228}
226 229
227fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 230fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
228 match def { 231 let mut targets: Vec<ModuleDef> = Vec::new();
229 Definition::Local(it) => { 232 let mut push_new_def = |item: ModuleDef| {
230 let mut targets: Vec<ModuleDef> = Vec::new(); 233 if !targets.contains(&item) {
231 let mut push_new_def = |item: ModuleDef| { 234 targets.push(item);
232 if !targets.contains(&item) {
233 targets.push(item);
234 }
235 };
236
237 it.ty(db).walk(db, |t| {
238 if let Some(adt) = t.as_adt() {
239 push_new_def(adt.into());
240 } else if let Some(trait_) = t.as_dyn_trait() {
241 push_new_def(trait_.into());
242 } else if let Some(traits) = t.as_impl_traits(db) {
243 traits.into_iter().for_each(|it| push_new_def(it.into()));
244 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
245 push_new_def(trait_.into());
246 }
247 });
248
249 let targets = targets
250 .into_iter()
251 .filter_map(|it| {
252 Some(HoverGotoTypeData {
253 mod_path: render_path(
254 db,
255 it.module(db)?,
256 it.name(db).map(|name| name.to_string()),
257 ),
258 nav: it.try_to_nav(db)?,
259 })
260 })
261 .collect();
262
263 Some(HoverAction::GoToType(targets))
264 } 235 }
265 _ => None, 236 };
237
238 if let Definition::TypeParam(it) = def {
239 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
240 } else {
241 let ty = match def {
242 Definition::Local(it) => it.ty(db),
243 Definition::ConstParam(it) => it.ty(db),
244 _ => return None,
245 };
246
247 ty.walk(db, |t| {
248 if let Some(adt) = t.as_adt() {
249 push_new_def(adt.into());
250 } else if let Some(trait_) = t.as_dyn_trait() {
251 push_new_def(trait_.into());
252 } else if let Some(traits) = t.as_impl_traits(db) {
253 traits.into_iter().for_each(|it| push_new_def(it.into()));
254 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
255 push_new_def(trait_.into());
256 }
257 });
266 } 258 }
259
260 let targets = targets
261 .into_iter()
262 .filter_map(|it| {
263 Some(HoverGotoTypeData {
264 mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())),
265 nav: it.try_to_nav(db)?,
266 })
267 })
268 .collect();
269
270 Some(HoverAction::GoToType(targets))
267} 271}
268 272
269fn hover_markup( 273fn hover_markup(
@@ -370,10 +374,8 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 } 374 }
371 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 375 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
372 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), 376 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
373 Definition::TypeParam(_) | Definition::ConstParam(_) => { 377 Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))),
374 // FIXME: Hover for generic param 378 Definition::ConstParam(it) => from_def_source(db, it, None),
375 None
376 }
377 }; 379 };
378 380
379 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 381 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
@@ -1393,7 +1395,7 @@ fn bar() { fo<|>o(); }
1393 r"unsafe trait foo<|>() {}", 1395 r"unsafe trait foo<|>() {}",
1394 expect![[r#" 1396 expect![[r#"
1395 [ 1397 [
1396 Implementaion( 1398 Implementation(
1397 FilePosition { 1399 FilePosition {
1398 file_id: FileId( 1400 file_id: FileId(
1399 0, 1401 0,
@@ -2105,7 +2107,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2105 r#"trait foo<|>() {}"#, 2107 r#"trait foo<|>() {}"#,
2106 expect![[r#" 2108 expect![[r#"
2107 [ 2109 [
2108 Implementaion( 2110 Implementation(
2109 FilePosition { 2111 FilePosition {
2110 file_id: FileId( 2112 file_id: FileId(
2111 0, 2113 0,
@@ -2124,7 +2126,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2124 r"struct foo<|>() {}", 2126 r"struct foo<|>() {}",
2125 expect![[r#" 2127 expect![[r#"
2126 [ 2128 [
2127 Implementaion( 2129 Implementation(
2128 FilePosition { 2130 FilePosition {
2129 file_id: FileId( 2131 file_id: FileId(
2130 0, 2132 0,
@@ -2143,7 +2145,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2143 r#"union foo<|>() {}"#, 2145 r#"union foo<|>() {}"#,
2144 expect![[r#" 2146 expect![[r#"
2145 [ 2147 [
2146 Implementaion( 2148 Implementation(
2147 FilePosition { 2149 FilePosition {
2148 file_id: FileId( 2150 file_id: FileId(
2149 0, 2151 0,
@@ -2162,7 +2164,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2162 r"enum foo<|>() { A, B }", 2164 r"enum foo<|>() { A, B }",
2163 expect![[r#" 2165 expect![[r#"
2164 [ 2166 [
2165 Implementaion( 2167 Implementation(
2166 FilePosition { 2168 FilePosition {
2167 file_id: FileId( 2169 file_id: FileId(
2168 0, 2170 0,
@@ -2176,6 +2178,25 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2176 } 2178 }
2177 2179
2178 #[test] 2180 #[test]
2181 fn test_hover_self_has_impl_action() {
2182 check_actions(
2183 r#"struct foo where Self<|>:;"#,
2184 expect![[r#"
2185 [
2186 Implementation(
2187 FilePosition {
2188 file_id: FileId(
2189 0,
2190 ),
2191 offset: 7,
2192 },
2193 ),
2194 ]
2195 "#]],
2196 );
2197 }
2198
2199 #[test]
2179 fn test_hover_test_has_action() { 2200 fn test_hover_test_has_action() {
2180 check_actions( 2201 check_actions(
2181 r#" 2202 r#"
@@ -3064,6 +3085,71 @@ fn main() { let s<|>t = test().get(); }
3064 } 3085 }
3065 3086
3066 #[test] 3087 #[test]
3088 fn test_hover_const_param_has_goto_type_action() {
3089 check_actions(
3090 r#"
3091struct Bar;
3092struct Foo<const BAR: Bar>;
3093
3094impl<const BAR: Bar> Foo<BAR<|>> {}
3095"#,
3096 expect![[r#"
3097 [
3098 GoToType(
3099 [
3100 HoverGotoTypeData {
3101 mod_path: "test::Bar",
3102 nav: NavigationTarget {
3103 file_id: FileId(
3104 0,
3105 ),
3106 full_range: 0..11,
3107 focus_range: 7..10,
3108 name: "Bar",
3109 kind: Struct,
3110 description: "struct Bar",
3111 },
3112 },
3113 ],
3114 ),
3115 ]
3116 "#]],
3117 );
3118 }
3119
3120 #[test]
3121 fn test_hover_type_param_has_goto_type_action() {
3122 check_actions(
3123 r#"
3124trait Foo {}
3125
3126fn foo<T: Foo>(t: T<|>){}
3127"#,
3128 expect![[r#"
3129 [
3130 GoToType(
3131 [
3132 HoverGotoTypeData {
3133 mod_path: "test::Foo",
3134 nav: NavigationTarget {
3135 file_id: FileId(
3136 0,
3137 ),
3138 full_range: 0..12,
3139 focus_range: 6..9,
3140 name: "Foo",
3141 kind: Trait,
3142 description: "trait Foo",
3143 },
3144 },
3145 ],
3146 ),
3147 ]
3148 "#]],
3149 );
3150 }
3151
3152 #[test]
3067 fn hover_displays_normalized_crate_names() { 3153 fn hover_displays_normalized_crate_names() {
3068 check( 3154 check(
3069 r#" 3155 r#"
@@ -3257,4 +3343,68 @@ fn foo() {
3257 "#]], 3343 "#]],
3258 ); 3344 );
3259 } 3345 }
3346
3347 #[test]
3348 fn hover_type_param() {
3349 check(
3350 r#"
3351struct Foo<T>(T);
3352trait Copy {}
3353trait Clone {}
3354trait Sized {}
3355impl<T: Copy + Clone> Foo<T<|>> where T: Sized {}
3356"#,
3357 expect![[r#"
3358 *T*
3359
3360 ```rust
3361 T: Copy + Clone + Sized
3362 ```
3363 "#]],
3364 );
3365 check(
3366 r#"
3367struct Foo<T>(T);
3368impl<T> Foo<T<|>> {}
3369"#,
3370 expect![[r#"
3371 *T*
3372
3373 ```rust
3374 T
3375 ```
3376 "#]],
3377 );
3378 // lifetimes aren't being substituted yet
3379 check(
3380 r#"
3381struct Foo<T>(T);
3382impl<T: 'static> Foo<T<|>> {}
3383"#,
3384 expect![[r#"
3385 *T*
3386
3387 ```rust
3388 T: {error}
3389 ```
3390 "#]],
3391 );
3392 }
3393
3394 #[test]
3395 fn hover_const_param() {
3396 check(
3397 r#"
3398struct Foo<const LEN: usize>;
3399impl<const LEN: usize> Foo<LEN<|>> {}
3400"#,
3401 expect![[r#"
3402 *LEN*
3403
3404 ```rust
3405 const LEN: usize
3406 ```
3407 "#]],
3408 );
3409 }
3260} 3410}
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 0f4c2ca47..0782ab070 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the assists module. 1//! This module contains an import search funcionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 5use syntax::{ast, AstNode, SyntaxKind::NAME};
6 6
7use crate::{ 7use crate::{
@@ -40,8 +40,9 @@ pub fn find_similar_imports<'a>(
40 krate: Crate, 40 krate: Crate,
41 limit: Option<usize>, 41 limit: Option<usize>,
42 fuzzy_search_string: String, 42 fuzzy_search_string: String,
43 ignore_assoc_items: bool,
43 name_only: bool, 44 name_only: bool,
44) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 45) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a {
45 let _p = profile::span("find_similar_imports"); 46 let _p = profile::span("find_similar_imports");
46 47
47 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 48 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -57,7 +58,21 @@ pub fn find_similar_imports<'a>(
57 external_query = external_query.limit(limit); 58 external_query = external_query.limit(limit);
58 } 59 }
59 60
60 find_imports(sema, krate, local_query, external_query) 61 let db = sema.db;
62 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| {
63 if ignore_assoc_items {
64 match import_candidate {
65 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(),
66 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(),
67 Either::Left(ModuleDef::TypeAlias(type_alias)) => {
68 type_alias.as_assoc_item(db).is_none()
69 }
70 _ => true,
71 }
72 } else {
73 true
74 }
75 })
61} 76}
62 77
63fn find_imports<'a>( 78fn find_imports<'a>(
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index ab5f87c48..fdc8844ce 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -309,7 +309,7 @@ impl<'a> TtIter<'a> {
309 } 309 }
310 } 310 }
311 311
312 let buffer = TokenBuffer::new(&self.inner.as_slice()); 312 let buffer = TokenBuffer::from_tokens(&self.inner.as_slice());
313 let mut src = SubtreeTokenSource::new(&buffer); 313 let mut src = SubtreeTokenSource::new(&buffer);
314 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; 314 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
315 315
@@ -336,11 +336,11 @@ impl<'a> TtIter<'a> {
336 err = Some(err!("no tokens consumed")); 336 err = Some(err!("no tokens consumed"));
337 } 337 }
338 let res = match res.len() { 338 let res = match res.len() {
339 1 => Some(res[0].clone()), 339 1 => Some(res[0].cloned()),
340 0 => None, 340 0 => None,
341 _ => Some(tt::TokenTree::Subtree(tt::Subtree { 341 _ => Some(tt::TokenTree::Subtree(tt::Subtree {
342 delimiter: None, 342 delimiter: None,
343 token_trees: res.into_iter().cloned().collect(), 343 token_trees: res.into_iter().map(|it| it.cloned()).collect(),
344 })), 344 })),
345 }; 345 };
346 ExpandResult { value: res, err } 346 ExpandResult { value: res, err }
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index d10d4b70e..d7433bd35 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -1,129 +1,104 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use parser::{Token, TokenSource}; 3use parser::{Token, TokenSource};
4use std::cell::{Cell, Ref, RefCell};
5use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; 4use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T};
6use tt::buffer::{Cursor, TokenBuffer}; 5use tt::buffer::TokenBuffer;
7 6
8#[derive(Debug, Clone, Eq, PartialEq)] 7#[derive(Debug, Clone, Eq, PartialEq)]
9struct TtToken { 8struct TtToken {
10 kind: SyntaxKind, 9 tt: Token,
11 is_joint_to_next: bool,
12 text: SmolStr, 10 text: SmolStr,
13} 11}
14 12
15pub(crate) struct SubtreeTokenSource<'a> { 13pub(crate) struct SubtreeTokenSource {
16 cached_cursor: Cell<Cursor<'a>>, 14 cached: Vec<TtToken>,
17 cached: RefCell<Vec<Option<TtToken>>>,
18 curr: (Token, usize), 15 curr: (Token, usize),
19} 16}
20 17
21impl<'a> SubtreeTokenSource<'a> { 18impl<'a> SubtreeTokenSource {
22 // Helper function used in test 19 // Helper function used in test
23 #[cfg(test)] 20 #[cfg(test)]
24 pub(crate) fn text(&self) -> SmolStr { 21 pub(crate) fn text(&self) -> SmolStr {
25 match *self.get(self.curr.1) { 22 match self.cached.get(self.curr.1) {
26 Some(ref tt) => tt.text.clone(), 23 Some(ref tt) => tt.text.clone(),
27 _ => SmolStr::new(""), 24 _ => SmolStr::new(""),
28 } 25 }
29 } 26 }
30} 27}
31 28
32impl<'a> SubtreeTokenSource<'a> { 29impl<'a> SubtreeTokenSource {
33 pub(crate) fn new(buffer: &'a TokenBuffer) -> SubtreeTokenSource<'a> { 30 pub(crate) fn new(buffer: &TokenBuffer) -> SubtreeTokenSource {
34 let cursor = buffer.begin(); 31 let mut current = buffer.begin();
32 let mut cached = Vec::with_capacity(100);
35 33
36 let mut res = SubtreeTokenSource { 34 while !current.eof() {
37 curr: (Token { kind: EOF, is_jointed_to_next: false }, 0), 35 let cursor = current;
38 cached_cursor: Cell::new(cursor), 36 let tt = cursor.token_tree();
39 cached: RefCell::new(Vec::with_capacity(10)),
40 };
41 res.curr = (res.mk_token(0), 0);
42 res
43 }
44 37
45 fn mk_token(&self, pos: usize) -> Token { 38 // Check if it is lifetime
46 match *self.get(pos) { 39 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
47 Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next },
48 None => Token { kind: EOF, is_jointed_to_next: false },
49 }
50 }
51
52 fn get(&self, pos: usize) -> Ref<Option<TtToken>> {
53 fn is_lifetime(c: Cursor) -> Option<(Cursor, SmolStr)> {
54 let tkn = c.token_tree();
55
56 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tkn {
57 if punct.char == '\'' { 40 if punct.char == '\'' {
58 let next = c.bump(); 41 let next = cursor.bump();
59 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = next.token_tree() { 42 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) =
60 let res_cursor = next.bump(); 43 next.token_tree()
61 let text = SmolStr::new("'".to_string() + &ident.to_string()); 44 {
62 45 let text = SmolStr::new("'".to_string() + &ident.text);
63 return Some((res_cursor, text)); 46 cached.push(TtToken {
47 tt: Token { kind: LIFETIME_IDENT, is_jointed_to_next: false },
48 text,
49 });
50 current = next.bump();
51 continue;
64 } else { 52 } else {
65 panic!("Next token must be ident : {:#?}", next.token_tree()); 53 panic!("Next token must be ident : {:#?}", next.token_tree());
66 } 54 }
67 } 55 }
68 } 56 }
69 57
70 None 58 current = match tt {
71 } 59 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
72 60 cached.push(convert_leaf(&leaf));
73 if pos < self.cached.borrow().len() { 61 cursor.bump()
74 return Ref::map(self.cached.borrow(), |c| &c[pos]);
75 }
76
77 {
78 let mut cached = self.cached.borrow_mut();
79 while pos >= cached.len() {
80 let cursor = self.cached_cursor.get();
81 if cursor.eof() {
82 cached.push(None);
83 continue;
84 } 62 }
85 63 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
86 if let Some((curr, text)) = is_lifetime(cursor) { 64 cached.push(convert_delim(subtree.delimiter_kind(), false));
87 cached.push(Some(TtToken { 65 cursor.subtree().unwrap()
88 kind: LIFETIME_IDENT,
89 is_joint_to_next: false,
90 text,
91 }));
92 self.cached_cursor.set(curr);
93 continue;
94 } 66 }
95 67 None => {
96 match cursor.token_tree() { 68 if let Some(subtree) = cursor.end() {
97 Some(tt::TokenTree::Leaf(leaf)) => { 69 cached.push(convert_delim(subtree.delimiter_kind(), true));
98 cached.push(Some(convert_leaf(&leaf))); 70 cursor.bump()
99 self.cached_cursor.set(cursor.bump()); 71 } else {
100 } 72 continue;
101 Some(tt::TokenTree::Subtree(subtree)) => {
102 self.cached_cursor.set(cursor.subtree().unwrap());
103 cached.push(Some(convert_delim(subtree.delimiter_kind(), false)));
104 }
105 None => {
106 if let Some(subtree) = cursor.end() {
107 cached.push(Some(convert_delim(subtree.delimiter_kind(), true)));
108 self.cached_cursor.set(cursor.bump());
109 }
110 } 73 }
111 } 74 }
112 } 75 };
113 } 76 }
114 77
115 Ref::map(self.cached.borrow(), |c| &c[pos]) 78 let mut res = SubtreeTokenSource {
79 curr: (Token { kind: EOF, is_jointed_to_next: false }, 0),
80 cached,
81 };
82 res.curr = (res.token(0), 0);
83 res
84 }
85
86 fn token(&self, pos: usize) -> Token {
87 match self.cached.get(pos) {
88 Some(it) => it.tt,
89 None => Token { kind: EOF, is_jointed_to_next: false },
90 }
116 } 91 }
117} 92}
118 93
119impl<'a> TokenSource for SubtreeTokenSource<'a> { 94impl<'a> TokenSource for SubtreeTokenSource {
120 fn current(&self) -> Token { 95 fn current(&self) -> Token {
121 self.curr.0 96 self.curr.0
122 } 97 }
123 98
124 /// Lookahead n token 99 /// Lookahead n token
125 fn lookahead_nth(&self, n: usize) -> Token { 100 fn lookahead_nth(&self, n: usize) -> Token {
126 self.mk_token(self.curr.1 + n) 101 self.token(self.curr.1 + n)
127 } 102 }
128 103
129 /// bump cursor to next token 104 /// bump cursor to next token
@@ -131,13 +106,12 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> {
131 if self.current().kind == EOF { 106 if self.current().kind == EOF {
132 return; 107 return;
133 } 108 }
134 109 self.curr = (self.token(self.curr.1 + 1), self.curr.1 + 1);
135 self.curr = (self.mk_token(self.curr.1 + 1), self.curr.1 + 1);
136 } 110 }
137 111
138 /// Is the current token a specified keyword? 112 /// Is the current token a specified keyword?
139 fn is_keyword(&self, kw: &str) -> bool { 113 fn is_keyword(&self, kw: &str) -> bool {
140 match *self.get(self.curr.1) { 114 match self.cached.get(self.curr.1) {
141 Some(ref t) => t.text == *kw, 115 Some(ref t) => t.text == *kw,
142 _ => false, 116 _ => false,
143 } 117 }
@@ -155,7 +129,7 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken {
155 let idx = closing as usize; 129 let idx = closing as usize;
156 let kind = kinds[idx]; 130 let kind = kinds[idx];
157 let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; 131 let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" };
158 TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text) } 132 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: SmolStr::new(text) }
159} 133}
160 134
161fn convert_literal(l: &tt::Literal) -> TtToken { 135fn convert_literal(l: &tt::Literal) -> TtToken {
@@ -169,7 +143,7 @@ fn convert_literal(l: &tt::Literal) -> TtToken {
169 }) 143 })
170 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); 144 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
171 145
172 TtToken { kind, is_joint_to_next: false, text: l.text.clone() } 146 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: l.text.clone() }
173} 147}
174 148
175fn convert_ident(ident: &tt::Ident) -> TtToken { 149fn convert_ident(ident: &tt::Ident) -> TtToken {
@@ -180,7 +154,7 @@ fn convert_ident(ident: &tt::Ident) -> TtToken {
180 _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), 154 _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
181 }; 155 };
182 156
183 TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } 157 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: ident.text.clone() }
184} 158}
185 159
186fn convert_punct(p: tt::Punct) -> TtToken { 160fn convert_punct(p: tt::Punct) -> TtToken {
@@ -194,7 +168,7 @@ fn convert_punct(p: tt::Punct) -> TtToken {
194 let s: &str = p.char.encode_utf8(&mut buf); 168 let s: &str = p.char.encode_utf8(&mut buf);
195 SmolStr::new(s) 169 SmolStr::new(s)
196 }; 170 };
197 TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text } 171 TtToken { tt: Token { kind, is_jointed_to_next: p.spacing == tt::Spacing::Joint }, text }
198} 172}
199 173
200fn convert_leaf(leaf: &tt::Leaf) -> TtToken { 174fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
@@ -208,6 +182,7 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
208#[cfg(test)] 182#[cfg(test)]
209mod tests { 183mod tests {
210 use super::{convert_literal, TtToken}; 184 use super::{convert_literal, TtToken};
185 use parser::Token;
211 use syntax::{SmolStr, SyntaxKind}; 186 use syntax::{SmolStr, SyntaxKind};
212 187
213 #[test] 188 #[test]
@@ -218,8 +193,7 @@ mod tests {
218 text: SmolStr::new("-42.0") 193 text: SmolStr::new("-42.0")
219 }), 194 }),
220 TtToken { 195 TtToken {
221 kind: SyntaxKind::FLOAT_NUMBER, 196 tt: Token { kind: SyntaxKind::FLOAT_NUMBER, is_jointed_to_next: false },
222 is_joint_to_next: false,
223 text: SmolStr::new("-42.0") 197 text: SmolStr::new("-42.0")
224 } 198 }
225 ); 199 );
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 265c0d63d..671036e1c 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -70,15 +70,12 @@ pub fn token_tree_to_syntax_node(
70 tt: &tt::Subtree, 70 tt: &tt::Subtree,
71 fragment_kind: FragmentKind, 71 fragment_kind: FragmentKind,
72) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { 72) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> {
73 let tmp; 73 let buffer = match tt {
74 let tokens = match tt { 74 tt::Subtree { delimiter: None, token_trees } => {
75 tt::Subtree { delimiter: None, token_trees } => token_trees.as_slice(), 75 TokenBuffer::from_tokens(token_trees.as_slice())
76 _ => {
77 tmp = [tt.clone().into()];
78 &tmp[..]
79 } 76 }
77 _ => TokenBuffer::from_subtree(tt),
80 }; 78 }